Compare commits
161 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bdb81bc9b1 | ||
|
|
023e10b496 | ||
|
|
27cb693bd5 | ||
|
|
80a9d94648 | ||
|
|
6fad30bf3c | ||
|
|
a0c2dc199e | ||
|
|
95980130c8 | ||
|
|
8cc0fdfdc5 | ||
|
|
f83605cbf4 | ||
|
|
90d6defa05 | ||
|
|
ca904ed380 | ||
|
|
784a9270c8 | ||
|
|
d585e75706 | ||
|
|
01509de495 | ||
|
|
fa227c88c9 | ||
|
|
cb607e34d7 | ||
|
|
de9a4e02b1 | ||
|
|
820838fae8 | ||
|
|
85f1f3e0dc | ||
|
|
2d1ffeb47c | ||
|
|
a86301312c | ||
|
|
206188f3ab | ||
|
|
d8a8107dc6 | ||
|
|
26e6cd28c2 | ||
|
|
02093f8497 | ||
|
|
62a0bcc69f | ||
|
|
7181d4c79e | ||
|
|
4551329db0 | ||
|
|
5c14fe4f4c | ||
|
|
d1d6b229ce | ||
|
|
94b4437a6a | ||
|
|
ebdfea25ad | ||
|
|
5f2d8b2bd5 | ||
|
|
85d6b2ef46 | ||
|
|
f424bea0de | ||
|
|
364684c79e | ||
|
|
4d7467a5d5 | ||
|
|
b299f7ed3f | ||
|
|
0c54c12772 | ||
|
|
65b4847874 | ||
|
|
33d2735970 | ||
|
|
822199c9be | ||
|
|
e50bded5d1 | ||
|
|
27763c860c | ||
|
|
634b9f8d08 | ||
|
|
3844638156 | ||
|
|
eb77d86136 | ||
|
|
025e0831e7 | ||
|
|
5bb35d4eed | ||
|
|
f013a88163 | ||
|
|
4f9d45335e | ||
|
|
fee8e78011 | ||
|
|
1be5c926d3 | ||
|
|
72632bc376 | ||
|
|
9890cc79c0 | ||
|
|
b3107dc4b1 | ||
|
|
eccb77aca3 | ||
|
|
5d0f0afdc6 | ||
|
|
33259764f7 | ||
|
|
c9aa3e9f4b | ||
|
|
8e9957adbe | ||
|
|
442c16bc3c | ||
|
|
1c83aa61d7 | ||
|
|
9e3d678536 | ||
|
|
9a944b614d | ||
|
|
bc78f6a573 | ||
|
|
da076c152c | ||
|
|
a2c39369c9 | ||
|
|
be6b23eff0 | ||
|
|
0ab4752643 | ||
|
|
10ff8aa116 | ||
|
|
4f61be2495 | ||
|
|
d5fa5fca3a | ||
|
|
8b543d1d12 | ||
|
|
87f1e26e39 | ||
|
|
9990f7c8e2 | ||
|
|
d6a08a60d9 | ||
|
|
628c8da716 | ||
|
|
79cda5cbe5 | ||
|
|
c34de52a03 | ||
|
|
eb928e0b8f | ||
|
|
84001139cc | ||
|
|
00f98457e8 | ||
|
|
ebd4c6844b | ||
|
|
3b4800de51 | ||
|
|
7b5117b185 | ||
|
|
d36b755d6d | ||
|
|
96729a4817 | ||
|
|
d8fd5b36f7 | ||
|
|
dfa6799fcc | ||
|
|
c64655ae71 | ||
|
|
5e93f2ca9b | ||
|
|
7db7c2927c | ||
|
|
ffdefd3b52 | ||
|
|
02341088eb | ||
|
|
8032f59d41 | ||
|
|
0193f8a6dc | ||
|
|
23cbb98162 | ||
|
|
9b3d65b1d4 | ||
|
|
e2b4dd5a79 | ||
|
|
19c7971630 | ||
|
|
5731366a1c | ||
|
|
ef100b8e82 | ||
|
|
7b92c9eb65 | ||
|
|
5547c2baba | ||
|
|
40ee82ee3e | ||
|
|
3c84575755 | ||
|
|
696db01dc8 | ||
|
|
e0cb88599d | ||
|
|
eff847f35a | ||
|
|
2da0428454 | ||
|
|
1b63fec4e5 | ||
|
|
f137174719 | ||
|
|
75d9246f5e | ||
|
|
cbaffd0b0d | ||
|
|
b33afe3efb | ||
|
|
19f63ea88f | ||
|
|
a1a1793221 | ||
|
|
8da49ff14e | ||
|
|
f0ebc6ed60 | ||
|
|
aaf174e85c | ||
|
|
13dd74efcc | ||
|
|
bafbca492b | ||
|
|
3472e229f1 | ||
|
|
3a04235001 | ||
|
|
0f385b386f | ||
|
|
28ab7372d2 | ||
|
|
e66192cbfd | ||
|
|
63cff7a5ab | ||
|
|
f1b23c96a9 | ||
|
|
ede0937c2b | ||
|
|
83698a14b0 | ||
|
|
2be1394748 | ||
|
|
8135867e06 | ||
|
|
87d5da00bf | ||
|
|
678f2e3878 | ||
|
|
81e8eb9a82 | ||
|
|
fe360afec7 | ||
|
|
80c8b06ec8 | ||
|
|
7ff42eeb08 | ||
|
|
3dbd5ac2c1 | ||
|
|
3c321ee8b7 | ||
|
|
f6128d262b | ||
|
|
d24a289af5 | ||
|
|
89cc59a2fd | ||
|
|
25f563229f | ||
|
|
33ea2d8ee6 | ||
|
|
9e8b01fd1e | ||
|
|
bb468a5b98 | ||
|
|
9a28d47c5b | ||
|
|
45763e12b4 | ||
|
|
316c6a536f | ||
|
|
f5392e3c8f | ||
|
|
4dd59c6b8d | ||
|
|
9e574ae602 | ||
|
|
1dd7e93063 | ||
|
|
13276cf52e | ||
|
|
baa6e4b380 | ||
|
|
3bc5306258 | ||
|
|
c45cfaf01b | ||
|
|
0612a8ac35 |
@@ -212,6 +212,7 @@
|
||||
"restrictions": [
|
||||
"vs/nls",
|
||||
"azdata",
|
||||
"azurecore",
|
||||
"**/{vs,sql}/base/common/**",
|
||||
"**/{vs,sql}/base/parts/*/common/**",
|
||||
"**/{vs,sql}/platform/*/common/**"
|
||||
@@ -472,6 +473,7 @@
|
||||
"restrictions": [
|
||||
"vscode",
|
||||
"azdata",
|
||||
"azurecore",
|
||||
"vs/nls",
|
||||
"**/{vs,sql}/base/common/**",
|
||||
"**/{vs,sql}/platform/*/common/**",
|
||||
@@ -577,6 +579,7 @@
|
||||
"vs/nls",
|
||||
"vs/css!./**/*",
|
||||
"azdata",
|
||||
"azurecore",
|
||||
"vscode",
|
||||
"**/{vs,sql}/base/**/{common,browser,worker}/**",
|
||||
"**/{vs,sql}/platform/**/{common,browser}/**",
|
||||
|
||||
16
.github/label-actions.yml
vendored
@@ -27,6 +27,22 @@ There are two types of logs to collect:
|
||||
|
||||
- This will open the log folder locally. Please zip up this folder and attach it to the issue."
|
||||
|
||||
# actions for Needs Logs - Azure label
|
||||
Needs Logs - Azure:
|
||||
comment: "We need more info to debug your Azure Active Directory issue. If you could attach your logs to the issue (ensure no private data is in them), it would help us fix the issue much faster.
|
||||
|
||||
- In the settings menu, find the setting titled `Azure: Logging Level` and select the `Verbose` option
|
||||
|
||||
- Run the process that produces your error
|
||||
|
||||
- Open command palette (Click **View** -> **Command Palette**)
|
||||
|
||||
- Run the command: **`Developer: Open Logs Folder`**
|
||||
|
||||
- Follow this path to find the Azure Accounts log file: `[default log folder]/exthost1/output_logging_[earliest timestamp]/#-Azure Acounts.log`
|
||||
|
||||
- Please attach the Azure-Accounts.log file to the issue."
|
||||
|
||||
# actions for Out of Scope label
|
||||
Out of Scope:
|
||||
comment: "Thank you for opening this suggestion! This enhancement is not planned in our
|
||||
|
||||
2
.github/workflows/ci.yml
vendored
@@ -13,7 +13,7 @@ on:
|
||||
jobs:
|
||||
windows:
|
||||
name: Windows
|
||||
runs-on: windows-latest
|
||||
runs-on: windows-2019
|
||||
timeout-minutes: 30
|
||||
env:
|
||||
CHILD_CONCURRENCY: "1"
|
||||
|
||||
58
CHANGELOG.md
@@ -1,5 +1,43 @@
|
||||
# Change Log
|
||||
|
||||
## Version 1.35.1
|
||||
* Release date: March 17, 2022
|
||||
* Release status: General Availability
|
||||
## Hotfix release
|
||||
- Fix for [Excel number format #18615](https://github.com/microsoft/azuredatastudio/issues/18615)
|
||||
- Fix for [Geometry Data Type Returned as Unknown Charset in Results Grid #18630](https://github.com/microsoft/azuredatastudio/issues/18630)
|
||||
|
||||
| Platform |
|
||||
| --------------------------------------- |
|
||||
| [Windows User Installer][win-user] |
|
||||
| [Windows System Installer][win-system] |
|
||||
| [Windows ZIP][win-zip] |
|
||||
| [macOS ZIP][osx-zip] |
|
||||
| [Linux TAR.GZ][linux-zip] |
|
||||
| [Linux RPM][linux-rpm] |
|
||||
| [Linux DEB][linux-deb] |
|
||||
|
||||
[win-user]: https://go.microsoft.com/fwlink/?linkid=2187459
|
||||
[win-system]: https://go.microsoft.com/fwlink/?linkid=2187520
|
||||
[win-zip]: https://go.microsoft.com/fwlink/?linkid=2187460
|
||||
[osx-zip]: https://go.microsoft.com/fwlink/?linkid=2187461
|
||||
[linux-zip]: https://go.microsoft.com/fwlink/?linkid=2187462
|
||||
[linux-rpm]: https://go.microsoft.com/fwlink/?linkid=2187521
|
||||
[linux-deb]: https://go.microsoft.com/fwlink/?linkid=2187522
|
||||
|
||||
## Version 1.35.0
|
||||
* Release date: February 24, 2022
|
||||
* Release status: General Availability
|
||||
## What's new in this version
|
||||
* New Features:
|
||||
* Table Designer - Added functionality for creation and management of tables for SQL Servers. Built using DacFx framework
|
||||
* Query Plan Viewer - Added functionality for users to view a graphic view of estimated and actual query plans without need for an extension
|
||||
* Azure Arc Extension - Updated the Data Controller deployment wizard and the SQL Managed Instance - Azure Arc deployment wizard to reflect the deployment experience in Azure Portal
|
||||
|
||||
* Bug Fixes:
|
||||
* Azure Arc Extension - SQL Managed Instance-Azure Arc is now fixed for both indirect connectivity mode and direct connectivity mode
|
||||
* Notebooks - Support for keyboard navigation between cells to minimize mouse clicking
|
||||
|
||||
## Version 1.34.0
|
||||
* Release date: December 15, 2021
|
||||
* Release status: General Availability
|
||||
@@ -31,26 +69,6 @@
|
||||
- Fix for [#16535 Unable to See Saved Connections in Restricted Mode](https://github.com/microsoft/azuredatastudio/issues/17535)
|
||||
- Fix for [#17579 Can't type in Notebook code cell after editing text cell](https://github.com/microsoft/azuredatastudio/issues/17579)
|
||||
|
||||
|
||||
|
||||
| Platform |
|
||||
| --------------------------------------- |
|
||||
| [Windows User Installer][win-user] |
|
||||
| [Windows System Installer][win-system] |
|
||||
| [Windows ZIP][win-zip] |
|
||||
| [macOS ZIP][osx-zip] |
|
||||
| [Linux TAR.GZ][linux-zip] |
|
||||
| [Linux RPM][linux-rpm] |
|
||||
| [Linux DEB][linux-deb] |
|
||||
|
||||
[win-user]: https://go.microsoft.com/fwlink/?linkid=2176805
|
||||
[win-system]: https://go.microsoft.com/fwlink/?linkid=2175910
|
||||
[win-zip]: https://go.microsoft.com/fwlink/?linkid=2176806
|
||||
[osx-zip]: https://go.microsoft.com/fwlink/?linkid=2176807
|
||||
[linux-zip]: https://go.microsoft.com/fwlink/?linkid=2176505
|
||||
[linux-rpm]: https://go.microsoft.com/fwlink/?linkid=2176005
|
||||
[linux-deb]: https://go.microsoft.com/fwlink/?linkid=2176006
|
||||
|
||||
## Version 1.33.0
|
||||
* Release date: October 27, 2021
|
||||
* Release status: General Availability
|
||||
|
||||
14
README.md
@@ -131,10 +131,10 @@ Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
Licensed under the [Source EULA](LICENSE.txt).
|
||||
|
||||
[win-user]: https://go.microsoft.com/fwlink/?linkid=2183280
|
||||
[win-system]: https://go.microsoft.com/fwlink/?linkid=2183423
|
||||
[win-zip]: https://go.microsoft.com/fwlink/?linkid=2183190
|
||||
[osx-zip]: https://go.microsoft.com/fwlink/?linkid=2183189
|
||||
[linux-zip]: https://go.microsoft.com/fwlink/?linkid=2183277
|
||||
[linux-rpm]: https://go.microsoft.com/fwlink/?linkid=2183342
|
||||
[linux-deb]: https://go.microsoft.com/fwlink/?linkid=2183341
|
||||
[win-user]: https://go.microsoft.com/fwlink/?linkid=2187459
|
||||
[win-system]: https://go.microsoft.com/fwlink/?linkid=2187520
|
||||
[win-zip]: https://go.microsoft.com/fwlink/?linkid=2187460
|
||||
[osx-zip]: https://go.microsoft.com/fwlink/?linkid=2187461
|
||||
[linux-zip]: https://go.microsoft.com/fwlink/?linkid=2187522
|
||||
[linux-rpm]: https://go.microsoft.com/fwlink/?linkid=2187521
|
||||
[linux-deb]: https://go.microsoft.com/fwlink/?linkid=2187462
|
||||
|
||||
@@ -45,6 +45,7 @@ make-error: https://github.com/JsCommunity/make-error
|
||||
mark.js: https://github.com/julmot/mark.js
|
||||
minimist: https://github.com/substack/minimist
|
||||
moment: https://github.com/moment/moment
|
||||
mxgraph: https://github.com/jgraph/mxgraph
|
||||
native-keymap: https://github.com/Microsoft/node-native-keymap
|
||||
native-watchdog: https://github.com/Microsoft/node-native-watchdog
|
||||
ng2-charts: https://github.com/valor-software/ng2-charts
|
||||
@@ -1538,6 +1539,193 @@ OTHER DEALINGS IN THE SOFTWARE.
|
||||
=========================================
|
||||
END OF moment NOTICES AND INFORMATION
|
||||
|
||||
|
||||
%% mxgraph NOTICES AND INFORMATION BEGIN HERE
|
||||
=========================================
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
(e) Neither the Work nor Derivative Works may be used or form any
|
||||
part of a larger work that integrates or is supposed to be
|
||||
integrated with a product or service owned or marketed by an
|
||||
Atlassian entity, including its successors and assignees in title.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
//SEIBERT/MEDIA GmbH, Wiesbaden, Germany is the exclusive licensee of
|
||||
JGraph for software products based on this codebase within the Atlassian
|
||||
ecosystem of products.
|
||||
=========================================
|
||||
END OF mxgraph NOTICES AND INFORMATION
|
||||
|
||||
%% native-keymap NOTICES AND INFORMATION BEGIN HERE
|
||||
=========================================
|
||||
Copyright (c) Microsoft Corporation
|
||||
|
||||
@@ -17,6 +17,13 @@ steps:
|
||||
mv azuredatastudio-darwin-unsigned.zip azuredatastudio-darwin.zip
|
||||
displayName: 'Rename the file'
|
||||
|
||||
- task: UseDotNet@2
|
||||
displayName: 'Install .NET Core sdk for signing'
|
||||
inputs:
|
||||
packageType: sdk
|
||||
version: 2.1.x
|
||||
installationPath: $(Agent.ToolsDirectory)/dotnet
|
||||
|
||||
- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1
|
||||
displayName: 'ESRP CodeSigning'
|
||||
inputs:
|
||||
|
||||
@@ -128,14 +128,24 @@ steps:
|
||||
yarn gulp compile-extensions
|
||||
displayName: Compile Extensions
|
||||
|
||||
# Per https://developercommunity.visualstudio.com/t/variablesexpressions-dont-work-with-continueonerro/1187733 we can't use variables
|
||||
# in continueOnError directly so instead make two copies of the task and only run one or the other based on the SMOKE_FAIL_ON_ERROR value
|
||||
- script: |
|
||||
set -e
|
||||
APP_ROOT=$(agent.builddirectory)/azuredatastudio-darwin-x64
|
||||
APP_NAME="`ls $APP_ROOT | head -n 1`"
|
||||
yarn smoketest --build "$APP_ROOT/$APP_NAME" --screenshots "$(build.artifactstagingdirectory)/smokeshots" --log "$(build.artifactstagingdirectory)/logs/darwin/smoke.log" --extensionsDir "$(build.sourcesdirectory)/extensions"
|
||||
displayName: Run smoke tests (Electron)
|
||||
displayName: Run smoke tests (Electron) (Continue on Error)
|
||||
continueOnError: true
|
||||
condition: and(succeeded(), eq(variables['RUN_TESTS'], 'true'))
|
||||
condition: and(succeeded(), and(eq(variables['RUN_TESTS'], 'true'), ne(variables['SMOKE_FAIL_ON_ERROR'], 'true')))
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
APP_ROOT=$(agent.builddirectory)/azuredatastudio-darwin-x64
|
||||
APP_NAME="`ls $APP_ROOT | head -n 1`"
|
||||
yarn smoketest --build "$APP_ROOT/$APP_NAME" --screenshots "$(build.artifactstagingdirectory)/smokeshots" --log "$(build.artifactstagingdirectory)/logs/darwin/smoke.log" --extensionsDir "$(build.sourcesdirectory)/extensions"
|
||||
displayName: Run smoke tests (Electron) (Fail on Error)
|
||||
condition: and(succeeded(), and(eq(variables['RUN_TESTS'], 'true'), eq(variables['SMOKE_FAIL_ON_ERROR'], 'true')))
|
||||
|
||||
# - script: |
|
||||
# set -e
|
||||
|
||||
@@ -6,6 +6,9 @@ trigger:
|
||||
|
||||
pr: none
|
||||
|
||||
pool:
|
||||
vmImage: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- task: NodeTool@0
|
||||
inputs:
|
||||
|
||||
@@ -47,7 +47,7 @@ jobs:
|
||||
steps:
|
||||
- template: linux/sql-product-build-linux.yml
|
||||
parameters:
|
||||
extensionsToUnitTest: ["admin-tool-ext-win", "agent", "azcli", "azurecore", "cms", "dacpac", "data-workspace", "import", "machine-learning", "notebook", "resource-deployment", "schema-compare", "sql-database-projects"]
|
||||
extensionsToUnitTest: ["admin-tool-ext-win", "agent", "azcli", "azurecore", "cms", "dacpac", "data-workspace", "import", "machine-learning", "notebook", "resource-deployment", "schema-compare", "sql-bindings", "sql-database-projects"]
|
||||
timeoutInMinutes: 90
|
||||
|
||||
- job: Windows
|
||||
@@ -60,16 +60,17 @@ jobs:
|
||||
- template: win32/sql-product-build-win32.yml
|
||||
timeoutInMinutes: 90
|
||||
|
||||
- job: Windows_Test
|
||||
condition: and(succeeded(), eq(variables['VSCODE_BUILD_WIN32'], 'true'))
|
||||
pool:
|
||||
name: mssqltools
|
||||
dependsOn:
|
||||
- Linux
|
||||
- Windows
|
||||
steps:
|
||||
- template: win32/sql-product-test-win32.yml
|
||||
timeoutInMinutes: 90
|
||||
# disable due to invalid machine pool (karlb 3/9/2022)
|
||||
# - job: Windows_Test
|
||||
# condition: and(succeeded(), eq(variables['VSCODE_BUILD_WIN32'], 'true'))
|
||||
# pool:
|
||||
# name: mssqltools
|
||||
# dependsOn:
|
||||
# - Linux
|
||||
# - Windows
|
||||
# steps:
|
||||
# - template: win32/sql-product-test-win32.yml
|
||||
# timeoutInMinutes: 90
|
||||
|
||||
- job: Release
|
||||
condition: and(succeeded(), or(eq(variables['VSCODE_RELEASE'], 'true'), and(eq(variables['VSCODE_QUALITY'], 'insider'), eq(variables['Build.Reason'], 'Schedule'))))
|
||||
@@ -79,7 +80,8 @@ jobs:
|
||||
- macOS
|
||||
- Linux
|
||||
- Windows
|
||||
- Windows_Test
|
||||
# disable due to invalid machine pool (karlb 3/9/2022)
|
||||
# - Windows_Test
|
||||
- macOS_Signing
|
||||
steps:
|
||||
- template: sql-release.yml
|
||||
|
||||
@@ -93,6 +93,7 @@ steps:
|
||||
AZURE_STORAGE_ACCESS_KEY="$(sourcemap-storage-key)" \
|
||||
node build/azure-pipelines/upload-sourcemaps
|
||||
displayName: Upload sourcemaps
|
||||
condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false'))
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
|
||||
@@ -104,14 +104,26 @@ steps:
|
||||
yarn gulp compile-extensions
|
||||
displayName: Compile Extensions
|
||||
|
||||
- script: |
|
||||
set -e
|
||||
node ./node_modules/playwright/install.js
|
||||
APP_ROOT=$(Agent.BuildDirectory)/vscode-reh-web-linux-x64
|
||||
xvfb-run yarn smoketest --build "$(Agent.BuildDirectory)/vscode-reh-web-linux-x64" --web --headless --screenshots "$(Build.ArtifactStagingDirectory)/smokeshots" --log "$(Build.ArtifactStagingDirectory)/logs/web/smoke.log"
|
||||
displayName: Run smoke tests (Browser)
|
||||
continueOnError: true
|
||||
condition: and(succeeded(), eq(variables['RUN_TESTS'], 'true'))
|
||||
# disable smoke tests (karlb 3/2/2022)
|
||||
# # Per https://developercommunity.visualstudio.com/t/variablesexpressions-dont-work-with-continueonerro/1187733 we can't use variables
|
||||
# # in continueOnError directly so instead make two copies of the task and only run one or the other based on the SMOKE_FAIL_ON_ERROR value
|
||||
# - script: |
|
||||
# set -e
|
||||
# node ./node_modules/playwright/install.js
|
||||
# APP_ROOT=$(Agent.BuildDirectory)/vscode-reh-web-linux-x64
|
||||
# xvfb-run yarn smoketest --build "$(Agent.BuildDirectory)/vscode-reh-web-linux-x64" --web --headless --screenshots "$(Build.ArtifactStagingDirectory)/smokeshots" --log "$(Build.ArtifactStagingDirectory)/logs/web/smoke.log"
|
||||
# displayName: Run smoke tests (Browser) (Continue on Error)
|
||||
# continueOnError: true
|
||||
# condition: and(succeeded(), and(eq(variables['RUN_TESTS'], 'true'), ne(variables['SMOKE_FAIL_ON_ERROR'], 'true')))
|
||||
|
||||
# disable smoke tests (karlb 3/2/2022)
|
||||
# - script: |
|
||||
# set -e
|
||||
# node ./node_modules/playwright/install.js
|
||||
# APP_ROOT=$(Agent.BuildDirectory)/vscode-reh-web-linux-x64
|
||||
# xvfb-run yarn smoketest --build "$(Agent.BuildDirectory)/vscode-reh-web-linux-x64" --web --headless --screenshots "$(Build.ArtifactStagingDirectory)/smokeshots" --log "$(Build.ArtifactStagingDirectory)/logs/web/smoke.log"
|
||||
# displayName: Run smoke tests (Browser) (Fail on Error)
|
||||
# condition: and(succeeded(), and(eq(variables['RUN_TESTS'], 'true'), eq(variables['SMOKE_FAIL_ON_ERROR'], 'true')))
|
||||
|
||||
# - script: |
|
||||
# set -e
|
||||
|
||||
@@ -51,7 +51,7 @@ steps:
|
||||
$AppNameShort = $AppProductJson.nameShort
|
||||
exec { $env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe"; $env:INTEGRATION_TEST_CLI_PATH = "$AppRoot\bin\$AppNameShort"; .\scripts\sql-test-integration.bat }
|
||||
continueOnError: false
|
||||
condition: and(succeeded(), eq(variables['RUN_TESTS'], 'true'))
|
||||
condition: and(succeeded(), and(eq(variables['RUN_TESTS'], 'true'), ne(variables['RUN_INTEGRATION_TESTS'], 'false')))
|
||||
displayName: Run stable tests
|
||||
env:
|
||||
BDC_BACKEND_USERNAME: $(ads-integration-test-bdc-server-username)
|
||||
@@ -75,7 +75,7 @@ steps:
|
||||
$AppNameShort = $AppProductJson.nameShort
|
||||
exec { $env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe"; .\scripts\sql-test-integration-unstable.bat }
|
||||
continueOnError: true
|
||||
condition: and(succeeded(), eq(variables['RUN_UNSTABLE_TESTS'], 'true'))
|
||||
condition: and(succeeded(), and(eq(variables['RUN_UNSTABLE_TESTS'], 'true'), ne(variables['RUN_INTEGRATION_TESTS'], 'false')))
|
||||
displayName: Run unstable integration tests
|
||||
env:
|
||||
BDC_BACKEND_USERNAME: $(ads-integration-test-bdc-server-username)
|
||||
@@ -99,4 +99,4 @@ steps:
|
||||
mergeTestResults: true
|
||||
failTaskOnFailedTests: true
|
||||
continueOnError: true
|
||||
condition: and(succeededOrFailed(), eq(variables['RUN_TESTS'], 'true'))
|
||||
condition: and(succeededOrFailed(), and(eq(variables['RUN_TESTS'], 'true'), ne(variables['RUN_INTEGRATION_TESTS'], 'false')))
|
||||
|
||||
@@ -129,16 +129,19 @@ gulp.task('package-external-extensions', task.series(
|
||||
const packageManifestPath = path.join(packageDir, 'package.json');
|
||||
const json = require('gulp-json-editor');
|
||||
const packageJsonStream = gulp.src(packageManifestPath) // Create stream for the original package.json
|
||||
.pipe(json(data => { // And now use gulp-json-editor to modify the contents
|
||||
.pipe(json(data => {
|
||||
// And now use gulp-json-editor to modify the contents
|
||||
const updateData = JSON.parse(fs.readFileSync(vscodeManifestFullPath)); // Read in the set of values to replace from package.vscode.json
|
||||
Object.keys(updateData).forEach(key => {
|
||||
data[key] = updateData[key];
|
||||
});
|
||||
// Remove ADS-only menus. This is a subset of the menus listed in https://github.com/microsoft/azuredatastudio/blob/main/src/vs/workbench/api/common/menusExtensionPoint.ts
|
||||
// More can be added to the list as needed.
|
||||
['objectExplorer/item/context', 'dataExplorer/context', 'dashboard/toolbar'].forEach(menu => {
|
||||
delete data.contributes.menus[menu];
|
||||
});
|
||||
if(data.contributes?.menus){
|
||||
// Remove ADS-only menus. This is a subset of the menus listed in https://github.com/microsoft/azuredatastudio/blob/main/src/vs/workbench/api/common/menusExtensionPoint.ts
|
||||
// More can be added to the list as needed.
|
||||
['objectExplorer/item/context', 'dataExplorer/context', 'dashboard/toolbar'].forEach(menu => {
|
||||
delete data.contributes.menus[menu];
|
||||
});
|
||||
}
|
||||
return data;
|
||||
}, { beautify: false }))
|
||||
.pipe(gulp.dest(packageDir));
|
||||
|
||||
@@ -135,6 +135,7 @@ const extensionsFilter = filter([
|
||||
'**/schema-compare.xlf',
|
||||
'**/server-report.xlf',
|
||||
'**/sql-assessment.xlf',
|
||||
'**/sql-bindings.xlf',
|
||||
'**/sql-database-projects.xlf',
|
||||
'**/sql-migration.xlf',
|
||||
'**/xml-language-features.xlf'
|
||||
|
||||
@@ -231,6 +231,7 @@ const externalExtensions = [
|
||||
'schema-compare',
|
||||
'server-report',
|
||||
'sql-assessment',
|
||||
'sql-bindings',
|
||||
'sql-database-projects',
|
||||
'sql-migration'
|
||||
];
|
||||
|
||||
@@ -267,6 +267,7 @@ const externalExtensions = [
|
||||
'schema-compare',
|
||||
'server-report',
|
||||
'sql-assessment',
|
||||
'sql-bindings',
|
||||
'sql-database-projects',
|
||||
'sql-migration'
|
||||
];
|
||||
|
||||
@@ -18,6 +18,13 @@ const File = require("vinyl");
|
||||
const rimraf = require("rimraf");
|
||||
const gulp = require("gulp");
|
||||
const vfs = require("vinyl-fs");
|
||||
/**
|
||||
* If you need to compile this file for any changes, please run: yarn tsc -p ./build/tsconfig.json
|
||||
*/
|
||||
//List of extensions that we changed from vscode, so we can exclude them from having "Microsoft." appended in front.
|
||||
const alteredVSCodeExtensions = [
|
||||
'git'
|
||||
];
|
||||
const root = path.dirname(path.dirname(__dirname));
|
||||
// Modified packageLocalExtensionsStream from extensions.ts, but for langpacks.
|
||||
function packageLangpacksStream() {
|
||||
@@ -134,10 +141,13 @@ function modifyI18nPackFiles(existingTranslationFolder, resultingTranslationPath
|
||||
for (let extension in extensionsPacks) {
|
||||
const translatedExtFile = i18n.createI18nFile(`extensions/${extension}`, extensionsPacks[extension]);
|
||||
this.queue(translatedExtFile);
|
||||
//handle edge case for 'Microsoft.sqlservernotebook' where extension name is the same as extension ID.
|
||||
//(Other extensions need to have publisher appended in front as their ID.)
|
||||
const adsExtensionId = (extension === 'Microsoft.sqlservernotebook') ? extension : 'Microsoft.' + extension;
|
||||
resultingTranslationPaths.push({ id: adsExtensionId, resourceName: `extensions/${extension}.i18n.json` });
|
||||
// exclude altered vscode extensions from having a new path even if we provide a new I18n file.
|
||||
if (alteredVSCodeExtensions.indexOf(extension) === -1) {
|
||||
//handle edge case for 'Microsoft.sqlservernotebook' where extension name is the same as extension ID.
|
||||
//(Other extensions need to have publisher appended in front as their ID.)
|
||||
let adsExtensionId = (extension === 'Microsoft.sqlservernotebook') ? extension : 'Microsoft.' + extension;
|
||||
resultingTranslationPaths.push({ id: adsExtensionId, resourceName: `extensions/${extension}.i18n.json` });
|
||||
}
|
||||
}
|
||||
this.queue(null);
|
||||
})
|
||||
@@ -162,7 +172,6 @@ const VSCODEExtensions = [
|
||||
"bat",
|
||||
"configuration-editing",
|
||||
"docker",
|
||||
"extension-editing",
|
||||
"git-ui",
|
||||
"git",
|
||||
"github-authentication",
|
||||
|
||||
@@ -17,6 +17,15 @@ import * as rimraf from 'rimraf';
|
||||
import * as gulp from 'gulp';
|
||||
import * as vfs from 'vinyl-fs';
|
||||
|
||||
/**
|
||||
* If you need to compile this file for any changes, please run: yarn tsc -p ./build/tsconfig.json
|
||||
*/
|
||||
|
||||
//List of extensions that we changed from vscode, so we can exclude them from having "Microsoft." appended in front.
|
||||
const alteredVSCodeExtensions = [
|
||||
'git'
|
||||
]
|
||||
|
||||
const root = path.dirname(path.dirname(__dirname));
|
||||
|
||||
// Modified packageLocalExtensionsStream from extensions.ts, but for langpacks.
|
||||
@@ -68,7 +77,7 @@ function updateMainI18nFile(existingTranslationFilePath: string, originalFilePat
|
||||
|
||||
// Delete any SQL strings that are no longer part of ADS in current langpack.
|
||||
for (let contentKey of Object.keys(objectContents)) {
|
||||
if(contentKey.startsWith('sql') && messages.contents[contentKey] === undefined){
|
||||
if (contentKey.startsWith('sql') && messages.contents[contentKey] === undefined) {
|
||||
delete objectContents[`${contentKey}`]
|
||||
}
|
||||
}
|
||||
@@ -148,10 +157,14 @@ export function modifyI18nPackFiles(existingTranslationFolder: string, resulting
|
||||
const translatedExtFile = i18n.createI18nFile(`extensions/${extension}`, extensionsPacks[extension]);
|
||||
this.queue(translatedExtFile);
|
||||
|
||||
//handle edge case for 'Microsoft.sqlservernotebook' where extension name is the same as extension ID.
|
||||
//(Other extensions need to have publisher appended in front as their ID.)
|
||||
const adsExtensionId = (extension === 'Microsoft.sqlservernotebook') ? extension : 'Microsoft.' + extension;
|
||||
resultingTranslationPaths.push({ id: adsExtensionId, resourceName: `extensions/${extension}.i18n.json` });
|
||||
// exclude altered vscode extensions from having a new path even if we provide a new I18n file.
|
||||
if (alteredVSCodeExtensions.indexOf(extension) === -1) {
|
||||
//handle edge case for 'Microsoft.sqlservernotebook' where extension name is the same as extension ID.
|
||||
//(Other extensions need to have publisher appended in front as their ID.)
|
||||
let adsExtensionId = (extension === 'Microsoft.sqlservernotebook') ? extension : 'Microsoft.' + extension;
|
||||
|
||||
resultingTranslationPaths.push({ id: adsExtensionId, resourceName: `extensions/${extension}.i18n.json` });
|
||||
}
|
||||
}
|
||||
this.queue(null);
|
||||
})
|
||||
@@ -399,7 +412,7 @@ export function renameVscodeLangpacks(): Promise<void> {
|
||||
|
||||
//Copy files to vscode langpack, then remove the ADS langpack, and finally rename the vscode langpack to match the ADS one.
|
||||
globMDArray.forEach(element => {
|
||||
fs.copyFileSync(element, path.join(locVSCODEFolder,path.parse(element).base));
|
||||
fs.copyFileSync(element, path.join(locVSCODEFolder, path.parse(element).base));
|
||||
});
|
||||
rimraf.sync(locADSFolder);
|
||||
fs.renameSync(locVSCODEFolder, locADSFolder);
|
||||
|
||||
@@ -48,6 +48,7 @@ exports.dirs = [
|
||||
'extensions/server-report',
|
||||
'extensions/simple-browser',
|
||||
'extensions/sql-assessment',
|
||||
'extensions/sql-bindings',
|
||||
'extensions/sql-database-projects',
|
||||
'extensions/sql-migration',
|
||||
'extensions/vscode-test-resolver',
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
"mime": "^1.4.1",
|
||||
"mkdirp": "^1.0.4",
|
||||
"p-limit": "^3.1.0",
|
||||
"plist": "^3.0.1",
|
||||
"plist": "^3.0.4",
|
||||
"rollup": "^1.20.3",
|
||||
"rollup-plugin-commonjs": "^10.1.0",
|
||||
"rollup-plugin-node-resolve": "^5.2.0",
|
||||
|
||||
@@ -783,7 +783,7 @@ balanced-match@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
||||
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
||||
|
||||
base64-js@^1.2.3:
|
||||
base64-js@^1.5.1:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
||||
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
|
||||
@@ -2038,14 +2038,13 @@ pify@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
|
||||
integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=
|
||||
|
||||
plist@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.1.tgz#a9b931d17c304e8912ef0ba3bdd6182baf2e1f8c"
|
||||
integrity sha512-GpgvHHocGRyQm74b6FWEZZVRroHKE1I0/BTjAmySaohK+cUn+hZpbqXkc3KWgW3gQYkqcQej35FohcT0FRlkRQ==
|
||||
plist@^3.0.1, plist@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.4.tgz#a62df837e3aed2bb3b735899d510c4f186019cbe"
|
||||
integrity sha512-ksrr8y9+nXOxQB2osVNqrgvX/XQPOXaU4BQMKjYq8PvaY1U18mo+fKgBSwzK+luSyinOuPae956lSVcBwxlAMg==
|
||||
dependencies:
|
||||
base64-js "^1.2.3"
|
||||
base64-js "^1.5.1"
|
||||
xmlbuilder "^9.0.7"
|
||||
xmldom "0.1.x"
|
||||
|
||||
"postcss@5 - 7":
|
||||
version "7.0.36"
|
||||
@@ -2676,11 +2675,6 @@ xmlbuilder@~11.0.0:
|
||||
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3"
|
||||
integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==
|
||||
|
||||
xmldom@0.1.x:
|
||||
version "0.1.31"
|
||||
resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.31.tgz#b76c9a1bd9f0a9737e5a72dc37231cf38375e2ff"
|
||||
integrity sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ==
|
||||
|
||||
yallist@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
"name": "arc",
|
||||
"displayName": "%arc.displayName%",
|
||||
"description": "%arc.description%",
|
||||
"version": "0.12.0",
|
||||
"version": "1.0.0",
|
||||
"publisher": "Microsoft",
|
||||
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/main/LICENSE.txt",
|
||||
"icon": "images/extension.png",
|
||||
"engines": {
|
||||
"vscode": "*",
|
||||
"azdata": ">=1.32.0"
|
||||
"azdata": ">=1.35.0"
|
||||
},
|
||||
"activationEvents": [
|
||||
"onCommand:arc.connectToController",
|
||||
|
||||
@@ -52,9 +52,6 @@ describe('radioOptionsGroup', function (): void {
|
||||
const label = radioOptionsGroup.items[0] as azdata.TextComponent;
|
||||
should(label.value).not.be.undefined();
|
||||
label.value!.should.deepEqual(loc.loadingClusterContextsError(loadingError));
|
||||
should(label.CSSStyles).not.be.undefined();
|
||||
should(label.CSSStyles!.color).not.be.undefined();
|
||||
label.CSSStyles!.color.should.equal('Red');
|
||||
});
|
||||
|
||||
describe('getters and setters', async () => {
|
||||
|
||||
@@ -67,7 +67,7 @@ export class RadioOptionsGroup {
|
||||
this.component().loadingCompletedText = this._loadingCompleteMessage;
|
||||
}
|
||||
catch (e) {
|
||||
const errorLabel = this._modelBuilder.text().withProps({ value: loc.loadingClusterContextsError(e), CSSStyles: { 'color': 'Red' } }).component();
|
||||
const errorLabel = this._modelBuilder.text().withProps({ value: loc.loadingClusterContextsError(e), textType: azdata.TextType.Error }).component();
|
||||
this._divContainer.addItem(errorLabel);
|
||||
this.component().loadingCompletedText = this._loadingCompleteErrorMessage(e);
|
||||
}
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
"name": "azcli",
|
||||
"displayName": "%azcli.arc.displayName%",
|
||||
"description": "%azcli.arc.description%",
|
||||
"version": "0.4.0",
|
||||
"version": "1.0.0",
|
||||
"publisher": "Microsoft",
|
||||
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/main/LICENSE.txt",
|
||||
"icon": "images/extension.png",
|
||||
"engines": {
|
||||
"vscode": "*",
|
||||
"azdata": ">=1.26.0"
|
||||
"azdata": ">=1.35.0"
|
||||
},
|
||||
"activationEvents": [
|
||||
"*"
|
||||
|
||||
@@ -172,6 +172,11 @@ export class AzureAccountProvider implements azdata.AccountProvider, vscode.Disp
|
||||
}
|
||||
|
||||
refresh(account: AzureAccount): Thenable<AzureAccount | azdata.PromptFailedResult> {
|
||||
return this._refresh(account);
|
||||
}
|
||||
|
||||
private async _refresh(account: AzureAccount): Promise<AzureAccount | azdata.PromptFailedResult> {
|
||||
await this._clear(account.key);
|
||||
return this.prompt();
|
||||
}
|
||||
|
||||
|
||||
6
extensions/azurecore/src/azurecore.d.ts
vendored
@@ -263,6 +263,12 @@ declare module 'azurecore' {
|
||||
}
|
||||
|
||||
export interface IExtension {
|
||||
/**
|
||||
* Gets the list of subscriptions for the specified AzureAccount
|
||||
* @param account The account to get the subscriptions for
|
||||
* @param ignoreErrors If true any errors are not thrown and instead collected and returned as part of the result
|
||||
* @param selectedOnly Whether to only list subscriptions the user has selected to filter to for this account
|
||||
*/
|
||||
getSubscriptions(account?: AzureAccount, ignoreErrors?: boolean, selectedOnly?: boolean): Promise<GetSubscriptionsResult>;
|
||||
getResourceGroups(account?: AzureAccount, subscription?: azureResource.AzureResourceSubscription, ignoreErrors?: boolean): Promise<GetResourceGroupsResult>;
|
||||
getLocations(account?: AzureAccount, subscription?: azureResource.AzureResourceSubscription, ignoreErrors?: boolean): Promise<GetLocationsResult>;
|
||||
|
||||
@@ -655,9 +655,9 @@ events@^3.0.0:
|
||||
integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
|
||||
|
||||
follow-redirects@^1.14.0:
|
||||
version "1.14.7"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.7.tgz#2004c02eb9436eee9a21446a6477debf17e81685"
|
||||
integrity sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==
|
||||
version "1.14.8"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.8.tgz#016996fb9a11a100566398b1c6839337d7bfa8fc"
|
||||
integrity sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==
|
||||
|
||||
form-data@^2.5.0:
|
||||
version "2.5.1"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "data-workspace",
|
||||
"displayName": "Data Workspace",
|
||||
"description": "Additional common functionality for database projects",
|
||||
"version": "0.1.1",
|
||||
"version": "0.2.0",
|
||||
"publisher": "Microsoft",
|
||||
"preview": true,
|
||||
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/main/LICENSE.txt",
|
||||
|
||||
@@ -49,6 +49,13 @@ export const SelectProjectType = localize('dataworkspace.selectProjectType', "Se
|
||||
export const SelectProjectLocation = localize('dataworkspace.selectProjectLocation', "Select Project Location");
|
||||
export const NameCannotBeEmpty = localize('dataworkspace.nameCannotBeEmpty', "Name cannot be empty");
|
||||
export const TargetPlatform = localize('dataworkspace.targetPlatform', "Target Platform");
|
||||
export const SdkStyleProject = localize('dataworkspace.sdkStyleProject', "SDK-style project (Preview)");
|
||||
export const LearnMore = localize('dataworkspace.learnMore', "Learn More");
|
||||
export const YesRecommended = localize('dataworkspace.yesRecommended', "Yes (Recommended)");
|
||||
export const No = localize('dataworkspace.no', "No");
|
||||
export const SdkLearnMorePlaceholder = localize('dataworkspace.sdkLearnMorePlaceholder', "Click \"Learn More\" button for more information about SDK-style projects");
|
||||
export const Default = localize('dataworkspace.default', "Default");
|
||||
export const SelectTargetPlatform = localize('dataworkspace.selectTargetPlatform', "Select Target Platform");
|
||||
|
||||
//Open Existing Dialog
|
||||
export const OpenExistingDialogTitle = localize('dataworkspace.openExistingDialogTitle', "Open Existing Project");
|
||||
|
||||
@@ -74,8 +74,9 @@ export interface IWorkspaceService {
|
||||
* @param location The location of the project
|
||||
* @param projectTypeId The project type id
|
||||
* @param projectTargetPlatform The target platform of the project
|
||||
* @param sdkStyleProject Whether or not the project is SDK-style
|
||||
*/
|
||||
createProject(name: string, location: vscode.Uri, projectTypeId: string, projectTargetPlatform?: string): Promise<vscode.Uri>;
|
||||
createProject(name: string, location: vscode.Uri, projectTypeId: string, projectTargetPlatform?: string, sdkStyleProject?: boolean): Promise<vscode.Uri>;
|
||||
|
||||
/**
|
||||
* Clones git repository and adds projects to workspace
|
||||
|
||||
11
extensions/data-workspace/src/dataworkspace.d.ts
vendored
@@ -67,8 +67,9 @@ declare module 'dataworkspace' {
|
||||
* @param location the parent directory of the project
|
||||
* @param projectTypeId the identifier of the selected project type
|
||||
* @param projectTargetPlatform the target platform of the project
|
||||
* @param sdkStyleProject whether or not a project is SDK-style
|
||||
*/
|
||||
createProject(name: string, location: vscode.Uri, projectTypeId: string, projectTargetPlatform?: string): Promise<vscode.Uri>;
|
||||
createProject(name: string, location: vscode.Uri, projectTypeId: string, projectTargetPlatform?: string, sdkStyleProject?: boolean): Promise<vscode.Uri>;
|
||||
|
||||
/**
|
||||
* Gets the project data corresponding to the project file, to be placed in the dashboard container
|
||||
@@ -131,14 +132,14 @@ declare module 'dataworkspace' {
|
||||
readonly defaultTargetPlatform?: string;
|
||||
|
||||
/**
|
||||
* Link display value for a link at the end of the project description. linkLocation also needs to be set to use this
|
||||
* Whether or not sdk style project is an option
|
||||
*/
|
||||
readonly linkDisplayValue?: string;
|
||||
readonly sdkStyleOption?: boolean;
|
||||
|
||||
/**
|
||||
* Location where clicking on the linkDisplayValue will go to
|
||||
* Location where clicking on the Learn More next to SDK style checkbox will go. sdkStyleOption needs to be set to true to use this
|
||||
*/
|
||||
readonly linkLocation?: string
|
||||
readonly sdkStyleLearnMoreUrl?: string
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,6 +22,7 @@ class NewProjectDialogModel {
|
||||
name: string = '';
|
||||
location: string = '';
|
||||
targetPlatform?: string;
|
||||
sdkStyleProject?: boolean;
|
||||
}
|
||||
|
||||
export async function openSpecificProjectNewProjectDialog(projectType: IProjectType, workspaceService: WorkspaceService): Promise<vscode.Uri | undefined> {
|
||||
@@ -35,6 +36,7 @@ export class NewProjectDialog extends DialogBase {
|
||||
public model: NewProjectDialogModel = new NewProjectDialogModel();
|
||||
public formBuilder: azdataType.FormBuilder | undefined;
|
||||
public targetPlatformDropdownFormComponent: azdataType.FormComponent | undefined;
|
||||
public sdkProjectCheckboxFormComponent: azdataType.FormComponent | undefined;
|
||||
public newProjectDialogComplete: Deferred<void> | undefined;
|
||||
public newDialogPromise: Promise<void> = new Promise<void>((resolve, reject) => this.newProjectDialogComplete = { resolve, reject });
|
||||
public projectUri: vscode.Uri | undefined;
|
||||
@@ -87,7 +89,7 @@ export class NewProjectDialog extends DialogBase {
|
||||
.withAdditionalProperties({ projectFileExtension: this.model.projectFileExtension, projectTemplateId: this.model.projectTypeId })
|
||||
.send();
|
||||
|
||||
this.projectUri = await this.workspaceService.createProject(this.model.name, vscode.Uri.file(this.model.location), this.model.projectTypeId, this.model.targetPlatform);
|
||||
this.projectUri = await this.workspaceService.createProject(this.model.name, vscode.Uri.file(this.model.location), this.model.projectTypeId, this.model.targetPlatform, this.model.sdkStyleProject);
|
||||
this.newProjectDialogComplete?.resolve();
|
||||
}
|
||||
catch (err) {
|
||||
@@ -122,8 +124,7 @@ export class NewProjectDialog extends DialogBase {
|
||||
'font-weight': 'bold'
|
||||
}
|
||||
}, {
|
||||
textValue: projectType.description,
|
||||
linkDisplayValue: projectType.linkDisplayValue
|
||||
textValue: projectType.description
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -138,15 +139,7 @@ export class NewProjectDialog extends DialogBase {
|
||||
selectedCardId: allProjectTypes.length > 0 ? allProjectTypes[0].id : undefined
|
||||
}).component();
|
||||
|
||||
projectTypeRadioCardGroup.onLinkClick(async (value) => {
|
||||
for (let projectType of allProjectTypes) {
|
||||
if (value.cardId === projectType.id) {
|
||||
void vscode.env.openExternal(vscode.Uri.parse(projectType.linkLocation!));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.register(projectTypeRadioCardGroup.onSelectionChanged((e) => {
|
||||
this.register(projectTypeRadioCardGroup.onSelectionChanged(async (e) => {
|
||||
this.model.projectTypeId = e.cardId;
|
||||
const selectedProject = allProjectTypes.find(p => p.id === e.cardId);
|
||||
|
||||
@@ -155,12 +148,30 @@ export class NewProjectDialog extends DialogBase {
|
||||
targetPlatformDropdown.values = selectedProject?.targetPlatforms;
|
||||
targetPlatformDropdown.value = this.getDefaultTargetPlatform(selectedProject);
|
||||
|
||||
this.formBuilder?.addFormItem(this.targetPlatformDropdownFormComponent!);
|
||||
this.formBuilder?.insertFormItem(this.targetPlatformDropdownFormComponent!, 3);
|
||||
} else {
|
||||
// remove the target version dropdown if the selected project type didn't provide values for this
|
||||
this.formBuilder?.removeFormItem(this.targetPlatformDropdownFormComponent!);
|
||||
this.model.targetPlatform = undefined;
|
||||
}
|
||||
|
||||
if (selectedProject?.sdkStyleOption) {
|
||||
sdkProjectCheckbox.checked = true;
|
||||
this.model.sdkStyleProject = true;
|
||||
|
||||
if (selectedProject.sdkStyleLearnMoreUrl) {
|
||||
await sdkLearnMore.updateProperty('url', selectedProject.sdkStyleLearnMoreUrl);
|
||||
sdkFormComponentGroup.addItem(sdkLearnMore);
|
||||
} else {
|
||||
// remove learn more link if the project type didn't provide it
|
||||
sdkFormComponentGroup.removeItem(sdkLearnMore);
|
||||
}
|
||||
|
||||
this.formBuilder?.addFormItem(this.sdkProjectCheckboxFormComponent!);
|
||||
} else {
|
||||
this.model.sdkStyleProject = false;
|
||||
this.formBuilder?.removeFormItem(this.sdkProjectCheckboxFormComponent!);
|
||||
}
|
||||
}));
|
||||
|
||||
const projectNameTextBox = view.modelBuilder.inputBox().withProps({
|
||||
@@ -227,6 +238,34 @@ export class NewProjectDialog extends DialogBase {
|
||||
component: targetPlatformDropdown
|
||||
};
|
||||
|
||||
const sdkProjectCheckbox = view.modelBuilder.checkBox().withProps({
|
||||
checked: true,
|
||||
label: constants.SdkStyleProject
|
||||
}).component();
|
||||
|
||||
this.register(sdkProjectCheckbox.onChanged(() => {
|
||||
this.model.sdkStyleProject = sdkProjectCheckbox.checked;
|
||||
}));
|
||||
|
||||
const sdkLearnMore = view.modelBuilder.hyperlink().withProps({
|
||||
label: constants.LearnMore,
|
||||
url: ''
|
||||
}).component();
|
||||
|
||||
const sdkFormComponentGroup = view.modelBuilder.flexContainer()
|
||||
.withLayout({ flexFlow: 'row', alignItems: 'baseline' })
|
||||
.withItems([sdkProjectCheckbox], { CSSStyles: { flex: '0 0 auto', 'margin-right': '10px' } })
|
||||
.component();
|
||||
|
||||
if (allProjectTypes[0].sdkStyleLearnMoreUrl) {
|
||||
await sdkLearnMore.updateProperty('url', allProjectTypes[0].sdkStyleLearnMoreUrl);
|
||||
sdkFormComponentGroup.addItem(sdkLearnMore);
|
||||
}
|
||||
|
||||
this.sdkProjectCheckboxFormComponent = {
|
||||
component: sdkFormComponentGroup,
|
||||
};
|
||||
|
||||
this.formBuilder = view.modelBuilder.formContainer().withFormItems([
|
||||
{
|
||||
title: constants.TypeTitle,
|
||||
@@ -250,6 +289,11 @@ export class NewProjectDialog extends DialogBase {
|
||||
this.formBuilder.addFormItem(this.targetPlatformDropdownFormComponent);
|
||||
}
|
||||
|
||||
// add sdk style checkbox is the first project has the option
|
||||
if (allProjectTypes[0].sdkStyleOption) {
|
||||
this.formBuilder.addFormItem(this.sdkProjectCheckboxFormComponent);
|
||||
}
|
||||
|
||||
await view.initializeModel(this.formBuilder.component());
|
||||
this.initDialogComplete?.resolve();
|
||||
}
|
||||
|
||||
@@ -19,8 +19,12 @@ export async function createNewProjectWithQuickpick(workspaceService: WorkspaceS
|
||||
return {
|
||||
label: projType.displayName,
|
||||
description: projType.description,
|
||||
id: projType.id
|
||||
} as vscode.QuickPickItem & { id: string };
|
||||
id: projType.id,
|
||||
targetPlatforms: projType.targetPlatforms,
|
||||
defaultTargetPlatform: projType.defaultTargetPlatform,
|
||||
sdkOption: projType.sdkStyleOption,
|
||||
sdkLearnMoreUrl: projType.sdkStyleLearnMoreUrl
|
||||
} as vscode.QuickPickItem & { id: string, sdkOption?: boolean, targetPlatforms?: string[], defaultTargetPlatform?: string, sdkLearnMoreUrl?: string };
|
||||
});
|
||||
|
||||
// 1. Prompt for project type
|
||||
@@ -87,5 +91,79 @@ export async function createNewProjectWithQuickpick(workspaceService: WorkspaceS
|
||||
continue;
|
||||
}
|
||||
|
||||
await workspaceService.createProject(projectName, vscode.Uri.file(projectLocation), projectType.id, undefined);
|
||||
let targetPlatform;
|
||||
if (projectType.targetPlatforms) {
|
||||
// 4. Target platform of the project
|
||||
let targetPlatforms: vscode.QuickPickItem[] = projectType.targetPlatforms.map(targetPlatform => { return { label: targetPlatform }; });
|
||||
|
||||
if (projectType.defaultTargetPlatform) {
|
||||
// move the default target platform to be the first one in the list
|
||||
const defaultIndex = targetPlatforms.findIndex(i => i.label === projectType.defaultTargetPlatform);
|
||||
if (defaultIndex > -1) {
|
||||
targetPlatforms.splice(defaultIndex, 1);
|
||||
}
|
||||
|
||||
// add default next to the default target platform
|
||||
targetPlatforms.unshift({ label: projectType.defaultTargetPlatform, description: constants.Default });
|
||||
}
|
||||
|
||||
const selectedTargetPlatform = await vscode.window.showQuickPick(targetPlatforms, { title: constants.SelectTargetPlatform, ignoreFocusOut: true });
|
||||
if (!selectedTargetPlatform) {
|
||||
// User cancelled
|
||||
return;
|
||||
}
|
||||
|
||||
targetPlatform = selectedTargetPlatform.label;
|
||||
}
|
||||
|
||||
let sdkStyle;
|
||||
if (projectType.sdkOption) {
|
||||
// 5. SDK-style project or not
|
||||
const sdkLearnMoreButton: vscode.QuickInputButton = {
|
||||
iconPath: new vscode.ThemeIcon('link-external'),
|
||||
tooltip: constants.LearnMore
|
||||
};
|
||||
const quickPick = vscode.window.createQuickPick();
|
||||
quickPick.items = [{ label: constants.YesRecommended }, { label: constants.No }];
|
||||
quickPick.title = constants.SdkStyleProject;
|
||||
quickPick.ignoreFocusOut = true;
|
||||
const disposables: vscode.Disposable[] = [];
|
||||
|
||||
try {
|
||||
if (projectType.sdkLearnMoreUrl) {
|
||||
// add button to open sdkLearnMoreUrl if it was provided
|
||||
quickPick.buttons = [sdkLearnMoreButton];
|
||||
quickPick.placeholder = constants.SdkLearnMorePlaceholder;
|
||||
}
|
||||
|
||||
let sdkStylePromise = new Promise<boolean | undefined>((resolve) => {
|
||||
disposables.push(
|
||||
quickPick.onDidHide(() => {
|
||||
resolve(undefined);
|
||||
}),
|
||||
quickPick.onDidChangeSelection((item) => {
|
||||
resolve(item[0].label === constants.YesRecommended);
|
||||
}));
|
||||
|
||||
if (projectType.sdkLearnMoreUrl) {
|
||||
disposables.push(quickPick.onDidTriggerButton(async () => {
|
||||
await vscode.env.openExternal(vscode.Uri.parse(projectType.sdkLearnMoreUrl!));
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
quickPick.show();
|
||||
sdkStyle = await sdkStylePromise;
|
||||
quickPick.hide();
|
||||
} finally {
|
||||
disposables.forEach(d => d.dispose());
|
||||
}
|
||||
|
||||
if (sdkStyle === undefined) {
|
||||
// User cancelled
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await workspaceService.createProject(projectName, vscode.Uri.file(projectLocation), projectType.id, targetPlatform, sdkStyle);
|
||||
}
|
||||
|
||||
@@ -169,10 +169,12 @@ export class ProjectDashboard {
|
||||
.component();
|
||||
header.addItem(locationLabel, { CSSStyles: { 'padding-left': '34px', 'padding-top': '15px', 'padding-bottom': '50px', 'font-size': '16px' } });
|
||||
|
||||
const image = this.projectProvider!.image; // background image added at the bottom right of the header
|
||||
const backgroundImage = this.projectProvider!.image; // background image added at the bottom right of the header
|
||||
// Files need to have the vscode-file scheme to be loaded by ADS
|
||||
const backgroundUri = vscode.Uri.file(backgroundImage!.light.toString()).with({ scheme: 'vscode-file' });
|
||||
headerContainer.addItem(header, {
|
||||
CSSStyles: {
|
||||
'background-image': `url(${vscode.Uri.file(image!.light.toString())})`,
|
||||
'background-image': `url(${backgroundUri})`,
|
||||
'background-repeat': 'no-repeat',
|
||||
'background-position': '85% bottom',
|
||||
'background-size': '10%',
|
||||
|
||||
@@ -197,10 +197,10 @@ export class WorkspaceService implements IWorkspaceService {
|
||||
return ProjectProviderRegistry.getProviderByProjectExtension(projectType);
|
||||
}
|
||||
|
||||
async createProject(name: string, location: vscode.Uri, projectTypeId: string, projectTargetVersion?: string): Promise<vscode.Uri> {
|
||||
async createProject(name: string, location: vscode.Uri, projectTypeId: string, projectTargetVersion?: string, sdkStyleProject?: boolean): Promise<vscode.Uri> {
|
||||
const provider = ProjectProviderRegistry.getProviderByProjectType(projectTypeId);
|
||||
if (provider) {
|
||||
const projectFile = await provider.createProject(name, location, projectTypeId, projectTargetVersion);
|
||||
const projectFile = await provider.createProject(name, location, projectTypeId, projectTargetVersion, sdkStyleProject);
|
||||
await this.addProjectsToWorkspace([projectFile]);
|
||||
this._onDidWorkspaceProjectsChange.fire();
|
||||
return projectFile;
|
||||
|
||||
@@ -50,9 +50,9 @@ delayed-stream@~1.0.0:
|
||||
integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
|
||||
|
||||
follow-redirects@^1.14.0:
|
||||
version "1.14.7"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.7.tgz#2004c02eb9436eee9a21446a6477debf17e81685"
|
||||
integrity sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==
|
||||
version "1.14.8"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.8.tgz#016996fb9a11a100566398b1c6839337d7bfa8fc"
|
||||
integrity sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==
|
||||
|
||||
form-data@^3.0.0:
|
||||
version "3.0.0"
|
||||
|
||||
@@ -38,10 +38,11 @@ export class DashboardWidget {
|
||||
}).component();
|
||||
const header = await this.createHeader(view);
|
||||
const footerContainer = this.createFooter(view);
|
||||
const backgroundImageUri = vscode.Uri.file(this.asAbsolutePath('images/background.svg')).with({ scheme: 'vscode-file' });
|
||||
container.addItem(header, {
|
||||
CSSStyles: {
|
||||
'background-image': `
|
||||
url(${vscode.Uri.file(this.asAbsolutePath('images/background.svg'))}),
|
||||
url(${backgroundImageUri}),
|
||||
linear-gradient(0deg, rgba(0,0,0,0.09) 0%, rgba(0,0,0,0) 100%)
|
||||
`,
|
||||
'background-repeat': 'no-repeat',
|
||||
@@ -300,9 +301,11 @@ export class DashboardWidget {
|
||||
await this._apiWrapper.openExternal(vscode.Uri.parse(linkMetaData.link));
|
||||
}
|
||||
});
|
||||
// Files need to have the vscode-file scheme to be loaded by ADS
|
||||
const imageUri = vscode.Uri.file(this.asAbsolutePath(linkMetaData.iconPath?.light as string || '')).with({ scheme: 'vscode-file' });
|
||||
videosContainer.addItem(video1Container, {
|
||||
CSSStyles: {
|
||||
'background-image': `url(${vscode.Uri.file(this.asAbsolutePath(linkMetaData.iconPath?.light as string || ''))})`,
|
||||
'background-image': `url(${imageUri})`,
|
||||
'background-repeat': 'no-repeat',
|
||||
'background-position': 'top',
|
||||
'width': `${maxWidth}px`,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
|
||||
"version": "3.0.0-release.204",
|
||||
"version": "3.0.0-release.214",
|
||||
"downloadFileNames": {
|
||||
"Windows_86": "win-x86-net6.0.zip",
|
||||
"Windows_64": "win-x64-net6.0.zip",
|
||||
|
||||
@@ -514,14 +514,14 @@
|
||||
{
|
||||
"command": "mssql.designTable",
|
||||
"when": "connectionProvider == MSSQL && nodeType == Table && config.workbench.enablePreviewFeatures",
|
||||
"group":"connection@3"
|
||||
"group": "connection@3"
|
||||
},
|
||||
{
|
||||
"command": "mssql.newTable",
|
||||
"when": "connectionProvider == MSSQL && nodeType == Folder && nodeLabel == Tables && config.workbench.enablePreviewFeatures",
|
||||
"group":"connection@1"
|
||||
"group": "connection@1"
|
||||
}
|
||||
],
|
||||
],
|
||||
"notebook/toolbar": [
|
||||
{
|
||||
"command": "mssql.exportNotebookToSql",
|
||||
@@ -759,6 +759,9 @@
|
||||
"connectionProvider": {
|
||||
"providerId": "MSSQL",
|
||||
"displayName": "%mssql.provider.displayName%",
|
||||
"supportedExecutionPlanFileExtensions": [
|
||||
"sqlplan"
|
||||
],
|
||||
"iconPath": [
|
||||
{
|
||||
"id": "mssql:cloud",
|
||||
|
||||
@@ -1096,7 +1096,7 @@ export namespace InitializeTableDesignerRequest {
|
||||
}
|
||||
|
||||
export namespace ProcessTableDesignerEditRequest {
|
||||
export const type = new RequestType<TableDesignerEditRequestParams, azdata.designers.DesignerEditResult, void, void>('tabledesigner/processedit');
|
||||
export const type = new RequestType<TableDesignerEditRequestParams, azdata.designers.DesignerEditResult<azdata.designers.TableDesignerView>, void, void>('tabledesigner/processedit');
|
||||
}
|
||||
|
||||
export namespace PublishTableDesignerChangesRequest {
|
||||
@@ -1108,9 +1108,22 @@ export namespace TableDesignerGenerateScriptRequest {
|
||||
}
|
||||
|
||||
export namespace TableDesignerGenerateChangePreviewReportRequest {
|
||||
export const type = new RequestType<azdata.designers.TableInfo, string, void, void>('tabledesigner/generatepreviewreport');
|
||||
export const type = new RequestType<azdata.designers.TableInfo, azdata.designers.GeneratePreviewReportResult, void, void>('tabledesigner/generatepreviewreport');
|
||||
}
|
||||
export namespace DisposeTableDesignerRequest {
|
||||
export const type = new RequestType<azdata.designers.TableInfo, void, void, void>('tabledesigner/dispose');
|
||||
}
|
||||
// ------------------------------- < Table Designer > ------------------------------------
|
||||
|
||||
|
||||
// ------------------------------- < Execution Plan > ------------------------------------
|
||||
|
||||
export interface GetExecutionPlanParams {
|
||||
graphInfo: azdata.executionPlan.ExecutionPlanGraphInfo,
|
||||
}
|
||||
|
||||
export namespace GetExecutionPlanRequest {
|
||||
export const type = new RequestType<GetExecutionPlanParams, azdata.executionPlan.GetExecutionPlanResult, void, void>('queryexecutionplan/getexecutionplan');
|
||||
}
|
||||
|
||||
// ------------------------------- < Execution Plan > ------------------------------------
|
||||
|
||||
@@ -43,16 +43,20 @@ export class SqlCredentialService extends SqlOpsFeature<any> {
|
||||
|
||||
protected override registerProvider(options: any): Disposable {
|
||||
let readCredential = async (credentialId: string): Promise<azdata.Credential> => {
|
||||
if (Utils.isLinux) {
|
||||
const password = await this._secretStorage.get(credentialId);
|
||||
return {
|
||||
credentialId: credentialId,
|
||||
password: password
|
||||
};
|
||||
}
|
||||
return this._client.sendRequest(Contracts.ReadCredentialRequest.type, { credentialId, password: undefined });
|
||||
};
|
||||
|
||||
let saveCredential = async (credentialId: string, password: string): Promise<boolean> => {
|
||||
if (Utils.isLinux) {
|
||||
/**
|
||||
* This is only done for linux because this is going to be
|
||||
* the default credential system for linux in the next release
|
||||
*/
|
||||
await this._secretStorage.store(credentialId, password);
|
||||
return true;
|
||||
}
|
||||
return this._client.sendRequest(Contracts.SaveCredentialRequest.type, { credentialId, password });
|
||||
};
|
||||
@@ -61,6 +65,7 @@ export class SqlCredentialService extends SqlOpsFeature<any> {
|
||||
if (Utils.isLinux) {
|
||||
try {
|
||||
await this._secretStorage.delete(credentialId);
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.log('credential does not exist in native secret store');
|
||||
}
|
||||
|
||||
@@ -1117,7 +1117,7 @@ export class TableDesignerFeature extends SqlOpsFeature<undefined> {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
};
|
||||
const processTableEdit = (tableInfo: azdata.designers.TableInfo, tableChangeInfo: azdata.designers.DesignerEdit): Thenable<azdata.designers.DesignerEditResult> => {
|
||||
const processTableEdit = (tableInfo: azdata.designers.TableInfo, tableChangeInfo: azdata.designers.DesignerEdit): Thenable<azdata.designers.DesignerEditResult<azdata.designers.TableDesignerView>> => {
|
||||
let params: contracts.TableDesignerEditRequestParams = {
|
||||
tableInfo: tableInfo,
|
||||
tableChangeInfo: tableChangeInfo
|
||||
@@ -1151,7 +1151,7 @@ export class TableDesignerFeature extends SqlOpsFeature<undefined> {
|
||||
}
|
||||
};
|
||||
|
||||
const generatePreviewReport = (tableInfo: azdata.designers.TableInfo): Thenable<string> => {
|
||||
const generatePreviewReport = (tableInfo: azdata.designers.TableInfo): Thenable<azdata.designers.GeneratePreviewReportResult> => {
|
||||
try {
|
||||
return client.sendRequest(contracts.TableDesignerGenerateChangePreviewReportRequest.type, tableInfo);
|
||||
}
|
||||
@@ -1183,3 +1183,48 @@ export class TableDesignerFeature extends SqlOpsFeature<undefined> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Execution Plan Service Feature
|
||||
* TODO: Move this feature to data protocol client repo once stablized
|
||||
*/
|
||||
export class ExecutionPlanServiceFeature extends SqlOpsFeature<undefined> {
|
||||
private static readonly messagesTypes: RPCMessageType[] = [
|
||||
contracts.GetExecutionPlanRequest.type,
|
||||
];
|
||||
|
||||
constructor(client: SqlOpsDataClient) {
|
||||
super(client, ExecutionPlanServiceFeature.messagesTypes);
|
||||
}
|
||||
|
||||
public fillClientCapabilities(capabilities: ClientCapabilities): void {
|
||||
}
|
||||
|
||||
public initialize(capabilities: ServerCapabilities): void {
|
||||
this.register(this.messages, {
|
||||
id: UUID.generateUuid(),
|
||||
registerOptions: undefined
|
||||
});
|
||||
}
|
||||
|
||||
protected registerProvider(options: undefined): Disposable {
|
||||
const client = this._client;
|
||||
|
||||
const getExecutionPlan = (planFile: azdata.executionPlan.ExecutionPlanGraphInfo): Thenable<azdata.executionPlan.GetExecutionPlanResult> => {
|
||||
const params: contracts.GetExecutionPlanParams = { graphInfo: planFile };
|
||||
return client.sendRequest(contracts.GetExecutionPlanRequest.type, params).then(
|
||||
r => r,
|
||||
e => {
|
||||
client.logFailedRequest(contracts.GetExecutionPlanRequest.type, e);
|
||||
return Promise.reject(e);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
return azdata.dataprotocol.registerExecutionPlanProvider({
|
||||
providerId: client.providerId,
|
||||
getExecutionPlan
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import * as path from 'path';
|
||||
import { getCommonLaunchArgsAndCleanupOldLogFiles, getOrDownloadServer } from './utils';
|
||||
import { Telemetry, LanguageClientErrorHandler } from './telemetry';
|
||||
import { SqlOpsDataClient, ClientOptions } from 'dataprotocol-client';
|
||||
import { TelemetryFeature, AgentServicesFeature, SerializationFeature, AccountFeature, SqlAssessmentServicesFeature, ProfilerFeature, TableDesignerFeature } from './features';
|
||||
import { TelemetryFeature, AgentServicesFeature, SerializationFeature, AccountFeature, SqlAssessmentServicesFeature, ProfilerFeature, TableDesignerFeature, ExecutionPlanServiceFeature } from './features';
|
||||
import { CredentialStore } from './credentialstore/credentialstore';
|
||||
import { AzureResourceProvider } from './resourceProvider/resourceProvider';
|
||||
import { SchemaCompareService } from './schemaCompare/schemaCompareService';
|
||||
@@ -165,7 +165,8 @@ function getClientOptions(context: AppContext): ClientOptions {
|
||||
ProfilerFeature,
|
||||
SqlMigrationService.asFeature(context),
|
||||
SqlCredentialService.asFeature(context),
|
||||
TableDesignerFeature
|
||||
TableDesignerFeature,
|
||||
ExecutionPlanServiceFeature
|
||||
],
|
||||
outputChannel: new CustomOutputChannel()
|
||||
};
|
||||
|
||||
@@ -280,9 +280,9 @@ agent-base@4, agent-base@^4.3.0:
|
||||
es6-promisify "^5.0.0"
|
||||
|
||||
ajv@^6.5.5:
|
||||
version "6.12.0"
|
||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.0.tgz#06d60b96d87b8454a5adaba86e7854da629db4b7"
|
||||
integrity sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==
|
||||
version "6.12.6"
|
||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
|
||||
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
|
||||
dependencies:
|
||||
fast-deep-equal "^3.1.1"
|
||||
fast-json-stable-stringify "^2.0.0"
|
||||
@@ -694,9 +694,9 @@ extsprintf@^1.2.0:
|
||||
integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
|
||||
|
||||
fast-deep-equal@^3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4"
|
||||
integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==
|
||||
version "3.1.3"
|
||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
|
||||
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
|
||||
|
||||
fast-json-stable-stringify@^2.0.0:
|
||||
version "2.1.0"
|
||||
@@ -1899,9 +1899,9 @@ typemoq@^2.1.0:
|
||||
postinstall-build "^5.0.1"
|
||||
|
||||
uri-js@^4.2.2:
|
||||
version "4.2.2"
|
||||
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
|
||||
integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==
|
||||
version "4.4.1"
|
||||
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
|
||||
integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
|
||||
dependencies:
|
||||
punycode "^2.1.0"
|
||||
|
||||
|
||||
@@ -329,6 +329,14 @@
|
||||
"Notebook"
|
||||
],
|
||||
"configuration": "./language-configuration.json"
|
||||
}, {
|
||||
"id": "dib",
|
||||
"extensions": [
|
||||
".dib"
|
||||
],
|
||||
"aliases": [
|
||||
".NET Interactive Notebook"
|
||||
]
|
||||
}
|
||||
],
|
||||
"menus": {
|
||||
@@ -703,7 +711,8 @@
|
||||
"connectionProviderIds": [
|
||||
"MSSQL"
|
||||
],
|
||||
"blockedOnSAW": true
|
||||
"blockedOnSAW": true,
|
||||
"supportedLanguages": ["python"]
|
||||
},
|
||||
{
|
||||
"name": "sparkkernel",
|
||||
@@ -711,6 +720,7 @@
|
||||
"connectionProviderIds": [
|
||||
"MSSQL"
|
||||
],
|
||||
"supportedLanguages": ["scala"],
|
||||
"blockedOnSAW": true
|
||||
},
|
||||
{
|
||||
@@ -719,17 +729,20 @@
|
||||
"connectionProviderIds": [
|
||||
"MSSQL"
|
||||
],
|
||||
"supportedLanguages": ["r"],
|
||||
"blockedOnSAW": true
|
||||
},
|
||||
{
|
||||
"name": "python3",
|
||||
"displayName": "Python 3",
|
||||
"connectionProviderIds": []
|
||||
"connectionProviderIds": [],
|
||||
"supportedLanguages": ["python"]
|
||||
},
|
||||
{
|
||||
"name": "powershell",
|
||||
"displayName": "PowerShell",
|
||||
"connectionProviderIds": []
|
||||
"connectionProviderIds": [],
|
||||
"supportedLanguages": ["powershell"]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -774,7 +787,7 @@
|
||||
"@microsoft/vscodetestcover": "^1.2.0"
|
||||
},
|
||||
"resolutions": {
|
||||
"url-parse": "^1.5.1"
|
||||
"url-parse": "^1.5.8"
|
||||
},
|
||||
"enableProposedApi": true
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { nb } from 'azdata';
|
||||
import { Kernel, KernelMessage } from '@jupyterlab/services';
|
||||
|
||||
@@ -90,7 +91,7 @@ export class JupyterKernel implements nb.IKernel {
|
||||
requestExecute(content: nb.IExecuteRequest, disposeOnDone?: boolean): nb.IFuture {
|
||||
content.code = Array.isArray(content.code) ? content.code.join('') : content.code;
|
||||
content.code = content.code.replace(/\r+\n/gm, '\n'); // Remove \r (if it exists) from newlines
|
||||
let futureImpl = this.kernelImpl.requestExecute(content as KernelMessage.IExecuteRequest & { cellIndex: number }, disposeOnDone);
|
||||
let futureImpl = this.kernelImpl.requestExecute(content as KernelMessage.IExecuteRequest & { notebookUri: vscode.Uri, cellUri: vscode.Uri, language: string, cellIndex: number }, disposeOnDone);
|
||||
return new JupyterFuture(futureImpl);
|
||||
}
|
||||
|
||||
|
||||
@@ -98,7 +98,10 @@ describe('Jupyter Session', function (): void {
|
||||
// When I request execute
|
||||
let future = kernel.requestExecute({
|
||||
code: code,
|
||||
cellIndex: 0
|
||||
cellIndex: 0,
|
||||
cellUri: undefined,
|
||||
notebookUri: undefined,
|
||||
language: ''
|
||||
}, true);
|
||||
|
||||
// Then expect wrapper to be returned
|
||||
|
||||
@@ -1798,10 +1798,10 @@ uri-js@^4.2.2:
|
||||
dependencies:
|
||||
punycode "^2.1.0"
|
||||
|
||||
url-parse@^1.5.1, url-parse@~1.4.3:
|
||||
version "1.5.3"
|
||||
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.3.tgz#71c1303d38fb6639ade183c2992c8cc0686df862"
|
||||
integrity sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ==
|
||||
url-parse@^1.5.8, url-parse@~1.4.3:
|
||||
version "1.5.10"
|
||||
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1"
|
||||
integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==
|
||||
dependencies:
|
||||
querystringify "^2.1.1"
|
||||
requires-port "^1.0.0"
|
||||
|
||||
@@ -531,9 +531,9 @@ escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5:
|
||||
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
|
||||
|
||||
follow-redirects@^1.14.0:
|
||||
version "1.14.7"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.7.tgz#2004c02eb9436eee9a21446a6477debf17e81685"
|
||||
integrity sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==
|
||||
version "1.14.8"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.8.tgz#016996fb9a11a100566398b1c6839337d7bfa8fc"
|
||||
integrity sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==
|
||||
|
||||
fs.realpath@^1.0.0:
|
||||
version "1.0.0"
|
||||
|
||||
@@ -898,8 +898,7 @@ export class SchemaCompareMainWindow {
|
||||
throw new Error(`Unsupported SchemaCompareEndpointType: ${getSchemaCompareEndpointString(this.targetEndpointInfo.endpointType)}`);
|
||||
}
|
||||
|
||||
if (!result || !result.success) {
|
||||
|
||||
if (!result || !result.success || result.errorMessage !== '') {
|
||||
TelemetryReporter.createErrorEvent(TelemetryViews.SchemaCompareMainWindow, 'SchemaCompareApplyFailed', undefined, getTelemetryErrorType(result?.errorMessage))
|
||||
.withAdditionalProperties({
|
||||
'operationId': this.comparisonResult.operationId,
|
||||
@@ -912,6 +911,11 @@ export class SchemaCompareMainWindow {
|
||||
this.generateScriptButton.title = loc.generateScriptEnabledMessage;
|
||||
this.applyButton.enabled = true;
|
||||
this.applyButton.title = loc.applyEnabledMessage;
|
||||
} else if (this.targetEndpointInfo.endpointType === mssql.SchemaCompareEndpointType.Project) {
|
||||
const workspaceApi = getDataWorkspaceExtensionApi();
|
||||
workspaceApi.showProjectsView();
|
||||
|
||||
void vscode.window.showInformationMessage(loc.applySuccess);
|
||||
}
|
||||
|
||||
TelemetryReporter.createActionEvent(TelemetryViews.SchemaCompareMainWindow, 'SchemaCompareApplyEnded')
|
||||
@@ -920,13 +924,6 @@ export class SchemaCompareMainWindow {
|
||||
'operationId': this.comparisonResult.operationId,
|
||||
'targetType': getSchemaCompareEndpointString(this.targetEndpointInfo.endpointType)
|
||||
}).send();
|
||||
|
||||
if (this.targetEndpointInfo.endpointType === mssql.SchemaCompareEndpointType.Project) {
|
||||
const workspaceApi = getDataWorkspaceExtensionApi();
|
||||
workspaceApi.showProjectsView();
|
||||
|
||||
void vscode.window.showInformationMessage(loc.applySuccess);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 847 B After Width: | Height: | Size: 847 B |
|
Before Width: | Height: | Size: 852 B After Width: | Height: | Size: 852 B |
|
Before Width: | Height: | Size: 339 B After Width: | Height: | Size: 339 B |
|
Before Width: | Height: | Size: 338 B After Width: | Height: | Size: 338 B |
|
Before Width: | Height: | Size: 282 B After Width: | Height: | Size: 282 B |
|
Before Width: | Height: | Size: 287 B After Width: | Height: | Size: 287 B |
|
Before Width: | Height: | Size: 247 B After Width: | Height: | Size: 247 B |
|
Before Width: | Height: | Size: 315 B After Width: | Height: | Size: 315 B |
|
Before Width: | Height: | Size: 969 B After Width: | Height: | Size: 969 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
@@ -32,32 +32,32 @@
|
||||
"command": "tempdb.startEvent",
|
||||
"title": "Start",
|
||||
"icon": {
|
||||
"light": "./dist/media/launch.svg",
|
||||
"dark": "./dist/media/launch_inverse.svg"
|
||||
"light": "./images/launch.svg",
|
||||
"dark": "./images/launch_inverse.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "tempdb.stopEvent",
|
||||
"title": "Stop",
|
||||
"icon": {
|
||||
"light": "./dist/media/blocker.svg",
|
||||
"dark": "./dist/media/blocker_inverse.svg"
|
||||
"light": "./images/blocker.svg",
|
||||
"dark": "./images/blocker_inverse.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "tempdb.contention",
|
||||
"title": "Contention Help for Tempdb",
|
||||
"icon": {
|
||||
"light": "./dist/media/documentation.svg",
|
||||
"dark": "./dist/media/documentation_inverse.svg"
|
||||
"light": "./images/documentation.svg",
|
||||
"dark": "./images/documentation_inverse.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "tempdb.pauseEvent",
|
||||
"title": "Toggle Auto Refresh",
|
||||
"icon": {
|
||||
"light": "./dist/media/insights.svg",
|
||||
"dark": "./dist/media/insights_inverse.svg"
|
||||
"light": "./images/insights.svg",
|
||||
"dark": "./images/insights_inverse.svg"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -77,8 +77,8 @@
|
||||
"id": "server-reports-monitoring",
|
||||
"title": "Monitor",
|
||||
"icon": {
|
||||
"light": "./dist/media/monitor.svg",
|
||||
"dark": "./dist/media/monitor_inverse.svg"
|
||||
"light": "./images/monitor.svg",
|
||||
"dark": "./images/monitor_inverse.svg"
|
||||
},
|
||||
"container": {
|
||||
"server-reports-monitoring-container": {}
|
||||
@@ -88,8 +88,8 @@
|
||||
"id": "server-reports-performance",
|
||||
"title": "Performance",
|
||||
"icon": {
|
||||
"light": "./dist/media/performance.svg",
|
||||
"dark": "./dist/media/performance_inverse.svg"
|
||||
"light": "./images/performance.svg",
|
||||
"dark": "./images/performance_inverse.svg"
|
||||
},
|
||||
"container": {
|
||||
"server-reports-performance-container": {}
|
||||
@@ -104,8 +104,8 @@
|
||||
"group": "monitoring",
|
||||
"description": "TempDB reports",
|
||||
"icon": {
|
||||
"light": "./dist/media/tempdb.svg",
|
||||
"dark": "./dist/media/tempdb_inverse.svg"
|
||||
"light": "./images/tempdb.svg",
|
||||
"dark": "./images/tempdb_inverse.svg"
|
||||
},
|
||||
"container": {
|
||||
"server-reports-tempdb-container": {}
|
||||
@@ -125,7 +125,7 @@
|
||||
"columnsAsLabels": true
|
||||
}
|
||||
},
|
||||
"queryFile": "./dist/sql/all_db_space_used.sql"
|
||||
"queryFile": "./sql/all_db_space_used.sql"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -140,7 +140,7 @@
|
||||
"columnsAsLabels": false
|
||||
}
|
||||
},
|
||||
"queryFile": "./dist/sql/cpumetric.sql"
|
||||
"queryFile": "./sql/cpumetric.sql"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -156,7 +156,7 @@
|
||||
"columnsAsLabels": false
|
||||
}
|
||||
},
|
||||
"queryFile": "./dist/sql/backup_size_trend.sql"
|
||||
"queryFile": "./sql/backup_size_trend.sql"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -171,9 +171,9 @@
|
||||
"columnsAsLabels": true
|
||||
}
|
||||
},
|
||||
"queryFile": "./dist/sql/waits_paul_randal.sql",
|
||||
"queryFile": "./sql/waits_paul_randal.sql",
|
||||
"details": {
|
||||
"queryFile": "./dist/sql/waits_detail_paul_randal.sql",
|
||||
"queryFile": "./sql/waits_detail_paul_randal.sql",
|
||||
"label": {
|
||||
"column": "WaitType",
|
||||
"state": []
|
||||
@@ -194,9 +194,9 @@
|
||||
"columnsAsLabels": true
|
||||
}
|
||||
},
|
||||
"queryFile": "./dist/sql/wait_resources.sql",
|
||||
"queryFile": "./sql/wait_resources.sql",
|
||||
"details": {
|
||||
"queryFile": "./dist/sql/wait_resources.sql",
|
||||
"queryFile": "./sql/wait_resources.sql",
|
||||
"label": {
|
||||
"column": "WaitType",
|
||||
"state": []
|
||||
@@ -217,7 +217,7 @@
|
||||
"columnsAsLabels": true
|
||||
}
|
||||
},
|
||||
"queryFile": "./dist/sql/memorybydb.sql"
|
||||
"queryFile": "./sql/memorybydb.sql"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -231,7 +231,7 @@
|
||||
"legendPosition": "none"
|
||||
}
|
||||
},
|
||||
"queryFile": "./dist/sql/typeofContentions.sql",
|
||||
"queryFile": "./sql/typeofContentions.sql",
|
||||
"autoRefreshInterval": 0.05
|
||||
}
|
||||
},
|
||||
@@ -247,7 +247,7 @@
|
||||
"xAxisLabel": "Object Ids for System Tables"
|
||||
}
|
||||
},
|
||||
"queryFile": "./dist/sql/metadataContention.sql",
|
||||
"queryFile": "./sql/metadataContention.sql",
|
||||
"autoRefreshInterval": 0.05
|
||||
}
|
||||
},
|
||||
@@ -263,7 +263,7 @@
|
||||
"xAxisLabel": "Page Types"
|
||||
}
|
||||
},
|
||||
"queryFile": "./dist/sql/allocationContention.sql",
|
||||
"queryFile": "./sql/allocationContention.sql",
|
||||
"autoRefreshInterval": 0.05
|
||||
}
|
||||
}
|
||||
|
||||
13
extensions/sql-bindings/.eslintrc.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"parserOptions": {
|
||||
"project": "./extensions/sql-bindings/tsconfig.json"
|
||||
},
|
||||
"rules": {
|
||||
"@typescript-eslint/no-floating-promises": [
|
||||
"error",
|
||||
{
|
||||
"ignoreVoid": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
1
extensions/sql-bindings/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.vsix
|
||||
21
extensions/sql-bindings/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
// This task is meant for running the VS Code version of the extension. See VSCODE_DEVELOPMENT.md for more information
|
||||
{
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"name": "Launch Extension in VS Code",
|
||||
"runtimeExecutable": "${execPath}",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/out/**/*.js"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
9
extensions/sql-bindings/.vscodeignore
Normal file
@@ -0,0 +1,9 @@
|
||||
src
|
||||
out
|
||||
tsconfig.json
|
||||
.gitignore
|
||||
coverage
|
||||
coverConfig.json
|
||||
extension.webpack.config.js
|
||||
*.vsix
|
||||
yarn.lock
|
||||
29
extensions/sql-bindings/README.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# Microsoft SQL Bindings for Azure Data Studio and VS Code
|
||||
|
||||
## Overview
|
||||
|
||||
Microsoft SQL Bindings for Azure Data Studio and VS Code enables users to develop Azure Functions with Azure SQL bindings
|
||||
|
||||
### VS Code
|
||||
|
||||
This extension is bundled into the `SQL Server (MSSQL)` extension for VS Code and will be installed automatically when that extension is updated or installed.
|
||||
|
||||
### Azure Data Studio
|
||||
|
||||
This extension is provided as a separate extension in the marketplace.
|
||||
|
||||
Please report issues and feature requests [here.](https://github.com/microsoft/azuredatastudio/issues)
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||
|
||||
## Privacy Statement
|
||||
|
||||
The [Microsoft Enterprise and Developer Privacy Statement](https://privacy.microsoft.com/privacystatement) describes the privacy statement of this software.
|
||||
|
||||
## License
|
||||
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
Licensed under the [Source EULA](https://raw.githubusercontent.com/Microsoft/azuredatastudio/main/LICENSE.txt).
|
||||
11
extensions/sql-bindings/VSCODE_DEVELOPMENT.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# VS Code Extension Development
|
||||
|
||||
For working on the VS Code version of the package follow these steps for local development/testing.
|
||||
|
||||
1. Copy the values from [package.vscode.json](./package.vscode.json) into [package.json](./package.json) (overwriting the properties with the same name there)
|
||||
2. Compile Azure Data Studio as normal and wait for it to finish
|
||||
3. Run `code <PathToAzureDataStudioSource>/extensions/sql-bindings` from the command line to open a new VS Code instance at the `sql-bindings` folder
|
||||
4. Run the `Launch Extension in VS Code` launch target from the `Run and Debug` view
|
||||
5. This should launch an `Extension Development Host` version of VS Code that is running the extension from sources.
|
||||
|
||||
If you have the compilation running as watch then once you make changes you can just reload the window to pick up the latest changes being made.
|
||||
20
extensions/sql-bindings/coverConfig.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"enabled": true,
|
||||
"relativeSourcePath": "..",
|
||||
"relativeCoverageDir": "../../coverage",
|
||||
"ignorePatterns": [
|
||||
"**/node_modules/**",
|
||||
"**/test/**",
|
||||
"extension.js"
|
||||
],
|
||||
"reports": [
|
||||
"cobertura",
|
||||
"lcov",
|
||||
"json"
|
||||
],
|
||||
"verbose": false,
|
||||
"remapOptions": {
|
||||
"basePath": "..",
|
||||
"useAbsolutePaths": true
|
||||
}
|
||||
}
|
||||
17
extensions/sql-bindings/extension.webpack.config.js
Normal file
@@ -0,0 +1,17 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
//@ts-check
|
||||
|
||||
'use strict';
|
||||
|
||||
const withDefaults = require('../shared.webpack.config');
|
||||
|
||||
module.exports = withDefaults({
|
||||
context: __dirname,
|
||||
entry: {
|
||||
extension: './src/extension.ts'
|
||||
}
|
||||
});
|
||||
82
extensions/sql-bindings/package.json
Normal file
@@ -0,0 +1,82 @@
|
||||
{
|
||||
"name": "sql-bindings",
|
||||
"displayName": "%displayName%",
|
||||
"description": "%description%",
|
||||
"version": "0.0.1",
|
||||
"publisher": "Microsoft",
|
||||
"preview": true,
|
||||
"engines": {
|
||||
"vscode": "^1.30.1",
|
||||
"azdata": ">=1.35.0"
|
||||
},
|
||||
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/main/LICENSE.txt",
|
||||
"icon": "",
|
||||
"aiKey": "AIF-37eefaf0-8022-4671-a3fb-64752724682e",
|
||||
"activationEvents": [
|
||||
"onCommand:sqlBindings.addSqlBinding",
|
||||
"onCommand:sqlBindings.createAzureFunction"
|
||||
],
|
||||
"main": "./out/extension",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Microsoft/azuredatastudio.git"
|
||||
},
|
||||
"extensionDependencies": [
|
||||
"Microsoft.mssql"
|
||||
],
|
||||
"capabilities": {
|
||||
"virtualWorkspaces": false,
|
||||
"untrustedWorkspaces": {
|
||||
"supported": true
|
||||
}
|
||||
},
|
||||
"contributes": {
|
||||
"commands": [
|
||||
{
|
||||
"command": "sqlBindings.addSqlBinding",
|
||||
"title": "%sqlBindings.addSqlBinding%",
|
||||
"category": "MS SQL"
|
||||
},
|
||||
{
|
||||
"command": "sqlBindings.createAzureFunction",
|
||||
"title": "%sqlBindings.createAzureFunction%",
|
||||
"category": "MS SQL",
|
||||
"when": "view == objectExplorer && viewItem == Table"
|
||||
}
|
||||
],
|
||||
"menus": {
|
||||
"commandPalette": [
|
||||
{
|
||||
"command": "sqlBindings.addSqlBinding",
|
||||
"when": "editorLangId == csharp && !azdataAvailable && resourceScheme != untitled"
|
||||
}
|
||||
],
|
||||
"view/item/context": [
|
||||
{
|
||||
"command": "sqlBindings.createAzureFunction",
|
||||
"when": "view == objectExplorer && viewItem == Table",
|
||||
"group": "zAzure_Function@1"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@microsoft/ads-extension-telemetry": "^1.1.5",
|
||||
"fast-glob": "^3.2.7",
|
||||
"jsonc-parser": "^2.3.1",
|
||||
"promisify-child-process": "^3.1.1",
|
||||
"vscode-nls": "^4.1.2",
|
||||
"vscode-languageclient": "5.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^14.14.16",
|
||||
"tslint": "^5.8.0",
|
||||
"should": "^13.2.1",
|
||||
"sinon": "^9.0.2",
|
||||
"typemoq": "^2.1.0",
|
||||
"@microsoft/vscodetestcover": "^1.2.0",
|
||||
"mocha": "^5.2.0",
|
||||
"mocha-junit-reporter": "^1.17.0",
|
||||
"mocha-multi-reporters": "^1.1.7"
|
||||
}
|
||||
}
|
||||
6
extensions/sql-bindings/package.nls.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"displayName": "SQL Bindings",
|
||||
"description": "Enables users to develop and publish Azure Functions with Azure SQL bindings",
|
||||
"sqlBindings.addSqlBinding": "Add SQL Binding (preview)",
|
||||
"sqlBindings.createAzureFunction": "Create Azure Function with SQL binding"
|
||||
}
|
||||
7
extensions/sql-bindings/package.vscode.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "sql-bindings-vscode",
|
||||
"publisher": "ms-mssql",
|
||||
"extensionDependencies": [
|
||||
"ms-mssql.mssql"
|
||||
]
|
||||
}
|
||||
526
extensions/sql-bindings/src/common/azureFunctionsUtils.ts
Normal file
@@ -0,0 +1,526 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as os from 'os';
|
||||
import * as fs from 'fs';
|
||||
import * as vscode from 'vscode';
|
||||
import * as utils from './utils';
|
||||
import * as constants from './constants';
|
||||
import { BindingType } from 'sql-bindings';
|
||||
import * as path from 'path';
|
||||
import { ConnectionDetails, IConnectionInfo } from 'vscode-mssql';
|
||||
// https://github.com/microsoft/vscode-azurefunctions/blob/main/src/vscode-azurefunctions.api.d.ts
|
||||
import { AzureFunctionsExtensionApi } from '../typings/vscode-azurefunctions.api';
|
||||
// https://github.com/microsoft/vscode-azuretools/blob/main/ui/api.d.ts
|
||||
import { AzureExtensionApiProvider } from '../typings/vscode-azuretools.api';
|
||||
/**
|
||||
* Represents the settings in an Azure function project's locawl.settings.json file
|
||||
*/
|
||||
export interface ILocalSettingsJson {
|
||||
IsEncrypted?: boolean;
|
||||
Values?: { [key: string]: string };
|
||||
Host?: { [key: string]: string };
|
||||
ConnectionStrings?: { [key: string]: string };
|
||||
}
|
||||
|
||||
export interface IFileFunctionObject {
|
||||
filePromise: Promise<string>;
|
||||
watcherDisposable: vscode.Disposable;
|
||||
}
|
||||
|
||||
/**
|
||||
* copied and modified from vscode-azurefunctions extension
|
||||
* https://github.com/microsoft/vscode-azurefunctions/blob/main/src/funcConfig/local.settings.ts
|
||||
* @param localSettingsPath full path to local.settings.json
|
||||
* @returns settings in local.settings.json. If no settings are found, returns default "empty" settings
|
||||
*/
|
||||
export async function getLocalSettingsJson(localSettingsPath: string): Promise<ILocalSettingsJson> {
|
||||
if (fs.existsSync(localSettingsPath)) {
|
||||
const data: string = (fs.readFileSync(localSettingsPath)).toString();
|
||||
try {
|
||||
return JSON.parse(data);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
throw new Error(utils.formatString(constants.failedToParse(error.message), constants.azureFunctionLocalSettingsFileName, error.message));
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
IsEncrypted: false // Include this by default otherwise the func cli assumes settings are encrypted and fails to run
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new setting to a project's local.settings.json file
|
||||
* modified from setLocalAppSetting code from vscode-azurefunctions extension
|
||||
* @param projectFolder full path to project folder
|
||||
* @param key Key of the new setting
|
||||
* @param value Value of the new setting
|
||||
* @returns true if successful adding the new setting, false if unsuccessful
|
||||
*/
|
||||
export async function setLocalAppSetting(projectFolder: string, key: string, value: string): Promise<boolean> {
|
||||
const localSettingsPath: string = path.join(projectFolder, constants.azureFunctionLocalSettingsFileName);
|
||||
const settings: ILocalSettingsJson = await getLocalSettingsJson(localSettingsPath);
|
||||
|
||||
settings.Values = settings.Values || {};
|
||||
if (settings.Values[key] === value) {
|
||||
// don't do anything if it's the same as the existing value
|
||||
return true;
|
||||
} else if (settings.Values[key]) {
|
||||
const result = await vscode.window.showWarningMessage(constants.settingAlreadyExists(key), { modal: true }, constants.yesString);
|
||||
if (result !== constants.yesString) {
|
||||
// key already exists and user doesn't want to overwrite it
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
settings.Values[key] = value;
|
||||
void fs.promises.writeFile(localSettingsPath, JSON.stringify(settings, undefined, 2));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Azure Functions extension API if it is installed
|
||||
* if it is not installed, prompt the user to install directly, learn more, or do not install
|
||||
* @returns the Azure Functions extension API if it is installed, prompt if it is not installed
|
||||
*/
|
||||
export async function getAzureFunctionsExtensionApi(): Promise<AzureFunctionsExtensionApi | undefined> {
|
||||
let apiProvider = await vscode.extensions.getExtension(constants.azureFunctionsExtensionName)?.activate() as AzureExtensionApiProvider;
|
||||
if (!apiProvider) {
|
||||
const response = await vscode.window.showInformationMessage(constants.azureFunctionsExtensionNotFound,
|
||||
constants.install, constants.learnMore, constants.doNotInstall);
|
||||
if (response === constants.install) {
|
||||
const extensionInstalled = new Promise<void>((resolve, reject) => {
|
||||
const timeout = setTimeout(async () => {
|
||||
reject(new Error(constants.timeoutExtensionError));
|
||||
extensionChange.dispose();
|
||||
}, 10000);
|
||||
let extensionChange = vscode.extensions.onDidChange(async () => {
|
||||
if (vscode.extensions.getExtension(constants.azureFunctionsExtensionName)) {
|
||||
resolve();
|
||||
extensionChange.dispose();
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
});
|
||||
});
|
||||
await vscode.window.withProgress(
|
||||
{
|
||||
location: vscode.ProgressLocation.Notification,
|
||||
title: constants.azureFunctionsExtensionName,
|
||||
cancellable: false
|
||||
}, async (_progress, _token) => {
|
||||
await vscode.commands.executeCommand('workbench.extensions.installExtension', constants.azureFunctionsExtensionName);
|
||||
}
|
||||
);
|
||||
// the extension has not been notified that the azure function extension is installed so wait till it is to then activate it
|
||||
await extensionInstalled;
|
||||
apiProvider = await vscode.extensions.getExtension(constants.azureFunctionsExtensionName)?.activate() as AzureExtensionApiProvider;
|
||||
} else if (response === constants.learnMore) {
|
||||
await vscode.env.openExternal(vscode.Uri.parse(constants.linkToAzureFunctionExtension));
|
||||
return undefined;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
const azureFunctionApi = apiProvider.getApi<AzureFunctionsExtensionApi>('*');
|
||||
if (azureFunctionApi) {
|
||||
return azureFunctionApi;
|
||||
} else {
|
||||
void vscode.window.showErrorMessage(constants.azureFunctionsExtensionNotInstalled);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO REMOVE defaultSqlBindingTextLines
|
||||
* Overwrites the Azure function methods body to work with the binding
|
||||
* @param filePath is the path for the function file (.cs for C# functions)
|
||||
*/
|
||||
export function overwriteAzureFunctionMethodBody(filePath: string): void {
|
||||
let defaultBindedFunctionText = fs.readFileSync(filePath, 'utf-8');
|
||||
// Replace default binding text
|
||||
let newValueLines = defaultBindedFunctionText.split(os.EOL);
|
||||
const defaultFunctionTextToSkip = new Set(constants.defaultSqlBindingTextLines);
|
||||
let replacedValueLines = [];
|
||||
for (let defaultLine of newValueLines) {
|
||||
// Skipped lines
|
||||
if (defaultFunctionTextToSkip.has(defaultLine.trimStart())) {
|
||||
continue;
|
||||
} else if (defaultLine.trimStart() === constants.defaultBindingResult) { // Result change
|
||||
replacedValueLines.push(defaultLine.replace(constants.defaultBindingResult, constants.sqlBindingResult));
|
||||
} else {
|
||||
// Normal lines to be included
|
||||
replacedValueLines.push(defaultLine);
|
||||
}
|
||||
}
|
||||
defaultBindedFunctionText = replacedValueLines.join(os.EOL);
|
||||
fs.writeFileSync(filePath, defaultBindedFunctionText, 'utf-8');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the azure function project for the user to choose from a list of projects files
|
||||
* If only one project is found that project is used to add the binding to
|
||||
* if no project is found, user is informed there needs to be a C# Azure Functions project
|
||||
* @returns the selected project file path
|
||||
*/
|
||||
export async function getAzureFunctionProject(): Promise<string | undefined> {
|
||||
let selectedProjectFile: string | undefined = '';
|
||||
if (vscode.workspace.workspaceFolders === undefined || vscode.workspace.workspaceFolders.length === 0) {
|
||||
return selectedProjectFile;
|
||||
} else {
|
||||
const projectFiles = await getAzureFunctionProjectFiles();
|
||||
if (projectFiles !== undefined) {
|
||||
if (projectFiles.length > 1) {
|
||||
// select project to add azure function to
|
||||
selectedProjectFile = (await vscode.window.showQuickPick(projectFiles, {
|
||||
canPickMany: false,
|
||||
title: constants.selectProject,
|
||||
ignoreFocusOut: true
|
||||
}));
|
||||
return selectedProjectFile;
|
||||
} else if (projectFiles.length === 1) {
|
||||
// only one azure function project found
|
||||
return projectFiles[0];
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the azure function project files based on the host file found in the same folder
|
||||
* @returns the azure function project files paths
|
||||
*/
|
||||
export async function getAzureFunctionProjectFiles(): Promise<string[] | undefined> {
|
||||
let projFiles: string[] = [];
|
||||
const hostFiles = await getHostFiles();
|
||||
if (!hostFiles) {
|
||||
return undefined;
|
||||
}
|
||||
for (let host of hostFiles) {
|
||||
let projectFile = await vscode.workspace.findFiles(new vscode.RelativePattern(path.dirname(host), '*.csproj'));
|
||||
projectFile.filter(file => path.dirname(file.fsPath) === path.dirname(host) ? projFiles.push(file?.fsPath) : projFiles);
|
||||
}
|
||||
return projFiles.length > 0 ? projFiles : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the host files from the workspace
|
||||
* @returns the host file paths
|
||||
*/
|
||||
export async function getHostFiles(): Promise<string[] | undefined> {
|
||||
const hostUris = await vscode.workspace.findFiles('**/host.json');
|
||||
const hostFiles = hostUris.map(uri => uri.fsPath);
|
||||
return hostFiles.length > 0 ? hostFiles : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the local.settings.json file path
|
||||
* @param projectFile path of the azure function project
|
||||
* @returns the local.settings.json file path
|
||||
*/
|
||||
export async function getSettingsFile(projectFile: string): Promise<string | undefined> {
|
||||
return path.join(path.dirname(projectFile), 'local.settings.json');
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the new function file once the file is created and the watcher disposable
|
||||
* @param projectFile is the path to the project file
|
||||
* @returns the function file path once created and the watcher disposable
|
||||
*/
|
||||
export function waitForNewFunctionFile(projectFile: string): IFileFunctionObject {
|
||||
const watcher = vscode.workspace.createFileSystemWatcher((
|
||||
path.dirname(projectFile), '**/*.cs'), false, true, true);
|
||||
const filePromise = new Promise<string>((resolve, _) => {
|
||||
watcher.onDidCreate((e) => {
|
||||
resolve(e.fsPath);
|
||||
});
|
||||
});
|
||||
return {
|
||||
filePromise,
|
||||
watcherDisposable: watcher
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the new host project file once it has created and the watcher disposable
|
||||
* @returns the host file path once created and the watcher disposable
|
||||
*/
|
||||
export function waitForNewHostFile(): IFileFunctionObject {
|
||||
const watcher = vscode.workspace.createFileSystemWatcher('**/host.json', false, true, true);
|
||||
const filePromise = new Promise<string>((resolve, _) => {
|
||||
watcher.onDidCreate((e) => {
|
||||
resolve(e.fsPath);
|
||||
});
|
||||
});
|
||||
return {
|
||||
filePromise,
|
||||
watcherDisposable: watcher
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the required nuget package to the project
|
||||
* @param selectedProjectFile is the users selected project file path
|
||||
*/
|
||||
export async function addNugetReferenceToProjectFile(selectedProjectFile: string): Promise<void> {
|
||||
await utils.executeCommand(`dotnet add ${selectedProjectFile} package ${constants.sqlExtensionPackageName} --prerelease`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the Sql Connection String to the local.settings.json
|
||||
* @param connectionString of the SQL Server connection that was chosen by the user
|
||||
*/
|
||||
export async function addConnectionStringToConfig(connectionString: string, projectFile: string): Promise<void> {
|
||||
const settingsFile = await getSettingsFile(projectFile);
|
||||
if (settingsFile) {
|
||||
await setLocalAppSetting(path.dirname(settingsFile), constants.sqlConnectionString, connectionString);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Azure Functions project that contains the given file if the project is open in one of the workspace folders
|
||||
* @param fileUri file that the containing project needs to be found for
|
||||
* @returns uri of project or undefined if project couldn't be found
|
||||
*/
|
||||
export async function getAFProjectContainingFile(fileUri: vscode.Uri): Promise<vscode.Uri | undefined> {
|
||||
// get functions csprojs in the workspace
|
||||
const projectPromises = vscode.workspace.workspaceFolders?.map(f => utils.getAllProjectsInFolder(f.uri, '.csproj')) ?? [];
|
||||
const functionsProjects = (await Promise.all(projectPromises)).reduce((prev, curr) => prev.concat(curr), []).filter(p => isFunctionProject(path.dirname(p.fsPath)));
|
||||
|
||||
// look for project folder containing file if there's more than one
|
||||
if (functionsProjects.length > 1) {
|
||||
// TODO: figure out which project contains the file
|
||||
// the new style csproj doesn't list all the files in the project anymore, unless the file isn't in the same folder
|
||||
// so we can't rely on using that to check
|
||||
console.error('need to find which project contains the file ' + fileUri.fsPath);
|
||||
return undefined;
|
||||
} else if (functionsProjects.length === 0) {
|
||||
throw new Error(constants.noAzureFunctionsProjectsInWorkspace);
|
||||
} else {
|
||||
return functionsProjects[0];
|
||||
}
|
||||
}
|
||||
|
||||
// Use 'host.json' as an indicator that this is a functions project
|
||||
// copied from verifyIsproject.ts in vscode-azurefunctions extension
|
||||
export async function isFunctionProject(folderPath: string): Promise<boolean> {
|
||||
return fs.existsSync(path.join(folderPath, constants.hostFileName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompts the user to select type of binding and returns result
|
||||
*/
|
||||
export async function promptForBindingType(): Promise<(vscode.QuickPickItem & { type: BindingType }) | undefined> {
|
||||
const inputOutputItems: (vscode.QuickPickItem & { type: BindingType })[] = [
|
||||
{
|
||||
label: constants.input,
|
||||
type: BindingType.input
|
||||
},
|
||||
{
|
||||
label: constants.output,
|
||||
type: BindingType.output
|
||||
}
|
||||
];
|
||||
|
||||
const selectedBinding = (await vscode.window.showQuickPick(inputOutputItems, {
|
||||
canPickMany: false,
|
||||
title: constants.selectBindingType,
|
||||
ignoreFocusOut: true
|
||||
}));
|
||||
|
||||
return selectedBinding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompts the user to enter object name for the SQL query
|
||||
* @param bindingType Type of SQL Binding
|
||||
*/
|
||||
export async function promptForObjectName(bindingType: BindingType): Promise<string | undefined> {
|
||||
return vscode.window.showInputBox({
|
||||
prompt: bindingType === BindingType.input ? constants.sqlTableOrViewToQuery : constants.sqlTableToUpsert,
|
||||
placeHolder: constants.placeHolderObject,
|
||||
validateInput: input => input ? undefined : constants.nameMustNotBeEmpty,
|
||||
ignoreFocusOut: true
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompts the user to enter connection setting and updates it from AF project
|
||||
* @param projectUri Azure Function project uri
|
||||
*/
|
||||
export async function promptAndUpdateConnectionStringSetting(projectUri: vscode.Uri | undefined): Promise<string | undefined> {
|
||||
let connectionStringSettingName: string | undefined;
|
||||
const vscodeMssqlApi = await utils.getVscodeMssqlApi();
|
||||
|
||||
// show the settings from project's local.settings.json if there's an AF functions project
|
||||
if (projectUri) {
|
||||
let settings;
|
||||
try {
|
||||
settings = await getLocalSettingsJson(path.join(path.dirname(projectUri.fsPath!), constants.azureFunctionLocalSettingsFileName));
|
||||
} catch (e) {
|
||||
void vscode.window.showErrorMessage(utils.getErrorMessage(e));
|
||||
return;
|
||||
}
|
||||
|
||||
let existingSettings: (vscode.QuickPickItem)[] = [];
|
||||
if (settings?.Values) {
|
||||
existingSettings = Object.keys(settings.Values).map(setting => {
|
||||
return {
|
||||
label: setting
|
||||
} as vscode.QuickPickItem;
|
||||
});
|
||||
}
|
||||
|
||||
existingSettings.unshift({ label: constants.createNewLocalAppSettingWithIcon });
|
||||
let sqlConnectionStringSettingExists = existingSettings.find(s => s.label === constants.sqlConnectionStringSetting);
|
||||
|
||||
while (!connectionStringSettingName) {
|
||||
const selectedSetting = await vscode.window.showQuickPick(existingSettings, {
|
||||
canPickMany: false,
|
||||
title: constants.selectSetting,
|
||||
ignoreFocusOut: true
|
||||
});
|
||||
if (!selectedSetting) {
|
||||
// User cancelled
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedSetting.label === constants.createNewLocalAppSettingWithIcon) {
|
||||
const newConnectionStringSettingName = await vscode.window.showInputBox(
|
||||
{
|
||||
title: constants.enterConnectionStringSettingName,
|
||||
ignoreFocusOut: true,
|
||||
value: sqlConnectionStringSettingExists ? '' : constants.sqlConnectionStringSetting,
|
||||
validateInput: input => input ? undefined : constants.nameMustNotBeEmpty
|
||||
}
|
||||
) ?? '';
|
||||
|
||||
if (!newConnectionStringSettingName) {
|
||||
// go back to select setting quickpick if user escapes from inputting the setting name in case they changed their mind
|
||||
continue;
|
||||
}
|
||||
|
||||
// show the connection string methods (user input and connection profile options)
|
||||
const listOfConnectionStringMethods = [constants.connectionProfile, constants.userConnectionString];
|
||||
while (true) {
|
||||
const selectedConnectionStringMethod = await vscode.window.showQuickPick(listOfConnectionStringMethods, {
|
||||
canPickMany: false,
|
||||
title: constants.selectConnectionString,
|
||||
ignoreFocusOut: true
|
||||
});
|
||||
if (!selectedConnectionStringMethod) {
|
||||
// User cancelled
|
||||
return;
|
||||
}
|
||||
|
||||
let connectionString: string = '';
|
||||
let includePassword: string | undefined;
|
||||
let connectionInfo: IConnectionInfo | undefined;
|
||||
let connectionDetails: ConnectionDetails;
|
||||
if (selectedConnectionStringMethod === constants.userConnectionString) {
|
||||
// User chooses to enter connection string manually
|
||||
connectionString = await vscode.window.showInputBox(
|
||||
{
|
||||
title: constants.enterConnectionString,
|
||||
ignoreFocusOut: true,
|
||||
value: 'Server=localhost;Initial Catalog={db_name};User ID=sa;Password={your_password};Persist Security Info=False',
|
||||
validateInput: input => input ? undefined : constants.valueMustNotBeEmpty
|
||||
}
|
||||
) ?? '';
|
||||
} else {
|
||||
// Let user choose from existing connections to create connection string from
|
||||
connectionInfo = await vscodeMssqlApi.promptForConnection(true);
|
||||
if (!connectionInfo) {
|
||||
// User cancelled return to selectedConnectionStringMethod prompt
|
||||
continue;
|
||||
}
|
||||
connectionDetails = { options: connectionInfo };
|
||||
try {
|
||||
// Prompt to include password in connection string if authentication type is SqlLogin and connection has password saved
|
||||
if (connectionInfo.authenticationType === 'SqlLogin' && connectionInfo.password) {
|
||||
includePassword = await vscode.window.showQuickPick([constants.yesString, constants.noString], {
|
||||
title: constants.includePassword,
|
||||
canPickMany: false,
|
||||
ignoreFocusOut: true
|
||||
});
|
||||
if (includePassword === constants.yesString) {
|
||||
// set connection string to include password
|
||||
connectionString = await vscodeMssqlApi.getConnectionString(connectionDetails, true, false);
|
||||
}
|
||||
}
|
||||
// set connection string to not include the password if connection info does not include password, or user chooses to not include password, or authentication type is not sql login
|
||||
if (includePassword !== constants.yesString) {
|
||||
connectionString = await vscodeMssqlApi.getConnectionString(connectionDetails, false, false);
|
||||
}
|
||||
} catch (e) {
|
||||
// failed to get connection string for selected connection and will go back to prompt for connection string methods
|
||||
console.warn(e);
|
||||
void vscode.window.showErrorMessage(constants.failedToGetConnectionString);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (connectionString) {
|
||||
try {
|
||||
const projectFolder: string = path.dirname(projectUri.fsPath);
|
||||
const localSettingsPath: string = path.join(projectFolder, constants.azureFunctionLocalSettingsFileName);
|
||||
let userPassword: string | undefined;
|
||||
// Ask user to enter password if auth type is sql login and password is not saved
|
||||
if (connectionInfo?.authenticationType === 'SqlLogin' && !connectionInfo?.password) {
|
||||
userPassword = await vscode.window.showInputBox({
|
||||
prompt: constants.enterPasswordPrompt,
|
||||
placeHolder: constants.enterPasswordManually,
|
||||
ignoreFocusOut: true,
|
||||
password: true,
|
||||
validateInput: input => input ? undefined : constants.valueMustNotBeEmpty
|
||||
});
|
||||
if (userPassword) {
|
||||
// if user enters password replace password placeholder with user entered password
|
||||
connectionString = connectionString.replace(constants.passwordPlaceholder, userPassword);
|
||||
}
|
||||
}
|
||||
if (includePassword !== constants.yesString && !userPassword && connectionInfo?.authenticationType === 'SqlLogin') {
|
||||
// if user does not want to include password or user does not enter password, show warning message that they will have to enter it manually later in local.settings.json
|
||||
void vscode.window.showWarningMessage(constants.userPasswordLater, constants.openFile, constants.closeButton).then(async (result) => {
|
||||
if (result === constants.openFile) {
|
||||
// open local.settings.json file
|
||||
void vscode.commands.executeCommand(constants.vscodeOpenCommand, vscode.Uri.file(localSettingsPath));
|
||||
}
|
||||
});
|
||||
}
|
||||
const success = await setLocalAppSetting(projectFolder, newConnectionStringSettingName, connectionString);
|
||||
if (success) {
|
||||
// exit both loops and insert binding
|
||||
connectionStringSettingName = newConnectionStringSettingName;
|
||||
break;
|
||||
} else {
|
||||
void vscode.window.showErrorMessage(constants.selectConnectionError());
|
||||
}
|
||||
} catch (e) {
|
||||
// display error message and show select setting quickpick again
|
||||
void vscode.window.showErrorMessage(constants.selectConnectionError(e));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If user cancels out of this or doesn't want to overwrite an existing setting
|
||||
// just return them to the select setting quickpick in case they changed their mind
|
||||
connectionStringSettingName = selectedSetting.label;
|
||||
}
|
||||
}
|
||||
// Add sql extension package reference to project. If the reference is already there, it doesn't get added again
|
||||
await addNugetReferenceToProjectFile(projectUri.fsPath);
|
||||
} else {
|
||||
// if no AF project was found or there's more than one AF functions project in the workspace,
|
||||
// ask for the user to input the setting name
|
||||
connectionStringSettingName = await vscode.window.showInputBox({
|
||||
prompt: constants.connectionStringSetting,
|
||||
placeHolder: constants.connectionStringSettingPlaceholder,
|
||||
ignoreFocusOut: true
|
||||
});
|
||||
}
|
||||
return connectionStringSettingName;
|
||||
}
|
||||
90
extensions/sql-bindings/src/common/constants.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
import * as utils from '../common/utils';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
// Azure Functions
|
||||
export const azureFunctionsExtensionName = 'ms-azuretools.vscode-azurefunctions';
|
||||
export const sqlConnectionString = 'SqlConnectionString';
|
||||
export const linkToAzureFunctionExtension = 'https://docs.microsoft.com/azure/azure-functions/functions-develop-vs-code';
|
||||
export const sqlBindingsDoc = 'https://aka.ms/sqlbindings';
|
||||
export const defaultSqlBindingTextLines =
|
||||
[
|
||||
'log.LogInformation(\"C# HTTP trigger function processed a request.\");',
|
||||
'string name = req.Query[\"name\"];',
|
||||
'string requestBody = await new StreamReader(req.Body).ReadToEndAsync();',
|
||||
'dynamic data = JsonConvert.DeserializeObject(requestBody);',
|
||||
'name = name ?? data?.name;',
|
||||
'string responseMessage = string.IsNullOrEmpty(name) ? \"This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.\" : $\"Hello, {name}. This HTTP triggered function executed successfully.\";'
|
||||
];
|
||||
export const defaultBindingResult = 'return new OkObjectResult(responseMessage);';
|
||||
export const sqlBindingResult = `return new OkObjectResult(result);`;
|
||||
export const sqlExtensionPackageName = 'Microsoft.Azure.WebJobs.Extensions.Sql';
|
||||
export const functionNameTitle = localize('functionNameTitle', 'Function Name');
|
||||
export const selectProject = localize('selectProject', 'Select the Azure Function project for the SQL Binding');
|
||||
export const azureFunctionsExtensionNotFound = localize('azureFunctionsExtensionNotFound', 'The Azure Functions extension is required to create a new Azure Function with SQL binding but is not installed, install it now?');
|
||||
export const install = localize('install', 'Install');
|
||||
export const learnMore = localize('learnMore', 'Learn more');
|
||||
export const doNotInstall = localize('doNotInstall', 'Do not install');
|
||||
export const createProject = localize('createProject', 'Create Azure Function Project');
|
||||
export const selectAzureFunctionProjFolder = localize('selectAzureFunctionProjFolder', 'Select folder for the Azure Function project');
|
||||
export const timeoutExtensionError = localize('timeoutExtensionError', 'Timed out waiting for extension to install');
|
||||
export const timeoutAzureFunctionFileError = localize('timeoutAzureFunctionFileError', 'Timed out waiting for Azure Function file to be created');
|
||||
export const timeoutProjectError = localize('timeoutProjectError', 'Timed out waiting for project to be created');
|
||||
export const errorNewAzureFunction = localize('errorNewAzureFunction', 'Error creating new Azure Function: {0}');
|
||||
export const azureFunctionsExtensionNotInstalled = localize('azureFunctionsExtensionNotInstalled', 'Azure Functions extension must be installed in order to use this feature.');
|
||||
export const azureFunctionsProjectMustBeOpened = localize('azureFunctionsProjectMustBeOpened', 'A C# Azure Functions project must be present in order to create a new Azure Function for this table.');
|
||||
|
||||
// Insert SQL binding
|
||||
export const hostFileName = 'host.json';
|
||||
export const placeHolderObject = '[dbo].[table1]';
|
||||
export const sqlBindingsHelpLink = 'https://github.com/Azure/azure-functions-sql-extension/blob/main/README.md';
|
||||
export const passwordPlaceholder = '******';
|
||||
export const azureFunctionLocalSettingsFileName = 'local.settings.json';
|
||||
export const vscodeOpenCommand = 'vscode.open';
|
||||
|
||||
export const nameMustNotBeEmpty = localize('nameMustNotBeEmpty', "Name must not be empty");
|
||||
export const yesString = localize('yesString', "Yes");
|
||||
export const noString = localize('noString', "No");
|
||||
export const input = localize('input', "Input");
|
||||
export const output = localize('output', "Output");
|
||||
export const selectBindingType = localize('selectBindingType', "Select type of binding");
|
||||
export const selectAzureFunction = localize('selectAzureFunction', "Select an Azure function in the current file to add SQL binding to");
|
||||
export const sqlTableOrViewToQuery = localize('sqlTableOrViewToQuery', "SQL table or view to query");
|
||||
export const sqlTableToUpsert = localize('sqlTableToUpsert', "SQL table to upsert into");
|
||||
export const connectionStringSetting = localize('connectionStringSetting', "Connection string setting name");
|
||||
export const selectSetting = localize('selectSetting', "Select SQL connection string setting from local.settings.json");
|
||||
export const connectionStringSettingPlaceholder = localize('connectionStringSettingPlaceholder', "Connection string setting specified in \"local.settings.json\"");
|
||||
export const noAzureFunctionsInFile = localize('noAzureFunctionsInFile', "No Azure functions in the current active file");
|
||||
export const noAzureFunctionsProjectsInWorkspace = localize('noAzureFunctionsProjectsInWorkspace', "No Azure functions projects found in the workspace");
|
||||
export const addPackage = localize('addPackage', "Add Package");
|
||||
export const createNewLocalAppSetting = localize('createNewLocalAppSetting', 'Create new local app setting');
|
||||
export const createNewLocalAppSettingWithIcon = `$(add) ${createNewLocalAppSetting}`;
|
||||
export const sqlConnectionStringSetting = 'SqlConnectionString';
|
||||
export const valueMustNotBeEmpty = localize('valueMustNotBeEmpty', "Value must not be empty");
|
||||
export const enterConnectionStringSettingName = localize('enterConnectionStringSettingName', "Enter connection string setting name");
|
||||
export const enterConnectionString = localize('enterConnectionString', "Enter connection string");
|
||||
export const saveChangesInFile = localize('saveChangesInFile', "There are unsaved changes in the current file. Save now?");
|
||||
export const save = localize('save', "Save");
|
||||
export function settingAlreadyExists(settingName: string) { return localize('SettingAlreadyExists', 'Local app setting \'{0}\' already exists. Overwrite?', settingName); }
|
||||
export function failedToParse(errorMessage: string) { return localize('failedToParse', 'Failed to parse "{0}": {1}.', azureFunctionLocalSettingsFileName, errorMessage); }
|
||||
export function jsonParseError(error: string, line: number, column: number) { return localize('jsonParseError', '{0} near line "{1}", column "{2}"', error, line, column); }
|
||||
export const moreInformation = localize('moreInformation', "More Information");
|
||||
export const addPackageReferenceMessage = localize('addPackageReferenceMessage', 'To use SQL bindings, ensure your Azure Functions project has a reference to {0}', sqlExtensionPackageName);
|
||||
export const addSqlBindingPackageError = localize('addSqlBindingPackageError', 'Error adding Sql Binding extension package to project');
|
||||
export const failedToGetConnectionString = localize('failedToGetConnectionString', 'An error occurred generating the connection string for the selected connection');
|
||||
export const connectionProfile = localize('connectionProfile', 'Select a connection profile');
|
||||
export const userConnectionString = localize('userConnectionString', 'Enter connection string');
|
||||
export const selectConnectionString = localize('selectConnectionString', 'Select SQL connection string method');
|
||||
export const selectConnectionError = (err?: any) => err ? localize('selectConnectionError', "Failed to set connection string app setting: {0}", utils.getErrorMessage(err)) : localize('unableToSetConnectionString', "Failed to set connection string app setting");
|
||||
export const includePassword = localize('includePassword', 'Do you want to include the password from this connection in your local.settings.json file?');
|
||||
export const enterPasswordPrompt = localize('enterPasswordPrompt', 'Enter the password to be used for the connection string');
|
||||
export const enterPasswordManually = localize('enterPasswordManually', 'Enter password or press escape to cancel');
|
||||
export const userPasswordLater = localize('userPasswordLater', 'In order to user the SQL connection string later you will need to manually enter the password in your local.settings.json file.');
|
||||
export const openFile = localize('openFile', "Open File");
|
||||
export const closeButton = localize('closeButton', "Close");
|
||||
45
extensions/sql-bindings/src/common/parseJson.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
// copied from vscode-azurefunctions extension
|
||||
|
||||
import * as jsonc from 'jsonc-parser';
|
||||
import * as constants from './constants';
|
||||
|
||||
/**
|
||||
* Parses and returns JSON
|
||||
* Has extra logic to remove a BOM character if it exists and handle comments
|
||||
*/
|
||||
export function parseJson<T extends object>(data: string): T {
|
||||
if (data.charCodeAt(0) === 0xFEFF) {
|
||||
data = data.slice(1);
|
||||
}
|
||||
|
||||
const errors: jsonc.ParseError[] = [];
|
||||
const result: T = <T>jsonc.parse(data, errors, { allowTrailingComma: true });
|
||||
if (errors.length > 0) {
|
||||
const [line, column]: [number, number] = getLineAndColumnFromOffset(data, errors[0].offset);
|
||||
throw new Error(constants.jsonParseError(jsonc.printParseErrorCode(errors[0].error), line, column));
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export function getLineAndColumnFromOffset(data: string, offset: number): [number, number] {
|
||||
const lines: string[] = data.split('\n');
|
||||
let charCount: number = 0;
|
||||
let lineCount: number = 0;
|
||||
let column: number = 0;
|
||||
for (const line of lines) {
|
||||
lineCount += 1;
|
||||
const lineLength: number = line.length + 1;
|
||||
charCount += lineLength;
|
||||
if (charCount >= offset) {
|
||||
column = offset - (charCount - lineLength);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return [lineCount, column];
|
||||
}
|
||||
19
extensions/sql-bindings/src/common/telemetry.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import AdsTelemetryReporter from '@microsoft/ads-extension-telemetry';
|
||||
import { getPackageInfo } from './utils';
|
||||
|
||||
const packageInfo = getPackageInfo()!;
|
||||
|
||||
export const TelemetryReporter = new AdsTelemetryReporter(packageInfo.name, packageInfo.version, packageInfo.aiKey);
|
||||
|
||||
export enum TelemetryViews {
|
||||
SqlBindingsQuickPick = 'SqlBindingsQuickPick'
|
||||
}
|
||||
|
||||
export enum TelemetryActions {
|
||||
startAddSqlBinding = 'startAddSqlBinding',
|
||||
finishAddSqlBinding = 'finishAddSqlBinding'
|
||||
}
|
||||
185
extensions/sql-bindings/src/common/utils.ts
Normal file
@@ -0,0 +1,185 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import type * as azdataType from 'azdata';
|
||||
import * as vscode from 'vscode';
|
||||
import * as vscodeMssql from 'vscode-mssql';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as glob from 'fast-glob';
|
||||
import * as cp from 'child_process';
|
||||
|
||||
export interface ValidationResult {
|
||||
errorMessage: string;
|
||||
validated: boolean
|
||||
}
|
||||
|
||||
export interface IPackageInfo {
|
||||
name: string;
|
||||
fullName: string;
|
||||
version: string;
|
||||
aiKey: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Consolidates on the error message string
|
||||
*/
|
||||
export function getErrorMessage(error: any): string {
|
||||
return (error instanceof Error)
|
||||
? (typeof error.message === 'string' ? error.message : '')
|
||||
: typeof error === 'string' ? error : `${JSON.stringify(error, undefined, '\t')}`;
|
||||
}
|
||||
|
||||
export async function getAzureFunctionService(): Promise<vscodeMssql.IAzureFunctionsService> {
|
||||
if (getAzdataApi()) {
|
||||
// this isn't supported in ADS
|
||||
throw new Error('Azure Functions service is not supported in Azure Data Studio');
|
||||
} else {
|
||||
const api = await getVscodeMssqlApi();
|
||||
return api.azureFunctions;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getVscodeMssqlApi(): Promise<vscodeMssql.IExtension> {
|
||||
const ext = vscode.extensions.getExtension(vscodeMssql.extension.name) as vscode.Extension<vscodeMssql.IExtension>;
|
||||
return ext.activate();
|
||||
}
|
||||
|
||||
// Try to load the azdata API - but gracefully handle the failure in case we're running
|
||||
// in a context where the API doesn't exist (such as VS Code)
|
||||
let azdataApi: typeof azdataType | undefined = undefined;
|
||||
try {
|
||||
azdataApi = require('azdata');
|
||||
if (!azdataApi?.version) {
|
||||
// webpacking makes the require return an empty object instead of throwing an error so make sure we clear the var
|
||||
azdataApi = undefined;
|
||||
}
|
||||
} catch {
|
||||
// no-op
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the azdata API if it's available in the context this extension is running in.
|
||||
* @returns The azdata API if it's available
|
||||
*/
|
||||
export function getAzdataApi(): typeof azdataType | undefined {
|
||||
return azdataApi;
|
||||
}
|
||||
|
||||
export async function executeCommand(command: string, cwd?: string): Promise<string> {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
cp.exec(command, { maxBuffer: 500 * 1024, cwd: cwd }, (error: Error | null, stdout: string, stderr: string) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
if (stderr && stderr.length > 0) {
|
||||
reject(new Error(stderr));
|
||||
return;
|
||||
}
|
||||
resolve(stdout);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the projects of the specified extension in the folder
|
||||
* @param folder
|
||||
* @param projectExtension project extension to filter on
|
||||
* @returns array of project uris
|
||||
*/
|
||||
export async function getAllProjectsInFolder(folder: vscode.Uri, projectExtension: string): Promise<vscode.Uri[]> {
|
||||
// path needs to use forward slashes for glob to work
|
||||
const escapedPath = glob.escapePath(folder.fsPath.replace(/\\/g, '/'));
|
||||
|
||||
// filter for projects with the specified project extension
|
||||
const projFilter = path.posix.join(escapedPath, '**', `*${projectExtension}`);
|
||||
|
||||
// glob will return an array of file paths with forward slashes, so they need to be converted back if on windows
|
||||
return (await glob(projFilter)).map(p => vscode.Uri.file(path.resolve(p)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a string. Behaves like C#'s string.Format() function.
|
||||
*/
|
||||
export function formatString(str: string, ...args: any[]): string {
|
||||
// This is based on code originally from https://github.com/Microsoft/vscode/blob/master/src/vs/nls.js
|
||||
// License: https://github.com/Microsoft/vscode/blob/master/LICENSE.txt
|
||||
let result: string;
|
||||
if (args.length === 0) {
|
||||
result = str;
|
||||
} else {
|
||||
result = str.replace(/\{(\d+)\}/g, (match, rest) => {
|
||||
let index = rest[0];
|
||||
return typeof args[index] !== 'undefined' ? args[index] : match;
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a quoted full name for the object
|
||||
* @param schema of the object
|
||||
* @param objectName object chosen by the user
|
||||
* @returns the quoted and escaped full name of the specified schema and object
|
||||
*/
|
||||
export function generateQuotedFullName(schema: string, objectName: string): string {
|
||||
return `[${escapeClosingBrackets(schema)}].[${escapeClosingBrackets(objectName)}]`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a promise that will reject after the specified timeout
|
||||
* @param errorMessage error message to be returned in the rejection
|
||||
* @param ms timeout in milliseconds. Default is 10 seconds
|
||||
* @returns a promise that rejects after the specified timeout
|
||||
*/
|
||||
export function timeoutPromise(errorMessage: string, ms: number = 10000): Promise<string> {
|
||||
return new Promise((_, reject) => {
|
||||
setTimeout(() => {
|
||||
reject(new Error(errorMessage));
|
||||
}, ms);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a unique file name
|
||||
* Increment the file name by adding 1 to function name if the file already exists
|
||||
* Undefined if the filename suffix count becomes greater than 1024
|
||||
* @param folderPath selected project folder path
|
||||
* @param fileName base filename to use
|
||||
* @returns a promise with the unique file name, or undefined
|
||||
*/
|
||||
export async function getUniqueFileName(folderPath: string, fileName: string): Promise<string | undefined> {
|
||||
let count: number = 0;
|
||||
const maxCount: number = 1024;
|
||||
let uniqueFileName = fileName;
|
||||
|
||||
while (count < maxCount) {
|
||||
if (!fs.existsSync(path.join(folderPath, uniqueFileName + '.cs'))) {
|
||||
return uniqueFileName;
|
||||
}
|
||||
count += 1;
|
||||
uniqueFileName = fileName + count.toString();
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function escapeClosingBrackets(str: string): string {
|
||||
return str.replace(']', ']]');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the package info for the extension based on where the extension is installed
|
||||
* @returns the package info object
|
||||
*/
|
||||
export function getPackageInfo(): IPackageInfo {
|
||||
const packageJson = require('../../package.json');
|
||||
return {
|
||||
name: packageJson.name,
|
||||
fullName: `${packageJson.publisher}.${packageJson.name}`,
|
||||
version: packageJson.version,
|
||||
aiKey: packageJson.aiKey
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { AddSqlBindingParams, GetAzureFunctionsParams, GetAzureFunctionsResult, ResultStatus } from 'sql-bindings';
|
||||
import { RequestType } from 'vscode-languageclient';
|
||||
|
||||
/**
|
||||
* Adds a SQL Binding to a specified Azure function in a file
|
||||
*/
|
||||
export namespace AddSqlBindingRequest {
|
||||
export const type = new RequestType<AddSqlBindingParams, ResultStatus, void, void>('azureFunctions/sqlBinding');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the names of the Azure functions in a file
|
||||
*/
|
||||
export namespace GetAzureFunctionsRequest {
|
||||
export const type = new RequestType<GetAzureFunctionsParams, GetAzureFunctionsResult, void, void>('azureFunctions/getAzureFunctions');
|
||||
}
|
||||
103
extensions/sql-bindings/src/dialogs/addSqlBindingQuickpick.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as constants from '../common/constants';
|
||||
import * as utils from '../common/utils';
|
||||
import * as azureFunctionsUtils from '../common/azureFunctionsUtils';
|
||||
import { TelemetryActions, TelemetryReporter, TelemetryViews } from '../common/telemetry';
|
||||
|
||||
export async function launchAddSqlBindingQuickpick(uri: vscode.Uri | undefined): Promise<void> {
|
||||
TelemetryReporter.sendActionEvent(TelemetryViews.SqlBindingsQuickPick, TelemetryActions.startAddSqlBinding);
|
||||
if (!uri) {
|
||||
// this command only shows in the command palette when the active editor is a .cs file, so we can safely assume that's the scenario
|
||||
// when this is called without a uri
|
||||
uri = vscode.window.activeTextEditor!.document.uri;
|
||||
|
||||
if (vscode.window.activeTextEditor!.document.isDirty) {
|
||||
const result = await vscode.window.showWarningMessage(constants.saveChangesInFile, { modal: true }, constants.save);
|
||||
|
||||
if (result !== constants.save) {
|
||||
return;
|
||||
}
|
||||
|
||||
await vscode.window.activeTextEditor!.document.save();
|
||||
}
|
||||
}
|
||||
|
||||
// get all the Azure functions in the file
|
||||
const azureFunctionsService = await utils.getAzureFunctionService();
|
||||
let getAzureFunctionsResult;
|
||||
try {
|
||||
getAzureFunctionsResult = await azureFunctionsService.getAzureFunctions(uri.fsPath);
|
||||
} catch (e) {
|
||||
void vscode.window.showErrorMessage(utils.getErrorMessage(e));
|
||||
return;
|
||||
}
|
||||
|
||||
const azureFunctions = getAzureFunctionsResult.azureFunctions;
|
||||
|
||||
if (azureFunctions.length === 0) {
|
||||
void vscode.window.showErrorMessage(constants.noAzureFunctionsInFile);
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. select Azure function from the current file
|
||||
const azureFunctionName = (await vscode.window.showQuickPick(azureFunctions, {
|
||||
canPickMany: false,
|
||||
title: constants.selectAzureFunction,
|
||||
ignoreFocusOut: true
|
||||
}));
|
||||
|
||||
if (!azureFunctionName) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. select input or output binding
|
||||
const selectedBinding = await azureFunctionsUtils.promptForBindingType();
|
||||
|
||||
if (!selectedBinding) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. ask for object name for the binding
|
||||
const objectName = await azureFunctionsUtils.promptForObjectName(selectedBinding.type);
|
||||
|
||||
if (!objectName) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. ask for connection string setting name
|
||||
let projectUri: vscode.Uri | undefined;
|
||||
try {
|
||||
projectUri = await azureFunctionsUtils.getAFProjectContainingFile(uri);
|
||||
} catch (e) {
|
||||
// continue even if there's no AF project found. The binding should still be able to be added as long as there was an azure function found in the file earlier
|
||||
}
|
||||
|
||||
let connectionStringSettingName = await azureFunctionsUtils.promptAndUpdateConnectionStringSetting(projectUri);
|
||||
if (!connectionStringSettingName) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 5. insert binding
|
||||
try {
|
||||
const result = await azureFunctionsService.addSqlBinding(selectedBinding.type, uri.fsPath, azureFunctionName, objectName, connectionStringSettingName);
|
||||
|
||||
if (!result.success) {
|
||||
void vscode.window.showErrorMessage(result.errorMessage);
|
||||
TelemetryReporter.sendErrorEvent(TelemetryViews.SqlBindingsQuickPick, TelemetryActions.finishAddSqlBinding);
|
||||
return;
|
||||
}
|
||||
|
||||
TelemetryReporter.createActionEvent(TelemetryViews.SqlBindingsQuickPick, TelemetryActions.finishAddSqlBinding)
|
||||
.withAdditionalProperties({ bindingType: selectedBinding.label })
|
||||
.send();
|
||||
} catch (e) {
|
||||
void vscode.window.showErrorMessage(utils.getErrorMessage(e));
|
||||
TelemetryReporter.sendErrorEvent(TelemetryViews.SqlBindingsQuickPick, TelemetryActions.finishAddSqlBinding);
|
||||
return;
|
||||
}
|
||||
}
|
||||