First check in for Migration Dashboard (#14309)

* Adding Dashboard
Fixing auth keys api
create status Dialog

* making some changes requested in the PR

* switched to text component from dom component

* Adding TODO comment

* Fixing image url to work on windows

* Fixing stuff pointed out in PR comments

* adding return type to dasboard register function

* Adding more todos
This commit is contained in:
Aasim Khan
2021-02-18 10:25:52 -08:00
committed by GitHub
parent 30f55be67d
commit b719099ad3
17 changed files with 739 additions and 34 deletions

View File

@@ -0,0 +1,38 @@
<svg width="753" height="242" viewBox="0 0 753 242" fill="none" xmlns="http://www.w3.org/2000/svg">
<g opacity="0.1" clip-path="url(#clip0)">
<path d="M800.6 183.598C800.071 167.361 793.039 151.822 780.765 139.764C768.49 127.707 751.774 119.92 733.614 117.798C732.488 95.043 721.302 73.5964 702.478 58.1003C683.653 42.6042 658.704 34.3053 633.032 34.9999C612.318 34.6988 592.007 40.0883 574.885 50.4291C557.762 60.77 544.667 75.5556 537.396 92.7577C515.512 95.1492 495.315 104.406 480.458 118.855C465.601 133.304 457.065 151.99 456.394 171.535C457.307 193.515 468.023 214.275 486.188 229.252C504.352 244.23 528.479 252.199 553.266 251.409H718.155C719.527 251.511 720.906 251.511 722.278 251.409C742.717 251.368 762.332 244.261 776.957 231.599C791.582 218.937 800.063 201.717 800.6 183.598Z" fill="url(#paint0_linear)"/>
<path d="M629.526 226.734C579.029 226.734 538.219 215.219 538.219 200.962V338.228C538.219 352.302 579.441 363.817 628.29 364H629.526C679.817 364 720.628 352.485 720.628 338.228V200.962C720.628 215.219 679.817 226.734 629.526 226.734Z" fill="url(#paint1_linear)"/>
<path d="M720.628 200.962C720.628 215.219 679.405 226.734 629.526 226.734C579.647 226.734 538.219 215.219 538.219 200.962C538.219 186.706 579.441 175.191 629.526 175.191C679.611 175.191 720.628 186.706 720.628 200.962Z" fill="#E8E8E8"/>
<path d="M699.4 199.5C699.4 208.456 668.071 215.95 629.528 215.95C590.986 215.95 559.451 208.456 559.451 199.5C559.451 190.544 590.78 183.05 629.528 183.05C668.277 183.05 699.4 190.361 699.4 199.5Z" fill="#50E6FF"/>
<path d="M629.526 202.79C610.799 202.403 592.112 204.497 574.082 209.004C592.093 213.609 610.786 215.766 629.526 215.402C648.264 215.741 666.953 213.584 684.97 209.004C666.934 204.524 648.252 202.43 629.526 202.79Z" fill="#32BEDD"/>
<path d="M572.646 126.389L626.028 78.5012C626.605 77.9761 627.294 77.5588 628.055 77.2738C628.816 76.9888 629.634 76.842 630.46 76.842C631.286 76.842 632.104 76.9888 632.865 77.2738C633.626 77.5588 634.315 77.9761 634.891 78.5012L688.274 126.389C688.732 126.715 689.056 127.166 689.197 127.672C689.338 128.178 689.287 128.711 689.053 129.189C688.818 129.666 688.414 130.063 687.9 130.316C687.387 130.57 686.794 130.667 686.213 130.593H653.441C652.676 130.593 651.942 130.862 651.401 131.342C650.86 131.822 650.556 132.473 650.556 133.152V191.823C650.588 192.095 650.552 192.369 650.449 192.626C650.347 192.883 650.181 193.116 649.963 193.309C649.746 193.502 649.483 193.649 649.193 193.74C648.903 193.831 648.594 193.863 648.288 193.834H612.425C611.824 193.834 611.247 193.622 610.822 193.245C610.397 192.868 610.158 192.357 610.158 191.823V132.603C610.218 132.001 610.017 131.401 609.596 130.926C609.174 130.45 608.565 130.135 607.891 130.045H574.501C574.055 129.987 573.632 129.831 573.272 129.59C572.912 129.35 572.627 129.033 572.442 128.668C572.257 128.304 572.179 127.904 572.214 127.505C572.25 127.107 572.398 126.723 572.646 126.389Z" fill="#F2F2F2"/>
</g>
<rect y="0.000427246" width="1018" height="403" fill="url(#paint2_linear)"/>
<defs>
<linearGradient id="paint0_linear" x1="628.497" y1="251.409" x2="628.497" y2="34.9999" gradientUnits="userSpaceOnUse">
<stop stop-color="#0078D4"/>
<stop offset="0.16" stop-color="#1380DA"/>
<stop offset="0.53" stop-color="#3C91E5"/>
<stop offset="0.82" stop-color="#559CEC"/>
<stop offset="1" stop-color="#5EA0EF"/>
</linearGradient>
<linearGradient id="paint1_linear" x1="538.219" y1="282.481" x2="720.628" y2="282.481" gradientUnits="userSpaceOnUse">
<stop stop-color="#32BEDD"/>
<stop offset="0.06" stop-color="#37C5E3"/>
<stop offset="0.3" stop-color="#49DDF7"/>
<stop offset="0.45" stop-color="#50E6FF"/>
<stop offset="0.55" stop-color="#50E6FF"/>
<stop offset="0.7" stop-color="#49DDF7"/>
<stop offset="0.94" stop-color="#37C5E3"/>
<stop offset="1" stop-color="#32BEDD"/>
</linearGradient>
<linearGradient id="paint2_linear" x1="509" y1="403" x2="509" y2="0.000435431" gradientUnits="userSpaceOnUse">
<stop stop-color="#F0F0F0"/>
<stop offset="1" stop-color="#F2F2F2" stop-opacity="0"/>
<stop offset="1" stop-color="#F2F2F2" stop-opacity="0.0572917"/>
</linearGradient>
<clipPath id="clip0">
<rect width="371" height="329" fill="white" transform="translate(443 35)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@@ -0,0 +1,32 @@
<svg width="310" height="207" viewBox="0 0 310 207" fill="none" xmlns="http://www.w3.org/2000/svg">
<g opacity="0.1" clip-path="url(#clip0)">
<path d="M357.6 148.598C357.071 132.361 350.039 116.822 337.765 104.764C325.49 92.7068 308.774 84.9197 290.614 82.7983C289.488 60.043 278.302 38.5964 259.478 23.1003C240.653 7.60415 215.704 -0.694702 190.032 -6.17467e-05C169.318 -0.301247 149.007 5.08827 131.885 15.4291C114.762 25.77 101.667 40.5556 94.3959 57.7577C72.5117 60.1492 52.3148 69.4064 37.4581 83.8552C22.6013 98.304 14.0655 116.99 13.3943 136.535C14.307 158.515 25.0232 179.275 43.1878 194.252C61.3524 209.23 85.4793 217.199 110.266 216.409H275.155C276.527 216.511 277.906 216.511 279.278 216.409C299.717 216.368 319.332 209.261 333.957 196.599C348.582 183.937 357.063 166.717 357.6 148.598Z" fill="url(#paint0_linear)"/>
<path d="M186.526 191.734C136.029 191.734 95.2192 180.219 95.2192 165.962V303.228C95.2192 317.302 136.441 328.817 185.29 329H186.526C236.818 329 277.628 317.485 277.628 303.228V165.962C277.628 180.219 236.818 191.734 186.526 191.734Z" fill="url(#paint1_linear)"/>
<path d="M277.628 165.962C277.628 180.219 236.405 191.734 186.526 191.734C136.648 191.734 95.2192 180.219 95.2192 165.962C95.2192 151.706 136.441 140.191 186.526 140.191C236.612 140.191 277.628 151.706 277.628 165.962Z" fill="#E8E8E8"/>
<path d="M256.4 164.5C256.4 173.456 225.071 180.95 186.528 180.95C147.986 180.95 116.451 173.456 116.451 164.5C116.451 155.544 147.78 148.05 186.528 148.05C225.277 148.05 256.4 155.361 256.4 164.5Z" fill="#50E6FF"/>
<path d="M186.526 167.79C167.799 167.403 149.112 169.497 131.082 174.004C149.093 178.609 167.787 180.766 186.526 180.402C205.264 180.741 223.954 178.584 241.97 174.004C223.934 169.524 205.252 167.43 186.526 167.79Z" fill="#32BEDD"/>
<path d="M129.646 91.389L183.028 43.5012C183.605 42.9761 184.294 42.5588 185.055 42.2738C185.816 41.9888 186.634 41.842 187.46 41.842C188.286 41.842 189.104 41.9888 189.865 42.2738C190.626 42.5588 191.315 42.9761 191.891 43.5012L245.274 91.389C245.732 91.7153 246.056 92.1662 246.197 92.672C246.338 93.1779 246.287 93.7107 246.053 94.1885C245.818 94.6663 245.414 95.0626 244.901 95.3164C244.387 95.5701 243.794 95.6673 243.213 95.5929H210.441C209.676 95.5929 208.942 95.8625 208.401 96.3423C207.86 96.8222 207.556 97.4731 207.556 98.1517V156.823C207.588 157.095 207.552 157.369 207.449 157.626C207.347 157.883 207.181 158.116 206.963 158.309C206.746 158.502 206.483 158.649 206.193 158.74C205.903 158.831 205.594 158.863 205.288 158.834H169.425C168.824 158.834 168.247 158.622 167.822 158.245C167.397 157.868 167.158 157.357 167.158 156.823V97.6034C167.218 97.0011 167.017 96.4012 166.596 95.9258C166.175 95.4503 165.565 95.1352 164.891 95.0445H131.501C131.055 94.9871 130.632 94.8309 130.272 94.5903C129.913 94.3497 129.627 94.0326 129.442 93.6681C129.257 93.3037 129.179 92.9038 129.214 92.5054C129.25 92.107 129.398 91.723 129.646 91.389Z" fill="#F2F2F2"/>
</g>
<defs>
<linearGradient id="paint0_linear" x1="185.497" y1="216.409" x2="185.497" y2="-5.72786e-05" gradientUnits="userSpaceOnUse">
<stop stop-color="#0078D4"/>
<stop offset="0.16" stop-color="#1380DA"/>
<stop offset="0.53" stop-color="#3C91E5"/>
<stop offset="0.82" stop-color="#559CEC"/>
<stop offset="1" stop-color="#5EA0EF"/>
</linearGradient>
<linearGradient id="paint1_linear" x1="95.2192" y1="247.481" x2="277.628" y2="247.481" gradientUnits="userSpaceOnUse">
<stop stop-color="#32BEDD"/>
<stop offset="0.06" stop-color="#37C5E3"/>
<stop offset="0.3" stop-color="#49DDF7"/>
<stop offset="0.45" stop-color="#50E6FF"/>
<stop offset="0.55" stop-color="#50E6FF"/>
<stop offset="0.7" stop-color="#49DDF7"/>
<stop offset="0.94" stop-color="#37C5E3"/>
<stop offset="1" stop-color="#32BEDD"/>
</linearGradient>
<clipPath id="clip0">
<rect width="371" height="329" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 32 32"><defs><style>.cls-1{fill:#fff;}.cls-2{mask:url(#mask);}.cls-3{fill:url(#linear-gradient);}.cls-4{fill:url(#linear-gradient-2);}.cls-5{fill:url(#linear-gradient-3);}.cls-6{fill:#9cebff;}.cls-7{fill:url(#linear-gradient-4);}.cls-8{fill:url(#linear-gradient-5);}.cls-9{fill:none;}</style><mask id="mask" x="3.556" y="-6.4" width="37.926" height="38.4" maskUnits="userSpaceOnUse"><g id="mask0"><path id="Union" class="cls-1" d="M39.9-6.4a1.59,1.59,0,0,1,1.58,1.6V10.4A1.59,1.59,0,0,1,39.9,12H31.21V30.4A1.59,1.59,0,0,1,29.63,32H5.136a1.59,1.59,0,0,1-1.58-1.6V1.6A1.59,1.59,0,0,1,5.136,0H18.568V-4.8a1.59,1.59,0,0,1,1.58-1.6Z"/></g></mask><linearGradient id="linear-gradient" x1="17.383" y1="32" x2="17.383" gradientUnits="userSpaceOnUse"><stop offset="0.243" stop-color="#0078d7"/><stop offset="1" stop-color="#5ea0ef"/></linearGradient><linearGradient id="linear-gradient-2" x1="3.556" y1="20.543" x2="3.556" y2="18.173" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#a3a3a3"/><stop offset="1" stop-color="#e6e6e6"/></linearGradient><linearGradient id="linear-gradient-3" x1="3.556" y1="27.259" x2="3.556" y2="24.889" xlink:href="#linear-gradient-2"/><linearGradient id="linear-gradient-4" x1="3.556" y1="7.111" x2="3.556" y2="4.741" xlink:href="#linear-gradient-2"/><linearGradient id="linear-gradient-5" x1="3.556" y1="13.827" x2="3.556" y2="11.457" xlink:href="#linear-gradient-2"/></defs><title>j_</title><g class="cls-2"><path id="Rectangle" class="cls-3" d="M3.556,1.6A1.59,1.59,0,0,1,5.136,0H29.63a1.59,1.59,0,0,1,1.58,1.6V30.4A1.59,1.59,0,0,1,29.63,32H5.136a1.59,1.59,0,0,1-1.58-1.6Z"/></g><g id="Union_2" data-name="Union 2"><path class="cls-4" d="M5.315,18.173H1.8a.918.918,0,0,0-1.006.79v.79a.918.918,0,0,0,1.006.79H5.315a.918.918,0,0,0,1.006-.79v-.79A.918.918,0,0,0,5.315,18.173Z"/></g><g id="Union_2-2" data-name="Union 2"><path class="cls-5" d="M5.315,24.889H1.8a.918.918,0,0,0-1.006.79v.79a.917.917,0,0,0,1.006.79H5.315a.917.917,0,0,0,1.006-.79v-.79A.918.918,0,0,0,5.315,24.889Z"/></g><g id="Union_2-3" data-name="Union 2"><path class="cls-6" d="M24.873,4.608H11.661A.736.736,0,0,0,11,5.4v4.667a.736.736,0,0,0,.666.79H24.873a.736.736,0,0,0,.666-.79V5.4A.736.736,0,0,0,24.873,4.608Z"/></g><g id="Union_2-4" data-name="Union 2"><path class="cls-7" d="M5.315,4.741H1.8a.917.917,0,0,0-1.006.79v.79a.918.918,0,0,0,1.006.79H5.315a.918.918,0,0,0,1.006-.79v-.79A.917.917,0,0,0,5.315,4.741Z"/></g><g id="Union_2-5" data-name="Union 2"><path class="cls-8" d="M5.315,11.457H1.8a.918.918,0,0,0-1.006.79v.79a.918.918,0,0,0,1.006.79H5.315a.918.918,0,0,0,1.006-.79v-.79A.918.918,0,0,0,5.315,11.457Z"/></g><rect class="cls-9" width="32" height="32"/></svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -0,0 +1,32 @@
<svg viewBox="0 0 371 329" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0)">
<path d="M357.6 148.598C357.071 132.361 350.039 116.822 337.765 104.764C325.49 92.7068 308.774 84.9197 290.614 82.7983C289.488 60.043 278.302 38.5964 259.478 23.1003C240.653 7.60415 215.704 -0.694702 190.032 -6.17467e-05C169.318 -0.301247 149.007 5.08827 131.885 15.4291C114.762 25.77 101.667 40.5556 94.3959 57.7577C72.5117 60.1492 52.3148 69.4064 37.4581 83.8552C22.6013 98.304 14.0655 116.99 13.3943 136.535C14.307 158.515 25.0232 179.275 43.1878 194.252C61.3524 209.23 85.4793 217.199 110.266 216.409H275.155C276.527 216.511 277.906 216.511 279.278 216.409C299.717 216.368 319.332 209.261 333.957 196.599C348.582 183.937 357.063 166.717 357.6 148.598Z" fill="url(#paint0_linear)"/>
<path d="M186.526 191.734C136.029 191.734 95.2191 180.219 95.2191 165.962V303.228C95.2191 317.302 136.441 328.817 185.29 329H186.526C236.818 329 277.628 317.485 277.628 303.228V165.962C277.628 180.219 236.818 191.734 186.526 191.734Z" fill="url(#paint1_linear)"/>
<path d="M277.628 165.962C277.628 180.219 236.405 191.734 186.526 191.734C136.647 191.734 95.2191 180.219 95.2191 165.962C95.2191 151.706 136.441 140.191 186.526 140.191C236.611 140.191 277.628 151.706 277.628 165.962Z" fill="#E8E8E8"/>
<path d="M256.4 164.5C256.4 173.456 225.071 180.95 186.529 180.95C147.986 180.95 116.451 173.456 116.451 164.5C116.451 155.544 147.78 148.05 186.529 148.05C225.277 148.05 256.4 155.361 256.4 164.5Z" fill="#50E6FF"/>
<path d="M186.526 167.79C167.799 167.403 149.112 169.497 131.082 174.004C149.093 178.609 167.786 180.766 186.526 180.402C205.264 180.741 223.953 178.584 241.97 174.004C223.934 169.524 205.252 167.43 186.526 167.79Z" fill="#32BEDD"/>
<path d="M129.646 91.389L183.028 43.5012C183.605 42.9761 184.294 42.5588 185.055 42.2738C185.816 41.9888 186.634 41.842 187.46 41.842C188.286 41.842 189.104 41.9888 189.865 42.2738C190.626 42.5588 191.315 42.9761 191.891 43.5012L245.274 91.389C245.732 91.7153 246.056 92.1662 246.197 92.672C246.338 93.1779 246.287 93.7107 246.053 94.1885C245.818 94.6663 245.414 95.0626 244.9 95.3164C244.387 95.5701 243.794 95.6673 243.213 95.5929H210.441C209.676 95.5929 208.942 95.8625 208.401 96.3423C207.86 96.8222 207.556 97.4731 207.556 98.1517V156.823C207.588 157.095 207.552 157.369 207.449 157.626C207.347 157.883 207.181 158.116 206.963 158.309C206.746 158.502 206.483 158.649 206.193 158.74C205.903 158.831 205.594 158.863 205.288 158.834H169.425C168.824 158.834 168.247 158.622 167.822 158.245C167.397 157.868 167.158 157.357 167.158 156.823V97.6034C167.218 97.0011 167.017 96.4012 166.596 95.9258C166.174 95.4503 165.565 95.1352 164.891 95.0445H131.501C131.055 94.9871 130.632 94.8309 130.272 94.5903C129.912 94.3497 129.627 94.0326 129.442 93.6681C129.257 93.3037 129.179 92.9038 129.214 92.5054C129.25 92.107 129.398 91.723 129.646 91.389Z" fill="#F2F2F2"/>
</g>
<defs>
<linearGradient id="paint0_linear" x1="185.497" y1="216.409" x2="185.497" y2="-5.72786e-05" gradientUnits="userSpaceOnUse">
<stop stop-color="#0078D4"/>
<stop offset="0.16" stop-color="#1380DA"/>
<stop offset="0.53" stop-color="#3C91E5"/>
<stop offset="0.82" stop-color="#559CEC"/>
<stop offset="1" stop-color="#5EA0EF"/>
</linearGradient>
<linearGradient id="paint1_linear" x1="95.2191" y1="247.481" x2="277.628" y2="247.481" gradientUnits="userSpaceOnUse">
<stop stop-color="#32BEDD"/>
<stop offset="0.06" stop-color="#37C5E3"/>
<stop offset="0.3" stop-color="#49DDF7"/>
<stop offset="0.45" stop-color="#50E6FF"/>
<stop offset="0.55" stop-color="#50E6FF"/>
<stop offset="0.7" stop-color="#49DDF7"/>
<stop offset="0.94" stop-color="#37C5E3"/>
<stop offset="1" stop-color="#32BEDD"/>
</linearGradient>
<clipPath id="clip0">
<rect width="371" height="329" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 187 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 166 KiB

View File

@@ -12,6 +12,7 @@
"azdata": ">=1.19.0"
},
"activationEvents": [
"onDashboardOpen",
"onCommand:sqlmigration.start",
"onCommand:sqlmigration.testDialog",
"onCommand:sqlmigration.openNotebooks"
@@ -41,6 +42,35 @@
"title": "%migration-notebook-command-title%",
"category": "SQL Migration"
}
],
"dashboard.tabs": [
{
"id": "migration-dashboard",
"description": "%migration-dashboard-title%",
"provider": "MSSQL",
"title": "%migration-dashboard-title%",
"icon": {
"light": "./images/migration.svg",
"dark": "./images/migration.svg"
},
"when": "connectionProvider == 'MSSQL' && !mssql:iscloud",
"container": {
"grid-container": [
{
"name": "",
"row": 0,
"col": 1,
"rowspan": 5,
"colspan": 5,
"widget": {
"modelview": {
"id": "migration.dashboard"
}
}
}
]
}
}
]
},
"dependencies": {

View File

@@ -1,5 +1,7 @@
{
"displayName": "SQL Migration",
"description": "SQL migration description",
"migration-notebook-command-title": "Open SQL migration notebooks"
"migration-notebook-command-title": "Open SQL migration notebooks",
"migration-dashboard-title": "SQL Migration",
"migration-dashboard-tasks": "Migration Tasks"
}

View File

@@ -127,8 +127,8 @@ export async function getMigrationControllerAuthKeys(account: azdata.Account, su
throw new Error(response.errors.toString());
}
return {
keyName1: response?.response?.data?.keyName1 ?? '',
keyName2: response?.response?.data?.keyName2 ?? ''
authKey1: response?.response?.data?.authKey1 ?? '',
authKey2: response?.response?.data?.authKey2 ?? ''
};
}
@@ -157,7 +157,7 @@ export async function getMigrationControllerMonitoringData(account: azdata.Accou
return response.response.data;
}
export async function startDatabaseMigration(account: azdata.Account, subscription: Subscription, resourceGroupName: string, regionName: string, managedInstance: string, migrationControllerName: string, requestBody: StartDatabaseMigrationRequest): Promise<any> {
export async function startDatabaseMigration(account: azdata.Account, subscription: Subscription, resourceGroupName: string, regionName: string, managedInstance: string, migrationControllerName: string, requestBody: StartDatabaseMigrationRequest): Promise<StartDatabaseMigrationResponse> {
const api = await getAzureCoreAPI();
const host = `https://${regionName}.management.azure.com`;
const path = `/subscriptions/${subscription.id}/resourceGroups/${resourceGroupName}/providers/Microsoft.Sql/managedInstances/${managedInstance}/providers/Microsoft.DataMigration/databaseMigrations/${migrationControllerName}?api-version=2020-09-01-preview`;
@@ -166,12 +166,23 @@ export async function startDatabaseMigration(account: azdata.Account, subscripti
throw new Error(response.errors.toString());
}
return {
errors: response.errors,
status: response.response.status,
databaseMigration: response.response.data
};
}
export async function getMigrationStatus(account: azdata.Account, subscription: Subscription, migration: DatabaseMigration): Promise<any> {
const api = await getAzureCoreAPI();
const host = `https://eastus2euap.management.azure.com`;
const path = `${migration.id}?$expand=MigrationStatusDetails&api-version=2020-09-01-preview`;
const response = await api.makeAzureRestRequest(account, subscription, path, azurecore.HttpRequestMethod.GET, undefined, true, host);
if (response.errors.length > 0) {
throw new Error(response.errors.toString());
}
return {
result: response.response.data
};
}
/**
* For now only east us euap is supported. Actual API calls will be added in the public release.
@@ -223,8 +234,8 @@ export interface MigrationController {
}
export interface GetMigrationControllerAuthKeysResult {
keyName1: string,
keyName2: string
authKey1: string,
authKey2: string
}
export interface GetStorageAccountAccessKeysResult {
@@ -285,3 +296,8 @@ export interface DatabaseMigration {
name: string,
type: string
}
export interface StartDatabaseMigrationResponse {
status: number,
databaseMigration: DatabaseMigration
}

View File

@@ -11,21 +11,37 @@ export interface IconPath {
}
export class IconPathHelper {
private static context: vscode.ExtensionContext;
public static copy: IconPath;
public static refresh: IconPath;
public static sqlMiImportHelpThumbnail: IconPath;
public static sqlVmImportHelpThumbnail: IconPath;
public static migrationDashboardHeaderBackground: IconPath;
public static sqlMigrationLogo: IconPath;
public static setExtensionContext(context: vscode.ExtensionContext) {
IconPathHelper.context = context;
IconPathHelper.copy = {
light: IconPathHelper.context.asAbsolutePath('images/copy.svg'),
dark: IconPathHelper.context.asAbsolutePath('images/copy.svg')
light: context.asAbsolutePath('images/copy.svg'),
dark: context.asAbsolutePath('images/copy.svg')
};
IconPathHelper.refresh = {
light: context.asAbsolutePath('images/refresh.svg'),
dark: context.asAbsolutePath('images/refresh.svg')
};
IconPathHelper.sqlMiImportHelpThumbnail = {
light: context.asAbsolutePath('images/sqlMiImportHelpThumbnail.svg'),
dark: context.asAbsolutePath('images/sqlMiImportHelpThumbnail.svg')
};
IconPathHelper.sqlVmImportHelpThumbnail = {
light: context.asAbsolutePath('images/sqlVmImportHelpThumbnail.svg'),
dark: context.asAbsolutePath('images/sqlVmImportHelpThumbnail.svg')
};
IconPathHelper.migrationDashboardHeaderBackground = {
light: context.asAbsolutePath('images/background.svg'),
dark: context.asAbsolutePath('images/background.svg')
};
IconPathHelper.sqlMigrationLogo = {
light: context.asAbsolutePath('images/migration.svg'),
dark: context.asAbsolutePath('images/migration.svg')
};
}
}

View File

@@ -0,0 +1,499 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata';
import * as vscode from 'vscode';
import { MigrationLocalStorage } from '../models/migrationLocalStorage';
import * as loc from '../models/strings';
import { IconPathHelper } from '../constants/iconPathHelper';
interface IActionMetadata {
title?: string,
description?: string,
link?: string,
iconPath?: { light: string | vscode.Uri; dark: string | vscode.Uri },
command?: string;
}
const maxWidth = 800;
export class DashboardWidget {
private _migrationStatusCardsContainer!: azdata.FlexContainer;
private _view!: azdata.ModelView;
constructor() {
}
public register(): void {
azdata.ui.registerModelViewProvider('migration.dashboard', async (view) => {
this._view = view;
const container = view.modelBuilder.flexContainer().withLayout({
flexFlow: 'column',
width: '100%',
height: '100%'
}).component();
const header = this.createHeader(view);
const tasksContainer = await this.createTasks(view);
container.addItem(header, {
CSSStyles: {
'background-image': `url(${vscode.Uri.file(<string>IconPathHelper.migrationDashboardHeaderBackground.light)})`,
'width': '1100px',
'height': '300px',
'background-size': '100%',
}
});
header.addItem(tasksContainer, {
CSSStyles: {
'width': `${maxWidth}px`,
'height': '150px',
}
});
header.addItem(await this.createFooter(view), {
CSSStyles: {
'margin-top': '20px'
}
});
const mainContainer = view.modelBuilder.flexContainer()
.withLayout({
flexFlow: 'column',
width: '100%',
height: '100%',
position: 'absolute'
}).component();
mainContainer.addItem(container, {
CSSStyles: { 'padding-top': '25px', 'padding-left': '5px' }
});
await view.initializeModel(mainContainer);
this.refreshMigrations();
});
}
private createHeader(view: azdata.ModelView): azdata.FlexContainer {
const header = view.modelBuilder.flexContainer().withLayout({
flexFlow: 'column',
width: maxWidth,
}).component();
const titleComponent = view.modelBuilder.text().withProps({
value: loc.DASHBOARD_TITLE,
CSSStyles: {
'font-size': '36px',
}
}).component();
const descComponent = view.modelBuilder.text().withProps({
value: loc.DASHBOARD_DESCRIPTION,
CSSStyles: {
'font-size': '12px',
}
}).component();
header.addItems([titleComponent, descComponent], {
CSSStyles: {
'width': `${maxWidth}px`,
'padding-left': '20px'
}
});
return header;
}
private async createTasks(view: azdata.ModelView): Promise<azdata.Component> {
const tasksContainer = view.modelBuilder.flexContainer().withLayout({
flexFlow: 'row',
width: '100%',
height: '50px',
}).component();
const migrateButtonMetadata: IActionMetadata = {
title: loc.DASHBOARD_MIGRATE_TASK_BUTTON_TITLE,
description: loc.DASHBOARD_MIGRATE_TASK_BUTTON_DESCRIPTION,
iconPath: IconPathHelper.sqlMigrationLogo,
command: 'sqlmigration.start'
};
const preRequisiteListTitle = view.modelBuilder.text().withProps({
value: loc.PRE_REQ_TITLE,
CSSStyles: {
'font-size': '14px',
'padding-left': '15px',
'margin-bottom': '-5px'
}
}).component();
const migrateButton = this.createTaskButton(view, migrateButtonMetadata);
const points = `${loc.PRE_REQ_1}
${loc.PRE_REQ_2}
${loc.PRE_REQ_3}`;
const preRequisiteListElement = view.modelBuilder.text().withProps({
value: points,
CSSStyles: {
'padding-left': '15px'
}
}).component();
const preRequisiteLearnMoreLink = view.modelBuilder.hyperlink().withProps({
label: loc.LEARN_MORE,
url: '', //TODO: add link for the pre req document.
CSSStyles: {
'padding-left': '10px'
}
}).component();
const preReqContainer = view.modelBuilder.flexContainer().withItems([
preRequisiteListTitle,
preRequisiteListElement
]).withLayout({
flexFlow: 'column'
}).component();
preReqContainer.addItem(preRequisiteLearnMoreLink, {
CSSStyles: {
'padding-left': '10px'
}
});
tasksContainer.addItem(migrateButton, {
CSSStyles: {
'margin-top': '20px',
'padding': '10px'
}
});
tasksContainer.addItems([preReqContainer], {
CSSStyles: {
'padding': '10px'
}
});
return tasksContainer;
}
private createTaskButton(view: azdata.ModelView, taskMetaData: IActionMetadata): azdata.Component {
const maxHeight: number = 84;
const maxWidth: number = 236;
const buttonContainer = view.modelBuilder.button().withProps({
buttonType: azdata.ButtonType.Informational,
description: taskMetaData.description,
height: maxHeight,
iconHeight: 32,
iconPath: taskMetaData.iconPath,
iconWidth: 32,
label: taskMetaData.title,
title: taskMetaData.title,
width: maxWidth,
}).component();
buttonContainer.onDidClick(async () => {
if (taskMetaData.command) {
await vscode.commands.executeCommand(taskMetaData.command);
}
});
return view.modelBuilder.divContainer().withItems([buttonContainer]).component();
}
private async refreshMigrations(): Promise<void> {
this._migrationStatusCardsContainer.clearItems();
const currentConnection = (await azdata.connection.getCurrentConnection());
const getMigrations = MigrationLocalStorage.getMigrations(currentConnection);
getMigrations.forEach((migration) => {
const button = this._view.modelBuilder.button().withProps({
label: `Migration to ${migration.targetManagedInstance.name} using controller ${migration.migrationContext.name}`
}).component();
button.onDidClick(async (e) => {
});
this._migrationStatusCardsContainer.addItem(
button
);
});
}
private async createFooter(view: azdata.ModelView): Promise<azdata.Component> {
const footerContainer = view.modelBuilder.flexContainer().withLayout({
flexFlow: 'row',
width: maxWidth,
height: '500px',
justifyContent: 'flex-start'
}).component();
const statusContainer = await this.createMigrationStatusContainer(view);
const videoLinksContainer = this.createVideoLinks(view);
footerContainer.addItem(statusContainer);
footerContainer.addItem(videoLinksContainer, {
CSSStyles: {
'padding-left': '10px',
}
});
return footerContainer;
}
private async createMigrationStatusContainer(view: azdata.ModelView): Promise<azdata.FlexContainer> {
const statusContainer = view.modelBuilder.flexContainer().withLayout({
flexFlow: 'column',
width: '400px',
height: '280px',
justifyContent: 'flex-start',
}).withProps({
CSSStyles: {
'border': '1px solid rgba(0, 0, 0, 0.1)',
'padding': '15px'
}
}).component();
const statusContainerTitle = view.modelBuilder.text().withProps({
value: loc.DATABASE_MIGRATION_STATUS,
CSSStyles: {
'font-size': '18px',
'font-weight': 'bold',
'margin': '0px',
'width': '290px'
}
}).component();
const viewAllButton = view.modelBuilder.hyperlink().withProps({
label: loc.VIEW_ALL,
url: ''
}).component();
const refreshButton = view.modelBuilder.hyperlink().withProps({
label: loc.REFRESH,
url: '',
CSSStyles: {
'text-align': 'right'
}
}).component();
refreshButton.onDidClick((e) => {
this.refreshMigrations();
});
const buttonContainer = view.modelBuilder.flexContainer().withLayout({
justifyContent: 'flex-end',
}).component();
buttonContainer.addItem(viewAllButton, {
flex: 'auto',
CSSStyles: {
'border-right': '1px solid rgba(0, 0, 0, 0.7)',
'width': '40px',
}
});
buttonContainer.addItem(refreshButton, {
flex: 'auto',
CSSStyles: {
'margin-left': '5px',
'width': '25px'
}
});
const header = view.modelBuilder.flexContainer().withItems(
[
statusContainerTitle,
buttonContainer
]
).withLayout({
flexFlow: 'row'
}).component();
this._migrationStatusCardsContainer = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'column' }).component();
statusContainer.addItem(
header, {
CSSStyles: {
'padding': '0px',
'padding-right': '5px',
'padding-top': '10px',
'height': '10px',
'margin': '0px'
}
}
);
statusContainer.addItem(this._migrationStatusCardsContainer, {
CSSStyles: {
'margin-top': '30px'
}
});
return statusContainer;
}
private createVideoLinks(view: azdata.ModelView): azdata.Component {
const linksContainer = view.modelBuilder.flexContainer().withLayout({
flexFlow: 'column',
width: '400px',
height: '280px',
justifyContent: 'flex-start',
}).withProps({
CSSStyles: {
'border': '1px solid rgba(0, 0, 0, 0.1)',
'padding': '15px'
}
}).component();
const titleComponent = view.modelBuilder.text().withProps({
value: loc.HELP_TITLE,
CSSStyles: {
'font-size': '18px',
'font-weight': 'bold',
'margin': '0px'
}
}).component();
linksContainer.addItems([titleComponent], {
CSSStyles: {
'padding': '0px',
'padding-right': '5px',
'padding-top': '10px',
'height': '10px',
'margin': '0px'
}
});
const links = [{
title: loc.HELP_LINK1_TITLE,
description: loc.HELP_LINK1_DESCRIPTION,
link: 'www.microsoft.com' //TODO: add proper link over here.
}];
const styles = {
'margin-top': '10px',
'padding': '10px 10px 10px 0'
};
linksContainer.addItems(links.map(l => this.createLink(view, l)), {
CSSStyles: styles
});
const videosContainer = this.createVideoLinkContainers(view, [
{
iconPath: IconPathHelper.sqlMiImportHelpThumbnail,
description: loc.HELP_VIDEO1_TITLE,
link: 'https://www.youtube.com/watch?v=sE99cSoFOHs' //TODO: Fix Video link
},
{
iconPath: IconPathHelper.sqlVmImportHelpThumbnail,
description: loc.HELP_VIDEO2_TITLE,
link: 'https://www.youtube.com/watch?v=R4GCBoxADyQ' //TODO: Fix video link
}
]);
const viewPanelStyle = {
'padding': '10px 5px 10px 10px',
'margin-top': '-15px'
};
linksContainer.addItem(videosContainer, {
CSSStyles: viewPanelStyle
});
return linksContainer;
}
private createLink(view: azdata.ModelView, linkMetaData: IActionMetadata): azdata.Component {
const maxWidth = 400;
const labelsContainer = view.modelBuilder.flexContainer().withLayout({
flexFlow: 'column',
width: maxWidth,
justifyContent: 'flex-start'
}).component();
const descriptionComponent = view.modelBuilder.text().withProps({
value: linkMetaData.description,
width: maxWidth,
CSSStyles: {
'font-size': '12px',
'line-height': '16px',
'margin': '0px'
}
}).component();
const linkContainer = view.modelBuilder.flexContainer().withLayout({
flexFlow: 'row',
width: maxWidth + 10,
justifyContent: 'flex-start'
}).component();
const linkComponent = view.modelBuilder.hyperlink().withProps({
label: linkMetaData.title!,
url: linkMetaData.link!,
showLinkIcon: true,
CSSStyles: {
'font-size': '14px',
'margin': '0px'
}
}).component();
linkContainer.addItem(linkComponent, {
CSSStyles: {
'font-size': '14px',
'line-height': '18px',
'padding': '0 5px 0 0',
}
});
labelsContainer.addItems([linkContainer, descriptionComponent], {
CSSStyles: {
'padding': '5px 0 0 0',
}
});
return labelsContainer;
}
private createVideoLinkContainers(view: azdata.ModelView, links: IActionMetadata[]): azdata.Component {
const maxWidth = 400;
const videosContainer = view.modelBuilder.flexContainer().withLayout({
flexFlow: 'row',
width: maxWidth,
}).component();
links.forEach(link => {
const videoContainer = this.createVideoLink(view, link);
videosContainer.addItem(videoContainer);
});
return videosContainer;
}
private createVideoLink(view: azdata.ModelView, linkMetaData: IActionMetadata): azdata.Component {
const maxWidth = 150;
const videosContainer = view.modelBuilder.flexContainer().withLayout({
flexFlow: 'column',
width: maxWidth,
justifyContent: 'flex-start'
}).component();
const video1Container = view.modelBuilder.divContainer().withProps({
clickable: true,
width: maxWidth,
height: '100px'
}).component();
const descriptionComponent = view.modelBuilder.text().withProps({
value: linkMetaData.description,
width: maxWidth,
height: '50px',
CSSStyles: {
'font-size': '13px',
'margin': '0px'
}
}).component();
video1Container.onDidClick(async () => {
if (linkMetaData.link) {
await vscode.env.openExternal(vscode.Uri.parse(linkMetaData.link));
}
});
videosContainer.addItem(video1Container, {
CSSStyles: {
'background-image': `url(${vscode.Uri.file(<string>linkMetaData.iconPath?.light)})`,
'background-repeat': 'no-repeat',
'background-position': 'top',
'width': `${maxWidth}px`,
'height': '104px',
'background-size': `${maxWidth}px 120px`
}
});
videosContainer.addItem(descriptionComponent);
return videosContainer;
}
}

View File

@@ -5,13 +5,13 @@
import * as azdata from 'azdata';
import * as vscode from 'vscode';
import { createMigrationController, getMigrationControllerRegions, getMigrationController, getResourceGroups, getMigrationControllerAuthKeys, getMigrationControllerMonitoringData } from '../api/azure';
import { MigrationStateModel } from '../models/stateMachine';
import * as constants from '../models/strings';
import { createMigrationController, getMigrationControllerRegions, getMigrationController, getResourceGroups, getMigrationControllerAuthKeys, getMigrationControllerMonitoringData } from '../../api/azure';
import { MigrationStateModel } from '../../models/stateMachine';
import * as constants from '../../models/strings';
import * as os from 'os';
import { azureResource } from 'azureResource';
import { IntergrationRuntimePage } from './integrationRuntimePage';
import { IconPathHelper } from '../constants/iconPathHelper';
import { IntergrationRuntimePage } from '../../wizard/integrationRuntimePage';
import { IconPathHelper } from '../../constants/iconPathHelper';
export class CreateMigrationControllerDialog {
@@ -464,7 +464,7 @@ export class CreateMigrationControllerDialog {
value: constants.CONTROLLER_KEY1_LABEL
},
{
value: keys.keyName1
value: keys.authKey1
},
{
value: this._copyKey1Button
@@ -478,7 +478,7 @@ export class CreateMigrationControllerDialog {
value: constants.CONTROLLER_KEY2_LABEL
},
{
value: keys.keyName2
value: keys.authKey2
},
{
value: this._copyKey2Button

View File

@@ -11,12 +11,15 @@ import { promises as fs } from 'fs';
import * as loc from './models/strings';
import { MigrationNotebookInfo, NotebookPathHelper } from './constants/notebookPathHelper';
import { IconPathHelper } from './constants/iconPathHelper';
import { DashboardWidget } from './dashboard/sqlServerDashboard';
import { MigrationLocalStorage } from './models/migrationLocalStorage';
class SQLMigration {
constructor(private readonly context: vscode.ExtensionContext) {
NotebookPathHelper.setExtensionContext(context);
IconPathHelper.setExtensionContext(context);
MigrationLocalStorage.setExtensionContext(context);
}
async start(): Promise<void> {
@@ -83,6 +86,8 @@ let sqlMigration: SQLMigration;
export async function activate(context: vscode.ExtensionContext) {
sqlMigration = new SQLMigration(context);
await sqlMigration.registerCommands();
let widget = new DashboardWidget();
widget.register();
}
export function deactivate(): void {

View File

@@ -23,7 +23,7 @@ export class MigrationLocalStorage {
const migrationMementos: MigrationContext[] = this.context.globalState.get(this.mementoToken) || [];
dataBaseMigrations = migrationMementos.filter((memento) => {
return memento.connection.serverName === connectionProfile.serverName;
return memento.sourceConnectionProfile.serverName === connectionProfile.serverName;
}).map((memento) => {
return memento;
});
@@ -35,13 +35,13 @@ export class MigrationLocalStorage {
return dataBaseMigrations;
}
public static saveMigration(connection: azdata.connection.ConnectionProfile, migration: DatabaseMigration, targetMI: SqlManagedInstance, azureAccount: azdata.Account, subscription: azureResource.AzureResourceSubscription): void {
public static saveMigration(connectionProfile: azdata.connection.ConnectionProfile, migrationContext: DatabaseMigration, targetMI: SqlManagedInstance, azureAccount: azdata.Account, subscription: azureResource.AzureResourceSubscription): void {
try {
const migrationMementos: MigrationContext[] = this.context.globalState.get(this.mementoToken) || [];
migrationMementos.push({
connection: connection,
migration: migration,
targetMI: targetMI,
sourceConnectionProfile: connectionProfile,
migrationContext: migrationContext,
targetManagedInstance: targetMI,
subscription: subscription,
azureAccount: azureAccount
});
@@ -57,9 +57,9 @@ export class MigrationLocalStorage {
}
export interface MigrationContext {
connection: azdata.connection.ConnectionProfile,
migration: DatabaseMigration,
targetMI: SqlManagedInstance,
sourceConnectionProfile: azdata.connection.ConnectionProfile,
migrationContext: DatabaseMigration,
targetManagedInstance: SqlManagedInstance,
azureAccount: azdata.Account,
subscription: azureResource.AzureResourceSubscription
}

View File

@@ -453,7 +453,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
Scope: this._targetManagedInstance.id
}
};
console.log(requestBody);
const response = await startDatabaseMigration(
this.azureAccount,
this._targetSubscription,
@@ -464,9 +464,8 @@ export class MigrationStateModel implements Model, vscode.Disposable {
requestBody
);
console.log(response);
if (!response.error) {
MigrationLocalStorage.saveMigration(currentConnection!, response, this._targetManagedInstance, this.azureAccount, this._targetSubscription);
if (response.status === 201) {
MigrationLocalStorage.saveMigration(currentConnection!, response.databaseMigration, this._targetManagedInstance, this.azureAccount, this._targetSubscription);
}
vscode.window.showInformationMessage(constants.MIGRATION_STARTED);

View File

@@ -149,7 +149,7 @@ export const CANCEL = localize('sql.migration.cancel', "Cancel");
export const TYPE = localize('sql.migration.type', "Type");
export const PATH = localize('sql.migration.path', "Path");
export const USER_ACCOUNT = localize('sql.migration.path.user.account', "User Account");
export const VIEW_ALL = localize('sql.migration.view.all', "View All");
//Summary Page
export const SUMMARY_PAGE_TITLE = localize('sql.migration.summary.page.title', "Summary");
@@ -171,3 +171,20 @@ export const NOTEBOOK_QUICK_PICK_PLACEHOLDER = localize('sql.migration.quick.pic
export const NOTEBOOK_INLINE_MIGRATION_TITLE = localize('sql.migration.inline.migration.notebook.title', "Inline migration");
export const NOTEBOOK_SQL_MIGRATION_ASSESSMENT_TITLE = localize('sql.migration.sql.assessment.notebook.title', "SQL migration assessment");
export const NOTEBOOK_OPEN_ERROR = localize('sql.migration.notebook.open.error', "Error opening migration notebook");
// Dashboard
export const DASHBOARD_TITLE = localize('sql.migration.dashboard.title', "Azure SQL Migration");
export const DASHBOARD_DESCRIPTION = localize('sql.migration.dashboard.description', "Determine the migration readiness of your SQL Server instances, identify a recommended Azure SQL target, and complete the migration of your SQL Server instance to Azure SQL Managed Instance or SQL Server on Azure Virtual Machines.");
export const DASHBOARD_MIGRATE_TASK_BUTTON_TITLE = localize('sql.migration.dashboard.migrate.task.button', "Migrate to Azure SQL");
export const DASHBOARD_MIGRATE_TASK_BUTTON_DESCRIPTION = localize('sql.migration.dashboard.migrate.task.button.description', "Migrate SQL Server instance to Azure SQL.");
export const DATABASE_MIGRATION_STATUS = localize('sql.migration.database.migration.status', "Database Migration Status");
export const HELP_VIDEO1_TITLE = localize('sql.migration.dashboard.video1.title', "Migrate to SQL Server to SQL Managed Instance");
export const HELP_VIDEO2_TITLE = localize('sql.migration.dashboard.video2.title', "Migrate to SQL Server to SQL Virtual Machine");
export const HELP_LINK1_TITLE = localize('sql.migration.dashboard.link1.title', "Migrating your SQL Server to cloud");
export const HELP_LINK1_DESCRIPTION = localize('sql.migration.dashboard.link1.description', "Lorem ipsum dolor sit amet, consectetur adipi. Lorem ipsum dolor sit amet, consectetur adipi. Lorem ipsum.");
export const HELP_TITLE = localize('sql.migration.dashboard.help.title', "Help Articles and Video Links");
export const PRE_REQ_TITLE = localize('sql.migration.pre.req.title', "Things you need before starting migration:");
export const PRE_REQ_1 = localize('sql.migration.pre.req.1', "Azure account details");
export const PRE_REQ_2 = localize('sql.migration.pre.req.2', "Azure SQL Managed Instance or SQL Server on Azure Virtual Machine");
export const PRE_REQ_3 = localize('sql.migration.pre.req.3', "Backup location details");

View File

@@ -6,7 +6,7 @@
import * as azdata from 'azdata';
import { MigrationWizardPage } from '../models/migrationWizardPage';
import { MigrationStateModel, StateChangeEvent } from '../models/stateMachine';
import { CreateMigrationControllerDialog } from './createMigrationControllerDialog';
import { CreateMigrationControllerDialog } from '../dialog/createMigrationDialog/createMigrationControllerDialog';
import * as constants from '../models/strings';
import * as os from 'os';
import { WIZARD_INPUT_COMPONENT_WIDTH } from './wizardController';