├── .all-contributorsrc ├── .eslintrc.json ├── .github ├── CODEOWNERS ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── issue-form-bug.yaml │ └── issues-form-feature-request.yaml ├── dependabot.yml ├── reviewers.yml └── workflows │ ├── build.yml │ ├── pr_automation.yml │ ├── release.yml │ └── stale.yml ├── .gitignore ├── .prettierrc.json ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── CodeFlowResult ├── index.html └── main.css ├── LICENSE ├── README.md ├── assets ├── AddNewTSFile.gif ├── Connection&EntityDetails.gif ├── Create&Connect.gif ├── FilterEntitiesBySolution.gif ├── FilterWRBySolution.gif ├── GenerateTypings.gif ├── IntellisenseForTypeScript.gif ├── PacCliCommands.gif ├── PacCliModelBuilder.gif ├── RememberConnection.gif ├── SmartMatchScreen.gif ├── TypeScriptInitialization.gif └── WebResourceUpload.gif ├── package-lock.json ├── package.json ├── package.nls.json ├── resources ├── DVDT-Icon.png ├── dark │ ├── cli.svg │ ├── column.svg │ ├── connect.svg │ ├── connection-details.svg │ ├── connection-type.svg │ ├── css.svg │ ├── dataverse-off.svg │ ├── dataverse.svg │ ├── delete.svg │ ├── file-green.svg │ ├── file-red.svg │ ├── filter-off.svg │ ├── filter-on.svg │ ├── folder.svg │ ├── generic.svg │ ├── html.svg │ ├── js.svg │ ├── light-bulb.svg │ ├── play.svg │ ├── popout.svg │ ├── powerapps.svg │ ├── refresh.svg │ ├── search-remove.svg │ ├── search.svg │ ├── table-light.svg │ └── table.svg ├── ddt.png ├── light │ ├── cli.svg │ ├── column.svg │ ├── connect.svg │ ├── connection-details.svg │ ├── connection-type.svg │ ├── css.svg │ ├── dataverse-off.svg │ ├── dataverse.svg │ ├── delete.svg │ ├── file-green.svg │ ├── file-red.svg │ ├── filter-off.svg │ ├── filter-on.svg │ ├── folder.svg │ ├── generic.svg │ ├── html.svg │ ├── js.svg │ ├── light-bulb.svg │ ├── play.svg │ ├── popout.svg │ ├── powerapps.svg │ ├── refresh.svg │ ├── search-remove.svg │ ├── search.svg │ ├── table-light.svg │ └── table.svg ├── templates │ ├── Files │ │ ├── dvts-js.txt │ │ ├── dvts-ts.txt │ │ └── dvts-wp.txt │ ├── JavaScript │ │ ├── .eslintrc.json │ │ ├── .prettierrc.json │ │ ├── jsconfig.json │ │ ├── package.json │ │ ├── webpack.config.dev.js │ │ ├── webpack.config.js │ │ └── webpack.config.prod.js │ ├── TypeScript │ │ ├── .eslintrc.json │ │ ├── .prettierrc.json │ │ ├── package-lock.json │ │ ├── package.json │ │ └── tsconfig.json │ ├── Webpack │ │ ├── package.json │ │ ├── tsconfig.json │ │ ├── webpack.config.dev.js │ │ ├── webpack.config.js │ │ └── webpack.config.prod.js │ └── dvdt.linker.xml ├── toolIcons │ ├── cmt.png │ ├── drb.png │ ├── pd.png │ └── prt.png └── views │ ├── connectiondetail.html │ ├── css │ └── base.css │ ├── drb.html │ ├── entitydetail.html │ ├── entitylist.html │ ├── js │ ├── base.js │ ├── drb_custom.js │ └── drb_requirements.js │ ├── smartmatch.html │ └── test.html ├── src ├── cliCommands │ ├── cliCommands.json │ ├── cliCommandsDataProvider.ts │ └── cliCommandsItemBase.ts ├── commands │ ├── connections.ts │ ├── registerCommands.ts │ └── registerTreeDataProviders.ts ├── extension.ts ├── helpers │ ├── cliHelper.ts │ ├── dataverseHelper.ts │ ├── drbHelper.ts │ ├── errorHandler.ts │ ├── requestHelper.ts │ ├── templateHelper.ts │ ├── typingsHelper.ts │ └── webResourceHelper.ts ├── login │ ├── login.ts │ └── server.ts ├── terminals │ ├── commands.ts │ └── console.ts ├── test │ ├── runTest.ts │ └── suite │ │ ├── extension.test.ts │ │ └── index.ts ├── tools │ ├── tools.json │ ├── toolsDataProvider.ts │ └── toolsItemBase.ts ├── trees │ ├── dataverseConnectionDataProvider.ts │ ├── entitiesDataProvider.ts │ ├── treeItemBase.ts │ └── webResourcesDataProvider.ts ├── utils │ ├── Config.ts │ ├── Constants.ts │ ├── ErrorMessages.ts │ ├── ExtensionMethods.ts │ ├── FileSystem.ts │ ├── Interfaces.ts │ ├── OpenUri.ts │ ├── Parsers.ts │ ├── Placeholders.ts │ └── State.ts └── views │ ├── ConnectionDetailsView.ts │ ├── DataverseRestBuilderView.ts │ ├── EntityDetailsView.ts │ ├── EntityListView.ts │ ├── SmartMatchView.ts │ ├── ViewBase.ts │ └── base │ ├── PanelBase.ts │ └── VsCodePanelBase.ts ├── tsconfig.json └── webpack.config.js /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "projectName": "DataverseDevTools-VSCode", 3 | "projectOwner": "Power-Maverick", 4 | "repoType": "github", 5 | "repoHost": "https://github.com", 6 | "files": ["README.md"], 7 | "imageSize": 100, 8 | "commit": true, 9 | "commitConvention": "eslint", 10 | "contributors": [ 11 | { 12 | "login": "Power-Maverick", 13 | "name": "Danish Naglekar", 14 | "avatar_url": "https://avatars.githubusercontent.com/u/36135520?v=4", 15 | "profile": "https://powermaverick.dev/", 16 | "contributions": ["question", "code", "content", "design", "doc", "infra", "security", "test", "tool", "tutorial"] 17 | }, 18 | { 19 | "login": "mohsinonxrm", 20 | "name": "mohsinonxrm", 21 | "avatar_url": "https://avatars.githubusercontent.com/u/21046804?v=4", 22 | "profile": "https://github.com/mohsinonxrm", 23 | "contributions": ["question", "bug", "code", "example", "ideas", "plugin", "research", "userTesting"] 24 | }, 25 | { 26 | "login": "JoshSmithXRM", 27 | "name": "Josh Smith", 28 | "avatar_url": "https://avatars.githubusercontent.com/u/6895577?v=4", 29 | "profile": "https://github.com/JoshSmithXRM", 30 | "contributions": ["bug", "maintenance", "userTesting"] 31 | }, 32 | { 33 | "login": "P-focT", 34 | "name": "P-focT", 35 | "avatar_url": "https://avatars.githubusercontent.com/u/81171713?v=4", 36 | "profile": "https://github.com/P-focT", 37 | "contributions": ["bug", "maintenance", "userTesting"] 38 | }, 39 | { 40 | "login": "BenediktBergmann", 41 | "name": "Benedikt Bergmann", 42 | "avatar_url": "https://avatars.githubusercontent.com/u/9703748?v=4", 43 | "profile": "https://benediktbergmann.eu/", 44 | "contributions": ["example", "ideas"] 45 | }, 46 | { 47 | "login": "filcole", 48 | "name": "Phil Cole", 49 | "avatar_url": "https://avatars.githubusercontent.com/u/6078398?v=4", 50 | "profile": "https://github.com/filcole", 51 | "contributions": ["bug", "ideas"] 52 | }, 53 | { 54 | "login": "improving-jeffd", 55 | "name": "Jeff Dodds", 56 | "avatar_url": "https://avatars.githubusercontent.com/u/1213947?v=4", 57 | "profile": "https://github.com/improving-jeffd", 58 | "contributions": ["mentoring"] 59 | }, 60 | { 61 | "login": "MattB-msft", 62 | "name": "MattB", 63 | "avatar_url": "https://avatars.githubusercontent.com/u/10568244?v=4", 64 | "profile": "http://www.powerapps.com/", 65 | "contributions": ["mentoring"] 66 | }, 67 | { 68 | "login": "thomassandsor", 69 | "name": "Thomas Sandsør", 70 | "avatar_url": "https://avatars.githubusercontent.com/u/33664322?v=4", 71 | "profile": "http://www.crmkeeper.com/", 72 | "contributions": ["bug", "ideas", "test", "userTesting"] 73 | }, 74 | { 75 | "login": "glemis", 76 | "name": "glemis", 77 | "avatar_url": "https://avatars.githubusercontent.com/u/4442368?v=4", 78 | "profile": "https://github.com/glemis", 79 | "contributions": ["bug", "code", "test", "userTesting"] 80 | }, 81 | { 82 | "login": "ericregnier", 83 | "name": "Eric Regnier", 84 | "avatar_url": "https://avatars.githubusercontent.com/u/9611006?v=4", 85 | "profile": "https://github.com/ericregnier", 86 | "contributions": ["bug"] 87 | }, 88 | { 89 | "login": "GuidoPreite", 90 | "name": "Guido Preite", 91 | "avatar_url": "https://avatars.githubusercontent.com/u/43754988?v=4", 92 | "profile": "https://www.crmanswers.net/", 93 | "contributions": ["bug", "code", "infra", "test", "maintenance", "userTesting"] 94 | }, 95 | { 96 | "login": "SatkunamSuganthar", 97 | "name": "SatkunamSuganthar", 98 | "avatar_url": "https://avatars.githubusercontent.com/u/97066265?v=4", 99 | "profile": "https://github.com/SatkunamSuganthar", 100 | "contributions": ["bug"] 101 | }, 102 | { 103 | "login": "rajyraman", 104 | "name": "Natraj Yegnaraman", 105 | "avatar_url": "https://avatars.githubusercontent.com/u/5035266?v=4", 106 | "profile": "http://dreamingincrm.com/", 107 | "contributions": ["code", "content", "design", "example", "ideas", "maintenance", "security", "userTesting", "tool"] 108 | }, 109 | { 110 | "login": "petrochuk", 111 | "name": "Andrew Petrochuk", 112 | "avatar_url": "https://avatars.githubusercontent.com/u/30735471?v=4", 113 | "profile": "https://github.com/petrochuk", 114 | "contributions": ["data", "ideas"] 115 | }, 116 | { 117 | "login": "cyco77", 118 | "name": "Lars Hildebrandt", 119 | "avatar_url": "https://avatars.githubusercontent.com/u/1198698?v=4", 120 | "profile": "https://github.com/cyco77", 121 | "contributions": ["code", "ideas", "bug", "maintenance", "test"] 122 | }, 123 | { 124 | "login": "uofirob", 125 | "name": "Rob Montague", 126 | "avatar_url": "https://avatars.githubusercontent.com/u/1754842?v=4", 127 | "profile": "https://github.com/uofirob", 128 | "contributions": ["bug", "userTesting"] 129 | }, 130 | { 131 | "login": "kkazala", 132 | "name": "Kinga", 133 | "avatar_url": "https://avatars.githubusercontent.com/u/22429087?v=4", 134 | "profile": "https://dev.to/kkazala", 135 | "contributions": ["bug", "code", "maintenance", "test", "userTesting"] 136 | } 137 | ], 138 | "contributorsPerLine": 7 139 | } 140 | 141 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "parserOptions": { 5 | "ecmaVersion": 6, 6 | "sourceType": "module" 7 | }, 8 | "plugins": ["@typescript-eslint"], 9 | "rules": { 10 | "@typescript-eslint/naming-convention": "warn", 11 | "@typescript-eslint/semi": "warn", 12 | "curly": "warn", 13 | "eqeqeq": "warn", 14 | "no-throw-literal": "warn", 15 | "semi": "off" 16 | }, 17 | "ignorePatterns": ["out", "dist", "**/*.d.ts"] 18 | } 19 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Each line is a file pattern followed by one or more owners. 2 | 3 | /src @power-maverick @BenediktBergmann @mohsinonxrm 4 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: power-maverick 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/issue-form-bug.yaml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: File a bug report 3 | title: "[Bug]: " 4 | labels: [bug, triage] 5 | assignees: 6 | - Power-Maverick 7 | body: 8 | - type: markdown 9 | attributes: 10 | value: | 11 | Thanks for taking the time to fill out this bug report! 12 | - type: textarea 13 | id: what-happened 14 | attributes: 15 | label: What happened? 16 | description: Also tell us, what did you expect to happen? 17 | placeholder: Tell us what you see! 18 | validations: 19 | required: true 20 | - type: input 21 | id: version 22 | attributes: 23 | label: What version of the tool are you using? 24 | description: You can find it under `Extension` in VSCode. 25 | validations: 26 | required: true 27 | - type: input 28 | id: vscode 29 | attributes: 30 | label: What version of Visual Studio Code are you running? 31 | description: You can find it under `Help` > `About` in VSCode. 32 | - type: input 33 | id: sessionid 34 | attributes: 35 | label: Any relevant session id from VSCode? 36 | - type: textarea 37 | id: logs 38 | attributes: 39 | label: Relevant log output 40 | description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. 41 | render: shell 42 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/issues-form-feature-request.yaml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Suggest an idea for this project 3 | title: "[Feature]: " 4 | labels: [enhancement, triage] 5 | assignees: 6 | - Power-Maverick 7 | body: 8 | - type: markdown 9 | attributes: 10 | value: | 11 | Thanks for taking the time to provide an enhancement idea for the tool! 12 | - type: dropdown 13 | id: is-problem 14 | attributes: 15 | label: Is your feature request related to a problem? 16 | options: 17 | - "Yes" 18 | - "No" 19 | validations: 20 | required: true 21 | - type: textarea 22 | id: problem 23 | attributes: 24 | label: Describe the problem? 25 | validations: 26 | required: false 27 | - type: textarea 28 | id: solution 29 | attributes: 30 | label: Provide a solution or an idea you would like to see in the tool? 31 | description: Also if you can provide any alternatives you have considered that would be great! 32 | validations: 33 | required: false 34 | - type: textarea 35 | id: logs 36 | attributes: 37 | label: Relevant log output 38 | description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. 39 | render: shell 40 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "npm" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "monthly" 12 | target-branch: "dependencies" 13 | # Specify labels for npm pull requests 14 | labels: 15 | - "dependencies" 16 | -------------------------------------------------------------------------------- /.github/reviewers.yml: -------------------------------------------------------------------------------- 1 | reviewers: 2 | # The default reviewers 3 | defaults: 4 | - repository-owners 5 | 6 | # Reviewer groups each of which has a list of GitHub usernames 7 | groups: 8 | repository-owners: 9 | - power-maverick 10 | - BenediktBergmann 11 | - mohsinonxrm 12 | 13 | options: 14 | ignore_draft: true 15 | ignored_keywords: 16 | - DO NOT REVIEW 17 | enable_group_assignment: false 18 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v2 17 | - name: Install Node.js 18 | uses: actions/setup-node@v1 19 | with: 20 | node-version: 18.x 21 | - name: Run Build 22 | run: | 23 | npm install 24 | npm ci 25 | npm run compile 26 | -------------------------------------------------------------------------------- /.github/workflows/pr_automation.yml: -------------------------------------------------------------------------------- 1 | name: 'Add Reviewer' 2 | on: 3 | pull_request: 4 | types: [opened, ready_for_review, reopened] 5 | 6 | jobs: 7 | auto-request-review: 8 | name: Auto Request Review 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Request reviewers 12 | uses: necojackarc/auto-request-review@v0.8.0 13 | with: 14 | token: ${{ secrets.GITHUB_TOKEN }} 15 | config: .github/reviewers.yml # Config file location override 16 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | push: 4 | branches: 5 | - release 6 | workflow_dispatch: 7 | 8 | jobs: 9 | deploy: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: actions/setup-node@v1 14 | with: 15 | node-version: 18.x 16 | - name: Run Build 17 | run: | 18 | npm install 19 | npm ci 20 | npm run compile 21 | - name: Publish 22 | uses: HaaLeo/publish-vscode-extension@v0 23 | with: 24 | pat: ${{ secrets.VSCE_PAT }} 25 | registryUrl: https://marketplace.visualstudio.com 26 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: 'Close stale issues and PRs' 2 | on: 3 | schedule: 4 | - cron: '30 1 * * *' 5 | 6 | jobs: 7 | stale: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/stale@v4 11 | with: 12 | stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.' 13 | stale-pr-message: 'This PR is stale because it has been open 45 days with no activity. Remove stale label or comment or this will be closed in 10 days.' 14 | close-issue-message: 'This issue was closed because it has been stalled for 5 days with no activity.' 15 | close-pr-message: 'This PR was closed because it has been stalled for 10 days with no activity.' 16 | days-before-issue-stale: 30 17 | days-before-pr-stale: 45 18 | days-before-issue-close: 5 19 | days-before-pr-close: 10 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | node_modulesout 3 | node_modules 4 | .vscode-test/ 5 | *.vsix 6 | dist -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "trailingComma": "all", 4 | "singleQuote": false, 5 | "printWidth": 200, 6 | "tabWidth": 4, 7 | "endOfLine": "auto" 8 | } -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": ["dbaeumer.vscode-eslint", "eamodio.tsl-problem-matcher"] 5 | } 6 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Run Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "args": ["--extensionDevelopmentPath=${workspaceFolder}"], 13 | "outFiles": ["${workspaceFolder}/dist/**/*.js"], 14 | "preLaunchTask": "webpack" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false // set this to true to hide the "out" folder with the compiled JS files 5 | }, 6 | "search.exclude": { 7 | "out": true // set this to false to include "out" folder in search results 8 | }, 9 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 10 | "typescript.tsc.autoDetect": "off" 11 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "compile", 9 | "problemMatcher": [], 10 | "label": "webpack", 11 | "detail": "webpack" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | .vscode-test/** 3 | node_modules 4 | out/ 5 | src/ 6 | tsconfig.json 7 | webpack.config.js 8 | .eslintrc.json 9 | .prettierrc.json 10 | assets/ 11 | .github/ 12 | .gitignore 13 | **/*.map 14 | **/*.ts 15 | **/*.md 16 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement via any channel available for communication. 63 | All complaints will be reviewed and investigated promptly and fairly. 64 | 65 | All community leaders are obligated to respect the privacy and security of the 66 | reporter of any incident. 67 | 68 | ## Enforcement Guidelines 69 | 70 | Community leaders will follow these Community Impact Guidelines in determining 71 | the consequences for any action they deem in violation of this Code of Conduct: 72 | 73 | ### 1. Correction 74 | 75 | **Community Impact**: Use of inappropriate language or other behavior deemed 76 | unprofessional or unwelcome in the community. 77 | 78 | **Consequence**: A private, written warning from community leaders, providing 79 | clarity around the nature of the violation and an explanation of why the 80 | behavior was inappropriate. A public apology may be requested. 81 | 82 | ### 2. Warning 83 | 84 | **Community Impact**: A violation through a single incident or series 85 | of actions. 86 | 87 | **Consequence**: A warning with consequences for continued behavior. No 88 | interaction with the people involved, including unsolicited interaction with 89 | those enforcing the Code of Conduct, for a specified period of time. This 90 | includes avoiding interactions in community spaces as well as external channels 91 | like social media. Violating these terms may lead to a temporary or 92 | permanent ban. 93 | 94 | ### 3. Temporary Ban 95 | 96 | **Community Impact**: A serious violation of community standards, including 97 | sustained inappropriate behavior. 98 | 99 | **Consequence**: A temporary ban from any sort of interaction or public 100 | communication with the community for a specified period of time. No public or 101 | private interaction with the people involved, including unsolicited interaction 102 | with those enforcing the Code of Conduct, is allowed during this period. 103 | Violating these terms may lead to a permanent ban. 104 | 105 | ### 4. Permanent Ban 106 | 107 | **Community Impact**: Demonstrating a pattern of violation of community 108 | standards, including sustained inappropriate behavior, harassment of an 109 | individual, or aggression toward or disparagement of classes of individuals. 110 | 111 | **Consequence**: A permanent ban from any sort of public interaction within 112 | the community. 113 | 114 | ## Attribution 115 | 116 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 117 | version 2.0, available at 118 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 119 | 120 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 121 | enforcement ladder](https://github.com/mozilla/diversity). 122 | 123 | [homepage]: https://www.contributor-covenant.org 124 | 125 | For answers to common questions about this code of conduct, see the FAQ at 126 | https://www.contributor-covenant.org/faq. Translations are available at 127 | https://www.contributor-covenant.org/translations. 128 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## How to contribute to Dataverse DevTools 2 | 3 | #### **Did you find a bug?** 4 | 5 | - **Ensure the bug was not already reported** by searching on GitHub under [Issues](https://github.com/Power-Maverick/DataverseDevTools-VSCode/issues). 6 | - If you're unable to find an open issue addressing the problem, [open a new one](https://github.com/Power-Maverick/DataverseDevTools-VSCode/issues/new). Be sure to include a **title and clear description**, as much relevant information as possible, and a **code sample** or **a demonstrating** of the expected behavior that is not occurring. 7 | 8 | #### **Did you wrote a patch that fixes a bug?** 9 | 10 | - Open a new GitHub pull request with the patch. 11 | - Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable. 12 | - Before submitting, please read the [Code of Conduct](https://github.com/Power-Maverick/DataverseDevTools-VSCode/blob/main/CODE_OF_CONDUCT.md). 13 | 14 | #### **Do you intend to add a new feature or change an existing one?** 15 | 16 | - Create a [discussion](https://github.com/Power-Maverick/DataverseDevTools-VSCode/discussions/new?category=show-and-tell) under **Show and tell** category. 17 | - Fork this repo and write code. You can find the guidelines [below](#guidelines). 18 | - Once you have gather a positive feedback about the change you are welcomed to create a PR with your changes. 19 | - Do reference the discussion number/link in the PR so that the reviewer can confirm. 20 | - If you failed to reference the discussion number/link then the PR might get rejected. 21 | 22 | #### **Do you have questions about the source code?** 23 | 24 | - Ask any question about how to use Dataverse DevTools can be discussed on the [Discussion](https://github.com/Power-Maverick/DataverseDevTools-VSCode/discussions) tab. 25 | 26 | ## Guidelines 27 | 28 | ### Registering a new command 29 | 30 | When adding a new command first list it in the `package.json` file under **"contributes"** > **"commands"** and make sure the category is properly listed. You can find the available categories in `package.nls.json` file and the name would end in **category**. Also make sure the `when` condition on the command is correctly specified. 31 | 32 | Once you add the command name in the package.json file, register the command under **src** > **commands** > **registerCommands.ts**. The callback function needs to invoke one of the helper classes. 33 | 34 | ### Choosing the right helper class to add your function 35 | 36 | #### CLI Helper 37 | 38 | This helper class is to work with Power Platform CLI. If you plan on using the PP CLI then write your function in this class file. All commands for CLI will be listed under **terminals** > **commands.ts**. 39 | 40 | #### Dataverse Helper 41 | 42 | This class is when you would like to interact with Dataverse API; e.g. fetch all solutions, update plugin assembly, etc. will go in here. Do not write the request function here but utilize already built **requestHelper.ts** file. 43 | 44 | #### Request Helper 45 | 46 | This is a generic request function class that will help you fetch the data or create or update the data either from Dataverse or any other system (in future). 47 | 48 | #### Template Helper 49 | 50 | This will contain the code to initialize any of the project templates, like TypeScript, etc. 51 | 52 | #### Typings Helper 53 | 54 | In this class you can find the code that will help in creating the type definition classes for selected entities. 55 | 56 | #### Upload Helper 57 | 58 | As the name suggests, this class will contain functions to upload different Dataverse components using Web APIs. 59 | 60 | ### Want to execute a command? 61 | 62 | To execute a command, first make sure your command is listed under **terminals** > **commands.ts**. Once you have the command listed in this file, head over to one of your helper functions and use the following code: 63 | 64 | ```TypeScript 65 | let commands: string[] = Array(); 66 | commands.push(Commands.YourCommandOne()); 67 | commands.push(Commands.YourCommandTwo()); 68 | Console.runCommand(commands); 69 | ``` 70 | 71 | ### Want to add a new webview to the Dataverse DevTools? 72 | 73 | Create a new TS file under **views** folder. Add a class in this file that extends **Panel** class. Finally add `getHtmlForWebview` method. You can add additional parameters to the constructors. 74 | 75 | For more information on Webview, read [here](https://code.visualstudio.com/api/extension-guides/webview). 76 | 77 | ### Want to add a new tree to the Dataverse DevTools explorer? 78 | 79 | First in `package.json` add the view details under **"contributes"** > **"views"** > **"dvd-explorer"**. Create a Tree Data Provider file under **trees** folder. This file will consist of two classes one will be the **TreeItem** class that will extend **TreeItemBase** class. Second class will be the actual **DataProvider** class that will implement **vscode.TreeDataProvider** with **TreeItem** type defined earlier. 80 | 81 | For e.g. 82 | If the TreeItem class that extends TreeItemBase is named MyTreeItem, then DataProvider will implement `vscode.TreeDataProvider`. 83 | 84 | For more information on Treeview, read [here](https://code.visualstudio.com/api/extension-guides/tree-view). 85 | 86 | --- 87 | 88 | Dataverse DevTools is a volunteer effort. We encourage you to pitch in and join the team! 89 | 90 | Thanks! ♥ ♥ ♥ 91 | 92 | Power Maverick 93 | -------------------------------------------------------------------------------- /CodeFlowResult/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Dataverse Account - Sign In 7 | 8 | 9 | 10 | 11 | Visual Studio Code 12 |
13 |
You are signed in now and can close this page.
14 |
15 | An error occurred while signing in: 16 |
17 |
18 |
19 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Danish Naglekar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /assets/AddNewTSFile.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Power-Maverick/DataverseDevTools-VSCode/0cf846e3fe21fbdb2c4e42e9acfcb7a6ebb454d8/assets/AddNewTSFile.gif -------------------------------------------------------------------------------- /assets/Connection&EntityDetails.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Power-Maverick/DataverseDevTools-VSCode/0cf846e3fe21fbdb2c4e42e9acfcb7a6ebb454d8/assets/Connection&EntityDetails.gif -------------------------------------------------------------------------------- /assets/Create&Connect.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Power-Maverick/DataverseDevTools-VSCode/0cf846e3fe21fbdb2c4e42e9acfcb7a6ebb454d8/assets/Create&Connect.gif -------------------------------------------------------------------------------- /assets/FilterEntitiesBySolution.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Power-Maverick/DataverseDevTools-VSCode/0cf846e3fe21fbdb2c4e42e9acfcb7a6ebb454d8/assets/FilterEntitiesBySolution.gif -------------------------------------------------------------------------------- /assets/FilterWRBySolution.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Power-Maverick/DataverseDevTools-VSCode/0cf846e3fe21fbdb2c4e42e9acfcb7a6ebb454d8/assets/FilterWRBySolution.gif -------------------------------------------------------------------------------- /assets/GenerateTypings.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Power-Maverick/DataverseDevTools-VSCode/0cf846e3fe21fbdb2c4e42e9acfcb7a6ebb454d8/assets/GenerateTypings.gif -------------------------------------------------------------------------------- /assets/IntellisenseForTypeScript.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Power-Maverick/DataverseDevTools-VSCode/0cf846e3fe21fbdb2c4e42e9acfcb7a6ebb454d8/assets/IntellisenseForTypeScript.gif -------------------------------------------------------------------------------- /assets/PacCliCommands.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Power-Maverick/DataverseDevTools-VSCode/0cf846e3fe21fbdb2c4e42e9acfcb7a6ebb454d8/assets/PacCliCommands.gif -------------------------------------------------------------------------------- /assets/PacCliModelBuilder.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Power-Maverick/DataverseDevTools-VSCode/0cf846e3fe21fbdb2c4e42e9acfcb7a6ebb454d8/assets/PacCliModelBuilder.gif -------------------------------------------------------------------------------- /assets/RememberConnection.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Power-Maverick/DataverseDevTools-VSCode/0cf846e3fe21fbdb2c4e42e9acfcb7a6ebb454d8/assets/RememberConnection.gif -------------------------------------------------------------------------------- /assets/SmartMatchScreen.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Power-Maverick/DataverseDevTools-VSCode/0cf846e3fe21fbdb2c4e42e9acfcb7a6ebb454d8/assets/SmartMatchScreen.gif -------------------------------------------------------------------------------- /assets/TypeScriptInitialization.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Power-Maverick/DataverseDevTools-VSCode/0cf846e3fe21fbdb2c4e42e9acfcb7a6ebb454d8/assets/TypeScriptInitialization.gif -------------------------------------------------------------------------------- /assets/WebResourceUpload.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Power-Maverick/DataverseDevTools-VSCode/0cf846e3fe21fbdb2c4e42e9acfcb7a6ebb454d8/assets/WebResourceUpload.gif -------------------------------------------------------------------------------- /package.nls.json: -------------------------------------------------------------------------------- 1 | { 2 | "dvdt.commands.category": "Dataverse DevTools", 3 | "dvdt.explorer.category": "Explorer", 4 | "dvdt.menus.group.ts": "dvdevtools-ts", 5 | "dvdt.menus.group.wr": "dvdevtools-wr", 6 | "dvdt.config.title": "Dataverse DevTools", 7 | "dvdt.config.enablePreview": "Enable a preview features for Dataverse DevTools. See [documentation for all available preview features](https://github.com/Power-Maverick/DataverseDevTools-VSCode#-early-access-preview).\n\n> _Note: these features are not fully tested; so if you encounter any bugs please report them on GitHub (your name will appear on the contributors list)._", 8 | "dvdt.config.defaultTSTemplate": "Specifies how Dataverse DevTools should behave when you initiate a TypeScript project. This will skip the extra question during TS project initialization when you either select `Plain TypeScript` or `Webpack`" 9 | } 10 | -------------------------------------------------------------------------------- /resources/DVDT-Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Power-Maverick/DataverseDevTools-VSCode/0cf846e3fe21fbdb2c4e42e9acfcb7a6ebb454d8/resources/DVDT-Icon.png -------------------------------------------------------------------------------- /resources/dark/cli.svg: -------------------------------------------------------------------------------- 1 | 3 | 6 | -------------------------------------------------------------------------------- /resources/dark/column.svg: -------------------------------------------------------------------------------- 1 | 4 | 7 | 8 | -------------------------------------------------------------------------------- /resources/dark/connect.svg: -------------------------------------------------------------------------------- 1 | 4 | 5 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /resources/dark/connection-details.svg: -------------------------------------------------------------------------------- 1 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /resources/dark/css.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/dataverse-off.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 15 | 16 | 17 | 18 | 20 | 21 | 22 | 23 | 25 | 26 | 27 | 28 | 29 | 30 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 48 | 49 | 50 | 53 | 56 | 59 | 61 | 62 | 65 | 66 | 67 | 70 | 71 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /resources/dark/dataverse.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /resources/dark/delete.svg: -------------------------------------------------------------------------------- 1 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /resources/dark/file-green.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/file-red.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/filter-off.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/filter-on.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/folder.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | 6 | 8 | 9 | -------------------------------------------------------------------------------- /resources/dark/generic.svg: -------------------------------------------------------------------------------- 1 | 4 | 7 | 8 | -------------------------------------------------------------------------------- /resources/dark/html.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/js.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/light-bulb.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/play.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/popout.svg: -------------------------------------------------------------------------------- 1 | 4 | 7 | 8 | -------------------------------------------------------------------------------- /resources/dark/powerapps.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/refresh.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/search-remove.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/search.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/table-light.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/dark/table.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/ddt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Power-Maverick/DataverseDevTools-VSCode/0cf846e3fe21fbdb2c4e42e9acfcb7a6ebb454d8/resources/ddt.png -------------------------------------------------------------------------------- /resources/light/cli.svg: -------------------------------------------------------------------------------- 1 | 3 | 6 | -------------------------------------------------------------------------------- /resources/light/column.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/connect.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/connection-details.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/css.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/dataverse-off.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 15 | 16 | 17 | 18 | 20 | 21 | 22 | 23 | 25 | 26 | 27 | 28 | 29 | 30 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 48 | 49 | 50 | 53 | 56 | 59 | 61 | 62 | 65 | 66 | 67 | 70 | 71 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /resources/light/dataverse.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /resources/light/delete.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/file-green.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/file-red.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/filter-off.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/filter-on.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/folder.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | 6 | 8 | 9 | -------------------------------------------------------------------------------- /resources/light/generic.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/html.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/js.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/light-bulb.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/play.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/popout.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/powerapps.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/refresh.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/search-remove.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/search.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/table-light.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/light/table.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/templates/Files/dvts-js.txt: -------------------------------------------------------------------------------- 1 | // Usage Example: FILENAME.onLoad 2 | 3 | export var FILENAME = (function () { 4 | ("use strict"); 5 | var formContext; 6 | 7 | return { 8 | onLoad: function (executionContext) { 9 | formContext = executionContext.getFormContext(); 10 | //formContext.getAttribute("accountnumber").getValue(); 11 | }, 12 | }; 13 | })(); -------------------------------------------------------------------------------- /resources/templates/Files/dvts-ts.txt: -------------------------------------------------------------------------------- 1 | // Usage Example: FILENAME.onLoad 2 | 3 | export class FILENAME { 4 | onLoad(executionContext: Xrm.Events.EventContext) { 5 | /* Use typings to create entity specific formContext. Like shown below is for 'Account' */ 6 | //const formContext: Xrm.Account = executionContext.getFormContext(); 7 | //formContext.getAttribute("accountnumber").getValue(); 8 | } 9 | } -------------------------------------------------------------------------------- /resources/templates/Files/dvts-wp.txt: -------------------------------------------------------------------------------- 1 | // Usage Example: NAMESPACE.FILENAME.onLoad 2 | 3 | export const onLoad = (executionContext: Xrm.Events.EventContext): void => { 4 | /* Use typings to create entity specific formContext. Like shown below is for 'Account' */ 5 | //const formContext: Xrm.Account = executionContext.getFormContext(); 6 | //formContext.getAttribute("accountnumber").getValue(); 7 | }; -------------------------------------------------------------------------------- /resources/templates/JavaScript/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "plugins": ["prettier"], 4 | "extends": ["eslint:recommended", "plugin:prettier/recommended", "prettier", "prettier/@typescript-eslint"], 5 | "rules": { 6 | "prettier/prettier": "error", 7 | "curly": "warn", 8 | "eqeqeq": "warn", 9 | "no-throw-literal": "warn", 10 | "semi": "off" 11 | }, 12 | "ignorePatterns": ["out", "dist", "node_modules"] 13 | } 14 | -------------------------------------------------------------------------------- /resources/templates/JavaScript/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "trailingComma": "all", 4 | "singleQuote": false, 5 | "printWidth": 200, 6 | "tabWidth": 4, 7 | "endOfLine": "auto" 8 | } 9 | -------------------------------------------------------------------------------- /resources/templates/JavaScript/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES5", 4 | "module": "CommonJS", 5 | "moduleResolution": "node", 6 | }, 7 | "include": ["./src/**/*"] 8 | } 9 | 10 | -------------------------------------------------------------------------------- /resources/templates/JavaScript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "javascript-dataverse-project", 3 | "version": "1.0.0", 4 | "description": "", 5 | "scripts": { 6 | "build-dev": "webpack --config webpack.config.dev.js", 7 | "build": "webpack --config webpack.config.prod.js", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "Dataverse DevTools", 11 | "devDependencies": { 12 | "@types/node": "^16.4.11", 13 | "@types/xrm": "^9.0.41", 14 | "eslint": "^7.32.0", 15 | "eslint-config-prettier": "^8.3.0", 16 | "eslint-plugin-prettier": "^4.0.0", 17 | "prettier": "2.3.2", 18 | "clean-webpack-plugin": "^3.0.0", 19 | "webpack": "^5.58.1", 20 | "webpack-cli": "^4.9.1", 21 | "webpack-merge": "^5.8.0", 22 | "terser-webpack-plugin": "^5.2.5" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /resources/templates/JavaScript/webpack.config.dev.js: -------------------------------------------------------------------------------- 1 | const { merge } = require("webpack-merge"); 2 | const sharedConfig = require("./webpack.config"); 3 | module.exports = merge(sharedConfig, { 4 | mode: "development", 5 | optimization: { 6 | minimize: false, 7 | }, 8 | }); -------------------------------------------------------------------------------- /resources/templates/JavaScript/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const { CleanWebpackPlugin } = require("clean-webpack-plugin"); 3 | module.exports = { 4 | devtool: "source-map", 5 | entry: { 6 | }, 7 | output: { 8 | filename: "[name].js", 9 | sourceMapFilename: "maps/[name].js.map", 10 | path: path.resolve(__dirname, "./Webresources/scripts"), 11 | library: ["NAMESPACE"], 12 | libraryTarget: "var", 13 | }, 14 | module: { 15 | rules: [ 16 | { 17 | test: /\.(ts|tsx)$/, 18 | use: "ts-loader", 19 | exclude: /node_modules/, 20 | }, 21 | ], 22 | }, 23 | plugins: [new CleanWebpackPlugin()], 24 | resolve: { 25 | extensions: [".ts", ".js"], 26 | }, 27 | }; 28 | 29 | -------------------------------------------------------------------------------- /resources/templates/JavaScript/webpack.config.prod.js: -------------------------------------------------------------------------------- 1 | const { merge } = require("webpack-merge"); 2 | const TerserPlugin = require("terser-webpack-plugin"); 3 | const sharedConfig = require("./webpack.config"); 4 | module.exports = merge(sharedConfig, { 5 | mode: "production", 6 | optimization: { 7 | minimize: true, 8 | minimizer: [ 9 | new TerserPlugin({ 10 | terserOptions: { 11 | compress: { 12 | // Power Apps Solution Checker will complain if strict equality operators 13 | // are optimised, rule: web-use-strict-equality-operators 14 | // see https://docs.microsoft.com/en-us/powerapps/maker/data-platform/use-powerapps-checker#best-practice-rules-used-by-solution-checker 15 | comparisons: false 16 | } 17 | } 18 | }) 19 | ] 20 | } 21 | }); 22 | -------------------------------------------------------------------------------- /resources/templates/TypeScript/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "plugins": ["@typescript-eslint", "prettier"], 5 | "extends": [ 6 | "eslint:recommended", 7 | "plugin:@typescript-eslint/eslint-recommended", 8 | "plugin:@typescript-eslint/recommended", 9 | "plugin:prettier/recommended", 10 | "prettier", 11 | "prettier/@typescript-eslint" 12 | ], 13 | "parserOptions": { 14 | "project": "./tsconfig.json" 15 | }, 16 | "rules": { 17 | "@typescript-eslint/naming-convention": "warn", 18 | "@typescript-eslint/semi": "warn", 19 | "prettier/prettier": "error", 20 | "curly": "warn", 21 | "eqeqeq": "warn", 22 | "no-throw-literal": "warn", 23 | "semi": "off" 24 | }, 25 | "ignorePatterns": ["out", "dist", "**/*.d.ts", "WebResources", "node_modules"] 26 | } 27 | -------------------------------------------------------------------------------- /resources/templates/TypeScript/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "trailingComma": "all", 4 | "singleQuote": false, 5 | "printWidth": 200, 6 | "tabWidth": 4, 7 | "endOfLine": "auto" 8 | } 9 | -------------------------------------------------------------------------------- /resources/templates/TypeScript/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript-dataverse-project", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/node": { 8 | "version": "16.6.1", 9 | "resolved": "https://registry.npmjs.org/@types/node/-/node-16.6.1.tgz", 10 | "integrity": "sha512-Sr7BhXEAer9xyGuCN3Ek9eg9xPviCF2gfu9kTfuU2HkTVAMYSDeX40fvpmo72n5nansg3nsBjuQBrsS28r+NUw==", 11 | "dev": true 12 | }, 13 | "@types/xrm": { 14 | "version": "9.0.41", 15 | "resolved": "https://registry.npmjs.org/@types/xrm/-/xrm-9.0.41.tgz", 16 | "integrity": "sha512-XJBs+kEZdwiVB0/xdxAUAg7dVCGfeJ98hWlNaBklHnOgZKGh2nnzaz6s30YnFa7Q41CXU5EOKEK+vCDTlR2Gdg==", 17 | "dev": true 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /resources/templates/TypeScript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript-dataverse-project", 3 | "version": "1.0.0", 4 | "description": "", 5 | "scripts": { 6 | "build": "tsc", 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Dataverse DevTools", 10 | "devDependencies": { 11 | "@types/node": "^16.4.11", 12 | "@types/xrm": "^9.0.41", 13 | "@typescript-eslint/eslint-plugin": "^4.30.0", 14 | "@typescript-eslint/parser": "^4.30.0", 15 | "eslint": "^7.32.0", 16 | "eslint-config-prettier": "^8.3.0", 17 | "eslint-plugin-prettier": "^4.0.0", 18 | "prettier": "2.3.2", 19 | "ts-loader": "^9.2.6", 20 | "typescript": "^4.1.2" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /resources/templates/TypeScript/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": true, 3 | "compilerOptions": { 4 | "target": "ES5", 5 | "module": "CommonJS", 6 | "outDir": "./WebResources/js", 7 | "sourceMap": true, 8 | "strict": true, 9 | "moduleResolution": "node", 10 | "removeComments": false, 11 | "allowJs": true, 12 | "resolveJsonModule": true 13 | /* Additional Checks */ 14 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 15 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 16 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 17 | }, 18 | "include": ["./typings/**/*", "./src/**/*"] 19 | } 20 | 21 | -------------------------------------------------------------------------------- /resources/templates/Webpack/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript-dataverse-project", 3 | "version": "1.0.0", 4 | "description": "", 5 | "scripts": { 6 | "build-dev": "webpack --config webpack.config.dev.js", 7 | "build": "webpack --config webpack.config.prod.js", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "Dataverse DevTools", 11 | "devDependencies": { 12 | "@types/node": "^16.4.11", 13 | "@types/xrm": "^9.0.41", 14 | "@typescript-eslint/eslint-plugin": "^4.30.0", 15 | "@typescript-eslint/parser": "^4.30.0", 16 | "clean-webpack-plugin": "^3.0.0", 17 | "eslint": "^7.32.0", 18 | "eslint-config-prettier": "^8.3.0", 19 | "eslint-plugin-prettier": "^4.0.0", 20 | "prettier": "2.3.2", 21 | "ts-loader": "^9.2.6", 22 | "typescript": "^4.1.2", 23 | "webpack": "^5.58.1", 24 | "webpack-cli": "^4.9.1", 25 | "webpack-merge": "^5.8.0", 26 | "terser-webpack-plugin": "^5.2.5" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /resources/templates/Webpack/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": true, 3 | "compilerOptions": { 4 | "target": "ES6", 5 | "module": "CommonJS", 6 | "outDir": "./WebResources/js", 7 | "sourceMap": true, 8 | "strict": true, 9 | "moduleResolution": "node", 10 | "removeComments": false, 11 | "allowJs": true, 12 | "resolveJsonModule": true 13 | /* Additional Checks */ 14 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 15 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 16 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 17 | }, 18 | "include": ["./typings/**/*", "./src/**/*"] 19 | } 20 | 21 | -------------------------------------------------------------------------------- /resources/templates/Webpack/webpack.config.dev.js: -------------------------------------------------------------------------------- 1 | const { merge } = require("webpack-merge"); 2 | const sharedConfig = require("./webpack.config"); 3 | module.exports = merge(sharedConfig, { 4 | mode: "development", 5 | devtool: "source-map", 6 | optimization: { 7 | minimize: false, 8 | }, 9 | }); -------------------------------------------------------------------------------- /resources/templates/Webpack/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const { CleanWebpackPlugin } = require("clean-webpack-plugin"); 3 | module.exports = { 4 | entry: { 5 | }, 6 | output: { 7 | filename: "[name].js", 8 | sourceMapFilename: "maps/[name].js.map", 9 | path: path.resolve(__dirname, "./Webresources/scripts"), 10 | library: ["NAMESPACE", '[name]'], 11 | libraryTarget: "var", 12 | }, 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.(ts|tsx)$/, 17 | use: "ts-loader", 18 | exclude: /node_modules/, 19 | }, 20 | ], 21 | }, 22 | plugins: [new CleanWebpackPlugin()], 23 | resolve: { 24 | extensions: [".ts", ".js"], 25 | }, 26 | }; 27 | 28 | -------------------------------------------------------------------------------- /resources/templates/Webpack/webpack.config.prod.js: -------------------------------------------------------------------------------- 1 | const { merge } = require("webpack-merge"); 2 | const TerserPlugin = require("terser-webpack-plugin"); 3 | const sharedConfig = require("./webpack.config"); 4 | module.exports = merge(sharedConfig, { 5 | mode: "production", 6 | optimization: { 7 | minimize: true, 8 | minimizer: [ 9 | new TerserPlugin({ 10 | terserOptions: { 11 | compress: { 12 | // Power Apps Solution Checker will complain if strict equality operators 13 | // are optimised, rule: web-use-strict-equality-operators 14 | // see https://docs.microsoft.com/en-us/powerapps/maker/data-platform/use-powerapps-checker#best-practice-rules-used-by-solution-checker 15 | comparisons: false 16 | } 17 | } 18 | }) 19 | ] 20 | } 21 | }); 22 | -------------------------------------------------------------------------------- /resources/templates/dvdt.linker.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/toolIcons/cmt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Power-Maverick/DataverseDevTools-VSCode/0cf846e3fe21fbdb2c4e42e9acfcb7a6ebb454d8/resources/toolIcons/cmt.png -------------------------------------------------------------------------------- /resources/toolIcons/drb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Power-Maverick/DataverseDevTools-VSCode/0cf846e3fe21fbdb2c4e42e9acfcb7a6ebb454d8/resources/toolIcons/drb.png -------------------------------------------------------------------------------- /resources/toolIcons/pd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Power-Maverick/DataverseDevTools-VSCode/0cf846e3fe21fbdb2c4e42e9acfcb7a6ebb454d8/resources/toolIcons/pd.png -------------------------------------------------------------------------------- /resources/toolIcons/prt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Power-Maverick/DataverseDevTools-VSCode/0cf846e3fe21fbdb2c4e42e9acfcb7a6ebb454d8/resources/toolIcons/prt.png -------------------------------------------------------------------------------- /resources/views/connectiondetail.html: -------------------------------------------------------------------------------- 1 |

!!{connName}

2 |

Connection Type: !!{connType}

3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 |
33 | -------------------------------------------------------------------------------- /resources/views/drb.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | 10 | -------------------------------------------------------------------------------- /resources/views/entitydetail.html: -------------------------------------------------------------------------------- 1 |

!!{entityLogicalName}

2 |

Schema Name: !!{entitySchemaName}

3 |
4 | 5 |
Description: !!{description}
6 | 7 |

Entity Details

8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |
41 | 42 |

Attributes

43 | 44 |
45 | 46 |
47 | 48 | 49 | 56 | 63 | 70 | 77 | 84 | 91 | 98 | 99 | 100 | !!{attributes} 101 | 102 |
50 | Id 51 |
52 | 53 | 54 |
55 |
57 | Logical Name 58 |
59 | 60 | 61 |
62 |
64 | Schema Name 65 |
66 | 67 | 68 |
69 |
71 | Type 72 |
73 | 74 | 75 |
76 |
78 | Attribute Type Name 79 |
80 | 81 | 82 |
83 |
85 | Attribute Of 86 |
87 | 88 | 89 |
90 |
92 | Required Level 93 |
94 | 95 | 96 |
97 |
103 |
104 | 105 |
106 |
107 | -------------------------------------------------------------------------------- /resources/views/entitylist.html: -------------------------------------------------------------------------------- 1 | 5 |

6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 20 | 27 | 34 | 41 | 48 | 55 | 62 | 69 | 76 | 83 | 90 | 97 | 104 | 111 | 118 | 125 | 132 | 133 | 134 | !!{entities} 135 | 136 |
14 | Id 15 |
16 | 17 | 18 |
19 |
21 | Object Type Code 22 |
23 | 24 | 25 |
26 |
28 | Logical Name 29 |
30 | 31 | 32 |
33 |
35 | Schema Name 36 |
37 | 38 | 39 |
40 |
42 | Description 43 |
44 | 45 | 46 |
47 |
49 | Table Type 50 |
51 | 52 | 53 |
54 |
56 | Has Activities 57 |
58 | 59 | 60 |
61 |
63 | Has Notes 64 |
65 | 66 | 67 |
68 |
70 | Is Activity 71 |
72 | 73 | 74 |
75 |
77 | Is Activity Party 78 |
79 | 80 | 81 |
82 |
84 | Is Audit Enabled 85 |
86 | 87 | 88 |
89 |
91 | Is Custom Entity 92 |
93 | 94 | 95 |
96 |
98 | Is Customizable 99 |
100 | 101 | 102 |
103 |
105 | Is Document Management Enabled 106 |
107 | 108 | 109 |
110 |
112 | Is Duplicate Detection Enabled 113 |
114 | 115 | 116 |
117 |
119 | Is Managed 120 |
121 | 122 | 123 |
124 |
126 | Ownership Type 127 |
128 | 129 | 130 |
131 |
137 | -------------------------------------------------------------------------------- /resources/views/js/base.js: -------------------------------------------------------------------------------- 1 | const vscode = acquireVsCodeApi(); 2 | 3 | var $matchTable = $("#matchTable"); 4 | 5 | $(document).ready(function () { 6 | $("#attrSearch").on("keyup", function () { 7 | var value = $(this).val().toLowerCase(); 8 | $("#attrTable tr").filter(function () { 9 | $(this).toggle($(this).text().toLowerCase().indexOf(value) > -1); 10 | }); 11 | }); 12 | 13 | $("#entitiesSearch").on("keyup", function () { 14 | var value = $(this).val().toLowerCase(); 15 | $("#entTable tr").filter(function () { 16 | $(this).toggle($(this).text().toLowerCase().indexOf(value) > -1); 17 | }); 18 | }); 19 | 20 | if ($matchTable && $matchTable.bootstrapTable) { 21 | $matchTable.bootstrapTable(); 22 | $matchTable.bootstrapTable("refreshOptions", { 23 | classes: "table table-bordered", 24 | }); 25 | } 26 | }); 27 | 28 | document.addEventListener("DOMContentLoaded", function () { 29 | var elems = document.querySelectorAll(".fixed-action-btn"); 30 | // var instances = M.FloatingActionButton.init(elems, { 31 | // direction: "left", 32 | // }); 33 | }); 34 | 35 | function copyToClipboard(event, id) { 36 | event.preventDefault(); // Prevents the default behavior 37 | 38 | var copyText = document.getElementById(id); 39 | 40 | navigator.clipboard.writeText(copyText.value); 41 | 42 | vscode.postMessage({ 43 | command: "showInfo", 44 | text: "Copied to clipboard", 45 | }); 46 | } 47 | 48 | function linkSpecific(cScore) { 49 | vscode.postMessage({ 50 | command: "link", 51 | value: cScore, 52 | }); 53 | } 54 | 55 | function linkAll() { 56 | vscode.postMessage({ 57 | command: "link", 58 | value: "all", 59 | }); 60 | } 61 | 62 | function uploadAll() { 63 | vscode.postMessage({ 64 | command: "upload", 65 | value: "all", 66 | }); 67 | } 68 | 69 | function upload(wrId) { 70 | vscode.postMessage({ 71 | command: "upload", 72 | value: wrId, 73 | }); 74 | } 75 | 76 | function link(fullPath, wrId) { 77 | vscode.postMessage({ 78 | command: "link", 79 | value: `{ "fp": "${fullPath}", "id": "${wrId}" }`, 80 | }); 81 | } 82 | 83 | function sortTable(tableId, column) { 84 | var table = document.getElementById(tableId); 85 | var tbody = table.querySelector("tbody"); 86 | var rows = Array.from(tbody.rows); 87 | var sortOrder = 1; 88 | var headerRow = table.querySelector("thead tr"); 89 | var sortIcons = headerRow.querySelectorAll(".sort-icon"); 90 | 91 | if (headerRow.cells[column].classList.contains("sorted")) { 92 | sortOrder = headerRow.cells[column].classList.contains("asc") ? -1 : 1; 93 | headerRow.cells[column].classList.toggle("asc"); 94 | headerRow.cells[column].classList.toggle("desc"); 95 | } else { 96 | var sortedCells = table.querySelectorAll(".sorted"); 97 | sortedCells.forEach(function (cell) { 98 | cell.classList.remove("sorted", "asc", "desc"); 99 | }); 100 | 101 | headerRow.cells[column].classList.add("sorted", "asc"); 102 | } 103 | 104 | sortIcons.forEach(function (sortIcon, index) { 105 | if (index === column) { 106 | sortIcon.classList.remove("asc", "desc"); 107 | sortIcon.classList.add(sortOrder === 1 ? "asc" : "desc"); 108 | } else { 109 | sortIcon.classList.remove("asc", "desc"); 110 | } 111 | }); 112 | 113 | rows.sort(function (a, b) { 114 | var cellA = a.cells[column].textContent.toLowerCase(); 115 | var cellB = b.cells[column].textContent.toLowerCase(); 116 | if (cellA < cellB) return -sortOrder; 117 | if (cellA > cellB) return sortOrder; 118 | return 0; 119 | }); 120 | 121 | rows.forEach(function (row) { 122 | tbody.appendChild(row); 123 | }); 124 | } 125 | -------------------------------------------------------------------------------- /resources/views/smartmatch.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 7 | 8 |
9 | 10 | 20 | 21 |
22 |
23 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | !!{matches} 56 | 57 |
LocalServerMappingAction
Local File NameLocal PathDisplay NameServer PathWeb Resource IdConfidenceStatus
58 |
59 | 60 |
61 |
62 |
Confidence Levels
63 | 64 |
65 |
66 |
    67 |
  1. 68 |
    69 |
    Confidence Level - 90
    70 | Matched on exact Local File Name & Server Display Name, plus matched on the content of the files. 71 |
    72 |
  2. 73 |
  3. 74 |
    75 |
    Confidence Level - 80
    76 | Matched on Local File Name in Server Path, plus matched on the content of the files. 77 |
    78 |
  4. 79 |
  5. 80 |
    81 |
    Confidence Level - 75
    82 | Matched on exact Local File Name & Server Display Name. 83 |
    84 |
  6. 85 |
  7. 86 |
    87 |
    Confidence Level - 60
    88 | Matched on Local File Name in Server Path. 89 |
    90 |
  8. 91 |
92 |
93 |
94 | 95 | 102 | -------------------------------------------------------------------------------- /resources/views/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 28 |
29 |

The above button will only link the files locally but won't upload anything to Dataverse.

30 | 38 |
39 |
40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 61 | 65 | 66 |
Web Resource IdDisplay NameLocal File NameServer PathLocal PathConfidence LevelLinked StatusAction
TestTestTestTestTest100 59 | check_circle 60 | 62 | 63 | Upload 64 |
67 |
68 |
69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /src/cliCommands/cliCommandsDataProvider.ts: -------------------------------------------------------------------------------- 1 | import * as path from "path"; 2 | import * as vscode from "vscode"; 3 | import { ICliCommandList, ICliCommandVerb } from "../utils/Interfaces"; 4 | import cliInJson from "./cliCommands.json"; 5 | import { CliCommandItemBase } from "./cliCommandsItemBase"; 6 | 7 | export class CliCommandDataProvider implements vscode.TreeDataProvider { 8 | private cliCommands: ICliCommandList | undefined; 9 | 10 | constructor(private vscontext: vscode.ExtensionContext) { 11 | this.cliCommands = cliInJson; 12 | } 13 | 14 | getTreeItem(element: CliCommandTreeItem): vscode.TreeItem | Thenable { 15 | return element; 16 | } 17 | 18 | getChildren(element?: CliCommandTreeItem): vscode.ProviderResult { 19 | //if (config.get("enableEarlyAccessPreview")) { 20 | if (element) { 21 | let cliCommandTree: CliCommandTreeItem[] = []; 22 | const cliExpand = this.cliCommands?.commands.find((c) => c.name === element.label); 23 | if (cliExpand) { 24 | cliExpand.verbs?.map((verb) => { 25 | cliCommandTree.push(new CliCommandTreeItem(verb.name, verb.help, vscode.TreeItemCollapsibleState.None, 2, cliExpand.name, verb)); 26 | }); 27 | } 28 | 29 | return Promise.resolve(cliCommandTree); 30 | } else { 31 | let parentTree: CliCommandTreeItem[] = []; 32 | this.cliCommands?.commands.map((t) => { 33 | if (t.name) { 34 | parentTree.push(new CliCommandTreeItem(t.name, t.help, vscode.TreeItemCollapsibleState.Collapsed, 1, undefined, undefined)); 35 | } 36 | }); 37 | return Promise.resolve(parentTree); 38 | } 39 | //} 40 | } 41 | 42 | //#endregion 43 | } 44 | 45 | export class CliCommandTreeItem extends CliCommandItemBase { 46 | constructor( 47 | public readonly cmdName: string, 48 | public readonly group: string | undefined, 49 | public readonly collapsibleState: vscode.TreeItemCollapsibleState, 50 | public readonly level: number, 51 | public readonly cmd: string | undefined, 52 | public readonly cmdVerb: ICliCommandVerb | undefined, 53 | ) { 54 | super(cmdName, group, collapsibleState); 55 | } 56 | 57 | iconPath = { 58 | light: vscode.Uri.file(path.join(__filename, "..", "resources", "light", this.level === 1 ? "folder.svg" : this.level === 2 ? "cli.svg" : "generic.svg")), 59 | dark: vscode.Uri.file(path.join(__filename, "..", "resources", "dark", this.level === 1 ? "folder.svg" : this.level === 2 ? "cli.svg" : "generic.svg")), 60 | }; 61 | 62 | contextValue = this.level === 2 ? "cli-command" : "cli-group"; 63 | } 64 | -------------------------------------------------------------------------------- /src/cliCommands/cliCommandsItemBase.ts: -------------------------------------------------------------------------------- 1 | import * as path from "path"; 2 | import * as vscode from "vscode"; 3 | 4 | export class CliCommandItemBase extends vscode.TreeItem { 5 | constructor(public readonly label: string, public readonly desc: string | undefined, public readonly collapsibleState: vscode.TreeItemCollapsibleState) { 6 | super(label, collapsibleState); 7 | 8 | this.tooltip = this.label; 9 | this.description = this.desc; 10 | } 11 | 12 | iconPath = { 13 | light: vscode.Uri.file(path.join(__filename, "..", "resources", "light", "generic.svg")), 14 | dark: vscode.Uri.file(path.join(__filename, "..", "resources", "dark", "generic.svg")), 15 | }; 16 | 17 | //contextValue = this.level === 2 ? "connection" : "connection-child"; 18 | } 19 | -------------------------------------------------------------------------------- /src/commands/connections.ts: -------------------------------------------------------------------------------- 1 | import { DataverseHelper } from "../helpers/dataverseHelper"; 2 | import { updateConnectionStatusBar } from "./registerCommands"; 3 | 4 | /** 5 | * Add a connection to a Dataverse instance. 6 | * @param {DataverseHelper} dvHelper - DataverseHelper 7 | */ 8 | export async function addConnection(dvHelper: DataverseHelper): Promise { 9 | const conn = await dvHelper.addConnection(); 10 | updateConnectionStatusBar(conn); 11 | } 12 | -------------------------------------------------------------------------------- /src/commands/registerTreeDataProviders.ts: -------------------------------------------------------------------------------- 1 | import TelemetryReporter from "@vscode/extension-telemetry"; 2 | import * as vscode from "vscode"; 3 | import { CliCommandDataProvider } from "../cliCommands/cliCommandsDataProvider"; 4 | import { DataverseHelper } from "../helpers/dataverseHelper"; 5 | import { ErrorHandler } from "../helpers/errorHandler"; 6 | import { WebResourceHelper } from "../helpers/webResourceHelper"; 7 | import { ToolsDataProvider } from "../tools/toolsDataProvider"; 8 | import { DataverseConnectionDataProvider } from "../trees/dataverseConnectionDataProvider"; 9 | import { EntitiesDataProvider } from "../trees/entitiesDataProvider"; 10 | import { WebResourcesDataProvider } from "../trees/webResourcesDataProvider"; 11 | import { ICommand } from "../utils/Interfaces"; 12 | import { ViewBase } from "../views/ViewBase"; 13 | 14 | /** 15 | * This function registers all the commands for Tree Data Provider that are available in the Dataverse DevTools extension. 16 | * @param vscontext - vscode.ExtensionContext 17 | * @param {TelemetryReporter} tr - The TelemetryReporter object. 18 | */ 19 | export const registerTreeDataProviders = (vscontext: vscode.ExtensionContext, tr: TelemetryReporter): void => { 20 | const dvHelper = new DataverseHelper(vscontext); 21 | const uploadHelper = new WebResourceHelper(vscontext, dvHelper); 22 | 23 | const views = new ViewBase(vscontext); 24 | const errorHandler = new ErrorHandler(tr); 25 | 26 | const dataverseConnProvider = new DataverseConnectionDataProvider(vscontext); 27 | vscode.window.registerTreeDataProvider("dvConnections", dataverseConnProvider); 28 | 29 | const entityMetadataProvider = new EntitiesDataProvider(vscontext, dvHelper); 30 | vscode.window.registerTreeDataProvider("dvEntities", entityMetadataProvider); 31 | 32 | const wrProvider = new WebResourcesDataProvider(vscontext, dvHelper, uploadHelper); 33 | vscode.window.registerTreeDataProvider("dvWebResources", wrProvider); 34 | 35 | const toolsProvider = new ToolsDataProvider(vscontext); 36 | vscode.window.registerTreeDataProvider("ppToolBox", toolsProvider); 37 | 38 | const cliProvider = new CliCommandDataProvider(vscontext); 39 | vscode.window.registerTreeDataProvider("ppCLICommands", cliProvider); 40 | 41 | const cmds: Array = new Array( 42 | { 43 | command: "dvdt.explorer.connections.refreshConnection", 44 | callback: () => dataverseConnProvider.refresh(), 45 | }, 46 | { 47 | command: "dvdt.explorer.entities.loadEntities", 48 | callback: () => entityMetadataProvider.refresh(), 49 | }, 50 | { 51 | command: "dvdt.explorer.entities.filteron", 52 | callback: () => entityMetadataProvider.filter(), 53 | }, 54 | { 55 | command: "dvdt.explorer.entities.filteroff", 56 | callback: () => entityMetadataProvider.filter(), 57 | }, 58 | { 59 | command: "dvdt.explorer.webresources.loadWebResources", 60 | callback: () => wrProvider.refresh(), 61 | }, 62 | { 63 | command: "dvdt.explorer.webresources.filteron", 64 | callback: () => wrProvider.filter(), 65 | }, 66 | { 67 | command: "dvdt.explorer.webresources.filteroff", 68 | callback: () => wrProvider.filter(), 69 | }, 70 | { 71 | callback: async () => { 72 | try { 73 | await dvHelper.showMetadataExplorer(views); 74 | } catch (error) { 75 | errorHandler.log(error, "showMetadataExplorer"); 76 | } 77 | }, 78 | command: "dvdt.explorer.entities.showMetadataExplorer", 79 | }, 80 | { 81 | command: "dvdt.explorer.entities.searchon", 82 | callback: () => entityMetadataProvider.search(), 83 | }, 84 | { 85 | command: "dvdt.explorer.entities.searchoff", 86 | callback: () => entityMetadataProvider.search(), 87 | }, 88 | { 89 | command: "dvdt.explorer.webresources.searchon", 90 | callback: () => wrProvider.search(), 91 | }, 92 | { 93 | command: "dvdt.explorer.webresources.searchoff", 94 | callback: () => wrProvider.search(), 95 | }, 96 | ); 97 | cmds.forEach((c) => { 98 | vscontext.subscriptions.push(vscode.commands.registerCommand(c.command, c.callback)); 99 | }); 100 | }; 101 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | // The module 'vscode' contains the VS Code extensibility API 2 | // Import the module and reference it with the alias vscode in your code below 3 | import TelemetryReporter from "@vscode/extension-telemetry"; 4 | import * as vscode from "vscode"; 5 | import { registerCommands } from "./commands/registerCommands"; 6 | import { registerTreeDataProviders } from "./commands/registerTreeDataProviders"; 7 | import { DataverseHelper } from "./helpers/dataverseHelper"; 8 | import * as config from "./utils/Config"; 9 | import { aiKey, extensionPrefix, fileExtensions } from "./utils/Constants"; 10 | 11 | const extensionId = "danish-naglekar.dataverse-devtools"; 12 | const extension = vscode.extensions.getExtension(extensionId)!; 13 | const extensionVersion = extension.packageJSON.version; 14 | 15 | // telemetry reporter 16 | let reporter: TelemetryReporter; 17 | 18 | // this method is called when your extension is activated 19 | // your extension is activated the very first time the command is executed 20 | export function activate(context: vscode.ExtensionContext) { 21 | // create telemetry reporter on extension activation 22 | reporter = new TelemetryReporter(aiKey); 23 | // ensure it gets property disposed 24 | context.subscriptions.push(reporter); 25 | 26 | vscode.commands.executeCommand("setContext", `${extensionPrefix}.resourcesExtn`, fileExtensions); 27 | vscode.commands.executeCommand("setContext", `${extensionPrefix}.showPreviewOptions`, config.get("enableEarlyAccessPreview")); 28 | 29 | registerTreeDataProviders(context, reporter); 30 | registerCommands(context, reporter); 31 | 32 | let dataverseToolsPublicApi = { 33 | currentConnectionToken() { 34 | const dvHelper = new DataverseHelper(context); 35 | return dvHelper.getTokenFromCurrentConnection(); 36 | }, 37 | }; 38 | // 'export' public api-surface 39 | return dataverseToolsPublicApi; 40 | } 41 | 42 | // this method is called when your extension is deactivated 43 | export function deactivate() { 44 | reporter.dispose(); 45 | } 46 | -------------------------------------------------------------------------------- /src/helpers/cliHelper.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import { CliCommandTreeItem } from "../cliCommands/cliCommandsDataProvider"; 3 | import { Commands } from "../terminals/commands"; 4 | import { Console } from "../terminals/console"; 5 | import { ICliCommandArgument } from "../utils/Interfaces"; 6 | 7 | export class CLIHelper { 8 | /** 9 | * Initialization constructor for VS Code Context 10 | */ 11 | constructor(private vscontext: vscode.ExtensionContext) {} 12 | 13 | public initiatePluginProject(path: string) { 14 | let commands: string[] = Array(); 15 | commands.push(Commands.ChangeDirectory(path)); 16 | commands.push(Commands.InitPlugin()); 17 | Console.runCommand(commands); 18 | } 19 | 20 | public launchPRT() { 21 | let commands: string[] = Array(); 22 | commands.push(Commands.LaunchPluginRegistration()); 23 | Console.runCommand(commands); 24 | } 25 | 26 | public launchCMT() { 27 | let commands: string[] = Array(); 28 | commands.push(Commands.LaunchConfigurationMigration()); 29 | Console.runCommand(commands); 30 | } 31 | 32 | public launchPD() { 33 | let commands: string[] = Array(); 34 | commands.push(Commands.LaunchPackageDeployer()); 35 | Console.runCommand(commands); 36 | } 37 | 38 | public async executeCliCommand(cliItem: CliCommandTreeItem) { 39 | let commands: string[] = Array(); 40 | 41 | if (cliItem.cmdVerb?.arguments?.length! > 0) { 42 | // With multiple parameters 43 | let params: string[] | undefined = await this.getParameters(cliItem.cmdVerb?.name!, cliItem.cmdVerb?.arguments!); 44 | let mainCommand: string = `pac ${cliItem.cmd} ${cliItem.cmdVerb?.name}`; 45 | if (params) { 46 | mainCommand += ` ${params.join(" ")}`; 47 | } 48 | commands.push(mainCommand); 49 | } else { 50 | // No parameters 51 | commands.push(`pac ${cliItem.cmd} ${cliItem.cmdVerb?.name}`); 52 | } 53 | 54 | if (commands.length > 0) { 55 | Console.runCommand(commands); 56 | } 57 | } 58 | 59 | async getParameters(cmd: string, parameters: ICliCommandArgument[]) { 60 | let pArray: string[] = Array(); 61 | let breakAll: boolean = false; 62 | 63 | for await (const param of parameters) { 64 | // Preview version will only include Required attributes 65 | if (param.isRequired) { 66 | if (param.isException && param.exception) { 67 | let optionResp = await vscode.window.showQuickPick(["true", "false"], { placeHolder: param.exception.help!, title: param.exception.name!, ignoreFocusOut: true }); 68 | if (optionResp === "false") { 69 | continue; 70 | } else if (optionResp === "true" && param.exception.ifTrueSkipAll) { 71 | breakAll = true; 72 | } 73 | } 74 | 75 | if (param.isFile) { 76 | let fileResp = await vscode.window.showOpenDialog({ canSelectFiles: true, canSelectFolders: false, canSelectMany: false, openLabel: `Select ${param.name}`, title: param.help! }); 77 | if (fileResp) { 78 | pArray.push(`${param.name} "${fileResp[0].fsPath}"`); 79 | } 80 | } else if (param.listOfValues) { 81 | // Show Quick Options 82 | var vals: string[] = new Array(); 83 | vals = param.listOfValues.split(","); 84 | let optionResp = await vscode.window.showQuickPick(vals, { placeHolder: param.help!, title: `Option for ${param.name}`, ignoreFocusOut: true }); 85 | if (optionResp) { 86 | pArray.push(`${param.name} ${optionResp}`); 87 | } else { 88 | vscode.window.showErrorMessage(`${param.name} is required.`); 89 | return undefined; 90 | } 91 | } else if (param.isSwitch) { 92 | let optionResp = await vscode.window.showQuickPick(["true", "false"], { placeHolder: param.help!, title: `Option for ${param.name}`, ignoreFocusOut: true }); 93 | if (optionResp) { 94 | pArray.push(`${param.name} ${optionResp}`); 95 | } else { 96 | vscode.window.showErrorMessage(`${param.name} is required.`); 97 | return undefined; 98 | } 99 | } else { 100 | // Show Input Option 101 | let paramResponse: string | undefined = await vscode.window.showInputBox({ title: `Option for ${param.name}`, prompt: param.name, placeHolder: param.help! }); 102 | if (paramResponse) { 103 | pArray.push(`${param.name} "${paramResponse}"`); 104 | } else { 105 | vscode.window.showErrorMessage(`${param.name} is required.`); 106 | return undefined; 107 | } 108 | } 109 | } 110 | 111 | if (breakAll) { 112 | break; 113 | } 114 | } 115 | return pArray; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/helpers/drbHelper.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import { connectionCurrentStoreKey } from "../utils/Constants"; 3 | import { ErrorMessages } from "../utils/ErrorMessages"; 4 | import { IConnection } from "../utils/Interfaces"; 5 | import { State } from "../utils/State"; 6 | import { DataverseRestBuilderView } from "../views/DataverseRestBuilderView"; 7 | import { ViewBase } from "../views/ViewBase"; 8 | 9 | export class DRBHelper { 10 | private vsstate: State; 11 | 12 | /** 13 | * Initialization constructor for VS Code Context 14 | */ 15 | constructor(private vscontext: vscode.ExtensionContext) { 16 | this.vsstate = new State(vscontext); 17 | } 18 | 19 | public async openDRB(view: ViewBase): Promise { 20 | const connFromWS: IConnection = this.vsstate.getFromWorkspace(connectionCurrentStoreKey); 21 | if (connFromWS && connFromWS.currentAccessToken) { 22 | const webview = await view.getWebView({ type: "openDRB", title: "Dataverse REST Builder" }); 23 | new DataverseRestBuilderView(webview, this.vscontext, connFromWS.currentAccessToken); 24 | } else { 25 | vscode.window.showErrorMessage(ErrorMessages.drbConnectionError); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/helpers/errorHandler.ts: -------------------------------------------------------------------------------- 1 | import TelemetryReporter from "@vscode/extension-telemetry"; 2 | import * as vscode from "vscode"; 3 | import { extensionName } from "../utils/Constants"; 4 | import { openUri } from "../utils/OpenUri"; 5 | 6 | export class ErrorHandler { 7 | /** 8 | * Initialization constructor for VS Code Context 9 | */ 10 | constructor(private reporter: TelemetryReporter) {} 11 | 12 | public log(err: any, cmd: string) { 13 | const extensionId = "danish-naglekar.dataverse-devtools"; 14 | const extension = vscode.extensions.getExtension(extensionId)!; 15 | const btnLogError: vscode.MessageItem = { title: "Log error on GitHub" }; 16 | 17 | vscode.window.showErrorMessage(`${extensionName}: Error occured - ${err.code}. Please report it on GitHub.`, btnLogError).then(async (result: vscode.MessageItem | undefined) => { 18 | if (result === btnLogError) { 19 | var reportMessage = `VSCode version: ${vscode.version}. \nExtension version: ${extension.packageJSON.version}. \nError occured in ${cmd} command. \nError code: ${err.code}. \nError message: ${err.message}. \nError stack: ${err.stack}.`; 20 | var sId = vscode.env.sessionId; 21 | await openUri( 22 | `https://github.com/Power-Maverick/DataverseDevTools-VSCode/issues/new?assignees=Power-Maverick&labels=bug%2Ctriage&template=issue-form-bug.yaml&title=%5BBug%5D:%20Error%20in%20${cmd}+&logs=${decodeURI( 23 | reportMessage, 24 | )}&sessionid=${sId}`, 25 | ); 26 | } 27 | }); 28 | // All errors are logged to customEvent telemetry 29 | this.reporter.sendTelemetryErrorEvent(cmd, { 30 | command: cmd, 31 | vscodeVersion: vscode.version, 32 | extensionVersion: extension.packageJSON.version, 33 | errorCode: err.code, 34 | errorMessage: err.message, 35 | errorStack: err.stack, 36 | sessionId: vscode.env.sessionId, 37 | }); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/helpers/requestHelper.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import fetch from "node-fetch"; 3 | import { v4 as uuid } from "uuid"; 4 | import { apiPartUrl, connectionCurrentStoreKey, customDataverseClientId, LoginTypes, maxRetries } from "../utils/Constants"; 5 | import { IConnection } from "../utils/Interfaces"; 6 | import { State } from "../utils/State"; 7 | import { DataverseHelper } from "./dataverseHelper"; 8 | 9 | export class RequestHelper { 10 | private vsstate: State; 11 | 12 | /** 13 | * Initialization constructor for VS Code Context 14 | */ 15 | constructor(context: vscode.ExtensionContext, private dvHelper: DataverseHelper) { 16 | this.vsstate = new State(context); 17 | } 18 | 19 | async requestData(query: string, retries?: number): Promise { 20 | try { 21 | const currentConnection: IConnection = this.vsstate.getFromWorkspace(connectionCurrentStoreKey); 22 | 23 | const requestUrl = `${currentConnection.environmentUrl}${apiPartUrl}${query}`; 24 | const response = await fetch(requestUrl, { 25 | headers: { 26 | // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access 27 | // eslint-disable-next-line @typescript-eslint/naming-convention 28 | Authorization: `Bearer ${currentConnection.currentAccessToken}`, 29 | "x-ms-client-request-id": uuid(), 30 | // eslint-disable-next-line @typescript-eslint/naming-convention 31 | "Content-Type": "application/json; charset=utf-8", 32 | }, 33 | }); 34 | 35 | if (response.ok) { 36 | return (await response.json()) as T; 37 | } else { 38 | if (response.statusText === "Unauthorized" && this.dvHelper) { 39 | if (retries && retries > maxRetries) { 40 | return undefined; 41 | } 42 | let tokenResponse = await this.dvHelper.reAuthenticate(currentConnection); 43 | if (tokenResponse) { 44 | return this.requestData(query, retries ? retries + 1 : 1); 45 | } else { 46 | throw new Error("Unable to finish the requested query"); 47 | } 48 | } else { 49 | return undefined; 50 | } 51 | } 52 | } catch (err) { 53 | console.log(err); 54 | throw err; 55 | } 56 | } 57 | 58 | async postData(query: string, data: string, retries?: number): Promise { 59 | const currentConnection: IConnection = this.vsstate.getFromWorkspace(connectionCurrentStoreKey); 60 | 61 | try { 62 | const requestUrl = `${currentConnection.environmentUrl}${apiPartUrl}${query}`; 63 | const response = await fetch(requestUrl, { 64 | method: "POST", 65 | headers: { 66 | // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access 67 | // eslint-disable-next-line @typescript-eslint/naming-convention 68 | Authorization: `Bearer ${currentConnection.currentAccessToken}`, 69 | // "x-ms-client-request-id": uuid(), 70 | // eslint-disable-next-line @typescript-eslint/naming-convention 71 | "Content-Type": "application/json", 72 | }, 73 | body: data, 74 | redirect: "follow", 75 | }); 76 | 77 | if (response.ok) { 78 | return response.headers.get("OData-EntityId") !== null ? response.headers.get("OData-EntityId")?.toString() : undefined; 79 | } else { 80 | if (response.statusText === "Unauthorized" && this.dvHelper) { 81 | if (retries && retries > maxRetries) { 82 | return undefined; 83 | } 84 | let tokenResponse = await this.dvHelper.reAuthenticate(currentConnection); 85 | if (tokenResponse) { 86 | return this.postData(query, data, retries ? retries + 1 : 1); 87 | } else { 88 | throw new Error("Unable to finish the requested query"); 89 | } 90 | } else { 91 | return undefined; 92 | } 93 | } 94 | } catch (err) { 95 | console.log(err); 96 | } 97 | } 98 | 99 | async patchData(query: string, data: string, retries?: number): Promise { 100 | try { 101 | const currentConnection: IConnection = this.vsstate.getFromWorkspace(connectionCurrentStoreKey); 102 | const requestUrl = `${currentConnection.environmentUrl}${apiPartUrl}${query}`; 103 | const response = await fetch(requestUrl, { 104 | method: "PATCH", 105 | headers: { 106 | // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access 107 | // eslint-disable-next-line @typescript-eslint/naming-convention 108 | Authorization: `Bearer ${currentConnection.currentAccessToken}`, 109 | // "x-ms-client-request-id": uuid(), 110 | // eslint-disable-next-line @typescript-eslint/naming-convention 111 | "Content-Type": "application/json", 112 | }, 113 | body: data, 114 | redirect: "follow", 115 | }); 116 | 117 | if (response.ok) { 118 | return response.headers.get("OData-EntityId") !== null ? response.headers.get("OData-EntityId")?.toString() : undefined; 119 | } else { 120 | if (response.statusText === "Unauthorized" && this.dvHelper) { 121 | if (retries && retries > maxRetries) { 122 | return undefined; 123 | } 124 | let tokenResponse = await this.dvHelper.reAuthenticate(currentConnection); 125 | if (tokenResponse) { 126 | return this.patchData(query, data, retries ? retries + 1 : 1); 127 | } else { 128 | throw new Error("Unable to finish the requested query"); 129 | } 130 | } else { 131 | return undefined; 132 | } 133 | } 134 | } catch (err) { 135 | console.log(err); 136 | } 137 | } 138 | } 139 | 140 | async function redirectTimeout(): Promise {} 141 | -------------------------------------------------------------------------------- /src/terminals/commands.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/naming-convention */ 2 | export class Commands { 3 | public static ChangeDirectory(path: string) { 4 | return `cd ${path}`; 5 | } 6 | 7 | public static InitPlugin() { 8 | return `pac plugin init`; 9 | } 10 | 11 | public static LoadNpmPackages() { 12 | return `npm install`; 13 | } 14 | 15 | public static LaunchPluginRegistration() { 16 | return `pac tool prt`; 17 | } 18 | 19 | public static LaunchConfigurationMigration() { 20 | return `pac tool cmt`; 21 | } 22 | 23 | public static LaunchPackageDeployer() { 24 | return `pac tool pd`; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/terminals/console.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import { terminalName } from "../utils/Constants"; 3 | 4 | export class Console { 5 | private static isTerminalCreated: boolean = false; 6 | private static dvdTerminal: vscode.Terminal; 7 | 8 | public static runCommand(commands: string[]) { 9 | if (!this.isTerminalCreated) { 10 | this.dvdTerminal = vscode.window.createTerminal(terminalName); 11 | this.isTerminalCreated = true; 12 | } else { 13 | vscode.commands.executeCommand("workbench.action.terminal.clear"); 14 | } 15 | 16 | this.dvdTerminal.show(false); 17 | 18 | commands.forEach((cmd) => { 19 | this.dvdTerminal.sendText(cmd, true); 20 | }); 21 | 22 | vscode.window.onDidCloseTerminal((t) => { 23 | if (t.name === terminalName) { 24 | Console.dispose(); 25 | } 26 | }); 27 | } 28 | 29 | public static dispose() { 30 | this.isTerminalCreated = false; 31 | this.dvdTerminal.dispose(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/test/runTest.ts: -------------------------------------------------------------------------------- 1 | import * as path from "path"; 2 | 3 | import { runTests } from "vscode-test"; 4 | 5 | async function main() { 6 | try { 7 | // The folder containing the Extension Manifest package.json 8 | // Passed to `--extensionDevelopmentPath` 9 | const extensionDevelopmentPath = path.resolve(__dirname, "../../"); 10 | 11 | // The path to test runner 12 | // Passed to --extensionTestsPath 13 | const extensionTestsPath = path.resolve(__dirname, "./suite/index"); 14 | 15 | // Download VS Code, unzip it and run the integration test 16 | await runTests({ extensionDevelopmentPath, extensionTestsPath }); 17 | } catch (err) { 18 | console.error("Failed to run tests"); 19 | process.exit(1); 20 | } 21 | } 22 | 23 | main(); 24 | -------------------------------------------------------------------------------- /src/test/suite/extension.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from "assert"; 2 | 3 | // You can import and use all API from the 'vscode' module 4 | // as well as import your extension to test it 5 | import * as vscode from "vscode"; 6 | // import * as myExtension from '../../extension'; 7 | 8 | suite("Extension Test Suite", () => { 9 | vscode.window.showInformationMessage("Start all tests."); 10 | 11 | test("Sample test", () => { 12 | assert.strictEqual(-1, [1, 2, 3].indexOf(5)); 13 | assert.strictEqual(-1, [1, 2, 3].indexOf(0)); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/test/suite/index.ts: -------------------------------------------------------------------------------- 1 | import * as path from "path"; 2 | import * as Mocha from "mocha"; 3 | import * as glob from "glob"; 4 | 5 | export function run(): Promise { 6 | // Create the mocha test 7 | const mocha = new Mocha({ 8 | ui: "tdd", 9 | color: true, 10 | }); 11 | 12 | const testsRoot = path.resolve(__dirname, ".."); 13 | 14 | return new Promise((c, e) => { 15 | glob("**/**.test.js", { cwd: testsRoot }, (err, files) => { 16 | if (err) { 17 | return e(err); 18 | } 19 | 20 | // Add files to the test suite 21 | files.forEach((f) => mocha.addFile(path.resolve(testsRoot, f))); 22 | 23 | try { 24 | // Run the mocha test 25 | mocha.run((failures) => { 26 | if (failures > 0) { 27 | e(new Error(`${failures} tests failed.`)); 28 | } else { 29 | c(); 30 | } 31 | }); 32 | } catch (err) { 33 | console.error(err); 34 | e(err); 35 | } 36 | }); 37 | }); 38 | } 39 | -------------------------------------------------------------------------------- /src/tools/tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "tools": [ 3 | { "toolName": "Dataverse REST Builder", "toolShortName": "drb", "toolAuthor": "Guido Preite" }, 4 | { "toolName": "Plugin Registration", "toolShortName": "prt", "toolAuthor": "Microsoft" }, 5 | { "toolName": "Configuration Migration", "toolShortName": "cmt", "toolAuthor": "Microsoft" }, 6 | { "toolName": "Package Deployer", "toolShortName": "pd", "toolAuthor": "Microsoft" } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /src/tools/toolsDataProvider.ts: -------------------------------------------------------------------------------- 1 | import * as path from "path"; 2 | import * as vscode from "vscode"; 3 | import { ITools } from "../utils/Interfaces"; 4 | import toolsInJson from "./tools.json"; 5 | import { ToolsItemBase } from "./toolsItemBase"; 6 | 7 | export class ToolsDataProvider implements vscode.TreeDataProvider { 8 | constructor(private vscontext: vscode.ExtensionContext) {} 9 | 10 | getTreeItem(element: ToolsTreeItem): vscode.TreeItem | Thenable { 11 | return element; 12 | } 13 | 14 | getChildren(element?: ToolsTreeItem): vscode.ProviderResult { 15 | const toolsArray: ITools = toolsInJson; 16 | let toolsTree: ToolsTreeItem[] = []; 17 | 18 | toolsArray.tools.map((tool) => { 19 | toolsTree.push(new ToolsTreeItem(tool.toolName, tool.toolShortName, tool.toolAuthor, vscode.TreeItemCollapsibleState.None)); 20 | }); 21 | 22 | return Promise.resolve(toolsTree); 23 | } 24 | } 25 | 26 | export class ToolsTreeItem extends ToolsItemBase { 27 | constructor(public readonly toolName: string, public readonly toolShortName: string, public readonly authorName: string, public readonly collapsibleState: vscode.TreeItemCollapsibleState) { 28 | super(toolName, authorName, collapsibleState); 29 | } 30 | 31 | iconPath = { 32 | light: vscode.Uri.file( 33 | path.join( 34 | __filename, 35 | "..", 36 | "resources", 37 | "toolIcons", 38 | this.toolShortName === "drb" ? "drb.png" : this.toolShortName === "prt" ? "prt.png" : this.toolShortName === "cmt" ? "cmt.png" : this.toolShortName === "pd" ? "pd.png" : "generic.svg", 39 | ), 40 | ), 41 | dark: vscode.Uri.file( 42 | path.join( 43 | __filename, 44 | "..", 45 | "resources", 46 | "toolIcons", 47 | this.toolShortName === "drb" ? "drb.png" : this.toolShortName === "prt" ? "prt.png" : this.toolShortName === "cmt" ? "cmt.png" : this.toolShortName === "pd" ? "pd.png" : "generic.svg", 48 | ), 49 | ), 50 | }; 51 | } 52 | -------------------------------------------------------------------------------- /src/tools/toolsItemBase.ts: -------------------------------------------------------------------------------- 1 | import * as path from "path"; 2 | import * as vscode from "vscode"; 3 | 4 | export class ToolsItemBase extends vscode.TreeItem { 5 | constructor(public readonly toolName: string, public readonly authorName: string | undefined, public readonly collapsibleState: vscode.TreeItemCollapsibleState) { 6 | super(toolName, collapsibleState); 7 | 8 | this.tooltip = this.authorName ? `${this.toolName}-${this.authorName}` : `${this.toolName}`; 9 | this.description = this.authorName; 10 | } 11 | 12 | iconPath = { 13 | light: vscode.Uri.file(path.join(__filename, "..", "resources", "light", "generic.svg")), 14 | dark: vscode.Uri.file(path.join(__filename, "..", "resources", "dark", "generic.svg")), 15 | }; 16 | 17 | //contextValue = this.level === 2 ? "connection" : "connection-child"; 18 | } 19 | -------------------------------------------------------------------------------- /src/trees/dataverseConnectionDataProvider.ts: -------------------------------------------------------------------------------- 1 | import { observable } from "mobx"; 2 | import * as path from "path"; 3 | import * as vscode from "vscode"; 4 | import { connectionCurrentStoreKey, connectionStoreKey, environmentTypes, extensionPrefix } from "../utils/Constants"; 5 | import { groupBy } from "../utils/ExtensionMethods"; 6 | import { IConnection, IStore } from "../utils/Interfaces"; 7 | import { State } from "../utils/State"; 8 | import { TreeItemBase } from "./treeItemBase"; 9 | 10 | export class DataverseConnectionDataProvider implements vscode.TreeDataProvider { 11 | private refreshTreeData: vscode.EventEmitter = new vscode.EventEmitter(); 12 | private connections: IConnection[] = []; 13 | 14 | constructor(private vscontext: vscode.ExtensionContext) { 15 | this.populateConnections(); 16 | } 17 | 18 | refresh(): void { 19 | this.populateConnections(); 20 | this.refreshTreeData.fire(); 21 | } 22 | 23 | getTreeItem(element: DataverseConnectionTreeItem): vscode.TreeItem { 24 | return element; 25 | } 26 | 27 | getChildren(element?: DataverseConnectionTreeItem): Thenable { 28 | if (element) { 29 | // Child 30 | const connExpand = this.connections.find((c) => c.connectionName === element.label); 31 | if (connExpand) { 32 | let childTree: DataverseConnectionTreeItem[] = []; 33 | if (connExpand.userName) { 34 | childTree.push(new DataverseConnectionTreeItem(`${connExpand.environmentUrl} (${connExpand.userName})`, undefined, vscode.TreeItemCollapsibleState.None, 3)); 35 | } else { 36 | childTree.push(new DataverseConnectionTreeItem(connExpand.environmentUrl, undefined, vscode.TreeItemCollapsibleState.None, 3)); 37 | } 38 | return Promise.resolve(childTree); 39 | } else { 40 | const noExpandCheck = this.connections.find((c) => c.environmentUrl === element.label || c.userName === element.label); 41 | if (noExpandCheck) { 42 | return Promise.resolve([]); 43 | } else { 44 | // Expand Environment 45 | return Promise.resolve(this.getConnectionItems(element.label, this.connections)); 46 | } 47 | } 48 | } else { 49 | // Parent 50 | const results = groupBy(this.connections, (c) => c.environmentType!); 51 | let parentTree: DataverseConnectionTreeItem[] = []; 52 | environmentTypes.map((t) => { 53 | if (results[t]) { 54 | parentTree.push(new DataverseConnectionTreeItem(t, undefined, vscode.TreeItemCollapsibleState.Expanded, 1)); 55 | } 56 | }); 57 | 58 | return Promise.resolve(parentTree); 59 | } 60 | } 61 | 62 | private getConnectionItems(connType: string, conns: IConnection[]): DataverseConnectionTreeItem[] { 63 | const toConnections = (name: string, version: string, isConnected: boolean): DataverseConnectionTreeItem => { 64 | if (conns) { 65 | return new DataverseConnectionTreeItem(name, version, vscode.TreeItemCollapsibleState.Collapsed, 2, isConnected); 66 | } else { 67 | return new DataverseConnectionTreeItem(name, version, vscode.TreeItemCollapsibleState.None, 0); 68 | } 69 | }; 70 | 71 | // prettier-ignore 72 | const filteredConnections = conns 73 | ? conns.filter((c) => { 74 | if (c.environmentType === connType) { 75 | return c; 76 | } 77 | }) 78 | : []; 79 | 80 | const finalConnections = filteredConnections.map((fc) => toConnections(fc.connectionName, fc.userName!, fc.isCurrentlyConnected!)); 81 | return finalConnections; 82 | } 83 | 84 | private populateConnections() { 85 | const vsstate = new State(this.vscontext); 86 | const jsonConn = vsstate.getFromGlobal(connectionStoreKey); 87 | if (jsonConn) { 88 | this.connections = JSON.parse(jsonConn); 89 | store.noConnections = false; 90 | const connFromWS: IConnection = vsstate.getFromWorkspace(connectionCurrentStoreKey); 91 | if (connFromWS) { 92 | let ind = this.connections.findIndex((c) => c.connectionName === connFromWS.connectionName && c.environmentType === connFromWS.environmentType); 93 | if (ind !== -1) { 94 | this.connections[ind].isCurrentlyConnected = true; 95 | } 96 | } 97 | } else { 98 | this.connections = []; 99 | store.noConnections = true; 100 | } 101 | vscode.commands.executeCommand("setContext", `${extensionPrefix}:noConnections`, store.noConnections); 102 | } 103 | 104 | readonly onDidChangeTreeData: vscode.Event = this.refreshTreeData.event; 105 | } 106 | 107 | export class DataverseConnectionTreeItem extends TreeItemBase { 108 | constructor( 109 | public readonly label: string, 110 | public readonly desc: string | undefined, 111 | public readonly collapsibleState: vscode.TreeItemCollapsibleState, 112 | public readonly level: number, 113 | public readonly current: boolean = false, 114 | ) { 115 | super(label, desc, collapsibleState); 116 | } 117 | 118 | iconPath = { 119 | light: vscode.Uri.file( 120 | path.join( 121 | __filename, 122 | "..", 123 | "resources", 124 | "light", 125 | this.level === 1 126 | ? "connection-type.svg" 127 | : this.level === 2 && this.current 128 | ? "dataverse.svg" 129 | : this.level === 2 && !this.current 130 | ? "dataverse-off.svg" 131 | : this.level === 3 132 | ? "connection-details.svg" 133 | : "generic.svg", 134 | ), 135 | ), 136 | dark: vscode.Uri.file( 137 | path.join( 138 | __filename, 139 | "..", 140 | "resources", 141 | "dark", 142 | this.level === 1 143 | ? "connection-type.svg" 144 | : this.level === 2 && this.current 145 | ? "dataverse.svg" 146 | : this.level === 2 && !this.current 147 | ? "dataverse-off.svg" 148 | : this.level === 3 149 | ? "connection-details.svg" 150 | : "generic.svg", 151 | ), 152 | ), 153 | }; 154 | 155 | contextValue = this.level === 2 ? "connection" : "connection-child"; 156 | } 157 | 158 | export const store: IStore = observable({}); 159 | -------------------------------------------------------------------------------- /src/trees/treeItemBase.ts: -------------------------------------------------------------------------------- 1 | import * as path from "path"; 2 | import * as vscode from "vscode"; 3 | 4 | export class TreeItemBase extends vscode.TreeItem { 5 | constructor(public readonly label: string, public readonly desc: string | undefined, public readonly collapsibleState: vscode.TreeItemCollapsibleState) { 6 | super(label, collapsibleState); 7 | 8 | this.tooltip = this.desc ? `${this.label}-${this.desc}` : `${this.label}`; 9 | this.description = this.desc; 10 | } 11 | 12 | iconPath = { 13 | light: vscode.Uri.file(path.join(__filename, "..", "resources", "light", "generic.svg")), 14 | dark: vscode.Uri.file(path.join(__filename, "..", "resources", "dark", "generic.svg")), 15 | }; 16 | 17 | //contextValue = this.level === 2 ? "connection" : "connection-child"; 18 | } 19 | -------------------------------------------------------------------------------- /src/utils/Config.ts: -------------------------------------------------------------------------------- 1 | import { workspace } from "vscode"; 2 | import { configSectionName } from "./Constants"; 3 | 4 | export function get(key: "enableEarlyAccessPreview"): boolean; 5 | export function get(key: "defaultTypeScriptTemplate"): "None" | "Plain TypeScript" | "Webpack"; 6 | 7 | export function get(key: any) { 8 | const extensionConfig = workspace.getConfiguration(configSectionName); 9 | return extensionConfig.get(key); 10 | } 11 | 12 | export async function set(key: string, value: any) { 13 | const extensionConfig = workspace.getConfiguration(configSectionName); 14 | return extensionConfig.update(key, value, true); 15 | } 16 | -------------------------------------------------------------------------------- /src/utils/Constants.ts: -------------------------------------------------------------------------------- 1 | export const extensionName: string = "Dataverse DevTools"; 2 | export const extensionPrefix: string = "devtools"; 3 | export const extensionCodeName: string = "dataverse-devtools"; 4 | export const extensionUniqueName: string = "danish-naglekar.dataverse-devtools"; 5 | export const aiKey: string = "490f2bf6-8f2a-4cc1-873b-c2a62d0a2ec8"; 6 | export const activeDirectoryEndpointUrl: string = "https://login.microsoftonline.com/"; 7 | export const authorityUrl: string = "https://login.windows.net/common"; 8 | export const genericTenant: string = "organizations"; 9 | export const tokenEndpointUrl: string = `${activeDirectoryEndpointUrl}${genericTenant}/oauth2/v2.0/token`; 10 | export const defaultDataverseClientId: string = `51f81489-12ee-4a9e-aaae-a2591f45987d`; 11 | export const customDataverseClientId: string = `12c47861-4bb0-48dd-8949-83df0a3fecc5`; 12 | export const environmentVersion: string = `v9.2`; 13 | export const apiPartUrl: string = `/api/data/${environmentVersion}/`; 14 | export const connectionStoreKey: string = `DataverseConnections`; 15 | export const connectionCurrentStoreKey: string = `LiveDVConnection`; 16 | export const entityDefinitionsStoreKey: string = `CurrentEntityDefinitions`; 17 | export const wrDefinitionsStoreKey: string = `CurrentWRs`; 18 | export const solDefinitionsStoreKey: string = `CurrentSolutions`; 19 | export const smartMatchStoreKey: string = `CurrentSmartMatch`; 20 | export const environmentTypes: string[] = ["Dev", "QA", "UAT", "Prod"]; 21 | export const tsTemplateType: string[] = ["Plain TypeScript", "Webpack"]; 22 | export const connectionStatusBarUniqueId: string = `${extensionPrefix}.StatusBarConnectStatus`; 23 | export const maxRetries: number = 5; 24 | export const terminalName: string = "Dataverse DevTools"; 25 | export const fileExtensions: string[] = [".js", ".html", ".css", ".xml", ".png", ".jpg", ".gif", ".xsl", ".ico", ".svg", ".resx"]; 26 | export const portADFS: number = 29827; 27 | export const redirectUrl: string = `http://localhost:${portADFS}/callback/`; 28 | export const configSectionName: string = "dataverse-devtools"; 29 | export const reservedWords: string[] = ["Dev", "QA", "UAT", "Prod"]; 30 | export const tsConfigFileName: string = "tsconfig.json"; 31 | export const jsConfigFileName: string = "jsconfig.json"; 32 | 33 | export enum LoginTypes { 34 | microsoftLogin = "Microsoft Login Prompt", 35 | clientIdSecret = "Client Id and Secret", 36 | userNamePassword = "Username/Password", 37 | // azure = "Azure", 38 | } 39 | 40 | export enum WebResourceType { 41 | html = 1, 42 | css = 2, 43 | script = 3, 44 | xml = 4, 45 | png = 5, 46 | jpg = 6, 47 | gif = 7, 48 | xsl = 9, 49 | ico = 10, 50 | svg = 11, 51 | resx = 12, 52 | others = -1, 53 | } 54 | 55 | export enum ConfidenceScores { 56 | _90 = 90, 57 | _80 = 80, 58 | _75 = 75, 59 | _60 = 60, 60 | } 61 | -------------------------------------------------------------------------------- /src/utils/ErrorMessages.ts: -------------------------------------------------------------------------------- 1 | export class ErrorMessages { 2 | public static dataverseEnvironmentUrlRequired: string = "Dataverse environment URL is required."; 3 | public static usernameRequired: string = "Username is required."; 4 | public static passwordRequired: string = "Password is required."; 5 | public static clientIdRequired: string = "Client Id is required."; 6 | public static clientSecretRequired: string = "Client Secret is required."; 7 | public static tenantIdRequired: string = "Tenant Id is required."; 8 | public static connNameRequired: string = "Connection Name is required."; 9 | public static connNameReservedWords: string = "Cannot used reserved words; choose some other friendly name for your connection."; 10 | public static wrDisplayNameRequired: string = "Web Resource Display Name is required."; 11 | public static tsFileNameRequired: string = "TypeScript filename is required."; 12 | public static jsFileNameRequired: string = "JavaScript filename is required."; 13 | public static webpackNamespaceRequired: string = "Namespace for Webpack is required."; 14 | public static wrCompareError: string = "The selected file is either not linked to a web resources or it does not exists in Dataverse."; 15 | public static drbConnectionError: string = "Connect to an environment before trying to load Dataverse REST Builder."; 16 | public static invalidLoginType: string = "Invalid Login Type."; 17 | } 18 | -------------------------------------------------------------------------------- /src/utils/ExtensionMethods.ts: -------------------------------------------------------------------------------- 1 | const stringIsNumber = (value: any) => isNaN(Number(value)) === false; 2 | 3 | /** 4 | * Performs a group by on an array based on the key 5 | * @param list array of an object 6 | * @param getKey anonymous function to identify key for grouping 7 | * @returns Record 8 | */ 9 | export const groupBy = (list: T[], getKey: (item: T) => K) => 10 | list.reduce((previous, currentItem) => { 11 | const group = getKey(currentItem); 12 | if (!previous[group]) { 13 | previous[group] = []; 14 | } 15 | previous[group].push(currentItem); 16 | return previous; 17 | }, {} as Record); 18 | 19 | /** 20 | * Helps in converting any object into array like Enum 21 | * @param e 22 | * @returns array of object 23 | */ 24 | export const toArray = (e: any) => { 25 | return Object.keys(e) 26 | .filter(stringIsNumber) 27 | .map((key) => e[key]); 28 | }; 29 | 30 | /** 31 | * Converts a string into camel case 32 | * @param str String that needs to be converted in camel case 33 | * @returns string 34 | */ 35 | export const camelize = (str: string) => { 36 | return str 37 | .replace(/(?:^\w|[A-Z]|\b\w)/g, function (word, index) { 38 | return index === 0 ? word.toLowerCase() : word.toUpperCase(); 39 | }) 40 | .replace(/\s+/g, "") 41 | .replace(/[^0-9a-zA-Z_]+/g, ""); 42 | }; 43 | 44 | /** 45 | * Converts a string into pascal case 46 | * @param str String that needs to be converted in pascal case 47 | * @returns string 48 | */ 49 | export const pascalize = (str: string) => { 50 | return str 51 | .replace(/\w+/g, function (w) { 52 | return w[0].toUpperCase() + w.slice(1).toLowerCase(); 53 | }) 54 | .replace(/\s+/g, "") 55 | .replace(/[^0-9a-zA-Z_]+/g, ""); 56 | }; 57 | 58 | /** 59 | * Sanitizes the string by replacing the first number with '_', removing all whitespace & any special characters 60 | * @param str String that needs to be cleaned 61 | * @returns string 62 | */ 63 | export const sanitize = (str: string) => { 64 | if (str.match(/^\d/)) { 65 | str = "_".concat(str); 66 | } 67 | return str.replace(/\s+/g, "").replace(/[^0-9a-zA-Z_]+/g, ""); 68 | }; 69 | 70 | /** 71 | * Extracts a GUID from a string 72 | * @param str String that has GUID 73 | * @returns string that is a GUID 74 | */ 75 | export const extractGuid = (str: string): string | undefined => { 76 | const matches = str.match(/(\{){0,1}[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}(\}){0,1}/g); 77 | if (matches) { 78 | return matches[0]; 79 | } 80 | }; 81 | 82 | export const decodeFromBase64 = (str: string): string => Buffer.from(str, "base64").toString("binary"); 83 | 84 | export const encodeToBase64 = (str: string): string => Buffer.from(str, "binary").toString("base64"); 85 | -------------------------------------------------------------------------------- /src/utils/FileSystem.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import * as os from "os"; 3 | import * as path from "path"; 4 | import * as fs from "fs-extra"; 5 | import { extensionCodeName } from "../utils/Constants"; 6 | 7 | export function readFileSync(source: string): any { 8 | return fs.readFileSync(source); 9 | } 10 | 11 | export function readFileAsBase64Sync(source: string): any { 12 | return fs.readFileSync(source, "base64"); 13 | } 14 | 15 | export function writeFileSync(source: string, data: string): any { 16 | return fs.writeFileSync(source, data); 17 | } 18 | 19 | export async function copyFolderOrFile(source: string, target: string) { 20 | await fs.copy(source, target, { overwrite: true }); 21 | } 22 | 23 | export async function createFolder(folderDirPath: string) { 24 | await fs.ensureDir(folderDirPath); 25 | } 26 | 27 | export function getFileName(fullPath: string) { 28 | return fullPath.replace(/^.*[\\\/]/, ""); 29 | } 30 | 31 | export function getRelativeFilePath(fullPath: string, currentDirPath: string) { 32 | return fullPath.replace(currentDirPath, "/").replace(/\\/g, "/").replace(/^\\/g, ""); 33 | } 34 | 35 | export function getWorkspaceFolder(): vscode.Uri | undefined { 36 | if (vscode.workspace.workspaceFolders !== undefined) { 37 | return vscode.workspace.workspaceFolders[0].uri; 38 | //let f = vscode.workspace.workspaceFolders[0].uri.fsPath; 39 | } 40 | } 41 | 42 | export function pathExists(fspath: string) { 43 | return fs.pathExistsSync(fspath); 44 | } 45 | 46 | export function getFileExtension(fullPath: string): string | undefined { 47 | return fullPath.split(".").pop(); 48 | } 49 | 50 | export async function createTempDirectory() { 51 | const scratchDirectory = path.join(os.tmpdir(), extensionCodeName); 52 | const dayjs = require("dayjs"); 53 | const timestamp = dayjs().format("YYYYMMDD"); 54 | const tempDirectory = path.join(scratchDirectory, timestamp); 55 | 56 | const uri = vscode.Uri.file(tempDirectory); 57 | await vscode.workspace.fs.createDirectory(uri); 58 | 59 | return uri; 60 | } 61 | -------------------------------------------------------------------------------- /src/utils/OpenUri.ts: -------------------------------------------------------------------------------- 1 | import { env, Uri } from "vscode"; 2 | 3 | export async function openUri(uri: string): Promise { 4 | await env.openExternal(Uri.parse(uri)); 5 | } 6 | -------------------------------------------------------------------------------- /src/utils/Parsers.ts: -------------------------------------------------------------------------------- 1 | import { XMLBuilder, XMLParser } from "fast-xml-parser"; 2 | 3 | const options = { 4 | ignoreAttributes: false, 5 | ignoreNameSpace: true, 6 | allowBooleanAttributes: true, 7 | parseNodeValue: true, 8 | parseAttributeValue: true, 9 | trimValues: true, 10 | attributeNamePrefix: "@_", 11 | format: true, 12 | }; 13 | 14 | export function xmlToJSON(xmlData: string): T { 15 | const parser = new XMLParser(options); 16 | var jsonObj: T = parser.parse(xmlData); 17 | let tempObj = jsonObj as any; 18 | // Check if xml attribute exists; if it does then add version = 1.0 19 | if ("?xml" in tempObj) { 20 | (jsonObj as any)["?xml"]["@_version"] = "1.0"; 21 | } 22 | return jsonObj; 23 | } 24 | 25 | export function jsonToXML(json: T): string { 26 | const builder = new XMLBuilder(options); 27 | var xmlObj = builder.build(json); 28 | return xmlObj; 29 | } 30 | -------------------------------------------------------------------------------- /src/utils/State.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | 3 | export class State { 4 | private vscontext: vscode.ExtensionContext; 5 | 6 | /** 7 | * Initialization constructor for VS Code Context 8 | */ 9 | constructor(context: vscode.ExtensionContext) { 10 | this.vscontext = context; 11 | } 12 | 13 | public saveInGlobal(key: string, value: any) { 14 | this.vscontext.globalState.update(key, value); 15 | } 16 | 17 | public saveInWorkspace(key: string, value: any) { 18 | this.vscontext.workspaceState.update(key, value); 19 | } 20 | 21 | public getFromGlobal(key: string): any | undefined { 22 | return this.vscontext.globalState.get(key); 23 | } 24 | 25 | public getFromWorkspace(key: string): any | undefined { 26 | return this.vscontext.workspaceState.get(key); 27 | } 28 | 29 | public unsetFromGlobal(key: string) { 30 | this.vscontext.globalState.update(key, undefined); 31 | } 32 | 33 | public unsetFromWorkspace(key: string) { 34 | this.vscontext.workspaceState.update(key, undefined); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/views/ConnectionDetailsView.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import * as path from "path"; 3 | import { IConnection } from "../utils/Interfaces"; 4 | import { readFileSync } from "../utils/FileSystem"; 5 | import _ = require("lodash"); 6 | import { VsCodePanel } from "./base/VsCodePanelBase"; 7 | 8 | export class ConnectionDetailsView extends VsCodePanel { 9 | conn?: IConnection; 10 | 11 | constructor(connDetails: IConnection, webview: vscode.WebviewPanel, vscontext: vscode.ExtensionContext) { 12 | super({ panel: webview, extensionUri: vscontext.extensionUri, webViewFileName: "connectiondetail.html" }); 13 | this.conn = connDetails; 14 | super.update(); 15 | } 16 | 17 | getHtmlForWebview(webviewFileName: string): string { 18 | const pathOnDisk = path.join(this.panelOptions.extensionUri.fsPath, "resources", "views", webviewFileName); 19 | const fileHtml = readFileSync(pathOnDisk).toString(); 20 | _.templateSettings.interpolate = /!!{([\s\S]+?)}/g; 21 | const compiled = _.template(fileHtml); 22 | const viewModel = { 23 | connName: this.conn?.connectionName ?? "--", 24 | connType: this.conn?.environmentType ?? "--", 25 | envUrl: this.conn?.environmentUrl ?? "--", 26 | loginType: this.conn?.loginType ?? "--", 27 | userName: this.conn?.userName ?? "--", 28 | token: this.conn?.currentAccessToken ?? "--", 29 | refreshToken: this.conn?.refreshToken ?? "--", 30 | }; 31 | 32 | // if (this._images && this._images.length > 0) { 33 | // Object.keys(this._images).forEach((key) => { 34 | // //viewModel.images.push(this._images[key]); 35 | // (viewModel.images)[key] = this._images.get(key); 36 | // }); 37 | // } 38 | 39 | return super.render(compiled(viewModel)); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/views/DataverseRestBuilderView.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import * as path from "path"; 3 | import { IConnection } from "../utils/Interfaces"; 4 | import { Panel } from "./base/PanelBase"; 5 | import { readFileSync } from "../utils/FileSystem"; 6 | import _ = require("lodash"); 7 | import { connectionCurrentStoreKey } from "../utils/Constants"; 8 | import { State } from "../utils/State"; 9 | 10 | export class DataverseRestBuilderView extends Panel { 11 | constructor(webviewPanel: vscode.WebviewPanel, vscontext: vscode.ExtensionContext, currentAccessToken: string) { 12 | super({ panel: webviewPanel, extensionUri: vscontext.extensionUri, webViewFileName: "drb.html", excludeExternalCss: true, excludeExternalJs: true }); 13 | super.update(); 14 | 15 | if (currentAccessToken) { 16 | webviewPanel.webview.postMessage({ command: "dvdt_connection", token: currentAccessToken }); 17 | } 18 | } 19 | 20 | getHtmlForWebview(webviewFileName: string): string { 21 | const pathOnDisk = path.join(this.panelOptions.extensionUri.fsPath, "resources", "views", webviewFileName); 22 | const fileHtml = readFileSync(pathOnDisk).toString(); 23 | _.templateSettings.interpolate = /!!{([\s\S]+?)}/g; 24 | const compiled = _.template(fileHtml); 25 | 26 | // const requirementJs = this.getFileUri("resources", "views", "js", "drb_custom.js"); 27 | // const customJs = this.getFileUri("resources", "views", "js", "drb_requirements.js"); 28 | 29 | const viewModel = {}; 30 | 31 | return super.render(compiled(viewModel)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/views/EntityDetailsView.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import * as path from "path"; 3 | import { IEntityDefinition } from "../utils/Interfaces"; 4 | import { readFileSync } from "../utils/FileSystem"; 5 | import _ = require("lodash"); 6 | import { VsCodePanel } from "./base/VsCodePanelBase"; 7 | 8 | export class EntityDetailsView extends VsCodePanel { 9 | entity?: IEntityDefinition; 10 | 11 | constructor(enDetails: IEntityDefinition, webview: vscode.WebviewPanel, vscontext: vscode.ExtensionContext) { 12 | super({ panel: webview, extensionUri: vscontext.extensionUri, webViewFileName: "entitydetail.html" }); 13 | this.entity = enDetails; 14 | // Set the webview's initial html content 15 | super.update(); 16 | } 17 | 18 | getHtmlForWebview(webviewFileName: string): string { 19 | const pathOnDisk = path.join(this.panelOptions.extensionUri.fsPath, "resources", "views", webviewFileName); 20 | const fileHtml = readFileSync(pathOnDisk).toString(); 21 | _.templateSettings.interpolate = /!!{([\s\S]+?)}/g; 22 | const compiled = _.template(fileHtml); 23 | const viewModel = { 24 | entityLogicalName: this.entity?.LogicalName ?? "--", 25 | entitySchemaName: this.entity?.SchemaName ?? "--", 26 | description: this.entity?.Description.UserLocalizedLabel.Label ?? "--", 27 | sla: this.entity?.IsSLAEnabled ? "Enabled" : "Disabled", 28 | entityType: this.entity?.IsActivity ? "Activity" : "Standard", 29 | objectTypeCode: this.entity?.ObjectTypeCode ?? "--", 30 | idAttr: this.entity?.PrimaryIdAttribute ?? "--", 31 | nameAttr: this.entity?.PrimaryNameAttribute ?? "--", 32 | setName: this.entity?.EntitySetName ?? "--", 33 | attributes: "", 34 | }; 35 | 36 | if (this.entity?.Attributes && this.entity?.Attributes.value.length > 0) { 37 | this.entity?.Attributes.value.forEach((a) => { 38 | viewModel.attributes += ``; 39 | viewModel.attributes += `${a.MetadataId}`; 40 | viewModel.attributes += `${a.LogicalName}`; 41 | viewModel.attributes += `${a.SchemaName}`; 42 | viewModel.attributes += `${a.AttributeType}`; 43 | viewModel.attributes += `${a.AttributeTypeName.Value}`; 44 | viewModel.attributes += `${a.AttributeOf ? a.AttributeOf : ''}`; 45 | viewModel.attributes += `${a.RequiredLevel.Value}`; 46 | viewModel.attributes += ``; 47 | }); 48 | } 49 | 50 | return super.render(compiled(viewModel)); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/views/EntityListView.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import * as path from "path"; 3 | import { IEntityDefinition } from "../utils/Interfaces"; 4 | import { readFileSync } from "../utils/FileSystem"; 5 | import _ from "lodash"; 6 | import { VsCodePanel } from "./base/VsCodePanelBase"; 7 | 8 | export class EntityListView extends VsCodePanel { 9 | private entities?: IEntityDefinition[]; 10 | 11 | constructor(entities: IEntityDefinition[], webview: vscode.WebviewPanel, vscontext: vscode.ExtensionContext) { 12 | super({ panel: webview, extensionUri: vscontext.extensionUri, webViewFileName: "entitylist.html" }); 13 | this.entities = entities; 14 | // Set the webview's initial html content 15 | super.update(); 16 | } 17 | 18 | public getHtmlForWebview(webviewFileName: string): string { 19 | const pathOnDisk = path.join(this.panelOptions.extensionUri.fsPath, "resources", "views", webviewFileName); 20 | const fileHtml = readFileSync(pathOnDisk).toString(); 21 | _.templateSettings.interpolate = /!!{([\s\S]+?)}/g; 22 | const compiled = _.template(fileHtml); 23 | 24 | const viewModel = { 25 | entities: '', 26 | }; 27 | 28 | this.entities?.forEach(element => { 29 | 30 | viewModel.entities += ``; 31 | viewModel.entities += `${element.MetadataId}`; 32 | viewModel.entities += `${element.ObjectTypeCode}`; 33 | viewModel.entities += `${element.LogicalName}`; 34 | viewModel.entities += `${element.SchemaName}`; 35 | viewModel.entities += `${element.Description?.UserLocalizedLabel?.Label}`; 36 | viewModel.entities += `${element.TableType}`; 37 | viewModel.entities += `${element.HasActivities}`; 38 | viewModel.entities += `${element.HasNotes}`; 39 | viewModel.entities += `${element.IsActivity}`; 40 | viewModel.entities += `${element.IsActivityParty}`; 41 | viewModel.entities += `${element.IsAuditEnabled?.Value}`; 42 | viewModel.entities += `${element.IsCustomEntity}`; 43 | viewModel.entities += `${element.IsCustomizable?.Value}`; 44 | viewModel.entities += `${element.IsDocumentManagementEnabled}`; 45 | viewModel.entities += `${element.IsDuplicateDetectionEnabled?.Value}`; 46 | viewModel.entities += `${element.IsManaged}`; 47 | viewModel.entities += `${element.OwnershipType}`; 48 | 49 | viewModel.entities += ``; 50 | }); 51 | 52 | return super.render(compiled(viewModel)); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/views/ViewBase.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import * as os from "os"; 3 | import * as path from "path"; 4 | import { extensionPrefix } from "../utils/Constants"; 5 | import { IViewOption } from "../utils/Interfaces"; 6 | 7 | export class ViewBase { 8 | public viewOptions: IViewOption | undefined; 9 | private vscontext: vscode.ExtensionContext; 10 | 11 | /** 12 | * Initialization constructor for VS Code Context 13 | */ 14 | constructor(context: vscode.ExtensionContext) { 15 | this.vscontext = context; 16 | } 17 | 18 | public async getWebView(view: IViewOption): Promise { 19 | const column = vscode.window.activeTextEditor ? vscode.ViewColumn.Active : vscode.ViewColumn.One; 20 | return vscode.window.createWebviewPanel(`${extensionPrefix}.${view.type}`, view.title, column || vscode.ViewColumn.One, getWebviewOptions(this.vscontext.extensionUri)); 21 | } 22 | } 23 | 24 | function getWebviewOptions(extensionUri: vscode.Uri): vscode.WebviewOptions & vscode.WebviewPanelOptions { 25 | return { 26 | // Enable javascript in the webview 27 | enableScripts: true, 28 | enableCommandUris: true, 29 | 30 | // And restrict the webview to only loading content from our extension's directory. 31 | localResourceRoots: [vscode.Uri.joinPath(extensionUri, "resources")], 32 | 33 | // And make sure the view is retained for DRB 34 | retainContextWhenHidden: true, 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /src/views/base/PanelBase.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import * as path from "path"; 3 | import { IConnection, IEntityDefinition, IPanel } from "../../utils/Interfaces"; 4 | import { readFileSync } from "../../utils/FileSystem"; 5 | import _ = require("lodash"); 6 | 7 | export class Panel { 8 | // public static currentPanel: Panel | undefined; 9 | public readonly webViewPanel: vscode.WebviewPanel; 10 | private disposables: vscode.Disposable[] = []; 11 | conn?: IConnection; 12 | entity?: IEntityDefinition; 13 | 14 | constructor(public panelOptions: IPanel) { 15 | this.webViewPanel = panelOptions.panel; 16 | // Listen for when the panel is disposed 17 | // This happens when the user closes the panel or when the panel is closed programmatically 18 | this.webViewPanel.onDidDispose(() => this.dispose(), null, this.disposables); 19 | 20 | // Update the content based on view changes 21 | this.webViewPanel.onDidChangeViewState( 22 | (e) => { 23 | if (this.webViewPanel.visible) { 24 | this.update(); 25 | } 26 | }, 27 | null, 28 | this.disposables, 29 | ); 30 | } 31 | 32 | public dispose() { 33 | // Panel.currentPanel = undefined; 34 | 35 | // Clean up our resources 36 | this.webViewPanel.dispose(); 37 | 38 | while (this.disposables.length) { 39 | const x = this.disposables.pop(); 40 | if (x) { 41 | x.dispose(); 42 | } 43 | } 44 | } 45 | 46 | update() { 47 | this.webViewPanel.webview.html = this.getHtmlForWebview(this.panelOptions.webViewFileName); 48 | } 49 | 50 | render(htmlPartial: string) { 51 | const baseCss = this.getFileUri("resources", "views", "css", "base.css"); 52 | const baseJs = this.getFileUri("resources", "views", "js", "base.js"); 53 | 54 | return ` 55 | 56 | 57 | 58 | 59 | 60 | ${this.panelOptions.excludeExternalCss 61 | ? "" 62 | : ` 63 | 64 | 65 | ` 66 | } 67 | 68 | ${this.panelOptions.excludeExternalJs 69 | ? "" 70 | : ` 71 | 72 | 73 | 74 | 75 | ` 76 | } 77 | 78 | 79 |
80 | ${htmlPartial} 81 |
82 | 88 | 94 | 95 | 96 | `; 97 | } 98 | 99 | getHtmlForWebview(webviewFileName: string): string { 100 | const pathOnDisk = path.join(this.panelOptions.extensionUri.fsPath, "resources", "views", webviewFileName); 101 | const fileHtml = readFileSync(pathOnDisk).toString(); 102 | _.templateSettings.interpolate = /!!@([\s\S]+?)/g; 103 | const compiled = _.template(fileHtml); 104 | const viewModel = {}; 105 | 106 | // if (this._images && this._images.length > 0) { 107 | // Object.keys(this._images).forEach((key) => { 108 | // //viewModel.images.push(this._images[key]); 109 | // (viewModel.images)[key] = this._images.get(key); 110 | // }); 111 | // } 112 | 113 | return this.render(compiled(viewModel)); 114 | } 115 | 116 | public getFileUri(...paths: string[]): vscode.Uri { 117 | const pathOnDisk = vscode.Uri.file(path.join(this.panelOptions.extensionUri.fsPath, ...paths)); 118 | return this.webViewPanel.webview.asWebviewUri(pathOnDisk); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/views/base/VsCodePanelBase.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import * as path from "path"; 3 | import { IConnection, IEntityDefinition, IPanel } from "../../utils/Interfaces"; 4 | import { readFileSync } from "../../utils/FileSystem"; 5 | import _ = require("lodash"); 6 | 7 | export class VsCodePanel { 8 | // public static currentPanel: Panel | undefined; 9 | public readonly webViewPanel: vscode.WebviewPanel; 10 | private disposables: vscode.Disposable[] = []; 11 | conn?: IConnection; 12 | entity?: IEntityDefinition; 13 | 14 | constructor(public panelOptions: IPanel) { 15 | this.webViewPanel = panelOptions.panel; 16 | // Listen for when the panel is disposed 17 | // This happens when the user closes the panel or when the panel is closed programmatically 18 | this.webViewPanel.onDidDispose(() => this.dispose(), null, this.disposables); 19 | 20 | this.webViewPanel.webview.onDidReceiveMessage( 21 | message => { 22 | switch (message.command) { 23 | case 'showInfo': 24 | vscode.window.showInformationMessage(message.text); 25 | return; 26 | } 27 | } 28 | ); 29 | 30 | // Update the content based on view changes 31 | this.webViewPanel.onDidChangeViewState( 32 | (e) => { 33 | if (this.webViewPanel.visible) { 34 | this.update(); 35 | } 36 | }, 37 | null, 38 | this.disposables, 39 | ); 40 | } 41 | 42 | public dispose() { 43 | // Panel.currentPanel = undefined; 44 | 45 | // Clean up our resources 46 | this.webViewPanel.dispose(); 47 | 48 | while (this.disposables.length) { 49 | const x = this.disposables.pop(); 50 | if (x) { 51 | x.dispose(); 52 | } 53 | } 54 | } 55 | 56 | update() { 57 | this.webViewPanel.webview.html = this.getHtmlForWebview(this.panelOptions.webViewFileName); 58 | } 59 | 60 | render(htmlPartial: string) { 61 | const baseCss = this.getFileUri("resources", "views", "css", "base.css"); 62 | const baseJs = this.getFileUri("resources", "views", "js", "base.js"); 63 | 64 | return ` 65 | 66 | 67 | 68 | 69 | 70 | 71 | ${this.panelOptions.excludeExternalJs 72 | ? "" 73 | : ` 74 | ` 75 | } 76 | 77 | 78 |
79 | ${htmlPartial} 80 |
81 | 82 | 83 | `; 84 | } 85 | 86 | getHtmlForWebview(webviewFileName: string): string { 87 | const pathOnDisk = path.join(this.panelOptions.extensionUri.fsPath, "resources", "views", webviewFileName); 88 | const fileHtml = readFileSync(pathOnDisk).toString(); 89 | _.templateSettings.interpolate = /!!@([\s\S]+?)/g; 90 | const compiled = _.template(fileHtml); 91 | const viewModel = {}; 92 | 93 | // if (this._images && this._images.length > 0) { 94 | // Object.keys(this._images).forEach((key) => { 95 | // //viewModel.images.push(this._images[key]); 96 | // (viewModel.images)[key] = this._images.get(key); 97 | // }); 98 | // } 99 | 100 | return this.render(compiled(viewModel)); 101 | } 102 | 103 | public getFileUri(...paths: string[]): vscode.Uri { 104 | const pathOnDisk = vscode.Uri.file(path.join(this.panelOptions.extensionUri.fsPath, ...paths)); 105 | return this.webViewPanel.webview.asWebviewUri(pathOnDisk); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "outDir": "out", 6 | "lib": ["es6"], 7 | "sourceMap": true, 8 | "rootDir": "src", 9 | "strict": true, 10 | "resolveJsonModule": true, 11 | "esModuleInterop": true 12 | /* Additional Checks */ 13 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 14 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 15 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 16 | }, 17 | "exclude": ["node_modules", ".vscode-test", "./src/test/**"] 18 | } 19 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | //@ts-check 2 | 3 | "use strict"; 4 | 5 | const path = require("path"); 6 | // eslint-disable-next-line @typescript-eslint/naming-convention 7 | const CopyPlugin = require("copy-webpack-plugin"); 8 | 9 | //@ts-check 10 | /** @typedef {import('webpack').Configuration} WebpackConfig **/ 11 | 12 | /** @type WebpackConfig */ 13 | const extensionConfig = { 14 | target: "node", // VS Code extensions run in a Node.js-context 📖 -> https://webpack.js.org/configuration/node/ 15 | mode: "none", // this leaves the source code as close as possible to the original (when packaging we set this to 'production') 16 | 17 | entry: "./src/extension.ts", // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/ 18 | output: { 19 | // the bundle is stored in the 'dist' folder (check package.json), 📖 -> https://webpack.js.org/configuration/output/ 20 | path: path.resolve(__dirname, "dist"), 21 | filename: "extension.js", 22 | libraryTarget: "commonjs2", 23 | }, 24 | externals: { 25 | vscode: "commonjs vscode", // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed, 📖 -> https://webpack.js.org/configuration/externals/ 26 | // modules added here also need to be added in the .vscodeignore file 27 | }, 28 | resolve: { 29 | // support reading TypeScript and JavaScript files, 📖 -> https://github.com/TypeStrong/ts-loader 30 | extensions: [".ts", ".js"], 31 | }, 32 | module: { 33 | rules: [ 34 | { 35 | test: /\.ts$/, 36 | exclude: /node_modules/, 37 | use: [ 38 | { 39 | loader: "ts-loader", 40 | }, 41 | ], 42 | }, 43 | ], 44 | }, 45 | devtool: "nosources-source-map", 46 | infrastructureLogging: { 47 | level: "log", // enables logging required for problem matchers 48 | }, 49 | plugins: [ 50 | new CopyPlugin({ 51 | patterns: [ 52 | { 53 | from: path.posix.join(__dirname.replace(/\\/g, "/"), "resources"), 54 | to: path.posix.join(__dirname.replace(/\\/g, "/"), "dist", "resources"), 55 | }, 56 | { 57 | from: path.posix.join(__dirname.replace(/\\/g, "/"), "CodeFlowResult"), 58 | to: path.posix.join(__dirname.replace(/\\/g, "/"), "dist", "CodeFlowResult"), 59 | }, 60 | ], 61 | }), 62 | ], 63 | }; 64 | 65 | module.exports = [extensionConfig]; 66 | --------------------------------------------------------------------------------