Initial Solution
This commit is contained in:
Родитель
f7f385a847
Коммит
0f81231fd7
|
@ -0,0 +1,70 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<configSections>
|
||||
<section name="SourceCosmosDBSettings" type="System.Configuration.NameValueSectionHandler" />
|
||||
<section name="TargetCosmosDBSettings" type="System.Configuration.NameValueSectionHandler" />
|
||||
<section name="SampleCosmosDBSettings" type="System.Configuration.NameValueSectionHandler" />
|
||||
</configSections>
|
||||
|
||||
<SourceCosmosDBSettings>
|
||||
<add key="EndpointUrl" value="__EndPoint__" />
|
||||
<add key="AccessKey" value="__Access__" />
|
||||
<add key="DatabaseName" value="TestDB" />
|
||||
<add key="CollectionName" value="TestSuperColl1" />
|
||||
<add key="ReadDelaybetweenRequestsInMs" value="2000" />
|
||||
</SourceCosmosDBSettings>
|
||||
<TargetCosmosDBSettings>
|
||||
<add key="EndpointUrl" value="__EndPoint__" />
|
||||
<add key="AccessKey" value="__Access__" />
|
||||
<add key="DatabaseName" value="TestDB" />
|
||||
<add key="CollectionName" value="TestSuperColl1" />
|
||||
<add key="OfferThroughputRUs" value="10000" />
|
||||
</TargetCosmosDBSettings>
|
||||
|
||||
|
||||
|
||||
<SampleCosmosDBSettings>
|
||||
<add key="EndpointUrl" value="__EndPoint__" />
|
||||
<add key="AccessKey" value="__Access_" />
|
||||
<add key="ConnString" value="__connString__" />
|
||||
<add key="DatabaseName" value="TestDB" />
|
||||
<add key="IsFixedCollection" value="true" />
|
||||
<add key="CollectionName" value="Sanity1" />
|
||||
<add key="OfferThroughputRUs" value="10000" />
|
||||
</SampleCosmosDBSettings>
|
||||
|
||||
|
||||
<appSettings>
|
||||
<add key="CopyStoredProcedures" value="true" />
|
||||
<add key="CopyUDFs" value="true" />
|
||||
<add key="CopyTriggers" value="true" />
|
||||
<add key="CopyDocuments" value="true" />
|
||||
<add key="CopyIndexingPolicy" value="true" />
|
||||
<add key="ReadBatchSize" value="3500" />
|
||||
<add key="CopyPartitionKey" value="true" />
|
||||
<add key="WriteBatchSize" value="9000" />
|
||||
<add key="WriteBatchCount" value="10000" />
|
||||
<add key="EnableTextLogging" value="true" />
|
||||
<add key="PopTextFileOnEnd" value="true" />
|
||||
<add key="SourceOfferThroughputRUs" value="10000" />
|
||||
<add key="TargetMigrationOfferThroughputRUs" value="10000" />
|
||||
<add key="TargetRestOfferThroughputRUs" value="1100" />
|
||||
<add key="ClientSettingsProvider.ServiceUri" value="" />
|
||||
<add key="ScrubbingRequired" value="true" />
|
||||
</appSettings>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
|
||||
</startup>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.Azure.Documents.Client" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-1.22.0.0" newVersion="1.22.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
|
@ -0,0 +1,62 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{1F09A007-9A2E-40D8-AD24-C469473152F4}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>CloneConsoleRun</RootNamespace>
|
||||
<AssemblyName>CloneConsoleRun</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\CosmosCloneCommon\CosmosCloneCommon.csproj">
|
||||
<Project>{523e1cc4-0286-498c-b184-3413e10d61de}</Project>
|
||||
<Name>CosmosCloneCommon</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
|
@ -0,0 +1,88 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using CosmosCloneCommon;
|
||||
using CosmosCloneCommon.Migrator;
|
||||
using CosmosCloneCommon.Model;
|
||||
using CosmosCloneCommon.Sample;
|
||||
using CosmosCloneCommon.Utility;
|
||||
using logger = CosmosCloneCommon.Utility.CloneLogger;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace CloneConsoleRun
|
||||
{
|
||||
class Program
|
||||
{
|
||||
//Use the console for test of common utils before integration with the UI
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
logger.LogInfo("Begin Clone Execution");
|
||||
|
||||
//Update the app.config settings in the console project to run the below directly
|
||||
//var documentMigrator = new CosmosCloneCommon.Migrator.DocumentMigrator();
|
||||
//documentMigrator.StartCopy().Wait();
|
||||
|
||||
//logger.LogInfo("Begin Code migration");
|
||||
//var codeMigrator = new CosmosCloneCommon.Migrator.CodeMigrator();
|
||||
//codeMigrator.StartCopy().Wait();
|
||||
|
||||
//var sampleMigrator = new CosmosCloneCommon.Sample.SampleDBCreator();
|
||||
//sampleMigrator.Start().Wait();
|
||||
//documentMigrator.StartCopy().Wait();
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
//added ip address
|
||||
logger.LogError(e);
|
||||
Console.ReadKey();
|
||||
}
|
||||
finally
|
||||
{
|
||||
//logger.Close();
|
||||
logger.LogInfo("Cosmos Collection cloning complete");
|
||||
Console.ReadKey();
|
||||
Console.ReadLine();
|
||||
}
|
||||
}
|
||||
|
||||
public static void TestCosmosScrubbing()
|
||||
{
|
||||
var tcs = new DataScrubMigrator();
|
||||
// ScrubRule rule1 = new ScrubRule("c.id=\"a5e66b8b-a57c-4788-a194-58d3735a9854\"", "c.CompositeName", RuleType.Singlevalue, "Test overwritten Value xyz",1);
|
||||
//ScrubRule rule2 = new ScrubRule("", "c.SuperKeys.KeyValue", RuleType.Shuffle, "",2);
|
||||
//ScrubRule rule3 = new ScrubRule("", "c.EntityValue.Name", RuleType.Shuffle, "", 3);
|
||||
//ScrubRule rule4 = new ScrubRule("", "c.EntityValue.Description", RuleType.Singlevalue, "OverWrite Filtered rule Description", 4);
|
||||
//ScrubRule rule3 = new ScrubRule("c.id=\"1402e84d-e034-45f9-8064-3ab174119e4f\"", "c.EntityValue.Name", RuleType.Singlevalue,"Test overwritten EntityName", 3);
|
||||
var scrubRules = new List<ScrubRule>();
|
||||
//scrubRules.Add(rule2);
|
||||
//scrubRules.Add(rule3);
|
||||
//scrubRules.Add(rule4);
|
||||
//ScrubRule rule5 = new ScrubRule("c.EntityType=\"External\"", "c.EmailAddress", RuleType.Singlevalue, "unknown@unknown.com", 4);
|
||||
ScrubRule rule6 = new ScrubRule("c.id=\"51b6b28d-1c5f-4385-af4b-8dbb3dc45f65\"", "c.EmailAddress", RuleType.SingleValue, "unknown@unknown.com", 4);
|
||||
//scrubRules.Add(rule6);
|
||||
var documentMigrator = new CosmosCloneCommon.Migrator.DocumentMigrator();
|
||||
documentMigrator.StartCopy(scrubRules).Wait();
|
||||
//var result = tcs.StartScrub(scrubRules);
|
||||
}
|
||||
|
||||
|
||||
public static async Task TestCollections()
|
||||
{
|
||||
logger.LogInfo("TestConnections");
|
||||
var dbhelper = new CosmosDBHelper();
|
||||
var vResult = await dbhelper.TestSourceConnection();
|
||||
var vResult2 = await dbhelper.TestTargetConnection();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("CloneConsoleRun")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("CloneConsoleRun")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2018")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("1f09a007-9a2e-40d8-ad24-c469473152f4")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Newtonsoft.Json" version="10.0.2" targetFramework="net461" />
|
||||
</packages>
|
|
@ -0,0 +1,81 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
|
||||
<configSections>
|
||||
<section name="SourceCosmosDBSettings" type="System.Configuration.NameValueSectionHandler" />
|
||||
<section name="TargetCosmosDBSettings" type="System.Configuration.NameValueSectionHandler" />
|
||||
<section name="SampleCosmosDBSettings" type="System.Configuration.NameValueSectionHandler" />
|
||||
</configSections>
|
||||
<SourceCosmosDBSettings>
|
||||
<add key="EndpointUrl" value="__url__" />
|
||||
<add key="AccessKey" value="__key__" />
|
||||
<add key="DatabaseName" value="TestDB" />
|
||||
<add key="CollectionName" value="__Coll__" />
|
||||
<add key="ReadDelaybetweenRequestsInMs" value="2000" />
|
||||
</SourceCosmosDBSettings>
|
||||
<TargetCosmosDBSettings>
|
||||
<add key="EndpointUrl" value="__url__" />
|
||||
<add key="AccessKey" value="__key__" />
|
||||
<add key="DatabaseName" value="TestDB" />
|
||||
<add key="CollectionName" value="__Coll__" />
|
||||
<add key="OfferThroughputRUs" value="10000" />
|
||||
</TargetCosmosDBSettings>
|
||||
<SampleCosmosDBSettings>
|
||||
<add key="EndpointUrl" value="__url__" />
|
||||
<add key="AccessKey" value="__AccessKey__" />
|
||||
<add key="ConnString" value="__connString__" />
|
||||
<add key="DatabaseName" value="TestDB" />
|
||||
<add key="IsFixedCollection" value="true" />
|
||||
<add key="CollectionName" value="TestSuperColl1" />
|
||||
<add key="OfferThroughputRUs" value="10000" />
|
||||
</SampleCosmosDBSettings>
|
||||
<appSettings>
|
||||
<add key="CopyStoredProcedures" value="true" />
|
||||
<add key="CopyUDFs" value="true" />
|
||||
<add key="CopyTriggers" value="true" />
|
||||
<add key="CopyDocuments" value="true" />
|
||||
<add key="CopyIndexingPolicy" value="true" />
|
||||
<add key="ReadBatchSize" value="3500" />
|
||||
<add key="CopyPartitionKey" value="true" />
|
||||
<add key="WriteBatchSize" value="9000" />
|
||||
<add key="WriteBatchCount" value="10000" />
|
||||
<add key="EnableTextLogging" value="true" />
|
||||
<add key="PopTextFileOnEnd" value="true" />
|
||||
<add key="SourceOfferThroughputRUs" value="10000" />
|
||||
<add key="TargetMigrationOfferThroughputRUs" value="10000" />
|
||||
<add key="TargetRestOfferThroughputRUs" value="1100" />
|
||||
<add key="ClientSettingsProvider.ServiceUri" value="" />
|
||||
<add key="ScrubbingRequired" value="true" />
|
||||
</appSettings>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
|
||||
</startup>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.Azure.Documents.Client" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-2.1.0.0" newVersion="2.1.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
<system.web>
|
||||
<membership defaultProvider="ClientAuthenticationMembershipProvider">
|
||||
<providers>
|
||||
<add name="ClientAuthenticationMembershipProvider" type="System.Web.ClientServices.Providers.ClientFormsAuthenticationMembershipProvider, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" serviceUri="" />
|
||||
</providers>
|
||||
</membership>
|
||||
<roleManager defaultProvider="ClientRoleProvider" enabled="true">
|
||||
<providers>
|
||||
<add name="ClientRoleProvider" type="System.Web.ClientServices.Providers.ClientRoleProvider, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" serviceUri="" cacheTimeout="86400" />
|
||||
</providers>
|
||||
</roleManager>
|
||||
</system.web>
|
||||
</configuration>
|
|
@ -0,0 +1,9 @@
|
|||
<Application x:Class="CosmicCloneUI.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:CosmicCloneUI"
|
||||
StartupUri="MainWindow.xaml">
|
||||
<Application.Resources>
|
||||
|
||||
</Application.Resources>
|
||||
</Application>
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
|
||||
namespace CosmicCloneUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for App.xaml
|
||||
/// </summary>
|
||||
public partial class App : Application
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
<Page x:Class="CosmicCloneUI.CloneOptionsPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:CosmicCloneUI"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="320" d:DesignWidth="800"
|
||||
Title="CloneOptionsPage">
|
||||
|
||||
<Grid>
|
||||
<StackPanel>
|
||||
<Label Content="Select Options for Copying Collection" FontSize="15" />
|
||||
<StackPanel Orientation="Horizontal" Margin="20,20,0,0">
|
||||
<CheckBox x:Name="Documents" VerticalAlignment="Center" IsChecked="True"/>
|
||||
<TextBlock Text="Documents" FontSize="15" VerticalAlignment="Center" Margin="10,0,0,0"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Margin="20,20,0,0">
|
||||
<CheckBox x:Name="SPs" VerticalAlignment="Center" IsChecked="True"/>
|
||||
<TextBlock Text="Stored Procedures" FontSize="15" VerticalAlignment="Center" Margin="10,0,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Orientation="Horizontal" Margin="20,20,0,0">
|
||||
<CheckBox x:Name="UDFs" VerticalAlignment="Center" IsChecked="True"/>
|
||||
<TextBlock Text="User Defined Functions" FontSize="15" VerticalAlignment="Center" Margin="10,0,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Orientation="Horizontal" Margin="20,20,0,0">
|
||||
<CheckBox x:Name="Triggers" VerticalAlignment="Center" IsChecked="True"/>
|
||||
<TextBlock Text="Triggers" FontSize="15" VerticalAlignment="Center" Margin="10,0,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Orientation="Horizontal" Margin="20,20,0,0">
|
||||
<CheckBox x:Name="IPs" VerticalAlignment="Center" IsChecked="True"/>
|
||||
<TextBlock Text="Indexing Policies" FontSize="15" VerticalAlignment="Center" Margin="10,0,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
|
||||
<StackPanel Orientation="Horizontal" Margin="20,20,0,0">
|
||||
<CheckBox x:Name="PKs" VerticalAlignment="Center" IsChecked="True"/>
|
||||
<TextBlock Text="Partition Keys" FontSize="15" VerticalAlignment="Center" Margin="10,0,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
|
||||
<StackPanel Orientation="Horizontal" Margin="20,20,0,0">
|
||||
<TextBlock Text="Offer Throughput" FontSize="15" VerticalAlignment="Center" Margin="10,0,0,0"/>
|
||||
<TextBox x:Name="OfferThroughput" Text="10000" HorizontalContentAlignment="Right" Width="80" Margin="10,0,0,0" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Page>
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace CosmicCloneUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for CloneOptionsPage.xaml
|
||||
/// </summary>
|
||||
public partial class CloneOptionsPage : Page
|
||||
{
|
||||
public CloneOptionsPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
<Page x:Class="CosmicCloneUI.CopyCollectionPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:CosmicCloneUI"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="320" d:DesignWidth="800"
|
||||
Title="CopyCollectionPage">
|
||||
|
||||
<Grid>
|
||||
<StackPanel Margin="15,10,5,5">
|
||||
<StackPanel x:Name="CollectionReadStackPanel" Margin="0,5,0,0">
|
||||
<TextBlock Text="Reading Collection"/>
|
||||
<ProgressBar x:Name="ReadProgress" Height="15" Margin="0,2,10,5" Value="0"/>
|
||||
</StackPanel>
|
||||
<StackPanel x:Name="CollectionWriteStackPanel" Margin="0,5,0,0">
|
||||
<TextBlock Text="Writing Collection"/>
|
||||
<ProgressBar x:Name="WriteProgress" Height="15" Margin="0,2,10,5" Value="0"/>
|
||||
</StackPanel>
|
||||
<StackPanel x:Name="ScrubStackPanel" Margin="0,5,0,0">
|
||||
<TextBlock Text="Anonymization progress"/>
|
||||
<ProgressBar x:Name="ScrubProgress" Height="15" Margin="0,2,10,5" Value="0"/>
|
||||
</StackPanel>
|
||||
<StackPanel Margin="0,10,0,0">
|
||||
<Button x:Name="ShowStatusButton" Content="Hide status" Margin="0,0,701,0" RenderTransformOrigin="15.969,-5.085" FontSize="9" Click="ShowStatusButton_Click"/>
|
||||
<Border x:Name="StatusTextBlockBorder" BorderThickness="1" Margin="0,5,10,2">
|
||||
<!--<ScrollViewer x:Name="StatusTextblockScrollViewer" HorizontalScrollBarVisibility="Auto" >-->
|
||||
<!--<TextBlock x:Name="StatusTextBlock" ScrollViewer.CanContentScroll="True" ScrollViewer.HorizontalScrollBarVisibility="Auto" ScrollViewer.VerticalScrollBarVisibility="Auto" TextWrapping="Wrap" Height="90" RenderTransformOrigin="0.498,1.559" Opacity="0.9 " FontSize="10"><Run Text="Copy Status Log"/></TextBlock>-->
|
||||
<!--</ScrollViewer>-->
|
||||
<TextBox x:Name="StatusTextBlock" Height="100" ScrollViewer.VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" ScrollViewer.CanContentScroll="True" Focusable="True" IsReadOnly="True">
|
||||
|
||||
</TextBox>
|
||||
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Page>
|
|
@ -0,0 +1,74 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
using CosmosCloneCommon.Utility;
|
||||
using logger = CosmosCloneCommon.Utility.CloneLogger;
|
||||
using CosmosCloneCommon.Model;
|
||||
|
||||
namespace CosmicCloneUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for CopyCollectionPage.xaml
|
||||
/// </summary>
|
||||
public partial class CopyCollectionPage : Page
|
||||
{
|
||||
public CopyCollectionPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
}
|
||||
|
||||
public void SetStatus()
|
||||
{
|
||||
//StatusTextBlock.Text = CloneLogger.getFullLog();
|
||||
|
||||
}
|
||||
|
||||
public void setRequiredprogressBars(List<ScrubRule> scrubRules)
|
||||
{
|
||||
if(CloneSettings.CopyDocuments==false)
|
||||
{
|
||||
CollectionReadStackPanel.Visibility = Visibility.Hidden;
|
||||
CollectionWriteStackPanel.Visibility = Visibility.Hidden;
|
||||
}
|
||||
if(CloneSettings.ScrubbingRequired)
|
||||
{
|
||||
if(scrubRules == null || scrubRules.Count<=0)
|
||||
{
|
||||
ScrubStackPanel.Visibility = Visibility.Hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowStatusButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if(StatusTextBlock.Visibility == Visibility.Visible)
|
||||
{
|
||||
StatusTextBlock.Visibility = Visibility.Hidden;
|
||||
StatusTextBlockBorder.Visibility = Visibility.Hidden;
|
||||
ShowStatusButton.Content = "Show status";
|
||||
}
|
||||
else if(StatusTextBlock.Visibility == Visibility.Hidden)
|
||||
{
|
||||
StatusTextBlock.Visibility = Visibility.Visible;
|
||||
StatusTextBlockBorder.Visibility = Visibility.Visible;
|
||||
ShowStatusButton.Content = "Hide status";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,199 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{D46B0805-591E-4080-A6A1-45613067D900}</ProjectGuid>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<RootNamespace>CosmicCloneUI</RootNamespace>
|
||||
<AssemblyName>CosmicCloneUI</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<IsWebBootstrapper>false</IsWebBootstrapper>
|
||||
<PublishUrl>C:\clonetest\Publish\</PublishUrl>
|
||||
<Install>true</Install>
|
||||
<InstallFrom>Disk</InstallFrom>
|
||||
<UpdateEnabled>false</UpdateEnabled>
|
||||
<UpdateMode>Foreground</UpdateMode>
|
||||
<UpdateInterval>7</UpdateInterval>
|
||||
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
|
||||
<UpdatePeriodically>false</UpdatePeriodically>
|
||||
<UpdateRequired>false</UpdateRequired>
|
||||
<MapFileExtensions>true</MapFileExtensions>
|
||||
<ApplicationRevision>1</ApplicationRevision>
|
||||
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
|
||||
<UseApplicationTrust>false</UseApplicationTrust>
|
||||
<PublishWizardCompleted>true</PublishWizardCompleted>
|
||||
<BootstrapperEnabled>true</BootstrapperEnabled>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ManifestCertificateThumbprint>53A79A685C21BB2A5CFFF49FE76E2CE192CE3F3B</ManifestCertificateThumbprint>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ManifestKeyFile>CosmicCloneUI_TemporaryKey.pfx</ManifestKeyFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<GenerateManifests>true</GenerateManifests>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<SignManifests>true</SignManifests>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Web.Extensions" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xaml">
|
||||
<RequiredTargetFramework>4.0</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="WindowsBase" />
|
||||
<Reference Include="PresentationCore" />
|
||||
<Reference Include="PresentationFramework" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ApplicationDefinition Include="App.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</ApplicationDefinition>
|
||||
<Compile Include="CloneOptionsPage.xaml.cs">
|
||||
<DependentUpon>CloneOptionsPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="CopyCollectionPage.xaml.cs">
|
||||
<DependentUpon>CopyCollectionPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="DataAnonymizationPage.xaml.cs">
|
||||
<DependentUpon>DataAnonymizationPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="DestinationPage.xaml.cs">
|
||||
<DependentUpon>DestinationPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Models\CloneOptions.cs" />
|
||||
<Compile Include="Models\CosmosCollection.cs" />
|
||||
<Compile Include="Models\ScrubDetails.cs" />
|
||||
<Compile Include="SourcePage.xaml.cs">
|
||||
<DependentUpon>SourcePage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Page Include="CloneOptionsPage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="CopyCollectionPage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="DataAnonymizationPage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="DestinationPage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="MainWindow.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Compile Include="App.xaml.cs">
|
||||
<DependentUpon>App.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="MainWindow.xaml.cs">
|
||||
<DependentUpon>MainWindow.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Page Include="SourcePage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Properties\Settings.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
</Compile>
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
<None Include="App.config" />
|
||||
<None Include="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\CosmosCloneCommon\CosmosCloneCommon.csproj">
|
||||
<Project>{523e1cc4-0286-498c-b184-3413e10d61de}</Project>
|
||||
<Name>CosmosCloneCommon</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="Images\fail.png" />
|
||||
<Resource Include="Images\success.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="Images\cosmosIcon1.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Resource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="Images\cosmicclonelogo.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="Images\closeIcon.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="Images\CosmosIcon2.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<BootstrapperPackage Include=".NETFramework,Version=v4.6.1">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>Microsoft .NET Framework 4.6.1 %28x86 and x64%29</ProductName>
|
||||
<Install>true</Install>
|
||||
</BootstrapperPackage>
|
||||
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
|
||||
<Visible>False</Visible>
|
||||
<ProductName>.NET Framework 3.5 SP1</ProductName>
|
||||
<Install>false</Install>
|
||||
</BootstrapperPackage>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
|
@ -0,0 +1,64 @@
|
|||
<Page x:Class="CosmicCloneUI.DataAnonymizationPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:CosmicCloneUI"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="320" d:DesignWidth="800"
|
||||
Title="DataAnonymizationPage">
|
||||
|
||||
<Grid>
|
||||
<StackPanel>
|
||||
<TextBlock FontSize="15" HorizontalAlignment="Left" Margin="20,0,0,0">
|
||||
<Span>Enter</Span>
|
||||
<Span FontWeight="Bold">Data Anonymization</Span>
|
||||
<Span>rules</Span>
|
||||
</TextBlock>
|
||||
|
||||
<!--<StackPanel Orientation="Horizontal" Height="30" Margin="20,20,0,0">
|
||||
<TextBlock Text="Filter Condition" FontSize="15" VerticalAlignment="Center" Margin="0,0,0,0"/>
|
||||
<TextBox x:Name="FilterCondition" Text="" HorizontalContentAlignment="Left" Width="430" Margin="30,0,0,0" />
|
||||
|
||||
</StackPanel>-->
|
||||
|
||||
<!--<Expander Name="ExpanderControl" Header="Rule 1" HorizontalAlignment="Left" Margin="20,20,0,0">
|
||||
<StackPanel Orientation="Horizontal" Height="30" Margin="20,20,0,0">
|
||||
<TextBlock Text="Attribute To Scrub" FontSize="15" VerticalAlignment="Center" Margin="0,0,0,0"/>
|
||||
<TextBox x:Name="ScrubAttribute" Text="" HorizontalContentAlignment="Left" Width="150" Margin="10,0,0,0" />
|
||||
<TextBlock Text="Scrub Type" FontSize="15" VerticalAlignment="Center" Margin="10,0,0,0"/>
|
||||
<ComboBox x:Name="ScrubType" Width="120" Margin="20,0,0,0">
|
||||
<ComboBoxItem Content="Null"></ComboBoxItem>
|
||||
<ComboBoxItem Content="Constant"></ComboBoxItem>
|
||||
<ComboBoxItem Content="Random"></ComboBoxItem>
|
||||
<ComboBoxItem Content="Shuffle"></ComboBoxItem>
|
||||
</ComboBox>
|
||||
<TextBlock Text="Value" FontSize="15" VerticalAlignment="Center" Margin="10,0,0,0"/>
|
||||
<TextBox x:Name="ScrubValue" Text="" HorizontalContentAlignment="Right" Width="150" Margin="10,0,0,0" />
|
||||
</StackPanel>
|
||||
</Expander>-->
|
||||
|
||||
<StackPanel Orientation="Horizontal" Height="30" Margin="20,20,0,0">
|
||||
<Button x:Name="Add" Width="100" Click="BtnAddScrubRule" HorizontalAlignment="Left" Margin="0,0,0,0">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="Add Rule" VerticalAlignment="Center" Margin="5" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button x:Name="ValidateRuleButton" Content="Validate" Width="100" Margin="300,0,0,0" Click="ValidateRuleButton_Click"/>
|
||||
<Button x:Name="SaveRuleButton" Content="Save" Width="100" Margin="20,0,0,0" Click="SaveRuleButton_Click"/>
|
||||
<Button x:Name="LoadRuleButton" Content="Load" Width="100" Margin="20,0,0,0" Click="LoadRuleButton_Click"/>
|
||||
</StackPanel>
|
||||
<ScrollViewer Height="200" VerticalScrollBarVisibility="Visible" >
|
||||
<Border BorderThickness="3">
|
||||
<StackPanel x:Name="RulesStackPanel" Orientation="Horizontal">
|
||||
<StackPanel x:Name="RulesStackPanelLeft" Width="390"/>
|
||||
<StackPanel x:Name="RulesStackPanelRight" Width="390"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</ScrollViewer>
|
||||
|
||||
</StackPanel>
|
||||
|
||||
|
||||
</Grid>
|
||||
</Page>
|
|
@ -0,0 +1,532 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using CosmicCloneUI.Models;
|
||||
using CosmosCloneCommon.Model;
|
||||
using CosmosCloneCommon.Utility;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
using Microsoft.Win32;
|
||||
using System.Xml.Serialization;
|
||||
using System.Globalization;
|
||||
|
||||
namespace CosmicCloneUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for DataAnonymizationPage.xaml
|
||||
/// </summary>
|
||||
public partial class DataAnonymizationPage : Page
|
||||
{
|
||||
int RuleIndex;
|
||||
|
||||
public DataAnonymizationPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
SaveRuleButton.IsEnabled = false;
|
||||
RuleIndex = 1;
|
||||
}
|
||||
|
||||
private bool Validation()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
private void BtnAddScrubRule(object sender, RoutedEventArgs e)
|
||||
{
|
||||
CreateScrubRule();
|
||||
}
|
||||
|
||||
private void CreateScrubRule(ScrubRule scrubRule = null)
|
||||
{
|
||||
StackPanel parentStackPanelLeft = (StackPanel)this.FindName("RulesStackPanelLeft");
|
||||
StackPanel parentStackPanelRight = (StackPanel)this.FindName("RulesStackPanelRight");
|
||||
|
||||
int newRuleIndex = newIndex(scrubRule);
|
||||
if (newRuleIndex % 2 == 1)
|
||||
{
|
||||
AddRuleWithData(parentStackPanelLeft, newRuleIndex, scrubRule);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddRuleWithData(parentStackPanelRight, newRuleIndex, scrubRule);
|
||||
}
|
||||
}
|
||||
|
||||
int newIndex(ScrubRule scrubRule = null)
|
||||
{
|
||||
int newRuleIndex = 0;
|
||||
if(scrubRule == null)
|
||||
{
|
||||
newRuleIndex = RuleIndex++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (scrubRule.RuleId == 0)
|
||||
{
|
||||
newRuleIndex = RuleIndex++;
|
||||
}
|
||||
else newRuleIndex = scrubRule.RuleId;
|
||||
}
|
||||
return newRuleIndex;
|
||||
}
|
||||
void AddRuleWithData(StackPanel parentStackPanel, int ruleIndex, ScrubRule scrubRule = null)
|
||||
{
|
||||
StackPanel RuleHeadersp = new StackPanel();
|
||||
RuleHeadersp.Orientation = Orientation.Horizontal;
|
||||
|
||||
TextBlock HeaderTB = new TextBlock();
|
||||
HeaderTB.Text = "Rule " + (ruleIndex);
|
||||
|
||||
Label RuleIdLabel = new Label();
|
||||
RuleIdLabel.Content = ruleIndex;
|
||||
RuleIdLabel.Visibility = Visibility.Hidden;
|
||||
|
||||
Button DeleteBtn = new Button();
|
||||
DeleteBtn.Name = "btnDelete_" + ruleIndex;
|
||||
DeleteBtn.HorizontalAlignment = HorizontalAlignment.Right;
|
||||
//DeleteBtn.Content = "Delete";
|
||||
DeleteBtn.Margin = new Thickness(265, 0, 0, 0);
|
||||
DeleteBtn.Click += DeleteBtn_Click;
|
||||
DeleteBtn.Content = new Image
|
||||
{
|
||||
Source = new BitmapImage(new Uri("/Images/closeIcon.png", UriKind.Relative)),
|
||||
VerticalAlignment = VerticalAlignment.Center
|
||||
};
|
||||
|
||||
RuleHeadersp.Children.Add(HeaderTB);
|
||||
RuleHeadersp.Children.Add(DeleteBtn);
|
||||
|
||||
Expander exp = new Expander();
|
||||
exp.Header = RuleHeadersp;
|
||||
exp.HorizontalAlignment = HorizontalAlignment.Left;
|
||||
exp.Margin = new Thickness(20, 30, 0, 0);
|
||||
exp.BorderThickness = new Thickness(1, 1, 1, 1);
|
||||
exp.Width = 350;
|
||||
exp.BorderBrush = Brushes.Gray;
|
||||
exp.Background = Brushes.White;
|
||||
exp.IsExpanded = true;
|
||||
exp.Name = "RuleExpander_" + ruleIndex;
|
||||
|
||||
StackPanel sp = new StackPanel();
|
||||
sp.Orientation = Orientation.Vertical;
|
||||
sp.Margin = new Thickness(20, 5, 0, 0);
|
||||
|
||||
StackPanel filterSP = new StackPanel();
|
||||
filterSP.Orientation = Orientation.Horizontal;
|
||||
filterSP.Margin = new Thickness(0, 5, 0, 2);
|
||||
|
||||
StackPanel attributeSP = new StackPanel();
|
||||
attributeSP.Orientation = Orientation.Horizontal;
|
||||
attributeSP.Margin = new Thickness(0, 5, 0, 2);
|
||||
|
||||
StackPanel scrubTypeSP = new StackPanel();
|
||||
scrubTypeSP.Orientation = Orientation.Horizontal;
|
||||
scrubTypeSP.Margin = new Thickness(0, 5, 0, 2);
|
||||
|
||||
StackPanel scrubValueSP = new StackPanel();
|
||||
scrubValueSP.Orientation = Orientation.Horizontal;
|
||||
scrubValueSP.Margin = new Thickness(0, 5, 0, 2);
|
||||
|
||||
TextBlock FilterLabel = new TextBlock();
|
||||
FilterLabel.VerticalAlignment = VerticalAlignment.Center;
|
||||
FilterLabel.Margin = new Thickness(10, 0, 0, 0);
|
||||
//FilterLabel.MaxWidth = 150;
|
||||
|
||||
Run runFilterLabel = new Run();
|
||||
runFilterLabel.Text = "Filter Query";
|
||||
runFilterLabel.FontSize = 15;
|
||||
|
||||
Run runFilterLabelHint = new Run();
|
||||
runFilterLabelHint.Text = " \nEx: c.Type = \"document\"";
|
||||
runFilterLabelHint.FontSize = 10;
|
||||
|
||||
FilterLabel.Inlines.Add(runFilterLabel);
|
||||
FilterLabel.Inlines.Add(runFilterLabelHint);
|
||||
|
||||
TextBox FilterTB = new TextBox();
|
||||
FilterTB.Name = "Filter" + ruleIndex;
|
||||
FilterTB.Width = 150;
|
||||
FilterTB.HorizontalContentAlignment = HorizontalAlignment.Left;
|
||||
FilterTB.VerticalAlignment = VerticalAlignment.Center;
|
||||
FilterTB.Margin = new Thickness(20, 0, 0, 0);
|
||||
|
||||
TextBlock AttributeScrubLabel = new TextBlock();
|
||||
AttributeScrubLabel.VerticalAlignment = VerticalAlignment.Center;
|
||||
AttributeScrubLabel.Margin = new Thickness(10, 0, 0, 0);
|
||||
//AttributeScrubLabel.Width = 120;
|
||||
|
||||
Run runAttributeScrubLabel = new Run();
|
||||
runAttributeScrubLabel.Text = "Attribute To Scrub";
|
||||
runAttributeScrubLabel.FontSize = 15;
|
||||
|
||||
Run runAttributeScrubLabelHint = new Run();
|
||||
runAttributeScrubLabelHint.Text = " \nEx: c.Person.Email";
|
||||
runAttributeScrubLabelHint.FontSize = 10;
|
||||
|
||||
AttributeScrubLabel.Inlines.Add(runAttributeScrubLabel);
|
||||
AttributeScrubLabel.Inlines.Add(runAttributeScrubLabelHint);
|
||||
|
||||
TextBox AttributeScrubTB = new TextBox();
|
||||
AttributeScrubTB.Name = "ScrubAttribute" + ruleIndex;
|
||||
AttributeScrubTB.Width = 150;
|
||||
AttributeScrubTB.HorizontalContentAlignment = HorizontalAlignment.Left;
|
||||
AttributeScrubTB.VerticalAlignment = VerticalAlignment.Center;
|
||||
AttributeScrubTB.Margin = new Thickness(20, 0, 0, 0);
|
||||
|
||||
TextBlock ScrubTypeLabel = new TextBlock();
|
||||
ScrubTypeLabel.Text = "Scrub Type";
|
||||
ScrubTypeLabel.FontSize = 15;
|
||||
ScrubTypeLabel.VerticalAlignment = VerticalAlignment.Center;
|
||||
ScrubTypeLabel.Margin = new Thickness(10, 0, 0, 0);
|
||||
|
||||
ComboBox ScrubTypeCB = new ComboBox();
|
||||
ScrubTypeCB.Name = "ScrubType" + ruleIndex;
|
||||
ScrubTypeCB.Width = 150;
|
||||
ScrubTypeCB.Margin = new Thickness(20, 0, 0, 0);
|
||||
|
||||
foreach (RuleType val in Enum.GetValues(typeof(RuleType)))
|
||||
{
|
||||
ScrubTypeCB.Items.Add(val.ToString());
|
||||
}
|
||||
|
||||
ScrubTypeCB.SelectionChanged += new SelectionChangedEventHandler(scrubTypeComboBox_SelectedIndexChanged);
|
||||
|
||||
TextBlock ScrubValueLabel = new TextBlock();
|
||||
ScrubValueLabel.Text = "Replace with";
|
||||
ScrubValueLabel.FontSize = 15;
|
||||
ScrubValueLabel.VerticalAlignment = VerticalAlignment.Center;
|
||||
ScrubValueLabel.Margin = new Thickness(10, 0, 0, 5);
|
||||
|
||||
TextBox ScrubValueTB = new TextBox();
|
||||
ScrubValueTB.Name = "ScrubValue" + ruleIndex;
|
||||
ScrubValueTB.Width = 150;
|
||||
ScrubValueTB.HorizontalContentAlignment = HorizontalAlignment.Left;
|
||||
ScrubValueTB.VerticalAlignment = VerticalAlignment.Center;
|
||||
ScrubValueTB.Margin = new Thickness(20, 0, 0, 0);
|
||||
|
||||
FilterLabel.Width = 120;
|
||||
FilterTB.Width = 150;
|
||||
AttributeScrubLabel.Width = 120;
|
||||
AttributeScrubTB.Width = 150;
|
||||
ScrubTypeLabel.Width = 120;
|
||||
ScrubTypeCB.Width = 150;
|
||||
ScrubValueLabel.Width = 120;
|
||||
|
||||
filterSP.Children.Add(FilterLabel);
|
||||
filterSP.Children.Add(FilterTB);
|
||||
|
||||
attributeSP.Children.Add(AttributeScrubLabel);
|
||||
attributeSP.Children.Add(AttributeScrubTB);
|
||||
|
||||
scrubTypeSP.Children.Add(ScrubTypeLabel);
|
||||
scrubTypeSP.Children.Add(ScrubTypeCB);
|
||||
|
||||
scrubValueSP.Children.Add(ScrubValueLabel);
|
||||
scrubValueSP.Children.Add(ScrubValueTB);
|
||||
scrubValueSP.Children.Add(RuleIdLabel);
|
||||
scrubValueSP.Visibility = Visibility.Hidden;
|
||||
|
||||
sp.Children.Add(attributeSP);
|
||||
sp.Children.Add(filterSP);
|
||||
sp.Children.Add(scrubTypeSP);
|
||||
sp.Children.Add(scrubValueSP);
|
||||
|
||||
exp.Content = sp;
|
||||
|
||||
if(scrubRule!=null)
|
||||
{
|
||||
FilterTB.Text = scrubRule.FilterCondition;
|
||||
AttributeScrubTB.Text = scrubRule.PropertyName;
|
||||
if(scrubRule.Type!=null) ScrubTypeCB.SelectedIndex = (int)scrubRule.Type;
|
||||
ScrubValueTB.Text = scrubRule.UpdateValue;
|
||||
}
|
||||
parentStackPanel.Children.Add(exp);
|
||||
if(!SaveRuleButton.IsEnabled)
|
||||
{
|
||||
SaveRuleButton.IsEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void scrubTypeComboBox_SelectedIndexChanged(object sender, EventArgs e)
|
||||
{
|
||||
//check comobox status based on it change visibility of scrubvalue stack panel
|
||||
var cbox = (ComboBox)sender;
|
||||
if (cbox.SelectedValue.ToString().Equals(RuleType.SingleValue.ToString()))
|
||||
{
|
||||
var parentPanel = (StackPanel)cbox.Parent;
|
||||
var gpPanel = (StackPanel)parentPanel.Parent;
|
||||
gpPanel.Children[3].Visibility = Visibility.Visible;
|
||||
}
|
||||
else
|
||||
{
|
||||
var parentPanel = (StackPanel)cbox.Parent;
|
||||
var gpPanel = (StackPanel)parentPanel.Parent;
|
||||
gpPanel.Children[3].Visibility = Visibility.Hidden;
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteBtn_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var btnDelete = (Button)sender;
|
||||
string expname = "RuleExpander_" + btnDelete.Name.Substring(btnDelete.Name.IndexOf('_')+1);
|
||||
|
||||
StackPanel parentStackPanel = (StackPanel)this.FindName("RulesStackPanel");
|
||||
UIElementCollection ChildrenSPs = parentStackPanel.Children;
|
||||
foreach (UIElement LeftRightSPUI in ChildrenSPs)
|
||||
{
|
||||
StackPanel sp = (StackPanel)LeftRightSPUI;
|
||||
UIElementCollection uiElementsLeftRight = sp.Children;
|
||||
|
||||
foreach (UIElement SPUI in uiElementsLeftRight)
|
||||
{
|
||||
Expander exp = (Expander)SPUI;
|
||||
if (exp.Name == expname)
|
||||
{
|
||||
sp.Children.Remove(SPUI);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var rules = getScrubRules();
|
||||
//Delete all scrub rules
|
||||
StackPanel parentStackPanelLeft = (StackPanel)this.FindName("RulesStackPanelLeft");
|
||||
StackPanel parentStackPanelRight = (StackPanel)this.FindName("RulesStackPanelRight");
|
||||
parentStackPanelLeft.Children.RemoveRange(0, parentStackPanelLeft.Children.Count);
|
||||
parentStackPanelRight.Children.RemoveRange(0, parentStackPanelRight.Children.Count);
|
||||
|
||||
//Re initialize rule index
|
||||
this.RuleIndex = 1;
|
||||
|
||||
foreach (var rule in rules)
|
||||
{
|
||||
rule.RuleId = 0;//reset Ids so they are re assigned
|
||||
CreateScrubRule(rule);
|
||||
}
|
||||
}
|
||||
|
||||
public List<ScrubRule> getScrubRules()
|
||||
{
|
||||
//List<ScrubRule> sb = new List<ScrubRule>();
|
||||
TextBox filterCondition = (TextBox)this.FindName("FilterCondition");
|
||||
//sb.filterQuery = filterCondition.Text;
|
||||
List<ScrubRule> srList = new List<ScrubRule>();
|
||||
|
||||
StackPanel parentStackPanel = (StackPanel)this.FindName("RulesStackPanel");
|
||||
UIElementCollection ChildrenSPs = parentStackPanel.Children;
|
||||
|
||||
foreach (UIElement LeftRightSPUI in ChildrenSPs)
|
||||
{
|
||||
StackPanel sp = (StackPanel)LeftRightSPUI;
|
||||
UIElementCollection uiElementsLeftRight = sp.Children;
|
||||
|
||||
foreach (UIElement SPUI in uiElementsLeftRight)
|
||||
{
|
||||
Expander exp = (Expander)SPUI;
|
||||
StackPanel lrsp = (StackPanel)exp.Content;
|
||||
UIElementCollection uiElementsSP = lrsp.Children;
|
||||
|
||||
ScrubRule sr = new ScrubRule();
|
||||
|
||||
foreach (UIElement uiElementSP in uiElementsSP)
|
||||
{
|
||||
StackPanel tempSP = (StackPanel)uiElementSP;
|
||||
UIElementCollection uiElements = tempSP.Children;
|
||||
|
||||
foreach (UIElement uiElement in uiElements)
|
||||
{
|
||||
if (uiElement.GetType().Name == "Label")
|
||||
{
|
||||
var ruleIdLabel = (Label)uiElement;
|
||||
int ruleId;
|
||||
if (int.TryParse(ruleIdLabel.Content.ToString(), out ruleId))
|
||||
{
|
||||
sr.RuleId = ruleId;
|
||||
}
|
||||
else sr.RuleId = 0;
|
||||
}
|
||||
|
||||
if (uiElement.GetType().Name == "TextBox")
|
||||
{
|
||||
TextBox tb = (TextBox)uiElement;
|
||||
string name = tb.Name.Substring(0, tb.Name.Length - 1);
|
||||
|
||||
if (name == "Filter")
|
||||
{
|
||||
sr.FilterCondition = tb.Text.Trim();
|
||||
}
|
||||
else if (name == "ScrubAttribute")
|
||||
{
|
||||
sr.PropertyName = tb.Text.Trim();
|
||||
}
|
||||
else if (name == "ScrubValue")
|
||||
{
|
||||
sr.UpdateValue = tb.Text.Trim();
|
||||
}
|
||||
}
|
||||
if (uiElement.GetType().Name == "ComboBox")
|
||||
{
|
||||
ComboBox cb = (ComboBox)uiElement;
|
||||
string name = cb.Name.Substring(0, cb.Name.Length - 1);
|
||||
if (name == "ScrubType")
|
||||
{
|
||||
//sr.Type = (RuleType) Enum.Parse(typeof(RuleType), cb.Text);
|
||||
RuleType rType;
|
||||
|
||||
if (Enum.TryParse<RuleType>(cb.Text, out rType))
|
||||
{
|
||||
sr.Type = rType;
|
||||
}
|
||||
else
|
||||
{
|
||||
sr.Type = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
srList.Add(sr);
|
||||
}
|
||||
}
|
||||
return srList;
|
||||
}
|
||||
|
||||
public bool validateInput()
|
||||
{
|
||||
var rules = getScrubRules();
|
||||
var orderedRules = rules.OrderBy(o => o.RuleId).ToList();
|
||||
bool isValidationSuccess;
|
||||
var validationMessages = new List<string>();
|
||||
|
||||
foreach (var rule in orderedRules)
|
||||
{
|
||||
if(rule.Type == null)
|
||||
{
|
||||
validationMessages.Add($"Rule:{rule.RuleId} - Please select a valid anonymization type");
|
||||
}
|
||||
if(string.IsNullOrEmpty(rule.PropertyName))
|
||||
{
|
||||
validationMessages.Add($"Rule:{rule.RuleId} - Attribute name is empty");
|
||||
}
|
||||
else if (!rule.PropertyName.StartsWith("c."))
|
||||
{
|
||||
validationMessages.Add($"Rule:{rule.RuleId} - Attribute name starts improperly. Sample c.EntityType");
|
||||
}
|
||||
else if (rule.PropertyName.EndsWith("."))
|
||||
{
|
||||
validationMessages.Add($"Rule:{rule.RuleId} - Attribute name Ends improperly. Sample c.EntityType");
|
||||
}
|
||||
if (!string.IsNullOrEmpty(rule.FilterCondition))
|
||||
{
|
||||
if (!rule.FilterCondition.StartsWith("c."))
|
||||
{
|
||||
validationMessages.Add($"Rule:{rule.RuleId} - Filter condition starts improperly. Sample c.EntityType=\"document\" ");
|
||||
}
|
||||
}
|
||||
}
|
||||
if(validationMessages.Count > 0)
|
||||
{
|
||||
isValidationSuccess = false;
|
||||
string message="";
|
||||
foreach(var msg in validationMessages)
|
||||
{
|
||||
message += (msg + System.Environment.NewLine);
|
||||
}
|
||||
MessageBox.Show(message, "Input validation",MessageBoxButton.OK,MessageBoxImage.Error);
|
||||
}
|
||||
else isValidationSuccess = true;
|
||||
return isValidationSuccess;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void SaveRuleButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var rules = this.getScrubRules();
|
||||
|
||||
if (rules == null || rules.Count == 0)
|
||||
{
|
||||
MessageBox.Show("No Rules found. Please add/load anonymization rules before Save", "No rules Found", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
SaveFileDialog saveFileDialog = new SaveFileDialog();
|
||||
saveFileDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
|
||||
saveFileDialog.Filter = "XML file (*.xml)|*.xml";
|
||||
saveFileDialog.Title = "CosmicClone save AnonymizationRules";
|
||||
saveFileDialog.FileName = "AnonymizationRules_"+ DateTime.Now.ToString("MM-dd-yyyy-HHmmss",CultureInfo.InvariantCulture);
|
||||
|
||||
if (saveFileDialog.ShowDialog() == true)
|
||||
{
|
||||
var xmlText = CloneSerializer.XMLSerialize(rules);
|
||||
File.WriteAllText(saveFileDialog.FileName, xmlText);
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadRuleButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
OpenFileDialog openFileDialog = new OpenFileDialog();
|
||||
openFileDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
|
||||
openFileDialog.Filter = "XML file (*.xml)|*.xml";
|
||||
openFileDialog.Title = "CosmicClone Load AnonymizationRules";
|
||||
if (openFileDialog.ShowDialog() == true)
|
||||
{
|
||||
string xmlText = File.ReadAllText(openFileDialog.FileName);
|
||||
var rules = CloneSerializer.XMLDeserialize<List<ScrubRule>>(xmlText);
|
||||
if (rules == null && rules.Count == 0)
|
||||
{
|
||||
MessageBox.Show("No rules to Load in file : "+openFileDialog.FileName , "No rules Found", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
var orderedRules = rules.OrderBy(o => o.RuleId).ToList();
|
||||
|
||||
//Delete all scrub rules
|
||||
StackPanel parentStackPanelLeft = (StackPanel)this.FindName("RulesStackPanelLeft");
|
||||
StackPanel parentStackPanelRight = (StackPanel)this.FindName("RulesStackPanelRight");
|
||||
parentStackPanelLeft.Children.RemoveRange(0, parentStackPanelLeft.Children.Count);
|
||||
parentStackPanelRight.Children.RemoveRange(0, parentStackPanelRight.Children.Count);
|
||||
|
||||
//Re initialize rule index
|
||||
this.RuleIndex = 1;
|
||||
|
||||
foreach (var rule in orderedRules)
|
||||
{
|
||||
CreateScrubRule(rule);
|
||||
}
|
||||
}
|
||||
//txtEditor.Text = File.ReadAllText(openFileDialog.FileName);
|
||||
//handle unable to load exception
|
||||
}
|
||||
|
||||
private void ValidateRuleButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var rules = getScrubRules();
|
||||
if (rules == null || rules.Count==0)
|
||||
{
|
||||
MessageBox.Show("No Rules found. Please add/load anonymization rules before Validation", "Data validation", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
bool isValidationSuccess = this.validateInput();
|
||||
if (isValidationSuccess)
|
||||
{
|
||||
MessageBox.Show("Rules Validated Successfully", "Data validation", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
<Page x:Class="CosmicCloneUI.DestinationPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:CosmicCloneUI"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="320" d:DesignWidth="800"
|
||||
Title="DestinationPage">
|
||||
|
||||
<Grid>
|
||||
<StackPanel Margin="20,20">
|
||||
<!--<Label Content="Enter Target Connection Details" FontSize="15" />-->
|
||||
<TextBlock FontSize="15">
|
||||
<Span>Enter</Span>
|
||||
<Span FontWeight="Bold">Target</Span>
|
||||
<Span>connection details</Span>
|
||||
</TextBlock>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,10,0,10">
|
||||
<Label Content="Url" Width="200" FontSize="15" />
|
||||
<TextBox x:Name="TargetURL" Width="400" VerticalContentAlignment="Center" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,0,0,10">
|
||||
<Label Content="Access Key" Width="200" FontSize="15" />
|
||||
<TextBox x:Name="TargetKey" Width="400" VerticalContentAlignment="Center" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,0,0,10">
|
||||
<Label Content="Database" Width="200" FontSize="15" />
|
||||
<TextBox x:Name="TargetDB" Width="400" VerticalContentAlignment="Center" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,0,0,10">
|
||||
<Label Content="Collection" Width="200" FontSize="15" />
|
||||
<TextBox x:Name="TargetCollection" Width="400" VerticalContentAlignment="Center" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<Image x:Name="ConnectionIcon" Width="30" Height="30" Margin="0,0,10,0"/>
|
||||
<Button Width="150" Height="40" Click="BtnTestTarget" HorizontalAlignment="Right" Margin="0,0,160,0">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="Test Connection" VerticalAlignment="Center" Margin="5" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Page>
|
|
@ -0,0 +1,65 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using CosmosCloneCommon.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace CosmicCloneUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for DestinationPage.xaml
|
||||
/// </summary>
|
||||
public partial class DestinationPage : Page
|
||||
{
|
||||
CosmosDBHelper cosmosHelper;
|
||||
public DestinationPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
cosmosHelper = new CosmosDBHelper();
|
||||
}
|
||||
|
||||
private void BtnTestTarget(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (TestConnection())
|
||||
{
|
||||
var connectionIcon = (Image)this.FindName("ConnectionIcon");
|
||||
ConnectionIcon.Source = new BitmapImage(new Uri("/Images/success.png", UriKind.Relative));
|
||||
}
|
||||
else
|
||||
{
|
||||
var connectionIcon = (Image)this.FindName("ConnectionIcon");
|
||||
ConnectionIcon.Source = new BitmapImage(new Uri("/Images/fail.png", UriKind.Relative));
|
||||
}
|
||||
}
|
||||
|
||||
private bool TestConnection()
|
||||
{
|
||||
CloneSettings.TargetSettings = new CosmosCollectionValues()
|
||||
{
|
||||
EndpointUrl = TargetURL.Text.ToString(),
|
||||
AccessKey = TargetKey.Text.ToString(),
|
||||
DatabaseName = TargetDB.Text.ToString(),
|
||||
CollectionName = TargetCollection.Text.ToString()
|
||||
};
|
||||
|
||||
var result = cosmosHelper.TestTargetConnection_v2();
|
||||
if (result.IsSuccess == true)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 102 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 589 B |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 94 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 4.2 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 1.6 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 1.5 KiB |
|
@ -0,0 +1,35 @@
|
|||
<Window x:Class="CosmicCloneUI.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:CosmicCloneUI"
|
||||
mc:Ignorable="d"
|
||||
Title="CosmicClone v2.0" Height="500" Width="800" ResizeMode="NoResize" WindowStartupLocation="CenterScreen" Icon="/Images/CosmosIcon2.ico">
|
||||
<Grid>
|
||||
<Border BorderBrush="Black" Background="LightGray" BorderThickness="1" HorizontalAlignment="Left" Height="80" VerticalAlignment="Top" Width="790" Margin="0,0,0,0">
|
||||
<!--<StackPanel>
|
||||
<TextBlock Text="Cosmic Clone" FontSize="20" HorizontalAlignment="Center"/>
|
||||
<TextBlock TextWrapping="Wrap" HorizontalAlignment="Center" Text="Cosmos Database Backup restore copy utility"
|
||||
FontSize="15" Margin="10,0,10,0"/>
|
||||
</StackPanel>-->
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Image x:Name="logo1" Width="75" Height="80" HorizontalAlignment="Left" Source="/Images/cosmosIcon1.png" Margin="0,0,0,0"/>
|
||||
<Image x:Name="headerlogo" Width="640" Height="80" HorizontalAlignment="Left" Source="/Images/cosmicclonelogo.png" Margin="0,0,0,0"/>
|
||||
<Image x:Name="logo2" Width="75" Height="80" Source="/Images/cosmosIcon1.png" Margin="0,0,0,0"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<Border Background="WhiteSmoke" BorderThickness="1" HorizontalAlignment="Left" Height="390" Margin="00,80,0,0" VerticalAlignment="Top" Width="790">
|
||||
<Grid HorizontalAlignment="Left" Height="390" Margin="00,00,0,0" VerticalAlignment="Top" Width="790">
|
||||
<StackPanel HorizontalAlignment="Left" Height="320" Margin="00,00,0,0" VerticalAlignment="Top" Width="790">
|
||||
<Frame x:Name="_mainFrame" NavigationUIVisibility="Hidden" LoadCompleted="MainFrameLoaded" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Height="70" Margin="0,300,0,0" Width="790">
|
||||
<Button x:Name="btn_previous" Content="Previous" MinWidth="100" Height="30" Margin="450,0,0,0" Click="BtnClickPrevious" />
|
||||
<Button x:Name="btn_next" Content="Next" MinWidth="100" Height="30" Margin="10,0,0,0" Click="BtnClickNext" />
|
||||
<Button x:Name="btn_finish" Content="Finish" MinWidth="100" Height="30" Margin="10,0,0,0" Click="BtnClickFinish" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Window>
|
|
@ -0,0 +1,337 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using CosmicCloneUI.Models;
|
||||
using CosmosCloneCommon.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
using System.ComponentModel;
|
||||
using CosmosCloneCommon.Migrator;
|
||||
using CosmosCloneCommon.Model;
|
||||
using logger = CosmosCloneCommon.Utility.CloneLogger;
|
||||
|
||||
namespace CosmicCloneUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for MainWindow.xaml
|
||||
/// </summary>
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
Page sourcePage;
|
||||
Page destinationPage;
|
||||
Page cloneOptionsPage;
|
||||
Page dataAnonymizationPage;
|
||||
Page copyCollectionPage;
|
||||
Page[] pages;
|
||||
|
||||
CosmosDBHelper cosmosHelper;
|
||||
List<ScrubRule> scrubRules;
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
InitializePages();
|
||||
cosmosHelper = new CosmosDBHelper();
|
||||
this.Dispatcher.UnhandledException += OnDispatcherUnhandledException;
|
||||
}
|
||||
|
||||
void OnDispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
|
||||
{
|
||||
string errorMessage = string.Format("An unhandled exception occurred: {0}", e.Exception.Message);
|
||||
MessageBox.Show(errorMessage, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
e.Handled = true;
|
||||
}
|
||||
private void InitializePages()
|
||||
{
|
||||
sourcePage = new SourcePage();
|
||||
destinationPage = new DestinationPage();
|
||||
cloneOptionsPage = new CloneOptionsPage();
|
||||
dataAnonymizationPage = new DataAnonymizationPage();
|
||||
copyCollectionPage = new CopyCollectionPage();
|
||||
//copyCollectionPage.SetStatus();
|
||||
|
||||
pages = new Page[10];
|
||||
pages[0] = sourcePage;
|
||||
pages[1] = destinationPage;
|
||||
pages[2] = cloneOptionsPage;
|
||||
pages[3] = dataAnonymizationPage;
|
||||
pages[4] = copyCollectionPage;
|
||||
|
||||
_mainFrame.Content = pages[0];
|
||||
}
|
||||
|
||||
private void BtnClickPrevious(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Page currentPage = (Page)_mainFrame.Content;
|
||||
_mainFrame.Navigate(getPreviousPage(currentPage));
|
||||
}
|
||||
|
||||
private void BtnClickNext(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Page currentPage = (Page)_mainFrame.Content;
|
||||
if(performAction(currentPage))
|
||||
{
|
||||
_mainFrame.Navigate(getNextPage(currentPage));
|
||||
}
|
||||
}
|
||||
|
||||
private void BtnClickFinish(object sender, RoutedEventArgs e)
|
||||
{
|
||||
this.Close();
|
||||
}
|
||||
|
||||
private void NavigationHelper()
|
||||
{
|
||||
Page currentPage = (Page)_mainFrame.Content;
|
||||
int pagenum = getPageNumber(currentPage);
|
||||
if (pagenum == 0)
|
||||
{
|
||||
btn_previous.IsEnabled = false;
|
||||
btn_next.IsEnabled = true;
|
||||
btn_finish.IsEnabled = false;
|
||||
}
|
||||
else if (pagenum == 4)
|
||||
{
|
||||
btn_previous.IsEnabled = false;
|
||||
btn_next.IsEnabled = false;
|
||||
btn_finish.IsEnabled = false;//is this required
|
||||
}
|
||||
else
|
||||
{
|
||||
btn_previous.IsEnabled = true;
|
||||
btn_next.IsEnabled = true;
|
||||
btn_finish.IsEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
private Page getNextPage(Page currentPage)
|
||||
{
|
||||
return pages[getPageNumber(currentPage) + 1];
|
||||
}
|
||||
|
||||
private Page getPreviousPage(Page currentPage)
|
||||
{
|
||||
return pages[getPageNumber(currentPage) - 1];
|
||||
}
|
||||
|
||||
private int getPageNumber(Page page)
|
||||
{
|
||||
for(int i=0;i<pages.Length;i++)
|
||||
{
|
||||
if(pages[i] == page)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void MainFrameLoaded(object sender, EventArgs e)
|
||||
{
|
||||
NavigationHelper();
|
||||
}
|
||||
|
||||
private bool performAction(Page currentPage)
|
||||
{
|
||||
if (getPageNumber(currentPage) == 0)
|
||||
{
|
||||
CloneSettings.SourceSettings = new CosmosCollectionValues()
|
||||
{
|
||||
EndpointUrl = ((TextBox)currentPage.FindName("SourceURL")).Text.ToString(),
|
||||
AccessKey = ((TextBox)currentPage.FindName("SourceKey")).Text.ToString(),
|
||||
DatabaseName = ((TextBox)currentPage.FindName("SourceDB")).Text.ToString(),
|
||||
CollectionName = ((TextBox)currentPage.FindName("SourceCollection")).Text.ToString()
|
||||
//OfferThroughputRUs = int.Parse(sourceConfigs["OfferThroughputRUs"])
|
||||
};
|
||||
|
||||
var result = cosmosHelper.TestSourceConnection_v2();
|
||||
if (result.IsSuccess == true)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
|
||||
}
|
||||
else if (getPageNumber(currentPage) == 1)
|
||||
{
|
||||
CloneSettings.TargetSettings = new CosmosCollectionValues()
|
||||
{
|
||||
EndpointUrl = ((TextBox)currentPage.FindName("TargetURL")).Text,
|
||||
AccessKey = ((TextBox)currentPage.FindName("TargetKey")).Text,
|
||||
DatabaseName = ((TextBox)currentPage.FindName("TargetDB")).Text,
|
||||
CollectionName = ((TextBox)currentPage.FindName("TargetCollection")).Text
|
||||
//OfferThroughputRUs = int.Parse(sourceConfigs["OfferThroughputRUs"])
|
||||
};
|
||||
|
||||
var result = cosmosHelper.TestTargetConnection_v2();
|
||||
if (result.IsSuccess == true)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
|
||||
}
|
||||
else if (getPageNumber(currentPage) == 2)
|
||||
{
|
||||
CloneSettings.CopyStoredProcedures = ((CheckBox)currentPage.FindName("SPs")).IsChecked.Value;
|
||||
CloneSettings.CopyUDFs = ((CheckBox)currentPage.FindName("UDFs")).IsChecked.Value;
|
||||
CloneSettings.CopyTriggers = ((CheckBox)currentPage.FindName("Triggers")).IsChecked.Value;
|
||||
CloneSettings.CopyDocuments = ((CheckBox)currentPage.FindName("Documents")).IsChecked.Value;
|
||||
CloneSettings.CopyIndexingPolicy = ((CheckBox)currentPage.FindName("IPs")).IsChecked.Value;
|
||||
CloneSettings.CopyPartitionKey = ((CheckBox)currentPage.FindName("PKs")).IsChecked.Value;
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (getPageNumber(currentPage) == 3)
|
||||
{
|
||||
btn_finish.IsEnabled = false;
|
||||
scrubRules = ((DataAnonymizationPage)currentPage).getScrubRules();
|
||||
bool isValidationSuccess = ((DataAnonymizationPage)currentPage).validateInput();
|
||||
if (!isValidationSuccess) return false;
|
||||
|
||||
BackgroundWorker worker = new BackgroundWorker();
|
||||
worker.WorkerReportsProgress = true;
|
||||
worker.DoWork += worker_DoWork;
|
||||
worker.ProgressChanged += worker_ProgressChanged;
|
||||
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
|
||||
worker.RunWorkerAsync(1000);
|
||||
|
||||
BackgroundWorker worker2 = new BackgroundWorker();
|
||||
worker2.WorkerReportsProgress = true;
|
||||
worker2.DoWork += worker_DoWork2;
|
||||
worker2.ProgressChanged += worker_ProgressChanged2;
|
||||
worker2.RunWorkerCompleted += worker_RunWorkerCompleted2;
|
||||
worker2.RunWorkerAsync(1000);
|
||||
|
||||
var nextPage = getNextPage(currentPage);
|
||||
((CopyCollectionPage)nextPage).setRequiredprogressBars(scrubRules);
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (getPageNumber(currentPage) == 4)
|
||||
{
|
||||
btn_finish.IsEnabled = false;
|
||||
((CopyCollectionPage)currentPage).setRequiredprogressBars(scrubRules);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void worker_DoWork(object sender, DoWorkEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var documentMigrator = new CosmosCloneCommon.Migrator.DocumentMigrator();
|
||||
documentMigrator.StartCopy(scrubRules).Wait();
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
logger.LogInfo("Main process exits with error");
|
||||
logger.LogError(ex);
|
||||
string excmessage = "Main process exits with error. /n" + ex.Message + "/n";
|
||||
if(ex.InnerException!=null)
|
||||
{
|
||||
excmessage += ex.InnerException.Message;
|
||||
}
|
||||
MessageBox.Show(excmessage, "Error occurred. App closure", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
|
||||
{
|
||||
//((ProgressBar)pages[3].FindName("ReadProgress")).Value = e.ProgressPercentage;
|
||||
}
|
||||
|
||||
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
|
||||
{
|
||||
//MessageBox.Show("Document Collection Copied Successfully : ");
|
||||
}
|
||||
|
||||
|
||||
void worker_DoWork2(object sender, DoWorkEventArgs e)
|
||||
{
|
||||
long readPercentProgress = 0;
|
||||
long writePercentProgress = 0;
|
||||
long scrubPercentProgress = 0;
|
||||
int sendPercent = 0;
|
||||
while (readPercentProgress < 100 || writePercentProgress < 100 || scrubPercentProgress < 100)
|
||||
{
|
||||
if (DocumentMigrator.TotalRecordsInSource == 0 && CloneSettings.CopyDocuments==true)
|
||||
{
|
||||
readPercentProgress = 0;
|
||||
writePercentProgress = 0;
|
||||
//scrubPercentProgress = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
readPercentProgress = (DocumentMigrator.TotalRecordsRetrieved * 100) / DocumentMigrator.TotalRecordsInSource;
|
||||
writePercentProgress = (DocumentMigrator.TotalRecordsSent * 100) / DocumentMigrator.TotalRecordsInSource;
|
||||
if(CloneSettings.ScrubbingRequired && DocumentMigrator.scrubRules!=null && DocumentMigrator.scrubRules.Count>0)
|
||||
{
|
||||
scrubPercentProgress = DocumentMigrator.ScrubPercentProgress;
|
||||
}
|
||||
else scrubPercentProgress = 100;
|
||||
|
||||
}
|
||||
|
||||
sendPercent = (int)scrubPercentProgress * 1000000 + (int)readPercentProgress * 1000 + (int)writePercentProgress ;
|
||||
(sender as BackgroundWorker).ReportProgress((int)sendPercent);
|
||||
Task.Delay(3000).Wait();
|
||||
}
|
||||
}
|
||||
void worker_ProgressChanged2(object sender, ProgressChangedEventArgs e)
|
||||
{
|
||||
int receivePercent = e.ProgressPercentage;
|
||||
|
||||
|
||||
int writePercent = (receivePercent % 1000);
|
||||
int readPercent = (receivePercent % 1000000) / 1000;
|
||||
int scrubPercent = receivePercent/1000000;
|
||||
|
||||
((ProgressBar)pages[4].FindName("ReadProgress")).Value = readPercent;
|
||||
((ProgressBar)pages[4].FindName("WriteProgress")).Value = writePercent;
|
||||
((ProgressBar)pages[4].FindName("ScrubProgress")).Value = scrubPercent;
|
||||
var statustextbox = ((TextBox)pages[4].FindName("StatusTextBlock"));
|
||||
statustextbox.Text = logger.FullLog;
|
||||
statustextbox.ScrollToEnd();
|
||||
}
|
||||
|
||||
void worker_RunWorkerCompleted2(object sender, RunWorkerCompletedEventArgs e)
|
||||
{
|
||||
while(!DocumentMigrator.IsCodeMigrationComplete)
|
||||
{
|
||||
Task.Delay(5000).Wait();
|
||||
}
|
||||
if(DocumentMigrator.IsCodeMigrationComplete)
|
||||
{
|
||||
string completeMessage = DocumentMigrator.TotalRecordsSent + " Documents Copied Successfully";
|
||||
completeMessage += "\n" + "Code Migration Complete";
|
||||
if (DocumentMigrator.scrubRules!=null && DocumentMigrator.scrubRules.Count>0)
|
||||
{
|
||||
completeMessage += "\n" + "Scrubbing completed for rules " + DocumentMigrator.scrubRules.Count;
|
||||
}
|
||||
|
||||
MessageBox.Show(completeMessage, "Completed", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
|
||||
btn_finish.IsEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CosmicCloneUI.Models
|
||||
{
|
||||
class CloneOptions
|
||||
{
|
||||
public bool copySP { get; set; }
|
||||
public bool copyUDF { get; set; }
|
||||
public bool copyTrigger { get; set; }
|
||||
public bool copyDocument { get; set; }
|
||||
public bool copyIndexingPolicy { get; set; }
|
||||
public bool copyPartitionKey { get; set; }
|
||||
public long offerThroughput { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CosmicCloneUI.Models
|
||||
{
|
||||
class CosmosCollection
|
||||
{
|
||||
public string Url { get; set; }
|
||||
public string AccessKey { get; set; }
|
||||
public string Database { get; set; }
|
||||
public string Collection { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CosmicCloneUI.Models
|
||||
{
|
||||
//class ScrubDetails
|
||||
//{
|
||||
// public List<ScrubRule> scrubRules { get; set; }
|
||||
//}
|
||||
|
||||
//class ScrubRule
|
||||
//{
|
||||
// public int id { get; set; }
|
||||
// public string filterQuery { get; set; }
|
||||
// public string attributeName { get; set; }
|
||||
// public string scrubType { get; set; }
|
||||
// public string scrubValue { get; set; }
|
||||
//}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
using System.Reflection;
|
||||
using System.Resources;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("CosmicCloneUI")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("CosmicCloneUI")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2018")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
//In order to begin building localizable applications, set
|
||||
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
|
||||
//inside a <PropertyGroup>. For example, if you are using US english
|
||||
//in your source files, set the <UICulture> to en-US. Then uncomment
|
||||
//the NeutralResourceLanguage attribute below. Update the "en-US" in
|
||||
//the line below to match the UICulture setting in the project file.
|
||||
|
||||
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
|
||||
|
||||
|
||||
[assembly: ThemeInfo(
|
||||
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||
//(used if a resource is not found in the page,
|
||||
// or application resource dictionaries)
|
||||
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||
//(used if a resource is not found in the page,
|
||||
// app, or any theme specific resource dictionaries)
|
||||
)]
|
||||
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
@ -0,0 +1,71 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace CosmicCloneUI.Properties
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources
|
||||
{
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((resourceMan == null))
|
||||
{
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("CosmicCloneUI.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture
|
||||
{
|
||||
get
|
||||
{
|
||||
return resourceCulture;
|
||||
}
|
||||
set
|
||||
{
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
|
@ -0,0 +1,30 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace CosmicCloneUI.Properties
|
||||
{
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
|
||||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
|
||||
{
|
||||
|
||||
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||
|
||||
public static Settings Default
|
||||
{
|
||||
get
|
||||
{
|
||||
return defaultInstance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
|
||||
<Profiles>
|
||||
<Profile Name="(Default)" />
|
||||
</Profiles>
|
||||
<Settings />
|
||||
</SettingsFile>
|
|
@ -0,0 +1,45 @@
|
|||
<Page x:Class="CosmicCloneUI.SourcePage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:CosmicCloneUI"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="320" d:DesignWidth="800"
|
||||
Title="SourcePage">
|
||||
|
||||
<Grid>
|
||||
<StackPanel Margin="20,20">
|
||||
<!--<Label Content="Enter Source connection details" FontSize="15" />-->
|
||||
<TextBlock FontSize="15">
|
||||
<Span>Enter</Span>
|
||||
<Span FontWeight="Bold">Source</Span>
|
||||
<Span>connection details</Span>
|
||||
</TextBlock>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,10,0,10">
|
||||
<Label Content="Url" Width="200" FontSize="15" />
|
||||
<TextBox x:Name="SourceURL" Width="400" VerticalContentAlignment="Center" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,0,0,10">
|
||||
<Label Content="Access Key" Width="200" FontSize="15" />
|
||||
<TextBox x:Name="SourceKey" Width="400" VerticalContentAlignment="Center" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,0,0,10">
|
||||
<Label Content="Database" Width="200" FontSize="15" />
|
||||
<TextBox x:Name="SourceDB" Width="400" VerticalContentAlignment="Center" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,0,0,10">
|
||||
<Label Content="Collection" Width="200" FontSize="15" />
|
||||
<TextBox x:Name="SourceCollection" Width="400" VerticalContentAlignment="Center" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<Image x:Name="ConnectionIcon" Width="30" Height="30" Margin="0,0,10,0"/>
|
||||
<Button x:Name="TestSrcButton" Width="150" Height="40" Click="BtnTestSource" HorizontalAlignment="Right" Margin="0,0,160,0">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="Test Connection" VerticalAlignment="Center" Margin="5" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Page>
|
|
@ -0,0 +1,62 @@
|
|||
using CosmosCloneCommon.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace CosmicCloneUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for SourcePage.xaml
|
||||
/// </summary>
|
||||
public partial class SourcePage : Page
|
||||
{
|
||||
CosmosDBHelper cosmosHelper;
|
||||
public SourcePage()
|
||||
{
|
||||
InitializeComponent();
|
||||
cosmosHelper = new CosmosDBHelper();
|
||||
}
|
||||
|
||||
private void BtnTestSource(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (TestConnection())
|
||||
{
|
||||
var connectionIcon = (Image)this.FindName("ConnectionIcon");
|
||||
ConnectionIcon.Source = new BitmapImage(new Uri("/Images/success.png", UriKind.Relative));
|
||||
}
|
||||
else
|
||||
{
|
||||
var connectionIcon = (Image)this.FindName("ConnectionIcon");
|
||||
ConnectionIcon.Source = new BitmapImage(new Uri("/Images/fail.png", UriKind.Relative));
|
||||
}
|
||||
}
|
||||
|
||||
private bool TestConnection()
|
||||
{
|
||||
CloneSettings.SourceSettings = new CosmosCollectionValues()
|
||||
{
|
||||
EndpointUrl = SourceURL.Text.ToString(),
|
||||
AccessKey = SourceKey.Text.ToString(),
|
||||
DatabaseName = SourceDB.Text.ToString(),
|
||||
CollectionName = SourceCollection.Text.ToString()
|
||||
};
|
||||
|
||||
var result = cosmosHelper.TestSourceConnection_v2();
|
||||
if (result.IsSuccess == true)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27703.2035
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CloneConsoleRun", "CloneConsoleRun\CloneConsoleRun.csproj", "{1F09A007-9A2E-40D8-AD24-C469473152F4}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CosmosCloneCommon", "CosmosCloneCommon\CosmosCloneCommon.csproj", "{523E1CC4-0286-498C-B184-3413E10D61DE}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CosmicCloneUI", "CosmicCloneUI\CosmicCloneUI.csproj", "{D46B0805-591E-4080-A6A1-45613067D900}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{1F09A007-9A2E-40D8-AD24-C469473152F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1F09A007-9A2E-40D8-AD24-C469473152F4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1F09A007-9A2E-40D8-AD24-C469473152F4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1F09A007-9A2E-40D8-AD24-C469473152F4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{523E1CC4-0286-498C-B184-3413E10D61DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{523E1CC4-0286-498C-B184-3413E10D61DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{523E1CC4-0286-498C-B184-3413E10D61DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{523E1CC4-0286-498C-B184-3413E10D61DE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D46B0805-591E-4080-A6A1-45613067D900}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D46B0805-591E-4080-A6A1-45613067D900}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D46B0805-591E-4080-A6A1-45613067D900}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D46B0805-591E-4080-A6A1-45613067D900}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {74BA2FD9-AD35-45D5-BC7E-7555517B9CDA}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
|
||||
</startup>
|
||||
</configuration>
|
|
@ -0,0 +1,9 @@
|
|||
<Application x:Class="CosmosClone.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:CosmosClone"
|
||||
StartupUri="MainWindow.xaml">
|
||||
<Application.Resources>
|
||||
|
||||
</Application.Resources>
|
||||
</Application>
|
|
@ -0,0 +1,17 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
|
||||
namespace CosmosClone
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for App.xaml
|
||||
/// </summary>
|
||||
public partial class App : Application
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{A9FEA8E3-D088-423D-91B1-0E205BF60970}</ProjectGuid>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<RootNamespace>CosmosClone</RootNamespace>
|
||||
<AssemblyName>CosmosClone</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xaml">
|
||||
<RequiredTargetFramework>4.0</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="WindowsBase" />
|
||||
<Reference Include="PresentationCore" />
|
||||
<Reference Include="PresentationFramework" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ApplicationDefinition Include="App.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</ApplicationDefinition>
|
||||
<Page Include="MainWindow.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Compile Include="App.xaml.cs">
|
||||
<DependentUpon>App.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="MainWindow.xaml.cs">
|
||||
<DependentUpon>MainWindow.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Properties\Settings.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
</Compile>
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
<None Include="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
|
@ -0,0 +1,13 @@
|
|||
<Window x:Class="CosmosClone.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:CosmosClone"
|
||||
mc:Ignorable="d"
|
||||
Title="MainWindow" Height="450" Width="800">
|
||||
<Grid>
|
||||
<Label Content="Cosmic Clone" HorizontalAlignment="Left" Margin="248,53,0,0" VerticalAlignment="Top" Width="236" FontSize="24"/>
|
||||
|
||||
</Grid>
|
||||
</Window>
|
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace CosmosClone
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for MainWindow.xaml
|
||||
/// </summary>
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
using System.Reflection;
|
||||
using System.Resources;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("CosmosClone")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("CosmosClone")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2018")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
//In order to begin building localizable applications, set
|
||||
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
|
||||
//inside a <PropertyGroup>. For example, if you are using US english
|
||||
//in your source files, set the <UICulture> to en-US. Then uncomment
|
||||
//the NeutralResourceLanguage attribute below. Update the "en-US" in
|
||||
//the line below to match the UICulture setting in the project file.
|
||||
|
||||
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
|
||||
|
||||
|
||||
[assembly: ThemeInfo(
|
||||
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||
//(used if a resource is not found in the page,
|
||||
// or application resource dictionaries)
|
||||
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||
//(used if a resource is not found in the page,
|
||||
// app, or any theme specific resource dictionaries)
|
||||
)]
|
||||
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
@ -0,0 +1,71 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace CosmosClone.Properties
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources
|
||||
{
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((resourceMan == null))
|
||||
{
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("CosmosClone.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture
|
||||
{
|
||||
get
|
||||
{
|
||||
return resourceCulture;
|
||||
}
|
||||
set
|
||||
{
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
|
@ -0,0 +1,30 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace CosmosClone.Properties
|
||||
{
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
|
||||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
|
||||
{
|
||||
|
||||
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||
|
||||
public static Settings Default
|
||||
{
|
||||
get
|
||||
{
|
||||
return defaultInstance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
|
||||
<Profiles>
|
||||
<Profile Name="(Default)" />
|
||||
</Profiles>
|
||||
<Settings />
|
||||
</SettingsFile>
|
|
@ -0,0 +1,109 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{523E1CC4-0286-498C-B184-3413E10D61DE}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>CosmosCloneCommon</RootNamespace>
|
||||
<AssemblyName>CosmosCloneCommon</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.Azure.CosmosDB.BulkImport, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Azure.CosmosDB.BulkExecutor.1.2.0\lib\net451\Microsoft.Azure.CosmosDB.BulkImport.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Azure.Documents.Client, Version=2.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Azure.DocumentDB.2.1.3\lib\net461\Microsoft.Azure.Documents.Client.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="MongoDB.Bson, Version=2.4.4.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\MongoDB.Bson.2.4.4\lib\net45\MongoDB.Bson.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Buffers.4.5.0\lib\netstandard2.0\System.Buffers.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Memory, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Memory.4.5.0\lib\netstandard2.0\System.Memory.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Net.Http.WinHttpHandler, Version=4.0.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Net.Http.WinHttpHandler.4.5.0\lib\net461\System.Net.Http.WinHttpHandler.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.Numerics.Vectors, Version=4.1.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Numerics.Vectors.4.4.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.4.5.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Migrator\CodeMigrator.cs" />
|
||||
<Compile Include="Migrator\DataScrubMigrator.cs" />
|
||||
<Compile Include="Migrator\DocumentMigrator.cs" />
|
||||
<Compile Include="Model\RandomNumberGenerator.cs" />
|
||||
<Compile Include="Model\ScrubRule.cs" />
|
||||
<Compile Include="Model\Validationresult.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Sample\EntityV2.cs" />
|
||||
<Compile Include="Sample\Entity.cs" />
|
||||
<Compile Include="Sample\SampleDBCreator.cs" />
|
||||
<Compile Include="Utility\CloneLogger.cs" />
|
||||
<Compile Include="Utility\CloneSerializer.cs" />
|
||||
<Compile Include="Utility\CloneSettings.cs" />
|
||||
<Compile Include="Utility\CosmosBulkImporter.cs" />
|
||||
<Compile Include="Utility\CosmosDBHelper.cs" />
|
||||
<Compile Include="Utility\ObjectScrubber.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
<None Include="packages.config">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="..\packages\Microsoft.Azure.DocumentDB.2.1.3\build\Microsoft.Azure.DocumentDB.targets" Condition="Exists('..\packages\Microsoft.Azure.DocumentDB.2.1.3\build\Microsoft.Azure.DocumentDB.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Azure.DocumentDB.2.1.3\build\Microsoft.Azure.DocumentDB.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Azure.DocumentDB.2.1.3\build\Microsoft.Azure.DocumentDB.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
|
@ -0,0 +1,209 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using logger = CosmosCloneCommon.Utility.CloneLogger;
|
||||
using CosmosCloneCommon.Utility;
|
||||
using Microsoft.Azure.Documents;
|
||||
using Microsoft.Azure.Documents.Client;
|
||||
|
||||
namespace CosmosCloneCommon.Migrator
|
||||
{
|
||||
public class CodeMigrator
|
||||
{
|
||||
#region declare variables
|
||||
private CosmosDBHelper cosmosHelper;
|
||||
private CosmosBulkImporter cosmosBulkImporter;
|
||||
protected DocumentClient sourceClient;
|
||||
protected DocumentClient targetClient;
|
||||
protected DocumentCollection sourceCollection;
|
||||
protected DocumentCollection targetCollection;
|
||||
private string SourceEndpointUrl;
|
||||
private string SourceAccessKey;
|
||||
private string sourceDatabaseName;
|
||||
private string sourceCollectionName;
|
||||
|
||||
private string TargetEndpointUrl;
|
||||
private string TargetAccessKey;
|
||||
private string TargetDatabaseName;
|
||||
private string TargetCollectionName;
|
||||
//private EntitySummary summary;
|
||||
|
||||
#endregion
|
||||
|
||||
public CodeMigrator()
|
||||
{
|
||||
//initialize settings and other utilities
|
||||
var SourceCosmosDBSettings = CloneSettings.GetConfigurationSection("SourceCosmosDBSettings");
|
||||
SourceEndpointUrl = CloneSettings.SourceSettings.EndpointUrl; ;
|
||||
SourceAccessKey = CloneSettings.SourceSettings.AccessKey;
|
||||
sourceDatabaseName = CloneSettings.SourceSettings.DatabaseName;
|
||||
sourceCollectionName = CloneSettings.SourceSettings.CollectionName;
|
||||
|
||||
//var TargetCosmosDBSettings = CloneSettings.GetConfigurationSection("TargetCosmosDBSettings");
|
||||
TargetEndpointUrl = CloneSettings.TargetSettings.EndpointUrl;
|
||||
TargetAccessKey = CloneSettings.TargetSettings.AccessKey;
|
||||
TargetDatabaseName = CloneSettings.TargetSettings.DatabaseName;
|
||||
TargetCollectionName = CloneSettings.TargetSettings.CollectionName;
|
||||
|
||||
cosmosHelper = new CosmosDBHelper();
|
||||
cosmosBulkImporter = new CosmosBulkImporter();
|
||||
//summary = new EntitySummary();
|
||||
//summary.EntityType = "DBCode";
|
||||
}
|
||||
private async Task Initialize()
|
||||
{
|
||||
logger.LogInfo("-----------------------------------------------");
|
||||
logger.LogInfo("Begin CosmosDBCodeMigrator");
|
||||
|
||||
sourceClient = cosmosHelper.GetSourceDocumentDbClient();
|
||||
sourceCollection = await cosmosHelper.GetSourceDocumentCollection(sourceClient);
|
||||
|
||||
targetClient = cosmosHelper.GetTargetDocumentDbClient();
|
||||
targetCollection = await cosmosHelper.CreateTargetDocumentCollection(targetClient, sourceCollection.IndexingPolicy, sourceCollection.PartitionKey);
|
||||
}
|
||||
|
||||
public async Task<bool> StartCopy()
|
||||
{
|
||||
await Initialize();
|
||||
if (CloneSettings.CopyStoredProcedures) { await CopyStoredProcedures(); }
|
||||
if (CloneSettings.CopyUDFs) { await CopyUDFs(); }
|
||||
if (CloneSettings.CopyTriggers) { await CopyTriggers(); }
|
||||
|
||||
//summary.isMigrationComplete = true;
|
||||
logger.LogInfo("CosmosDBCodeMigrator End");
|
||||
logger.LogInfo("-----------------------------------------------");
|
||||
//return summary;
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task CopyTriggers()
|
||||
{
|
||||
try
|
||||
{
|
||||
logger.LogInfo("-----------------------------------------------");
|
||||
logger.LogInfo("Begin CopyTriggers");
|
||||
FeedOptions feedOptions = new FeedOptions { MaxItemCount = -1, EnableCrossPartitionQuery = true };
|
||||
var requestOptions = new RequestOptions { OfferEnableRUPerMinuteThroughput = true };
|
||||
var triggerFeedResponse = await sourceClient.ReadTriggerFeedAsync(UriFactory.CreateDocumentCollectionUri(sourceDatabaseName, sourceCollectionName), feedOptions);
|
||||
var triggerList = triggerFeedResponse.ToList();
|
||||
logger.LogInfo($"Triggers retrieved from source {triggerList.Count}");
|
||||
//summary.totalRecordsRetrieved += triggerList.Count;
|
||||
|
||||
var targetResponse = await targetClient.ReadTriggerFeedAsync(UriFactory.CreateDocumentCollectionUri(TargetDatabaseName, TargetCollectionName), feedOptions);
|
||||
var targetTriggerList = targetResponse.ToList();
|
||||
logger.LogInfo($"Triggers already in target {targetTriggerList.Count}");
|
||||
var targetTriggerIds = new HashSet<string>();
|
||||
targetTriggerList.ForEach(sp => targetTriggerIds.Add(sp.Id));
|
||||
|
||||
foreach (var trigger in triggerList)
|
||||
{
|
||||
if (targetTriggerIds.Contains(trigger.Id))
|
||||
{
|
||||
logger.LogInfo($"Trigger {trigger.Id} already Exists in destination DB");
|
||||
continue;
|
||||
}
|
||||
logger.LogInfo($"Create Trigger {trigger.Id} start");
|
||||
await targetClient.CreateTriggerAsync(UriFactory.CreateDocumentCollectionUri(TargetDatabaseName, TargetCollectionName), trigger, requestOptions);
|
||||
logger.LogInfo($"Create Trigger {trigger.Id} complete");
|
||||
//summary.totalRecordsSent++;
|
||||
}
|
||||
logger.LogInfo("Copy Triggers end.");
|
||||
logger.LogInfo("");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogInfo("Exception while CopyTriggers");
|
||||
logger.LogError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CopyUDFs()
|
||||
{
|
||||
try
|
||||
{
|
||||
logger.LogInfo("-----------------------------------------------");
|
||||
logger.LogInfo("Begin CopyUDFs");
|
||||
FeedOptions feedOptions = new FeedOptions { MaxItemCount = -1, EnableCrossPartitionQuery = true };
|
||||
var udfFeedResponse = await sourceClient.ReadUserDefinedFunctionFeedAsync(UriFactory.CreateDocumentCollectionUri(sourceDatabaseName, sourceCollectionName), feedOptions);
|
||||
var udfList = udfFeedResponse.ToList<UserDefinedFunction>();
|
||||
logger.LogInfo($"UDFs retrieved from source {udfList.Count}");
|
||||
//summary.totalRecordsRetrieved += udfList.Count;
|
||||
|
||||
var targetResponse = await targetClient.ReadUserDefinedFunctionFeedAsync(UriFactory.CreateDocumentCollectionUri(TargetDatabaseName, TargetCollectionName), feedOptions);
|
||||
var targetUdfList = targetResponse.ToList();
|
||||
logger.LogInfo($"Triggers already in target {targetUdfList.Count}");
|
||||
var targetUDFIds = new HashSet<string>();
|
||||
targetUdfList.ForEach(sp => targetUDFIds.Add(sp.Id));
|
||||
|
||||
var requestOptions = new RequestOptions { OfferEnableRUPerMinuteThroughput = true };
|
||||
foreach (var udf in udfList)
|
||||
{
|
||||
if (targetUDFIds.Contains(udf.Id))
|
||||
{
|
||||
logger.LogInfo($"UDF {udf.Id} already Exists in destination DB");
|
||||
continue;
|
||||
}
|
||||
logger.LogInfo($"Create Trigger {udf.Id} start");
|
||||
await targetClient.CreateUserDefinedFunctionAsync(UriFactory.CreateDocumentCollectionUri(TargetDatabaseName, TargetCollectionName), udf, requestOptions);
|
||||
logger.LogInfo($"Create Trigger {udf.Id} complete");
|
||||
//summary.totalRecordsSent++;
|
||||
}
|
||||
logger.LogInfo("CopyUDFs end.");
|
||||
logger.LogInfo("");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogInfo("Exception while CopyUDFs");
|
||||
logger.LogError(ex);
|
||||
}
|
||||
}
|
||||
private async Task CopyStoredProcedures()
|
||||
{
|
||||
try
|
||||
{
|
||||
logger.LogInfo("-----------------------------------------------");
|
||||
logger.LogInfo("Begin CopyStoredProcedures");
|
||||
FeedOptions feedOptions = new FeedOptions { MaxItemCount = -1, EnableCrossPartitionQuery = true };
|
||||
var requestOptions = new RequestOptions { OfferEnableRUPerMinuteThroughput = true };
|
||||
var sourceResponse = await sourceClient.ReadStoredProcedureFeedAsync(UriFactory.CreateDocumentCollectionUri(sourceDatabaseName, sourceCollectionName), feedOptions);
|
||||
var splist = sourceResponse.ToList<StoredProcedure>();
|
||||
logger.LogInfo($"StoredProcedures retrieved from source {splist.Count}");
|
||||
//summary.totalRecordsRetrieved += splist.Count;
|
||||
|
||||
var targetResponse = await targetClient.ReadStoredProcedureFeedAsync(UriFactory.CreateDocumentCollectionUri(TargetDatabaseName, TargetCollectionName), feedOptions);
|
||||
var targetSPList = targetResponse.ToList();
|
||||
logger.LogInfo($"StoredProcedures already retrieved in target {targetSPList.Count}");
|
||||
var targetSPIds = new HashSet<string>();
|
||||
targetSPList.ForEach(sp => targetSPIds.Add(sp.Id));
|
||||
|
||||
foreach (var sp in splist)
|
||||
{
|
||||
if (targetSPIds.Contains(sp.Id))
|
||||
{
|
||||
logger.LogInfo($"StoredProcedure {sp.Id} already Exists in destination DB");
|
||||
continue;
|
||||
}
|
||||
logger.LogInfo($"Create StoredProcedure {sp.Id} start");
|
||||
await targetClient.CreateStoredProcedureAsync(UriFactory.CreateDocumentCollectionUri(TargetDatabaseName, TargetCollectionName), sp, requestOptions);
|
||||
logger.LogInfo($"Create StoredProcedure {sp.Id} complete");
|
||||
//summary.totalRecordsSent++;
|
||||
}
|
||||
logger.LogInfo("CopyStoredProcedures end.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogInfo("Exception while CopyStoredProcedures");
|
||||
logger.LogError(ex);
|
||||
logger.LogInfo("");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,176 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Azure.Documents;
|
||||
using Microsoft.Azure.Documents.Client;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.Azure.CosmosDB.BulkExecutor.BulkImport;
|
||||
using Microsoft.Azure.Documents.Linq;
|
||||
using CosmosCloneCommon.Utility;
|
||||
using CosmosCloneCommon.Model;
|
||||
using logger = CosmosCloneCommon.Utility.CloneLogger;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace CosmosCloneCommon.Migrator
|
||||
{
|
||||
public class DataScrubMigrator
|
||||
{
|
||||
#region declare variables
|
||||
protected int ReadDelaybetweenRequestsInMs = 2000;
|
||||
protected CosmosDBHelper cosmosHelper;
|
||||
protected CosmosBulkImporter cosmosBulkImporter;
|
||||
protected DocumentClient targetClient;
|
||||
protected DocumentCollection targetCollection;
|
||||
//protected ScrubRule scrubRule;
|
||||
protected IQueryable<string> ScrubDataFetchQuery;
|
||||
|
||||
public static List<ScrubRule> scrubRules;
|
||||
public long TotalRecordsRetrieved { get; set; }
|
||||
public long TotalRecordsScrubbed { get; set; }
|
||||
public long TotalRecordsToScrub { get; set; }
|
||||
#endregion
|
||||
public DataScrubMigrator()
|
||||
{
|
||||
cosmosHelper = new CosmosDBHelper();
|
||||
cosmosBulkImporter = new CosmosBulkImporter();
|
||||
}
|
||||
public async Task<bool> StartScrub(List<ScrubRule> scrubRules)
|
||||
{
|
||||
DataScrubMigrator.scrubRules = scrubRules;
|
||||
|
||||
if (!CloneSettings.ScrubbingRequired)
|
||||
{
|
||||
logger.LogInfo("No Scrubbing required");
|
||||
return false;
|
||||
}
|
||||
await InitializeMigration();
|
||||
//group by filtered Rules
|
||||
var distinctFilters = scrubRules.Select(o => o.FilterCondition).Distinct();
|
||||
//get distinct filterConditions
|
||||
//foreach filterCondition obtain set of rules and send at once
|
||||
foreach (var filterCondition in distinctFilters)
|
||||
{
|
||||
|
||||
logger.LogInfo($"Initialize process for scrub rule on filter {filterCondition}");
|
||||
var sRules = scrubRules.Where(o => o.FilterCondition.Equals(filterCondition)).ToList();
|
||||
logger.LogInfo($"Scrub rules found {sRules.Count}");
|
||||
ScrubDataFetchQuery = cosmosHelper.GetScrubDataDocumentQuery<string>(targetClient, filterCondition, CloneSettings.ReadBatchSize);
|
||||
await ReadUploadInbatches((IDocumentQuery<string>)ScrubDataFetchQuery, sRules);
|
||||
|
||||
foreach(var srule in DataScrubMigrator.scrubRules)
|
||||
{
|
||||
if(srule.FilterCondition.Equals(filterCondition))
|
||||
{
|
||||
srule.IsComplete = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
public async Task InitializeMigration()
|
||||
{
|
||||
logger.LogInfo("Initialize data scrubbing");
|
||||
targetClient = cosmosHelper.GetTargetDocumentDbClient();
|
||||
targetCollection = await cosmosHelper.GetTargetDocumentCollection(targetClient);
|
||||
await cosmosBulkImporter.InitializeBulkExecutor(targetClient, targetCollection);
|
||||
}
|
||||
|
||||
public async Task ReadUploadInbatches(IDocumentQuery<string> query, List<ScrubRule> scrubRules)
|
||||
{
|
||||
#region batchVariables
|
||||
//initialize Batch Process variables
|
||||
int batchCount = 0;
|
||||
TotalRecordsRetrieved = 0;
|
||||
TotalRecordsScrubbed = 0;
|
||||
var badEntities = new List<Object>();
|
||||
Stopwatch stopwatch = new Stopwatch();
|
||||
stopwatch.Start();
|
||||
var objScrubber = new ObjectScrubber();
|
||||
#endregion
|
||||
while (query.HasMoreResults)
|
||||
{
|
||||
batchCount++;
|
||||
logger.LogInfo($"BatchNumber : {batchCount} begins ");
|
||||
List<string> entities = await GetCommonStringEntitiesinBatch(query);
|
||||
TotalRecordsRetrieved += entities.Count();
|
||||
BulkImportResponse uploadResponse = new BulkImportResponse();
|
||||
var scrubbedEntities = entities;
|
||||
if (entities.Any())
|
||||
{
|
||||
var jEntities = new List<JToken>();
|
||||
foreach (var scrubRule in scrubRules)
|
||||
{
|
||||
jEntities = objScrubber.ScrubObjectList(scrubbedEntities, scrubRule);
|
||||
var nentities = new List<string>();
|
||||
foreach (var jobj in jEntities)
|
||||
{
|
||||
nentities.Add(JsonConvert.SerializeObject(jobj));
|
||||
}
|
||||
scrubbedEntities = nentities;
|
||||
}
|
||||
var objEntities = jEntities.Cast<Object>().ToList();
|
||||
try
|
||||
{
|
||||
uploadResponse = await cosmosBulkImporter.BulkSendToNewCollection<dynamic>(objEntities);
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
logger.LogError(ex);
|
||||
throw (ex);
|
||||
}
|
||||
|
||||
}
|
||||
badEntities = uploadResponse.BadInputDocuments;
|
||||
TotalRecordsScrubbed += uploadResponse.NumberOfDocumentsImported;
|
||||
|
||||
logger.LogInfo($"Summary of Batch {batchCount} records retrieved {entities.Count()}. Records Uploaded: {uploadResponse.NumberOfDocumentsImported}");
|
||||
logger.LogInfo($"Total records retrieved {TotalRecordsRetrieved}. Total records uploaded {TotalRecordsScrubbed}");
|
||||
logger.LogInfo($"Time elapsed : {stopwatch.Elapsed} ");
|
||||
}
|
||||
|
||||
stopwatch.Stop();
|
||||
logger.LogInfo("Document Scrubbing completed");
|
||||
}
|
||||
|
||||
protected async Task<List<string>> GetCommonStringEntitiesinBatch(IDocumentQuery<string> query)
|
||||
{
|
||||
List<string> entities = new List<string>();
|
||||
List<object> objEntities = new List<object>();
|
||||
int attempts = 0;
|
||||
try
|
||||
{
|
||||
while (query.HasMoreResults && objEntities.Count < CloneSettings.WriteBatchSize)
|
||||
{
|
||||
attempts++;
|
||||
var prevRecordCount = entities.Count;
|
||||
var res = await query.ExecuteNextAsync();
|
||||
objEntities.AddRange((IEnumerable<object>)res);
|
||||
logger.LogInfo($"Records retrieved from source: {objEntities.Count - prevRecordCount}");
|
||||
}
|
||||
foreach(var obj in objEntities)
|
||||
{
|
||||
entities.Add(JsonConvert.SerializeObject(obj));
|
||||
}
|
||||
logger.LogInfo($"Total Records retrieved from Source {entities.Count}");
|
||||
return entities;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogInfo("Exception while fetching Entities from source. Will Increase delay time and continue");
|
||||
logger.LogError(ex);
|
||||
ReadDelaybetweenRequestsInMs += 2000;
|
||||
System.Threading.Thread.Sleep(ReadDelaybetweenRequestsInMs);
|
||||
return entities;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,398 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Azure.Documents;
|
||||
using Microsoft.Azure.Documents.Client;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.Azure.CosmosDB.BulkExecutor;
|
||||
using Microsoft.Azure.CosmosDB.BulkExecutor.BulkImport;
|
||||
using Microsoft.Azure.Documents.Linq;
|
||||
using CosmosCloneCommon.Utility;
|
||||
using CosmosCloneCommon.Model;
|
||||
using Newtonsoft.Json;
|
||||
using logger = CosmosCloneCommon.Utility.CloneLogger;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace CosmosCloneCommon.Migrator
|
||||
{
|
||||
public class DocumentMigrator
|
||||
{
|
||||
#region declare variables
|
||||
protected int ReadDelaybetweenRequestsInMs = 2000;
|
||||
|
||||
protected CosmosDBHelper cosmosHelper;
|
||||
protected CosmosBulkImporter cosmosBulkImporter;
|
||||
protected DocumentClient sourceClient;
|
||||
|
||||
protected DocumentClient targetClient;
|
||||
|
||||
protected DocumentCollection sourceCollection;
|
||||
protected DocumentCollection targetCollection;
|
||||
protected ObjectScrubber objectScrubber;
|
||||
//protected static List<ScrubRule> scrubRules;
|
||||
protected List<ScrubRule> noFilterScrubRules;
|
||||
protected List<ScrubRule> filteredScrubRules;
|
||||
|
||||
|
||||
protected IQueryable<dynamic> SourceCommonDataFetchQuery;
|
||||
|
||||
public static List<ScrubRule> scrubRules;
|
||||
public static long TotalRecordsRetrieved { get; set; }
|
||||
public static long TotalRecordsSent { get; set; }
|
||||
public static long TotalRecordsInSource { get; set; }
|
||||
public static bool IsCodeMigrationComplete { get; set; }
|
||||
|
||||
public static int ScrubPercentProgress
|
||||
{
|
||||
get
|
||||
{
|
||||
if (scrubRules != null && scrubRules.Count > 0)
|
||||
{
|
||||
int totalRules = scrubRules.Count();
|
||||
int noFilterRuleCompleteCount = scrubRules.Where(x => x.IsComplete == true && string.IsNullOrEmpty(x.FilterCondition) ).ToList().Count();
|
||||
int filterRuleCompleteCount = 0;
|
||||
if(DataScrubMigrator.scrubRules!=null && DataScrubMigrator.scrubRules.Count>0)
|
||||
{
|
||||
filterRuleCompleteCount = DataScrubMigrator.scrubRules.Where(x => x.IsComplete == true).ToList().Count();
|
||||
}
|
||||
|
||||
var completedRules = scrubRules.Where(x => x.IsComplete == true).ToList().Count();
|
||||
int percent = (int)((noFilterRuleCompleteCount + filterRuleCompleteCount) * 100 / scrubRules.Count());
|
||||
return percent;
|
||||
}
|
||||
else return 100;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public DocumentMigrator()
|
||||
{
|
||||
cosmosHelper = new CosmosDBHelper();
|
||||
cosmosBulkImporter = new CosmosBulkImporter();
|
||||
objectScrubber = new ObjectScrubber();
|
||||
}
|
||||
public async Task<bool> StartCopy(List<ScrubRule> scrubRules = null)
|
||||
{
|
||||
DocumentMigrator.scrubRules = scrubRules;
|
||||
|
||||
await InitializeMigration();
|
||||
if (CloneSettings.CopyDocuments)
|
||||
{
|
||||
await ReadUploadInbatches((IDocumentQuery<dynamic>)SourceCommonDataFetchQuery);
|
||||
}
|
||||
IsCodeMigrationComplete = false;
|
||||
if (CloneSettings.CopyStoredProcedures) { await CopyStoredProcedures(); }
|
||||
if (CloneSettings.CopyUDFs) { await CopyUDFs(); }
|
||||
if (CloneSettings.CopyTriggers) { await CopyTriggers(); }
|
||||
|
||||
IsCodeMigrationComplete = true;
|
||||
if (CloneSettings.ScrubbingRequired && filteredScrubRules != null && filteredScrubRules.Count > 0)
|
||||
{
|
||||
var dcs = new DataScrubMigrator();
|
||||
var result = dcs.StartScrub(filteredScrubRules);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public async Task InitializeMigration()
|
||||
{
|
||||
logger.LogInfo("Begin Document Migration.");
|
||||
logger.LogInfo($"Source Database: {CloneSettings.SourceSettings.DatabaseName} Source Collection: {CloneSettings.SourceSettings.CollectionName}");
|
||||
logger.LogInfo($"Target Database: {CloneSettings.TargetSettings.DatabaseName} Target Collection: {CloneSettings.TargetSettings.CollectionName}");
|
||||
|
||||
sourceClient = cosmosHelper.GetSourceDocumentDbClient();
|
||||
sourceCollection = await cosmosHelper.GetSourceDocumentCollection(sourceClient);
|
||||
|
||||
targetClient = cosmosHelper.GetTargetDocumentDbClient();
|
||||
var indexPolicy = (CloneSettings.CopyIndexingPolicy)? sourceCollection.IndexingPolicy : new IndexingPolicy();
|
||||
targetCollection = await cosmosHelper.CreateTargetDocumentCollection(targetClient, indexPolicy, sourceCollection.PartitionKey);
|
||||
|
||||
if (CloneSettings.CopyDocuments)
|
||||
{
|
||||
TotalRecordsInSource = cosmosHelper.GetSourceRecordCount();
|
||||
logger.LogInfo($"Total records in Source: {TotalRecordsInSource} ");
|
||||
SourceCommonDataFetchQuery = cosmosHelper.GetSourceEntityDocumentQuery<dynamic>(sourceClient, CloneSettings.ReadBatchSize);
|
||||
await cosmosBulkImporter.InitializeBulkExecutor(targetClient, targetCollection);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogInfo("Document Migration is disabled through configuration. ");
|
||||
}
|
||||
|
||||
if (CloneSettings.ScrubbingRequired && scrubRules != null && scrubRules.Count > 0)
|
||||
{
|
||||
noFilterScrubRules = new List<ScrubRule>();
|
||||
filteredScrubRules = new List<ScrubRule>();
|
||||
foreach (var sRule in scrubRules)
|
||||
{
|
||||
if(string.IsNullOrEmpty(sRule.FilterCondition))
|
||||
{
|
||||
noFilterScrubRules.Add(sRule);
|
||||
}
|
||||
else
|
||||
{
|
||||
filteredScrubRules.Add(sRule);
|
||||
//DataScrubMigrator.scrubRules.Add(sRule);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public async Task ReadUploadInbatches(IDocumentQuery<dynamic> query)
|
||||
{
|
||||
|
||||
#region batchVariables
|
||||
//initialize Batch Process variables
|
||||
int batchCount = 0;
|
||||
TotalRecordsRetrieved = 0;
|
||||
TotalRecordsSent = 0;
|
||||
var badEntities = new List<Object>();
|
||||
Stopwatch stopwatch = new Stopwatch();
|
||||
stopwatch.Start();
|
||||
#endregion
|
||||
while (query.HasMoreResults)
|
||||
{
|
||||
batchCount++;
|
||||
logger.LogInfo($"BatchNumber : {batchCount} begins ");
|
||||
List<dynamic> entities = await GetCommonEntitiesinBatch(query);
|
||||
TotalRecordsRetrieved += entities.Count();
|
||||
List<object> objEntities = new List<object>();
|
||||
objEntities.AddRange((IEnumerable<object>)entities);
|
||||
List<string> strEntities = new List<string>();
|
||||
foreach (var obj in objEntities)
|
||||
{
|
||||
strEntities.Add(JsonConvert.SerializeObject(obj));
|
||||
}
|
||||
|
||||
BulkImportResponse uploadResponse = new BulkImportResponse();
|
||||
var scrubbedEntities = strEntities;
|
||||
if (entities.Any())
|
||||
{
|
||||
if( noFilterScrubRules == null || noFilterScrubRules.Count==0)
|
||||
{
|
||||
uploadResponse = await cosmosBulkImporter.BulkSendToNewCollection<dynamic>(entities);
|
||||
}
|
||||
else
|
||||
{
|
||||
var jEntities = new List<JToken>();
|
||||
foreach (var sRule in noFilterScrubRules)
|
||||
{
|
||||
jEntities = objectScrubber.ScrubObjectList(scrubbedEntities, sRule);
|
||||
var nentities = new List<string>();
|
||||
foreach (var jobj in jEntities)
|
||||
{
|
||||
nentities.Add(JsonConvert.SerializeObject(jobj));
|
||||
}
|
||||
scrubbedEntities = nentities;
|
||||
}
|
||||
var objDocuments = jEntities.Cast<Object>().ToList();
|
||||
uploadResponse = await cosmosBulkImporter.BulkSendToNewCollection<dynamic>(objDocuments);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogInfo("No Entities retrieved from query");
|
||||
continue;
|
||||
}
|
||||
badEntities = uploadResponse.BadInputDocuments;
|
||||
TotalRecordsSent += uploadResponse.NumberOfDocumentsImported;
|
||||
|
||||
logger.LogInfo($"Summary of Batch {batchCount} records retrieved {entities.Count()}. Records Uploaded: {uploadResponse.NumberOfDocumentsImported}");
|
||||
logger.LogInfo($"Total records retrieved {TotalRecordsRetrieved}. Total records uploaded {TotalRecordsSent}");
|
||||
logger.LogInfo($"Time elapsed : {stopwatch.Elapsed} ");
|
||||
}
|
||||
setCompleteOnNoFilterRules();
|
||||
stopwatch.Stop();
|
||||
logger.LogInfo("Document Migration completed");
|
||||
}
|
||||
|
||||
public async Task TestOffers()
|
||||
{
|
||||
//sourceClient = cosmosHelper.GetSourceDocumentDbClient();
|
||||
//sourceCollection = await cosmosHelper.GetSourceDocumentCollection(sourceClient);
|
||||
var setCorrect = await cosmosHelper.SetTargetRestOfferThroughPut();
|
||||
}
|
||||
public bool setCompleteOnNoFilterRules()
|
||||
{
|
||||
if (scrubRules != null && scrubRules.Count > 0)
|
||||
{
|
||||
foreach (var sRule in scrubRules)
|
||||
{
|
||||
if (string.IsNullOrEmpty(sRule.FilterCondition))
|
||||
{
|
||||
sRule.IsComplete = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.noFilterScrubRules != null && this.noFilterScrubRules.Count > 0)
|
||||
{
|
||||
foreach (var sRule in this.noFilterScrubRules)
|
||||
{
|
||||
if (string.IsNullOrEmpty(sRule.FilterCondition))
|
||||
{
|
||||
sRule.IsComplete = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
protected async Task<List<dynamic>> GetCommonEntitiesinBatch(IDocumentQuery<dynamic> query)
|
||||
{
|
||||
List<dynamic> entities = new List<dynamic>();
|
||||
int attempts = 0;
|
||||
try
|
||||
{
|
||||
while (query.HasMoreResults && entities.Count < CloneSettings.WriteBatchSize)
|
||||
{
|
||||
attempts++;
|
||||
var prevRecordCount = entities.Count;
|
||||
var res = await query.ExecuteNextAsync();
|
||||
entities.AddRange((IEnumerable<dynamic>)res);
|
||||
logger.LogInfo($"Records retrieved from source: {entities.Count - prevRecordCount}");
|
||||
}
|
||||
logger.LogInfo($"Total Records retrieved from Source {entities.Count}");
|
||||
return entities;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogInfo("Exception while fetching Entities from source. Will Increase delay time and continue");
|
||||
logger.LogError(ex);
|
||||
ReadDelaybetweenRequestsInMs += 2000;//increased delay
|
||||
System.Threading.Thread.Sleep(ReadDelaybetweenRequestsInMs);
|
||||
return entities;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CopyTriggers()
|
||||
{
|
||||
try
|
||||
{
|
||||
logger.LogInfo("-----------------------------------------------");
|
||||
logger.LogInfo("Begin CopyTriggers");
|
||||
FeedOptions feedOptions = new FeedOptions { MaxItemCount = -1, EnableCrossPartitionQuery = true };
|
||||
var requestOptions = new RequestOptions { OfferEnableRUPerMinuteThroughput = true };
|
||||
var triggerFeedResponse = await sourceClient.ReadTriggerFeedAsync(UriFactory.CreateDocumentCollectionUri(CloneSettings.SourceSettings.DatabaseName, CloneSettings.SourceSettings.CollectionName), feedOptions);
|
||||
var triggerList = triggerFeedResponse.ToList();
|
||||
logger.LogInfo($"Triggers retrieved from source {triggerList.Count}");
|
||||
//summary.totalRecordsRetrieved += triggerList.Count;
|
||||
|
||||
var targetResponse = await targetClient.ReadTriggerFeedAsync(UriFactory.CreateDocumentCollectionUri(CloneSettings.TargetSettings.DatabaseName, CloneSettings.TargetSettings.CollectionName), feedOptions);
|
||||
var targetTriggerList = targetResponse.ToList();
|
||||
logger.LogInfo($"Triggers already in target {targetTriggerList.Count}");
|
||||
var targetTriggerIds = new HashSet<string>();
|
||||
targetTriggerList.ForEach(sp => targetTriggerIds.Add(sp.Id));
|
||||
|
||||
foreach (var trigger in triggerList)
|
||||
{
|
||||
if (targetTriggerIds.Contains(trigger.Id))
|
||||
{
|
||||
logger.LogInfo($"Trigger {trigger.Id} already Exists in destination DB");
|
||||
continue;
|
||||
}
|
||||
logger.LogInfo($"Create Trigger {trigger.Id} start");
|
||||
await targetClient.CreateTriggerAsync(UriFactory.CreateDocumentCollectionUri(CloneSettings.TargetSettings.DatabaseName, CloneSettings.TargetSettings.CollectionName), trigger, requestOptions);
|
||||
logger.LogInfo($"Create Trigger {trigger.Id} complete");
|
||||
//summary.totalRecordsSent++;
|
||||
}
|
||||
logger.LogInfo("Copy Triggers end.");
|
||||
logger.LogInfo("");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogInfo("Exception while CopyTriggers");
|
||||
logger.LogError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CopyUDFs()
|
||||
{
|
||||
try
|
||||
{
|
||||
logger.LogInfo("-----------------------------------------------");
|
||||
logger.LogInfo("Begin CopyUDFs");
|
||||
FeedOptions feedOptions = new FeedOptions { MaxItemCount = -1, EnableCrossPartitionQuery = true };
|
||||
var udfFeedResponse = await sourceClient.ReadUserDefinedFunctionFeedAsync(UriFactory.CreateDocumentCollectionUri(CloneSettings.SourceSettings.DatabaseName, CloneSettings.SourceSettings.CollectionName), feedOptions);
|
||||
var udfList = udfFeedResponse.ToList<UserDefinedFunction>();
|
||||
logger.LogInfo($"UDFs retrieved from source {udfList.Count}");
|
||||
//summary.totalRecordsRetrieved += udfList.Count;
|
||||
|
||||
var targetResponse = await targetClient.ReadUserDefinedFunctionFeedAsync(UriFactory.CreateDocumentCollectionUri(CloneSettings.TargetSettings.DatabaseName, CloneSettings.TargetSettings.CollectionName), feedOptions);
|
||||
var targetUdfList = targetResponse.ToList();
|
||||
logger.LogInfo($"Triggers already in target {targetUdfList.Count}");
|
||||
var targetUDFIds = new HashSet<string>();
|
||||
targetUdfList.ForEach(sp => targetUDFIds.Add(sp.Id));
|
||||
|
||||
var requestOptions = new RequestOptions { OfferEnableRUPerMinuteThroughput = true };
|
||||
foreach (var udf in udfList)
|
||||
{
|
||||
if (targetUDFIds.Contains(udf.Id))
|
||||
{
|
||||
logger.LogInfo($"UDF {udf.Id} already Exists in destination DB");
|
||||
continue;
|
||||
}
|
||||
logger.LogInfo($"Create Trigger {udf.Id} start");
|
||||
await targetClient.CreateUserDefinedFunctionAsync(UriFactory.CreateDocumentCollectionUri(CloneSettings.TargetSettings.DatabaseName, CloneSettings.TargetSettings.CollectionName), udf, requestOptions);
|
||||
logger.LogInfo($"Create Trigger {udf.Id} complete");
|
||||
//summary.totalRecordsSent++;
|
||||
}
|
||||
logger.LogInfo("CopyUDFs end.");
|
||||
logger.LogInfo("");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogInfo("Exception while CopyUDFs");
|
||||
logger.LogError(ex);
|
||||
}
|
||||
}
|
||||
private async Task CopyStoredProcedures()
|
||||
{
|
||||
try
|
||||
{
|
||||
logger.LogInfo("-----------------------------------------------");
|
||||
logger.LogInfo("Begin CopyStoredProcedures");
|
||||
FeedOptions feedOptions = new FeedOptions { MaxItemCount = -1, EnableCrossPartitionQuery = true };
|
||||
var requestOptions = new RequestOptions { OfferEnableRUPerMinuteThroughput = true };
|
||||
var sourceResponse = await sourceClient.ReadStoredProcedureFeedAsync(UriFactory.CreateDocumentCollectionUri(CloneSettings.SourceSettings.DatabaseName, CloneSettings.SourceSettings.CollectionName), feedOptions);
|
||||
var splist = sourceResponse.ToList<StoredProcedure>();
|
||||
logger.LogInfo($"StoredProcedures retrieved from source {splist.Count}");
|
||||
//summary.totalRecordsRetrieved += splist.Count;
|
||||
|
||||
var targetResponse = await targetClient.ReadStoredProcedureFeedAsync(UriFactory.CreateDocumentCollectionUri(CloneSettings.TargetSettings.DatabaseName, CloneSettings.TargetSettings.CollectionName), feedOptions);
|
||||
var targetSPList = targetResponse.ToList();
|
||||
logger.LogInfo($"StoredProcedures already retrieved in target {targetSPList.Count}");
|
||||
var targetSPIds = new HashSet<string>();
|
||||
targetSPList.ForEach(sp => targetSPIds.Add(sp.Id));
|
||||
|
||||
foreach (var sp in splist)
|
||||
{
|
||||
if (targetSPIds.Contains(sp.Id))
|
||||
{
|
||||
logger.LogInfo($"StoredProcedure {sp.Id} already Exists in destination DB");
|
||||
continue;
|
||||
}
|
||||
logger.LogInfo($"Create StoredProcedure {sp.Id} start");
|
||||
await targetClient.CreateStoredProcedureAsync(UriFactory.CreateDocumentCollectionUri(CloneSettings.TargetSettings.DatabaseName, CloneSettings.TargetSettings.CollectionName), sp, requestOptions);
|
||||
logger.LogInfo($"Create StoredProcedure {sp.Id} complete");
|
||||
//summary.totalRecordsSent++;
|
||||
}
|
||||
logger.LogInfo("CopyStoredProcedures end.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogInfo("Exception while CopyStoredProcedures");
|
||||
logger.LogError(ex);
|
||||
logger.LogInfo("");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CosmosCloneCommon.Model
|
||||
{
|
||||
public static class RandomNumberGenerator
|
||||
{
|
||||
private static Random _random;
|
||||
static RandomNumberGenerator()
|
||||
{
|
||||
_random = new Random(unchecked(Environment.TickCount * 31 + Thread.CurrentThread.ManagedThreadId));
|
||||
_random.Next();
|
||||
}
|
||||
public static int getNext(int maxValue)
|
||||
{
|
||||
_random.Next();
|
||||
_random.Next(0, maxValue);
|
||||
return _random.Next(0,maxValue);
|
||||
}
|
||||
public static int getNext()
|
||||
{
|
||||
return _random.Next(1, 99999999);
|
||||
}
|
||||
public static string GetRandomEntityType()
|
||||
{
|
||||
string returnvalue;
|
||||
switch (RandomNumberGenerator.getNext() % 5)
|
||||
{
|
||||
case 1:
|
||||
returnvalue = "Individual"; break;
|
||||
case 2:
|
||||
returnvalue = "Organization"; break;
|
||||
case 3:
|
||||
returnvalue = "Supplier"; break;
|
||||
case 4:
|
||||
returnvalue = "Partner"; break;
|
||||
case 0:
|
||||
returnvalue = "External"; break;
|
||||
default:
|
||||
returnvalue = "External"; break;
|
||||
}
|
||||
return returnvalue;
|
||||
}
|
||||
|
||||
//A simple implementation of Fisher yates algorithm for shuffling
|
||||
public static List<T> Shuffle<T>(List<T> list)
|
||||
{
|
||||
int n = list.Count-1;
|
||||
while (n > 1)
|
||||
{
|
||||
n--;
|
||||
int k = RandomNumberGenerator.getNext(n + 1);
|
||||
T value = list[k];
|
||||
list[k] = list[n];
|
||||
list[n] = value;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CosmosCloneCommon.Model
|
||||
{
|
||||
public class ScrubRule
|
||||
{
|
||||
public int RuleId { get; set; }
|
||||
public string FilterCondition { get; set; }
|
||||
public string PropertyName { get; set; }
|
||||
public string UpdateValue { get; set; }
|
||||
|
||||
public RuleType? Type { get; set; }
|
||||
|
||||
private int RecordsByFilter { get; set; }
|
||||
private int RecordsUpdated { get; set; }
|
||||
private int CountNullAttributes { get; set; }
|
||||
public bool IsComplete { get; set; }
|
||||
|
||||
public ScrubRule() { }
|
||||
public ScrubRule(string filterCondition, string propertyName, RuleType type, string updateValue, int ruleId)
|
||||
{
|
||||
|
||||
this.FilterCondition = filterCondition;
|
||||
this.PropertyName = propertyName;
|
||||
this.UpdateValue = updateValue;
|
||||
this.Type = type;
|
||||
this.RuleId = ruleId;
|
||||
this.IsComplete = false;
|
||||
}
|
||||
}
|
||||
public enum RuleType { SingleValue, NullValue, Shuffle };//Can add random later if required.
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CosmosCloneCommon.Model
|
||||
{
|
||||
public class ValidationResult
|
||||
{
|
||||
public string Message { get; set; }
|
||||
public bool IsSuccess { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("CosmosCloneCommon")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("CosmosCloneCommon")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2018")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("523e1cc4-0286-498c-b184-3413e10d61de")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
@ -0,0 +1,112 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using CosmosCloneCommon.Model;
|
||||
|
||||
namespace CosmosCloneCommon.Sample
|
||||
{
|
||||
public class Entity
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Description { get; set; }
|
||||
public Boolean IsActive { get; set; }
|
||||
public DateTime ModifiedTime { get; set; }
|
||||
|
||||
public List<EntityKey> BaseKeys { get; set; }
|
||||
|
||||
public static Entity getRandomEntity()
|
||||
{
|
||||
var entity = new Entity();
|
||||
entity.Id = RandomNumberGenerator.getNext();
|
||||
entity.Name = "Test Sample Name " + entity.Id.ToString();
|
||||
entity.Description = "Test Sample Description " + entity.Id.ToString();
|
||||
entity.IsActive = true;
|
||||
entity.ModifiedTime = DateTime.UtcNow;
|
||||
|
||||
entity.BaseKeys = new List<EntityKey>();
|
||||
entity.BaseKeys.Add(EntityKey.getRandomEntityKey());
|
||||
entity.BaseKeys.Add(EntityKey.getRandomEntityKey());
|
||||
entity.BaseKeys.Add(EntityKey.getRandomEntityKey());
|
||||
entity.BaseKeys.Add(EntityKey.getRandomEntityKey());
|
||||
entity.BaseKeys.Add(EntityKey.getRandomEntityKey());
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public class CompositeEntity
|
||||
{
|
||||
public string id { get; set; }
|
||||
public string _etag { get; set; }
|
||||
public string CompositeName { get; set; }
|
||||
public string EmployeeId { get; set; }
|
||||
public string EmailAddress { get; set; }
|
||||
public string EntityType { get; set; }
|
||||
public Entity EntityValue { get; set; }
|
||||
public List<EntityKey> SuperKeys { get; set; }
|
||||
|
||||
public static CompositeEntity getRandomCompositeEntity()
|
||||
{
|
||||
var compositeEntity = new CompositeEntity();
|
||||
compositeEntity.id = Guid.NewGuid().ToString();
|
||||
compositeEntity.EmployeeId = RandomNumberGenerator.getNext().ToString();
|
||||
compositeEntity.EntityType = RandomNumberGenerator.GetRandomEntityType();
|
||||
compositeEntity.CompositeName = "Test Sample CompositeName " + RandomNumberGenerator.getNext();
|
||||
compositeEntity.EmailAddress = "test" + RandomNumberGenerator.getNext() + "@microsoft.com";
|
||||
compositeEntity.EntityValue = Entity.getRandomEntity();
|
||||
|
||||
compositeEntity.SuperKeys = new List<EntityKey>();
|
||||
compositeEntity.SuperKeys.Add(EntityKey.getRandomEntityKey());
|
||||
compositeEntity.SuperKeys.Add(EntityKey.getRandomEntityKey());
|
||||
compositeEntity.SuperKeys.Add(EntityKey.getRandomEntityKey());
|
||||
compositeEntity.SuperKeys.Add(EntityKey.getRandomEntityKey());
|
||||
compositeEntity.SuperKeys.Add(EntityKey.getRandomEntityKey());
|
||||
return compositeEntity;
|
||||
}
|
||||
|
||||
}
|
||||
public class EntityKey
|
||||
{
|
||||
public string KeyName { get; set; }
|
||||
public string KeyValue { get; set; }
|
||||
public string KeyId { get; set; }
|
||||
public static EntityKey getRandomEntityKey()
|
||||
{
|
||||
var entityKey = new EntityKey();
|
||||
entityKey.KeyId = RandomNumberGenerator.getNext().ToString();
|
||||
entityKey.KeyName = "TestKeyName-" + entityKey.KeyId.ToString();
|
||||
entityKey.KeyValue = "TestKeyValue-" + entityKey.KeyId.ToString();
|
||||
return entityKey;
|
||||
}
|
||||
}
|
||||
|
||||
public class Entitytest
|
||||
{
|
||||
public Entity EntityValue { get; set; }
|
||||
public List<string> Words { get; set; }
|
||||
public static Entitytest getRandomTestEntity()
|
||||
{
|
||||
var testEntity = new Entitytest();
|
||||
testEntity.EntityValue = Entity.getRandomEntity();
|
||||
testEntity.Words = new List<string>();
|
||||
testEntity.Words.Add("test123");
|
||||
testEntity.Words.Add("value123");
|
||||
testEntity.Words.Add("rex123");
|
||||
return testEntity;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using CosmosCloneCommon.Model;
|
||||
|
||||
namespace CosmosCloneCommon.Sample
|
||||
{
|
||||
public class EntityV2
|
||||
{
|
||||
public string FullName { get; set; }
|
||||
public string Description { get; set; }
|
||||
public Boolean IsActive { get; set; }
|
||||
public DateTime ModifiedTime { get; set; }
|
||||
public string EmployeeId { get; set; }
|
||||
public string EmailAddress { get; set; }
|
||||
public string PhoneNumber { get; set; }
|
||||
public List<EntityKey> BaseKeys { get; set; }
|
||||
public string _etag { get; set; }
|
||||
public int SuperId { get; set; }
|
||||
public string id { get; set; }
|
||||
public string EntityType { get; set; }
|
||||
public static EntityV2 getRandomEntity()
|
||||
{
|
||||
var entity = new EntityV2();
|
||||
entity.SuperId = RandomNumberGenerator.getNext();
|
||||
entity.id = Guid.NewGuid().ToString();
|
||||
entity.FullName = "Test Sample Name " + entity.SuperId.ToString();
|
||||
entity.Description = "Test Sample Description " + entity.SuperId.ToString();
|
||||
entity.EntityType = RandomNumberGenerator.GetRandomEntityType();
|
||||
var employeeid = RandomNumberGenerator.getNext();
|
||||
entity.EmailAddress = "intialTest"+ employeeid .ToString()+ "@test.com";
|
||||
entity.PhoneNumber = RandomNumberGenerator.getNext().ToString();
|
||||
entity.IsActive = true;
|
||||
entity.ModifiedTime = DateTime.UtcNow;
|
||||
|
||||
entity.BaseKeys = new List<EntityKey>();
|
||||
entity.BaseKeys.Add(EntityKey.getRandomEntityKey());
|
||||
entity.BaseKeys.Add(EntityKey.getRandomEntityKey());
|
||||
entity.BaseKeys.Add(EntityKey.getRandomEntityKey());
|
||||
entity.BaseKeys.Add(EntityKey.getRandomEntityKey());
|
||||
entity.BaseKeys.Add(EntityKey.getRandomEntityKey());
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Azure.Documents;
|
||||
using Microsoft.Azure.Documents.Client;
|
||||
using CosmosCloneCommon.Utility;
|
||||
//using CollectionMigrator.Model;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.Azure.CosmosDB.BulkExecutor;
|
||||
using Microsoft.Azure.CosmosDB.BulkExecutor.BulkImport;
|
||||
using Microsoft.Azure.Documents.Linq;
|
||||
using logger = CosmosCloneCommon.Utility.CloneLogger;
|
||||
|
||||
namespace CosmosCloneCommon.Sample
|
||||
{
|
||||
public class SampleDBCreator
|
||||
{
|
||||
#region declare variables
|
||||
protected int WriteBatchSize = 10000;
|
||||
protected int ReadDelaybetweenRequestsInMs = 2000;
|
||||
protected int maxtestDocumentCount = 50000;
|
||||
protected bool IsFixedCollection = true;
|
||||
protected CosmosDBHelper cosmosHelper;
|
||||
protected CosmosBulkImporter cosmosBulkImporter;
|
||||
protected DocumentClient sampleClient;
|
||||
protected DocumentCollection sampleCollection;
|
||||
#endregion
|
||||
|
||||
public SampleDBCreator()
|
||||
{
|
||||
cosmosHelper = new CosmosDBHelper();
|
||||
cosmosBulkImporter = new CosmosBulkImporter();
|
||||
}
|
||||
|
||||
public async Task InitializeMigration()
|
||||
{
|
||||
logger.LogInfo("Inside Initialize migration for SampleDBCreator. ");
|
||||
sampleClient = cosmosHelper.GetSampleDocumentDbClient();
|
||||
sampleCollection = await cosmosHelper.CreateSampleDocumentCollection(sampleClient, this.IsFixedCollection);
|
||||
await cosmosBulkImporter.InitializeBulkExecutor(sampleClient, sampleCollection);
|
||||
}
|
||||
public async Task<bool> Start()
|
||||
{
|
||||
await InitializeMigration();
|
||||
await CreateUploadTestDataInbatches();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected List<dynamic> GetCommonEntitiesinBatch()
|
||||
{
|
||||
List<dynamic> entities = new List<dynamic>();
|
||||
for(int i=0; i<this.WriteBatchSize; i++)
|
||||
{
|
||||
entities.Add(EntityV2.getRandomEntity());
|
||||
}
|
||||
return entities;
|
||||
}
|
||||
|
||||
public async Task<bool> CreateUploadTestDataInbatches()
|
||||
{
|
||||
#region batchVariables
|
||||
//initialize Batch Process variables
|
||||
int batchCount = 0;
|
||||
int totalUploaded = 0;
|
||||
var badEntities = new List<Object>();
|
||||
Stopwatch stopwatch = new Stopwatch();
|
||||
stopwatch.Start();
|
||||
#endregion
|
||||
while (totalUploaded < maxtestDocumentCount)
|
||||
{
|
||||
batchCount++;
|
||||
logger.LogInfo("Begin Sample Db creation");
|
||||
|
||||
List<dynamic> entities = GetCommonEntitiesinBatch();
|
||||
BulkImportResponse uploadResponse = new BulkImportResponse();
|
||||
if (entities.Any())
|
||||
{
|
||||
uploadResponse = await cosmosBulkImporter.BulkSendToNewCollection<dynamic>(entities);
|
||||
}
|
||||
badEntities = uploadResponse.BadInputDocuments;
|
||||
//summary.totalRecordsSent += uploadResponse.NumberOfDocumentsImported;
|
||||
totalUploaded += entities.Count();
|
||||
|
||||
logger.LogInfo($"Summary of Batch {batchCount} records retrieved {entities.Count()}. Records Uploaded: {uploadResponse.NumberOfDocumentsImported}");
|
||||
//logger.LogInfo($"Total records retrieved {summary.totalRecordsRetrieved}. Total records uploaded {summary.totalRecordsSent}");
|
||||
logger.LogInfo($"Time elapsed : {stopwatch.Elapsed} ");
|
||||
}
|
||||
stopwatch.Stop();
|
||||
logger.LogInfo("Completed Sample DB creation.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace CosmosCloneCommon.Utility
|
||||
{
|
||||
public static class CloneLogger
|
||||
{
|
||||
static CloneLogger()
|
||||
{
|
||||
_logBuilder = new StringBuilder("Collection Copy log");
|
||||
}
|
||||
static StringBuilder _logBuilder;
|
||||
|
||||
public static string FullLog
|
||||
{
|
||||
get
|
||||
{
|
||||
return _logBuilder.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public static string getFullLog()
|
||||
{
|
||||
return _logBuilder.ToString();
|
||||
}
|
||||
public static void LogInfo(string info)
|
||||
{
|
||||
Console.WriteLine(info);
|
||||
_logBuilder.Append("\n"+info);
|
||||
}
|
||||
public static void LogError(string s)
|
||||
{
|
||||
LogInfo("Error Occurred");
|
||||
LogInfo(s);
|
||||
}
|
||||
public static void LogError(Exception e)
|
||||
{
|
||||
LogInfo("LogError");
|
||||
Exception baseException = e.GetBaseException();
|
||||
LogInfo($"Error: {e.Message}, Message: {baseException.Message}");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace CosmosCloneCommon.Utility
|
||||
{
|
||||
public class CloneSerializer
|
||||
{
|
||||
public static string XMLSerialize<T>(T dataToSerialize)
|
||||
{
|
||||
try
|
||||
{
|
||||
var stringwriter = new System.IO.StringWriter();
|
||||
var serializer = new XmlSerializer(typeof(T));
|
||||
serializer.Serialize(stringwriter, dataToSerialize);
|
||||
return stringwriter.ToString();
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public static T XMLDeserialize<T>(string xmlText)
|
||||
{
|
||||
try
|
||||
{
|
||||
var stringReader = new System.IO.StringReader(xmlText);
|
||||
var serializer = new XmlSerializer(typeof(T));
|
||||
return (T)serializer.Deserialize(stringReader);
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Configuration;
|
||||
using System.Collections.Specialized;
|
||||
using logger = CosmosCloneCommon.Utility.CloneLogger;
|
||||
|
||||
namespace CosmosCloneCommon.Utility
|
||||
{
|
||||
|
||||
public static class CloneSettings
|
||||
{
|
||||
public static bool CopyStoredProcedures { get; set; }
|
||||
public static bool CopyUDFs { get; set; }
|
||||
public static bool CopyTriggers { get; set; }
|
||||
public static bool CopyDocuments { get; set; }
|
||||
public static bool CopyIndexingPolicy { get; set; }
|
||||
public static bool CopyPartitionKey { get; set; }
|
||||
public static int ReadBatchSize { get; set; }
|
||||
public static int WriteBatchSize { get; private set; }
|
||||
public static bool EnableTextLogging { get; set; }
|
||||
public static bool ScrubbingRequired { get; set; }
|
||||
|
||||
public static int SourceOfferThroughputRUs { get; set; }
|
||||
public static int TargetMigrationOfferThroughputRUs { get; set; }
|
||||
public static int TargetRestOfferThroughputRUs { get; set; }
|
||||
public static CosmosCollectionValues SourceSettings { get; set; }
|
||||
public static CosmosCollectionValues TargetSettings { get; set; }
|
||||
|
||||
|
||||
|
||||
static CloneSettings()
|
||||
{
|
||||
ConfigurationManager.RefreshSection("appSettings");
|
||||
|
||||
CopyStoredProcedures = bool.Parse(ConfigurationManager.AppSettings["CopyStoredProcedures"]);
|
||||
CopyUDFs = bool.Parse(ConfigurationManager.AppSettings["CopyUDFs"]);
|
||||
CopyTriggers = bool.Parse(ConfigurationManager.AppSettings["CopyTriggers"]);
|
||||
CopyDocuments = bool.Parse(ConfigurationManager.AppSettings["CopyDocuments"]);
|
||||
CopyIndexingPolicy = bool.Parse(ConfigurationManager.AppSettings["CopyIndexingPolicy"]);
|
||||
CopyPartitionKey = bool.Parse(ConfigurationManager.AppSettings["CopyPartitionKey"]);
|
||||
var value = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
|
||||
|
||||
ReadBatchSize = int.Parse(ConfigurationManager.AppSettings["ReadBatchSize"].ToString());
|
||||
WriteBatchSize = int.Parse(ConfigurationManager.AppSettings["WriteBatchCount"]);
|
||||
EnableTextLogging = bool.Parse(ConfigurationManager.AppSettings["EnableTextLogging"]);
|
||||
SourceOfferThroughputRUs = int.Parse(ConfigurationManager.AppSettings["SourceOfferThroughputRUs"]);
|
||||
TargetMigrationOfferThroughputRUs = int.Parse(ConfigurationManager.AppSettings["TargetMigrationOfferThroughputRUs"]);
|
||||
TargetRestOfferThroughputRUs = int.Parse(ConfigurationManager.AppSettings["TargetRestOfferThroughputRUs"]);
|
||||
ScrubbingRequired = bool.Parse(ConfigurationManager.AppSettings["ScrubbingRequired"]);
|
||||
var sourceConfigs = GetConfigurationSection("SourceCosmosDBSettings");
|
||||
SourceSettings = new CosmosCollectionValues()
|
||||
{
|
||||
EndpointUrl = sourceConfigs["EndpointUrl"],
|
||||
AccessKey = sourceConfigs["AccessKey"],
|
||||
DatabaseName = sourceConfigs["DatabaseName"],
|
||||
CollectionName = sourceConfigs["CollectionName"]
|
||||
//OfferThroughputRUs = int.Parse(sourceConfigs["OfferThroughputRUs"])
|
||||
};
|
||||
|
||||
var targetConfigs = GetConfigurationSection("TargetCosmosDBSettings");
|
||||
TargetSettings = new CosmosCollectionValues()
|
||||
{
|
||||
EndpointUrl = targetConfigs["EndpointUrl"],
|
||||
AccessKey = targetConfigs["AccessKey"],
|
||||
DatabaseName = targetConfigs["DatabaseName"],
|
||||
CollectionName = targetConfigs["CollectionName"]
|
||||
// OfferThroughputRUs = int.Parse(sourceConfigs["OfferThroughputRUs"])
|
||||
};
|
||||
}
|
||||
|
||||
public static NameValueCollection GetConfigurationSection(string sectionName)
|
||||
{
|
||||
var appSettings = ConfigurationManager.GetSection(sectionName) as NameValueCollection;
|
||||
if (appSettings.Count == 0)
|
||||
{
|
||||
logger.LogInfo($"Application Settings are not defined for {sectionName}");
|
||||
}
|
||||
return appSettings;
|
||||
}
|
||||
public static string AppSettings(string key)
|
||||
{
|
||||
return ConfigurationManager.AppSettings[key];
|
||||
}
|
||||
}
|
||||
|
||||
public class CosmosCollectionValues
|
||||
{
|
||||
public string EndpointUrl { get; set; }
|
||||
public string AccessKey { get; set; }
|
||||
public string DatabaseName { get; set; }
|
||||
public string CollectionName { get; set; }
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using Microsoft.Azure.CosmosDB.BulkExecutor;
|
||||
using Microsoft.Azure.CosmosDB.BulkExecutor.BulkImport;
|
||||
using Microsoft.Azure.Documents;
|
||||
using Microsoft.Azure.Documents.Client;
|
||||
using logger = CosmosCloneCommon.Utility.CloneLogger;
|
||||
|
||||
namespace CosmosCloneCommon.Utility
|
||||
{
|
||||
public class CosmosBulkImporter
|
||||
{
|
||||
private IBulkExecutor bulkExecutor;
|
||||
private static readonly ConnectionPolicy ConnectionPolicy = new ConnectionPolicy
|
||||
{
|
||||
ConnectionMode = ConnectionMode.Direct,
|
||||
ConnectionProtocol = Protocol.Tcp
|
||||
};
|
||||
public CosmosBulkImporter()
|
||||
{
|
||||
//var TargetCosmosDBSettings = CloneSettings.GetConfigurationSection("TargetCosmosDBSettings");
|
||||
var TargetEndpointUrl = CloneSettings.TargetSettings.EndpointUrl;
|
||||
var TargetAccessKey = CloneSettings.TargetSettings.AccessKey;
|
||||
var TargetDatabaseName = CloneSettings.TargetSettings.DatabaseName;
|
||||
var TargetCollectionName = CloneSettings.TargetSettings.CollectionName;
|
||||
}
|
||||
|
||||
public async Task InitializeBulkExecutor(DocumentClient targetClient, DocumentCollection targetCollection)
|
||||
{
|
||||
logger.LogInfo("Inside InitializeBulkExecutor ");
|
||||
// Set retry options high for initialization (default values).
|
||||
targetClient.ConnectionPolicy.RetryOptions.MaxRetryWaitTimeInSeconds = 30;
|
||||
targetClient.ConnectionPolicy.RetryOptions.MaxRetryAttemptsOnThrottledRequests = 9;
|
||||
|
||||
bulkExecutor = new BulkExecutor(targetClient, targetCollection);
|
||||
await bulkExecutor.InitializeAsync();
|
||||
// Set retries to 0 to pass control to bulk executor.
|
||||
targetClient.ConnectionPolicy.RetryOptions.MaxRetryWaitTimeInSeconds = 0;
|
||||
targetClient.ConnectionPolicy.RetryOptions.MaxRetryAttemptsOnThrottledRequests = 0;
|
||||
}
|
||||
public async Task<BulkImportResponse> BulkSendToNewCollection<T>(List<T> entityList)
|
||||
{
|
||||
BulkImportResponse bulkImportResponse = null;
|
||||
var tokenSource = new CancellationTokenSource();
|
||||
var token = tokenSource.Token;
|
||||
int attempts = 0;
|
||||
var objList = entityList.Cast<Object>();
|
||||
do
|
||||
{
|
||||
bulkImportResponse = await bulkExecutor.BulkImportAsync(
|
||||
documents: objList,
|
||||
enableUpsert: true,
|
||||
disableAutomaticIdGeneration: true,
|
||||
maxConcurrencyPerPartitionKeyRange: null,
|
||||
maxInMemorySortingBatchSize: null,
|
||||
cancellationToken: token);
|
||||
attempts++;
|
||||
} while (bulkImportResponse.NumberOfDocumentsImported < entityList.Count() && attempts <= 5);
|
||||
|
||||
var badDocumentList = bulkImportResponse.BadInputDocuments;
|
||||
|
||||
#region log bulk Summary
|
||||
logger.LogInfo(String.Format("\n Batch Upload completed "));
|
||||
logger.LogInfo("--------------------------------------------------------------------- ");
|
||||
logger.LogInfo(String.Format("Inserted {0} docs @ {1} writes/s, {2} RU/s in {3} sec",
|
||||
bulkImportResponse.NumberOfDocumentsImported,
|
||||
Math.Round(bulkImportResponse.NumberOfDocumentsImported / bulkImportResponse.TotalTimeTaken.TotalSeconds, 2),
|
||||
Math.Round(bulkImportResponse.TotalRequestUnitsConsumed / bulkImportResponse.TotalTimeTaken.TotalSeconds, 2),
|
||||
bulkImportResponse.TotalTimeTaken.TotalSeconds));
|
||||
logger.LogInfo(String.Format("Average RU consumption per document: {0}",
|
||||
Math.Round(bulkImportResponse.TotalRequestUnitsConsumed / bulkImportResponse.NumberOfDocumentsImported, 2)));
|
||||
|
||||
if (badDocumentList != null && badDocumentList.Count > 0)
|
||||
{
|
||||
logger.LogInfo($"bad Documents detected {badDocumentList.Count}");
|
||||
}
|
||||
|
||||
logger.LogInfo("---------------------------------------------------------------------\n ");
|
||||
#endregion
|
||||
|
||||
return bulkImportResponse;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,504 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Azure.Documents.Client;
|
||||
using Microsoft.Azure.Documents;
|
||||
using CosmosCloneCommon.Model;
|
||||
using logger = CosmosCloneCommon.Utility.CloneLogger;
|
||||
|
||||
namespace CosmosCloneCommon.Utility
|
||||
{
|
||||
public class CosmosDBHelper
|
||||
{
|
||||
private int OfferThroughput = 1000;
|
||||
private ConnectionPolicy ConnectionPolicy;
|
||||
|
||||
public CosmosDBHelper()
|
||||
{
|
||||
ConnectionPolicy = new ConnectionPolicy
|
||||
{
|
||||
ConnectionMode = ConnectionMode.Direct,
|
||||
ConnectionProtocol = Protocol.Tcp,
|
||||
RetryOptions = new RetryOptions()
|
||||
};
|
||||
this.ConnectionPolicy.RetryOptions.MaxRetryAttemptsOnThrottledRequests = 20;
|
||||
this.ConnectionPolicy.RetryOptions.MaxRetryWaitTimeInSeconds = 600;
|
||||
}
|
||||
|
||||
public async Task<ValidationResult> TestSourceConnection()
|
||||
{
|
||||
var result = new ValidationResult();
|
||||
DocumentClient sourceDocumentClient;
|
||||
try
|
||||
{
|
||||
sourceDocumentClient = new DocumentClient(new Uri(CloneSettings.SourceSettings.EndpointUrl), CloneSettings.SourceSettings.AccessKey, ConnectionPolicy);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex);
|
||||
result.IsSuccess = false;
|
||||
result.Message = "Unable to connect to Source. Check your input url and key are accurate. If Firewall security is enabled for your database please add the ip address of the current machine.";
|
||||
return result;
|
||||
}
|
||||
try
|
||||
{
|
||||
var cosmosDBURI = UriFactory.CreateDocumentCollectionUri(CloneSettings.SourceSettings.DatabaseName, CloneSettings.SourceSettings.CollectionName);
|
||||
//var sourceDatabase = await sourceDocumentClient.ReadDatabaseAsync(Database);
|
||||
var sourceCollection = await sourceDocumentClient.ReadDocumentCollectionAsync(cosmosDBURI);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex);
|
||||
result.IsSuccess = false;
|
||||
result.Message = "Incorrect DatabaseName or Collection. Check your input DatabaseName and Collection are accurate.";
|
||||
return result;
|
||||
}
|
||||
result.IsSuccess = true;
|
||||
result.Message = "OK";
|
||||
return result;
|
||||
}
|
||||
|
||||
public ValidationResult TestSourceConnection_v2()
|
||||
{
|
||||
var result = new ValidationResult();
|
||||
DocumentClient sourceDocumentClient;
|
||||
try
|
||||
{
|
||||
sourceDocumentClient = new DocumentClient(new Uri(CloneSettings.SourceSettings.EndpointUrl), CloneSettings.SourceSettings.AccessKey, ConnectionPolicy);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex);
|
||||
result.IsSuccess = false;
|
||||
result.Message = "Unable to connect to Source. Check your input url and key are accurate. If Firewall security is enabled for your database please add the ip address of the current machine.";
|
||||
return result;
|
||||
}
|
||||
try
|
||||
{
|
||||
var cosmosDBURI = UriFactory.CreateDocumentCollectionUri(CloneSettings.SourceSettings.DatabaseName, CloneSettings.SourceSettings.CollectionName);
|
||||
//var sourceDatabase = await sourceDocumentClient.ReadDatabaseAsync(Database);
|
||||
var sourceCollection = sourceDocumentClient.ReadDocumentCollectionAsync(cosmosDBURI);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex);
|
||||
result.IsSuccess = false;
|
||||
result.Message = "Incorrect DatabaseName or Collection. Check your input DatabaseName and Collection are accurate.";
|
||||
return result;
|
||||
}
|
||||
result.IsSuccess = true;
|
||||
result.Message = "OK";
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<ValidationResult> TestTargetConnection()
|
||||
{
|
||||
var result = new ValidationResult();
|
||||
DocumentClient targetDocumentClient;
|
||||
try
|
||||
{
|
||||
targetDocumentClient = new DocumentClient(new Uri(CloneSettings.TargetSettings.EndpointUrl), CloneSettings.TargetSettings.AccessKey, ConnectionPolicy);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex);
|
||||
result.IsSuccess = false;
|
||||
result.Message = "Unable to connect to Target. Check your input url and key are accurate. If Firewall security is enabled for your database please add the ip address of the current machine.";
|
||||
return result;
|
||||
}
|
||||
try
|
||||
{
|
||||
var cosmosDBURI = UriFactory.CreateDatabaseUri(CloneSettings.TargetSettings.DatabaseName);
|
||||
var sourceDatabase = await targetDocumentClient.ReadDatabaseAsync(cosmosDBURI);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex);
|
||||
result.IsSuccess = false;
|
||||
result.Message = "Incorrect DatabaseName .Check whether the input DatabaseName is accurate.";
|
||||
return result;
|
||||
}
|
||||
result.IsSuccess = true;
|
||||
result.Message = "OK";
|
||||
return result;
|
||||
}
|
||||
|
||||
public ValidationResult TestTargetConnection_v2()
|
||||
{
|
||||
var result = new ValidationResult();
|
||||
DocumentClient targetDocumentClient;
|
||||
try
|
||||
{
|
||||
targetDocumentClient = new DocumentClient(new Uri(CloneSettings.TargetSettings.EndpointUrl), CloneSettings.TargetSettings.AccessKey, ConnectionPolicy);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex);
|
||||
result.IsSuccess = false;
|
||||
result.Message = "Unable to connect to Target. Check your input url and key are accurate. If Firewall security is enabled for your database please add the ip address of the current machine.";
|
||||
return result;
|
||||
}
|
||||
try
|
||||
{
|
||||
var cosmosDBURI = UriFactory.CreateDatabaseUri(CloneSettings.TargetSettings.DatabaseName);
|
||||
var sourceDatabase = targetDocumentClient.ReadDatabaseAsync(cosmosDBURI);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex);
|
||||
result.IsSuccess = false;
|
||||
result.Message = "Incorrect DatabaseName .Check whether the input DatabaseName is accurate.";
|
||||
return result;
|
||||
}
|
||||
result.IsSuccess = true;
|
||||
result.Message = "OK";
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public DocumentClient GetSourceDocumentDbClient()
|
||||
{
|
||||
try
|
||||
{
|
||||
string SourceEndpointUrl = CloneSettings.SourceSettings.EndpointUrl;
|
||||
string SourceAccessKey = CloneSettings.SourceSettings.AccessKey;
|
||||
var sourceDocumentClient = new DocumentClient(new Uri(SourceEndpointUrl), SourceAccessKey, ConnectionPolicy);
|
||||
return sourceDocumentClient;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public DocumentClient GetTargetDocumentDbClient()
|
||||
{
|
||||
try
|
||||
{
|
||||
string targetEndpointUrl = CloneSettings.TargetSettings.EndpointUrl;
|
||||
string targetAccessKey = CloneSettings.TargetSettings.AccessKey;
|
||||
OfferThroughput = CloneSettings.TargetMigrationOfferThroughputRUs;
|
||||
var targetDocumentClient = new DocumentClient(new Uri(targetEndpointUrl), targetAccessKey, ConnectionPolicy);
|
||||
return targetDocumentClient;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public DocumentClient GetSampleDocumentDbClient()
|
||||
{
|
||||
try
|
||||
{
|
||||
var SourceCosmosDBSettings = CloneSettings.GetConfigurationSection("SampleCosmosDBSettings");
|
||||
string SourceEndpointUrl = SourceCosmosDBSettings["EndpointUrl"];
|
||||
string SourceAccessKey = SourceCosmosDBSettings["AccessKey"];
|
||||
var sourceDocumentClient = new DocumentClient(new Uri(SourceEndpointUrl), SourceAccessKey, ConnectionPolicy);
|
||||
return sourceDocumentClient;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<DocumentCollection> GetTargetDocumentCollection(DocumentClient targetClient)
|
||||
{
|
||||
try
|
||||
{
|
||||
//var SourceCosmosDBSettings = CloneSettings.GetConfigurationSection("SourceCosmosDBSettings");
|
||||
string DatabaseName = CloneSettings.TargetSettings.DatabaseName;
|
||||
string CollectionName = CloneSettings.TargetSettings.CollectionName;
|
||||
|
||||
var cosmosDBURI = UriFactory.CreateDocumentCollectionUri(DatabaseName, CollectionName);
|
||||
//sourceClient.ReadDocumentCollectionAsync
|
||||
var targetCollection = (DocumentCollection)await targetClient.ReadDocumentCollectionAsync(UriFactory.CreateDocumentCollectionUri(DatabaseName, CollectionName), new RequestOptions { OfferEnableRUPerMinuteThroughput = true, OfferThroughput = CloneSettings.TargetMigrationOfferThroughputRUs });
|
||||
//targetClient.ReadDocumentCollectionAsync()
|
||||
//var sourceCollection = (DocumentCollection)await sourceClient.CreateDocumentCollectionIfNotExistsAsync(UriFactory.CreateDatabaseUri(sourceDatabaseName), new DocumentCollection { Id = sourceCollectionName });
|
||||
return targetCollection;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<DocumentCollection> GetTargetDocumentCollection_v2(DocumentClient targetClient)
|
||||
{
|
||||
try
|
||||
{
|
||||
//var SourceCosmosDBSettings = CloneSettings.GetConfigurationSection("SourceCosmosDBSettings");
|
||||
string DatabaseName = CloneSettings.TargetSettings.DatabaseName;
|
||||
string CollectionName = CloneSettings.TargetSettings.CollectionName;
|
||||
|
||||
var cosmosDBURI = UriFactory.CreateDocumentCollectionUri(DatabaseName, CollectionName);
|
||||
//sourceClient.ReadDocumentCollectionAsync
|
||||
var targetCollection = (DocumentCollection)await targetClient.ReadDocumentCollectionAsync(UriFactory.CreateDocumentCollectionUri(DatabaseName, CollectionName), new RequestOptions { OfferEnableRUPerMinuteThroughput = true, OfferThroughput = CloneSettings.TargetMigrationOfferThroughputRUs });
|
||||
//targetClient.ReadDocumentCollectionAsync()
|
||||
//var sourceCollection = (DocumentCollection)await sourceClient.CreateDocumentCollectionIfNotExistsAsync(UriFactory.CreateDatabaseUri(sourceDatabaseName), new DocumentCollection { Id = sourceCollectionName });
|
||||
return targetCollection;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public async Task<DocumentCollection> GetSourceDocumentCollection(DocumentClient sourceClient)
|
||||
{
|
||||
try
|
||||
{
|
||||
//var SourceCosmosDBSettings = CloneSettings.GetConfigurationSection("SourceCosmosDBSettings");
|
||||
string sourceDatabaseName = CloneSettings.SourceSettings.DatabaseName;
|
||||
string sourceCollectionName = CloneSettings.SourceSettings.CollectionName;
|
||||
|
||||
var cosmosDBURI = UriFactory.CreateDocumentCollectionUri(sourceDatabaseName, sourceCollectionName);
|
||||
var sourceCollection = (DocumentCollection)await sourceClient.CreateDocumentCollectionIfNotExistsAsync(UriFactory.CreateDatabaseUri(sourceDatabaseName), new DocumentCollection { Id = sourceCollectionName });
|
||||
return sourceCollection;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<DocumentCollection> CreateSampleDocumentCollection(DocumentClient sampleClient, bool IsFixedCollection = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
var sampleCosmosDBSettings = CloneSettings.GetConfigurationSection("SampleCosmosDBSettings");
|
||||
string sampleDatabaseName = sampleCosmosDBSettings["DatabaseName"]; ;
|
||||
string sampleCollectionName = sampleCosmosDBSettings["CollectionName"];
|
||||
int offerThroughput = 1000;
|
||||
int.TryParse(sampleCosmosDBSettings["OfferThroughputRUs"], out offerThroughput);
|
||||
await sampleClient.CreateDatabaseIfNotExistsAsync(new Database { Id = sampleDatabaseName });
|
||||
DocumentCollection newDocumentCollection;
|
||||
if (!IsFixedCollection)
|
||||
{
|
||||
var partitionKeyDefinition = new PartitionKeyDefinition();
|
||||
partitionKeyDefinition.Paths.Add("/CompositeName");
|
||||
newDocumentCollection = (DocumentCollection)await sampleClient.CreateDocumentCollectionIfNotExistsAsync
|
||||
(UriFactory.CreateDatabaseUri(sampleDatabaseName),
|
||||
new DocumentCollection { Id = sampleCollectionName, PartitionKey = partitionKeyDefinition },
|
||||
new RequestOptions { OfferThroughput = offerThroughput });
|
||||
}
|
||||
else
|
||||
{
|
||||
//no partition key if it is a fixed collection
|
||||
newDocumentCollection = (DocumentCollection)await sampleClient.CreateDocumentCollectionIfNotExistsAsync
|
||||
(UriFactory.CreateDatabaseUri(sampleDatabaseName),
|
||||
new DocumentCollection { Id = sampleCollectionName},
|
||||
new RequestOptions { OfferThroughput = offerThroughput });
|
||||
}
|
||||
return newDocumentCollection;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<DocumentCollection> CreateTargetDocumentCollection(DocumentClient targetClient, IndexingPolicy indexingPolicy, PartitionKeyDefinition partitionKeyDefinition)
|
||||
{
|
||||
try
|
||||
{
|
||||
//var targetCosmosDBSettings = CloneSettings.GetConfigurationSection("TargetCosmosDBSettings");
|
||||
string targetDatabaseName = CloneSettings.TargetSettings.DatabaseName;
|
||||
string targetCollectionName = CloneSettings.TargetSettings.CollectionName;
|
||||
|
||||
await targetClient.CreateDatabaseIfNotExistsAsync(new Database { Id = targetDatabaseName });
|
||||
DocumentCollection newDocumentCollection;
|
||||
if (partitionKeyDefinition != null && partitionKeyDefinition.Paths.Count>0)
|
||||
{
|
||||
if(CloneSettings.CopyPartitionKey)
|
||||
{
|
||||
// Partition key exists in Source (Unlimited Storage)
|
||||
newDocumentCollection = (DocumentCollection)await targetClient.CreateDocumentCollectionIfNotExistsAsync
|
||||
(UriFactory.CreateDatabaseUri(targetDatabaseName),
|
||||
new DocumentCollection { Id = targetCollectionName, PartitionKey = partitionKeyDefinition, IndexingPolicy = indexingPolicy },
|
||||
new RequestOptions { OfferEnableRUPerMinuteThroughput = true, OfferThroughput = CloneSettings.TargetMigrationOfferThroughputRUs });
|
||||
}
|
||||
else
|
||||
{
|
||||
newDocumentCollection = (DocumentCollection)await targetClient.CreateDocumentCollectionIfNotExistsAsync
|
||||
(UriFactory.CreateDatabaseUri(targetDatabaseName),
|
||||
new DocumentCollection { Id = targetCollectionName, IndexingPolicy = indexingPolicy },
|
||||
new RequestOptions { OfferEnableRUPerMinuteThroughput = true, OfferThroughput = CloneSettings.TargetMigrationOfferThroughputRUs });
|
||||
}
|
||||
}
|
||||
else
|
||||
{ //no partition key set in source (Fixed storage)
|
||||
newDocumentCollection = (DocumentCollection)await targetClient.CreateDocumentCollectionIfNotExistsAsync
|
||||
(UriFactory.CreateDatabaseUri(targetDatabaseName),
|
||||
new DocumentCollection { Id = targetCollectionName, IndexingPolicy = indexingPolicy },
|
||||
new RequestOptions { OfferEnableRUPerMinuteThroughput = true, OfferThroughput = CloneSettings.TargetMigrationOfferThroughputRUs });
|
||||
}
|
||||
logger.LogInfo($"SuccessFully Created Target. Database: {targetDatabaseName} Collection:{targetCollectionName}");
|
||||
return newDocumentCollection;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
public IQueryable<T> GetSourceEntityDocumentQuery<T>(DocumentClient sourceClient, int batchSize = -1)
|
||||
{
|
||||
try
|
||||
{
|
||||
//var SourceCosmosDBSettings = CloneSettings.GetConfigurationSection("SourceCosmosDBSettings");
|
||||
string sourceDatabaseName = CloneSettings.SourceSettings.DatabaseName;
|
||||
string sourceCollectionName = CloneSettings.SourceSettings.CollectionName;
|
||||
FeedOptions queryOptions = new FeedOptions { MaxItemCount = batchSize, EnableCrossPartitionQuery = true };
|
||||
string EntityDataQuery = $"SELECT * FROM c";
|
||||
var documentQuery = sourceClient.CreateDocumentQuery<T>(UriFactory.CreateDocumentCollectionUri(sourceDatabaseName, sourceCollectionName), EntityDataQuery, queryOptions);
|
||||
return documentQuery;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
public IQueryable<T> GetScrubDataDocumentQuery<T>(DocumentClient targetClient,string filterCondition, int batchSize = -1)
|
||||
{
|
||||
try
|
||||
{
|
||||
//var SourceCosmosDBSettings = CloneSettings.GetConfigurationSection("SourceCosmosDBSettings");
|
||||
string DatabaseName = CloneSettings.TargetSettings.DatabaseName;
|
||||
string CollectionName = CloneSettings.TargetSettings.CollectionName;
|
||||
FeedOptions queryOptions = new FeedOptions { MaxItemCount = batchSize, EnableCrossPartitionQuery = true };
|
||||
string scrubDataQuery;
|
||||
|
||||
if (string.IsNullOrEmpty(filterCondition))
|
||||
{
|
||||
scrubDataQuery = "SELECT * FROM c";
|
||||
}
|
||||
else
|
||||
{
|
||||
scrubDataQuery = "SELECT * FROM c" + " where " + filterCondition;
|
||||
}
|
||||
|
||||
var documentQuery = targetClient.CreateDocumentQuery<T>(UriFactory.CreateDocumentCollectionUri(DatabaseName, CollectionName), scrubDataQuery, queryOptions);
|
||||
return documentQuery;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> SetTargetRestOfferThroughPut()
|
||||
{
|
||||
using (var client = GetTargetDocumentDbClient())
|
||||
{
|
||||
var collection = this.GetTargetDocumentCollection(client);
|
||||
|
||||
FeedOptions queryOptions = new FeedOptions { MaxItemCount = -1, EnableCrossPartitionQuery = true };
|
||||
//var Ioffer = cosmosClient.CreateOfferQuery(queryOptions);
|
||||
//var offer = Ioffer.AsEnumerable().SingleOrDefault();
|
||||
Offer offer = client.CreateOfferQuery()
|
||||
.Where(r => r.ResourceLink == collection.Result.SelfLink)
|
||||
.AsEnumerable()
|
||||
.SingleOrDefault();
|
||||
offer = new OfferV2(offer, CloneSettings.TargetRestOfferThroughputRUs);
|
||||
await client.ReplaceOfferAsync(offer);
|
||||
//.Where(r => r.ResourceLink == collection.)
|
||||
//.AsEnumerable()
|
||||
//.SingleOrDefault();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public bool CompareRecordCount()
|
||||
{
|
||||
//var SourceCosmosDBSettings = CloneSettings.GetConfigurationSection("SourceCosmosDBSettings");
|
||||
string sourceDatabaseName = CloneSettings.SourceSettings.DatabaseName;
|
||||
string sourceCollectionName = CloneSettings.SourceSettings.CollectionName;
|
||||
|
||||
//var TargetCosmosDBSettings = CloneSettings.GetConfigurationSection("SourceCosmosDBSettings");
|
||||
string targetDatabaseName = CloneSettings.TargetSettings.DatabaseName;
|
||||
string targetCollectionName = CloneSettings.TargetSettings.CollectionName;
|
||||
|
||||
string totalCountQuery = "SELECT VALUE COUNT(1) FROM p";
|
||||
FeedOptions queryOptions = new FeedOptions { MaxItemCount = -1, EnableCrossPartitionQuery = true };
|
||||
long sourceTotalRecordCount, targetTotalRecordCount;
|
||||
using (var cosmosClient = GetSourceDocumentDbClient())
|
||||
{
|
||||
sourceTotalRecordCount = cosmosClient.CreateDocumentQuery<long>(
|
||||
UriFactory.CreateDocumentCollectionUri(sourceDatabaseName, sourceCollectionName), totalCountQuery, queryOptions)
|
||||
.AsEnumerable().First();
|
||||
}
|
||||
using (var cosmosClient = GetTargetDocumentDbClient())
|
||||
{
|
||||
targetTotalRecordCount = cosmosClient.CreateDocumentQuery<long>(
|
||||
UriFactory.CreateDocumentCollectionUri(targetDatabaseName, targetCollectionName), totalCountQuery, queryOptions)
|
||||
.AsEnumerable().First();
|
||||
}
|
||||
return (sourceTotalRecordCount == targetTotalRecordCount) ? true: false;
|
||||
}
|
||||
|
||||
public long GetSourceRecordCount()
|
||||
{
|
||||
//var SourceCosmosDBSettings = CloneSettings.GetConfigurationSection("SourceCosmosDBSettings");
|
||||
string sourceDatabaseName = CloneSettings.SourceSettings.DatabaseName;
|
||||
string sourceCollectionName = CloneSettings.SourceSettings.CollectionName;
|
||||
|
||||
string totalCountQuery = "SELECT VALUE COUNT(1) FROM c";
|
||||
FeedOptions queryOptions = new FeedOptions { MaxItemCount = -1, EnableCrossPartitionQuery = true };
|
||||
long sourceTotalRecordCount;
|
||||
using (var cosmosClient = GetSourceDocumentDbClient())
|
||||
{
|
||||
sourceTotalRecordCount = cosmosClient.CreateDocumentQuery<long>(
|
||||
UriFactory.CreateDocumentCollectionUri(sourceDatabaseName, sourceCollectionName), totalCountQuery, queryOptions)
|
||||
.AsEnumerable().First();
|
||||
}
|
||||
return sourceTotalRecordCount;
|
||||
}
|
||||
|
||||
public long GetRecordCountToScrub(ScrubRule scrubbingRule)
|
||||
{
|
||||
//var SourceCosmosDBSettings = CloneSettings.GetConfigurationSection("SourceCosmosDBSettings");
|
||||
string DatabaseName = CloneSettings.TargetSettings.DatabaseName;
|
||||
string CollectionName = CloneSettings.TargetSettings.CollectionName;
|
||||
string totalCountQuery;
|
||||
|
||||
if (string.IsNullOrEmpty(scrubbingRule.FilterCondition))
|
||||
{
|
||||
totalCountQuery = "SELECT VALUE COUNT(1) FROM c";
|
||||
}
|
||||
else
|
||||
{
|
||||
totalCountQuery = "SELECT VALUE COUNT(1) FROM c" + " where " + scrubbingRule.FilterCondition;
|
||||
}
|
||||
FeedOptions queryOptions = new FeedOptions { MaxItemCount = -1, EnableCrossPartitionQuery = true };
|
||||
long sourceTotalRecordCount;
|
||||
using (var cosmosClient = GetTargetDocumentDbClient())
|
||||
{
|
||||
sourceTotalRecordCount = cosmosClient.CreateDocumentQuery<long>(
|
||||
UriFactory.CreateDocumentCollectionUri(DatabaseName, CollectionName), totalCountQuery, queryOptions)
|
||||
.AsEnumerable().First();
|
||||
}
|
||||
return sourceTotalRecordCount;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,280 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using CosmosCloneCommon.Model;
|
||||
using CosmosCloneCommon.Sample;
|
||||
|
||||
namespace CosmosCloneCommon.Utility
|
||||
{
|
||||
public class ObjectScrubber
|
||||
{
|
||||
public List<JToken> ScrubObjectList(List<string> srcList, ScrubRule scrubRule)
|
||||
{
|
||||
//var scrubbedObjects = new List<string>();
|
||||
var scrubbedObjects = new List<JToken>();
|
||||
var propNames = scrubRule.PropertyName.Split('.').ToList();
|
||||
if(scrubRule.Type == RuleType.NullValue || scrubRule.Type == RuleType.SingleValue)
|
||||
{
|
||||
foreach (var strObj in srcList)
|
||||
{
|
||||
try
|
||||
{
|
||||
JToken jToken = getUpdatedJsonArrayValue((JToken)JObject.Parse(strObj), propNames, scrubRule.UpdateValue);
|
||||
scrubbedObjects.Add(jToken);
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
CloneLogger.LogInfo("Log failed");
|
||||
CloneLogger.LogError(ex);
|
||||
throw ;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else if(scrubRule.Type == RuleType.Shuffle)
|
||||
{
|
||||
//get all similar values
|
||||
var propertyValues = new List<JToken>();
|
||||
foreach (var strObj in srcList)
|
||||
{
|
||||
try
|
||||
{
|
||||
List<JToken> jTokenList = new List<JToken>();
|
||||
getPropertyValues((JToken)JObject.Parse(strObj), propNames, ref jTokenList);
|
||||
propertyValues.AddRange(jTokenList);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CloneLogger.LogInfo("Log failed");
|
||||
CloneLogger.LogError(ex);
|
||||
throw ;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var shuffledTokens = RandomNumberGenerator.Shuffle(propertyValues);
|
||||
var shuffledTokenQ = new Queue<JToken>(shuffledTokens);
|
||||
|
||||
foreach (var strObj in srcList)
|
||||
{
|
||||
try
|
||||
{
|
||||
JToken jToken = getDocumentShuffledToken((JToken)JObject.Parse(strObj), propNames, ref shuffledTokenQ);
|
||||
scrubbedObjects.Add(jToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CloneLogger.LogInfo("Log failed");
|
||||
CloneLogger.LogError(ex);
|
||||
throw ;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var strObj in srcList)
|
||||
{
|
||||
scrubbedObjects.Add((JToken)strObj);
|
||||
}
|
||||
}
|
||||
|
||||
return scrubbedObjects;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public List<JToken> getPropertyValues(JToken token, List<string> propNames, ref List<JToken> jTokenList)
|
||||
{
|
||||
if(jTokenList == null)
|
||||
{
|
||||
jTokenList = new List<JToken>();
|
||||
}
|
||||
if (token == null || token.Type == JTokenType.Null) return jTokenList;
|
||||
|
||||
bool isLeaflevel = false;
|
||||
for (int i = 1; i < propNames.Count; i++)
|
||||
{
|
||||
if (i == propNames.Count - 1) isLeaflevel = true;
|
||||
var currentProperty = propNames[i];
|
||||
|
||||
if (token.Type == JTokenType.Array)
|
||||
{
|
||||
var jArray = (JArray)token;
|
||||
for (int k = 0; k < jArray.Count; k++)
|
||||
{
|
||||
if (isLeaflevel == true)
|
||||
{
|
||||
if (jArray[k][currentProperty] != null && jArray[k][currentProperty].Type != JTokenType.Null)
|
||||
{
|
||||
jTokenList.Add(jArray[k][currentProperty]);
|
||||
}
|
||||
else
|
||||
{
|
||||
jTokenList.Add(null);//In future, to retain null feature modify this to conditional
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
getPropertyValues(jArray[k], propNames.GetRange(i, propNames.Count - i), ref jTokenList);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var jObj = (JObject)token;
|
||||
if (isLeaflevel == true)
|
||||
{
|
||||
if (jObj[currentProperty] != null )
|
||||
{
|
||||
jTokenList.Add(jObj[currentProperty]);
|
||||
}
|
||||
else
|
||||
{
|
||||
jTokenList.Add(null);//In future, to retain null feature modify this to conditional
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
getPropertyValues((JToken)jObj[currentProperty], propNames.GetRange(i, propNames.Count - i), ref jTokenList);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return jTokenList;
|
||||
}
|
||||
public JToken getDocumentShuffledToken(JToken token, List<string> propNames, ref Queue<JToken> tokenQ)
|
||||
{
|
||||
if (token == null || token.Type == JTokenType.Null) return null;
|
||||
|
||||
JToken jTokenResult = token;//just to initialize
|
||||
bool isLeaflevel = false;
|
||||
for (int i = 1; i < propNames.Count; i++)
|
||||
{
|
||||
if (i == propNames.Count - 1) isLeaflevel = true;
|
||||
var currentProperty = propNames[i];
|
||||
|
||||
if (token.Type == JTokenType.Array)
|
||||
{
|
||||
var jArray = (JArray)token;
|
||||
for (int k = 0; k < jArray.Count; k++)
|
||||
{
|
||||
if (isLeaflevel == true)
|
||||
{
|
||||
if (jArray[k][currentProperty] != null && jArray[k][currentProperty].Type != JTokenType.Null)
|
||||
{
|
||||
jArray[k][currentProperty] = tokenQ.Dequeue();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
jArray[k] = getDocumentShuffledToken(jArray[k], propNames.GetRange(i, propNames.Count - i), ref tokenQ);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
var str2 = jArray.ToString();
|
||||
jTokenResult = (JToken)jArray;
|
||||
}
|
||||
else
|
||||
{
|
||||
var jObj = (JObject)token;
|
||||
if (isLeaflevel == true)
|
||||
{
|
||||
if (jObj[currentProperty] != null)
|
||||
{
|
||||
jObj[currentProperty] = tokenQ.Dequeue();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
jObj[currentProperty] = getDocumentShuffledToken((JToken)jObj[currentProperty], propNames.GetRange(i, propNames.Count - i), ref tokenQ);
|
||||
}
|
||||
var str3 = jObj.ToString();
|
||||
jTokenResult = (JToken)jObj;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (jTokenResult == null)
|
||||
{
|
||||
jTokenResult = token;
|
||||
}
|
||||
return jTokenResult;
|
||||
}
|
||||
|
||||
public JToken getUpdatedJsonArrayValue(JToken token, List<string> propNames, string overwritevalue)
|
||||
{
|
||||
if (token == null || token.Type == JTokenType.Null) return null;
|
||||
|
||||
JToken jTokenResult=token;//just to initialize
|
||||
bool isLeaflevel = false;
|
||||
for (int i = 1; i < propNames.Count; i++)
|
||||
{
|
||||
if (i == propNames.Count - 1) isLeaflevel = true;
|
||||
var currentProperty = propNames[i];
|
||||
|
||||
if (token.Type == JTokenType.Array)
|
||||
{
|
||||
var jArray = (JArray)token;
|
||||
for (int k = 0; k < jArray.Count; k++)
|
||||
{
|
||||
if (isLeaflevel == true)
|
||||
{
|
||||
if (jArray[k][currentProperty] != null && jArray[k][currentProperty].Type != JTokenType.Null)
|
||||
{
|
||||
jArray[k][currentProperty] = overwritevalue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (jArray[k] != null && jArray[k][currentProperty].Type != JTokenType.Null)
|
||||
{
|
||||
jArray[k] = getUpdatedJsonArrayValue(jArray[k], propNames.GetRange(i, propNames.Count - i), overwritevalue);
|
||||
continue;
|
||||
}
|
||||
//else return null;
|
||||
}
|
||||
}
|
||||
var str2 = jArray.ToString();
|
||||
jTokenResult = (JToken)jArray;
|
||||
}
|
||||
else
|
||||
{
|
||||
var jObj = (JObject)token;
|
||||
if (isLeaflevel == true)
|
||||
{
|
||||
if(jObj[currentProperty] != null && jObj[currentProperty].Type != JTokenType.Null)
|
||||
{
|
||||
jObj[currentProperty] = overwritevalue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (jObj[currentProperty] != null && jObj[currentProperty].Type != JTokenType.Null)
|
||||
{
|
||||
jObj[currentProperty] = getUpdatedJsonArrayValue((JToken)jObj[currentProperty], propNames.GetRange(i, propNames.Count - i), overwritevalue);
|
||||
}
|
||||
//else return null;
|
||||
}
|
||||
var str3 = jObj.ToString();
|
||||
jTokenResult = (JToken)jObj;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if(jTokenResult == null)
|
||||
{
|
||||
jTokenResult = token;
|
||||
}
|
||||
return jTokenResult;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<appSettings>
|
||||
<add key="EndPointUrl" value="__DBUrl__" />
|
||||
<add key="AuthorizationKey" value="__accesskey__" />
|
||||
<add key="DatabaseName" value="TestDB" />
|
||||
<add key="CollectionName" value="TestColl1" />
|
||||
<add key="CollectionThroughput" value="10000" />
|
||||
<add key="ShouldCleanupOnStart" value="true" />
|
||||
<add key="ShouldCleanupOnFinish" value="false" />
|
||||
<add key="NumberOfDocumentsToImport" value="10000" />
|
||||
<add key="NumberOfBatches" value="10" />
|
||||
<add key="CollectionPartitionKey" value="/profileid" />
|
||||
</appSettings>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.Azure.Documents.Client" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-2.1.0.0" newVersion="2.1.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Azure.CosmosDB.BulkExecutor" version="1.2.0" targetFramework="net461" />
|
||||
<package id="Microsoft.Azure.DocumentDB" version="2.1.3" targetFramework="net461" />
|
||||
<package id="MongoDB.Bson" version="2.4.4" targetFramework="net461" />
|
||||
<package id="Newtonsoft.Json" version="10.0.2" targetFramework="net461" />
|
||||
<package id="System.Buffers" version="4.5.0" targetFramework="net461" />
|
||||
<package id="System.Memory" version="4.5.0" targetFramework="net461" />
|
||||
<package id="System.Net.Http.WinHttpHandler" version="4.5.0" targetFramework="net461" />
|
||||
<package id="System.Numerics.Vectors" version="4.4.0" targetFramework="net461" />
|
||||
<package id="System.Runtime.CompilerServices.Unsafe" version="4.5.0" targetFramework="net461" />
|
||||
</packages>
|
Загрузка…
Ссылка в новой задаче