├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.yml │ └── feature-request.yml ├── codeql │ └── codeql-config.yml └── workflows │ ├── build-tools-build.yaml │ ├── build-tools-publish.yaml │ ├── codeql-analysis.yml │ ├── deploy-tools-build.yaml │ ├── deploy-tools-publish.yaml │ ├── docs-publish.yaml │ ├── generator-build.yaml │ ├── generator-integration-test.yaml │ └── generator-publish.yaml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── LICENSE.md ├── README.md ├── docs ├── assets │ ├── demo.gif │ ├── pnp-teams-generator-black.svg │ ├── pnp-teams-generator-ocean.png │ ├── pnp-teams-generator-ocean.svg │ └── pnp-teams-generator-white.svg ├── docs │ ├── about │ │ ├── halloffame.md │ │ ├── license.md │ │ ├── telemetry.md │ │ └── why-generator.md │ ├── concepts │ │ ├── ngrok.md │ │ └── project-structure.md │ ├── contributing │ │ ├── contribution-guidelines.md │ │ ├── getting-started.md │ │ └── minimal-path.md │ ├── images │ │ ├── contributing-didchanges.png │ │ ├── contributing-dochanges.png │ │ ├── contributing-nochanges.png │ │ ├── contributing-npminstall.png │ │ ├── contributing-npmrunbuild.png │ │ ├── contributing-run-docs-container-interactive.png │ │ ├── contributing-run-docs-container.png │ │ ├── favicon.ico │ │ ├── parker-teams-300.png │ │ ├── parker-teams.svg │ │ ├── pnp-teams-generator-black.svg │ │ ├── pnp-teams-generator-ocean.png │ │ ├── pnp-teams-generator-ocean.svg │ │ ├── pnp-teams-generator-white.svg │ │ ├── teams-bot-sso-1.png │ │ ├── teams-bot-sso-10.png │ │ ├── teams-bot-sso-11.png │ │ ├── teams-bot-sso-12.png │ │ ├── teams-bot-sso-13.png │ │ ├── teams-bot-sso-2.png │ │ ├── teams-bot-sso-3.png │ │ ├── teams-bot-sso-4.png │ │ ├── teams-bot-sso-5.png │ │ ├── teams-bot-sso-6.png │ │ ├── teams-bot-sso-7-1.png │ │ ├── teams-bot-sso-7.png │ │ ├── teams-bot-sso-8.png │ │ ├── teams-bot-sso-9.png │ │ ├── teams-connector-1.png │ │ ├── teams-connector-10.png │ │ ├── teams-connector-11.png │ │ ├── teams-connector-2.png │ │ ├── teams-connector-3.png │ │ ├── teams-connector-4.png │ │ ├── teams-connector-5.png │ │ ├── teams-connector-6.png │ │ ├── teams-connector-7.png │ │ ├── teams-connector-8.png │ │ ├── teams-connector-9.png │ │ ├── teams-first-app-1.png │ │ ├── teams-first-app-2.png │ │ ├── teams-first-app-3.png │ │ ├── teams-first-app-4.png │ │ ├── teams-first-app-5.png │ │ ├── teams-tab-sso-1.jpg │ │ ├── teams-tab-sso-2.jpg │ │ ├── teams-tab-sso-3.jpg │ │ ├── teams-tab-sso-4.jpg │ │ ├── teams-tab-sso-5.jpg │ │ ├── teams-tab-sso-6.jpg │ │ ├── teams-tab-sso-7.jpg │ │ ├── teams-tab-sso-8.jpg │ │ ├── teams-webpart-1.png │ │ ├── teams-webpart-2.png │ │ ├── teams-webpart-3.png │ │ ├── teams-webpart-4.png │ │ ├── upgrade-project.png │ │ ├── yoTeams_logo.svg │ │ └── yoTeams_logo_inverse.svg │ ├── index.md │ ├── releases │ │ ├── release-notes.md │ │ └── releases.md │ ├── tutorials │ │ ├── build-a-connector-for-microsoft-teams.md │ │ ├── build-a-sharepoint-online-provider-hosted-web-part.md │ │ ├── build-a-tab-with-sso-support.md │ │ └── build-your-first-microsoft-teams-app.md │ └── user-guide │ │ ├── bot-configuration.md │ │ ├── codespaces.md │ │ ├── connector-configuration.md │ │ ├── deploy-to-teams.md │ │ ├── outgoing-webhook-configuration.md │ │ ├── setup-machine.md │ │ ├── setup-tenant.md │ │ ├── upgrading-projects.md │ │ ├── using-multiple-manifests.md │ │ └── vscode.md └── mkdocs.yml ├── generator-teams.yml ├── package-lock.json └── packages ├── README.md ├── generator-teams ├── .editorconfig ├── .npmignore ├── CHANGELOG.md ├── README.md ├── package-lock.json ├── package.json ├── src │ ├── app │ │ ├── EmptyGuid.ts │ │ ├── GeneratorTeamsApp.ts │ │ ├── GeneratorTeamsAppOptions.ts │ │ ├── USAGE │ │ ├── Yotilities.ts │ │ ├── coreFilesUpdater │ │ │ ├── BaseCoreFilesUpdater.ts │ │ │ ├── CoreFilesUpdaterFactory.ts │ │ │ └── coreFilesUpdaters │ │ │ │ ├── CoreFilesUpdaterNop.ts │ │ │ │ └── CoreFilesUpdater_3.ts │ │ ├── index.ts │ │ ├── manifestGeneration │ │ │ ├── BaseManifestGenerator.ts │ │ │ ├── IManifestUpdater.ts │ │ │ ├── ManifestGeneratorFactory.ts │ │ │ ├── ManifestVersions.ts │ │ │ └── manifestGenerators │ │ │ │ ├── generator110 │ │ │ │ └── ManifestGenerator.ts │ │ │ │ ├── generator111 │ │ │ │ └── ManifestGenerator.ts │ │ │ │ ├── generator112 │ │ │ │ └── ManifestGenerator.ts │ │ │ │ ├── generator113 │ │ │ │ └── ManifestGenerator.ts │ │ │ │ ├── generator114 │ │ │ │ └── ManifestGenerator.ts │ │ │ │ ├── generator115 │ │ │ │ └── ManifestGenerator.ts │ │ │ │ ├── generator18 │ │ │ │ ├── BotManifestUpdater.ts │ │ │ │ ├── ConnectorManifestUpdater.ts │ │ │ │ ├── LocalizationManifestUpdater.ts │ │ │ │ ├── ManifestGenerator.ts │ │ │ │ ├── MessageExtensionManifestUpdater.ts │ │ │ │ └── TabManifestUpdater.ts │ │ │ │ ├── generator19 │ │ │ │ ├── ManifestGenerator.ts │ │ │ │ └── TabManifestUpdater.ts │ │ │ │ ├── generatorDevPreview │ │ │ │ └── ManifestGenerator.ts │ │ │ │ └── generatorM365DevPreview │ │ │ │ └── ManifestGenerator.ts │ │ └── templates │ │ │ ├── .env │ │ │ ├── Dockerfile │ │ │ ├── README.md │ │ │ ├── _eslintignore │ │ │ ├── _eslintrc.json │ │ │ ├── _gitignore │ │ │ ├── _vscode │ │ │ └── launch.json │ │ │ ├── gulpfile.js │ │ │ ├── package.json │ │ │ ├── src │ │ │ ├── client │ │ │ │ ├── .eslintrc.json │ │ │ │ ├── client.ts │ │ │ │ ├── jest.config.js │ │ │ │ └── tsconfig.json │ │ │ ├── manifest │ │ │ │ ├── icon-color.png │ │ │ │ └── icon-outline.png │ │ │ ├── public │ │ │ │ ├── assets │ │ │ │ │ └── icon.png │ │ │ │ ├── index.html │ │ │ │ ├── privacy.html │ │ │ │ ├── styles │ │ │ │ │ └── main.scss │ │ │ │ └── tou.html │ │ │ ├── server │ │ │ │ ├── .eslintrc.json │ │ │ │ ├── TeamsAppsComponents.ts │ │ │ │ ├── jest.config.js │ │ │ │ ├── server.ts │ │ │ │ └── tsconfig.json │ │ │ └── test │ │ │ │ ├── test-setup.js │ │ │ │ └── test-shim.js │ │ │ └── webpack.config.js │ ├── bot │ │ ├── BotGenerator.ts │ │ ├── index.ts │ │ └── templates │ │ │ └── src │ │ │ ├── client │ │ │ └── {botName} │ │ │ │ ├── __tests__ │ │ │ │ └── {staticTabClassName}Tab.spec.tsx │ │ │ │ └── {staticTabClassName}Tab.tsx │ │ │ ├── public │ │ │ └── {botName} │ │ │ │ └── {staticTabName}.html │ │ │ └── server │ │ │ └── {botName} │ │ │ ├── cards │ │ │ ├── welcomeCard.json │ │ │ └── welcomeCard.ts │ │ │ ├── dialogBot.ts │ │ │ ├── dialogs │ │ │ ├── __tests__ │ │ │ │ └── HelpDialog.spec.ts │ │ │ ├── helpDialog.ts │ │ │ ├── mainDialog.ts │ │ │ ├── mentionUserDialog.ts │ │ │ └── teamsInfoDialog.ts │ │ │ └── {botClassName}.ts │ ├── connector │ │ ├── ConnectorGenerator.ts │ │ ├── index.ts │ │ └── templates │ │ │ └── src │ │ │ ├── client │ │ │ └── {connectorName} │ │ │ │ ├── __tests__ │ │ │ │ └── {connectorComponentName}Config.spec.tsx │ │ │ │ └── {connectorComponentName}Config.tsx │ │ │ ├── public │ │ │ └── {connectorName} │ │ │ │ └── config.html │ │ │ └── server │ │ │ └── {connectorName} │ │ │ └── {connectorComponentName}.ts │ ├── custombot │ │ ├── CustomBotGenerator.ts │ │ ├── index.ts │ │ └── templates │ │ │ └── src │ │ │ └── server │ │ │ └── {customBotName} │ │ │ └── {customBotClassName}.ts │ ├── localization │ │ ├── LocalizationGenerator.ts │ │ └── index.ts │ ├── messageExtension │ │ ├── MessageExtensionGenerator.ts │ │ ├── index.ts │ │ └── templates │ │ │ └── src │ │ │ ├── client │ │ │ └── {messageExtensionName} │ │ │ │ ├── __tests__ │ │ │ │ └── {messageExtensionClassName}Config.spec.tsx │ │ │ │ ├── {messageExtensionClassName}Action.tsx │ │ │ │ └── {messageExtensionClassName}Config.tsx │ │ │ ├── public │ │ │ └── {messageExtensionName} │ │ │ │ ├── action.html │ │ │ │ └── config.html │ │ │ └── server │ │ │ └── {messageExtensionName} │ │ │ ├── __tests__ │ │ │ └── {messageExtensionClassName}.spec.ts │ │ │ └── {messageExtensionClassName}.ts │ └── tab │ │ ├── TabGenerator.ts │ │ ├── index.ts │ │ └── templates │ │ └── src │ │ ├── client │ │ └── {tabName} │ │ │ ├── __tests__ │ │ │ ├── {tabReactComponentName}.spec.tsx │ │ │ ├── {tabReactComponentName}Config.spec.tsx │ │ │ └── {tabReactComponentName}Remove.spec.tsx │ │ │ ├── {tabReactComponentName}.tsx │ │ │ ├── {tabReactComponentName}Config.tsx │ │ │ └── {tabReactComponentName}Remove.tsx │ │ ├── public │ │ ├── assets │ │ │ └── tab-preview.png │ │ └── {tabName} │ │ │ ├── config.html │ │ │ ├── index.html │ │ │ └── remove.html │ │ └── server │ │ └── {tabName} │ │ └── {tabReactComponentName}.ts ├── tests │ ├── BotGenerator.spec.ts │ ├── ConnectorGenerator.spec.ts │ ├── LocalizationGenerator.spec.ts │ ├── MessageExtensionGenerator.spec.ts │ ├── generic.spec.ts │ ├── helpers │ │ └── TestHelper.ts │ ├── tabGenerator.spec.ts │ └── tests.json ├── tsconfig.json └── webpack.config.js ├── yoteams-build-core ├── .eslintrc.json ├── .npmignore ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── package-lock.json ├── package.json ├── src │ ├── buildTasks.ts │ ├── codespacesTasks.ts │ ├── iBuildCoreConfig.ts │ ├── index.ts │ ├── loadPlugins.ts │ ├── manifest.ts │ ├── ngrokTasks.ts │ ├── nukeTasks.ts │ ├── schemas.json │ ├── serveTasks.ts │ ├── styleTasks.ts │ ├── types.d.ts │ ├── webTasks.ts │ ├── webpackServe.ts │ └── webpackTasks.ts └── tsconfig.json └── yoteams-deploy ├── .eslintrc.json ├── .npmignore ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── package-lock.json ├── package.json ├── src ├── deployTask.ts ├── execute.ts └── index.ts └── tsconfig.json /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.yml: -------------------------------------------------------------------------------- 1 | name: Feature request 2 | description: Let us know anything that we can do better or that could benefit YoTeams 3 | title: "Feature request: " 4 | labels: ["request: feature"] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Thank you for suggesting an idea to make Yo Teams better. 10 | 11 | Please complete the below form to ensure we have all the details to get things started. 12 | - type: textarea 13 | id: solution 14 | attributes: 15 | label: 💡 Idea 16 | description: | 17 | Please describe the desired behavior, pitch your idea, or suggest improvements 18 | validations: 19 | required: true 20 | - type: input 21 | id: bugrelation 22 | attributes: 23 | label: Is your feature related to a bug 24 | description: | 25 | Refer to a an existing bug, you can use `#bugid` 26 | validations: 27 | required: true 28 | - type: textarea 29 | id: alternatives 30 | attributes: 31 | label: Alternatives 32 | description: | 33 | Have you considered alternative solutions or implementations? Do you have an idea on how this should be implemented? Let us know! 34 | - type: textarea 35 | id: info 36 | attributes: 37 | label: Additional Info 38 | description: | 39 | Provide additional information or links to resources that can help with the creation of this command. 40 | validations: 41 | required: false 42 | -------------------------------------------------------------------------------- /.github/codeql/codeql-config.yml: -------------------------------------------------------------------------------- 1 | paths: 2 | - packages/generator-teams/generators 3 | - packages/yoteams-build-core/dist 4 | - packages/yoteams-deploy/dist 5 | -------------------------------------------------------------------------------- /.github/workflows/build-tools-build.yaml: -------------------------------------------------------------------------------- 1 | name: Build yoteams-build-core 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | strategy: 11 | matrix: 12 | node-version: [16.x, 18.x] 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Use Node.js ${{ matrix.node-version }} 17 | uses: actions/setup-node@v1 18 | with: 19 | node-version: ${{ matrix.node-version }} 20 | - run: npm install 21 | working-directory: packages/yoteams-build-core 22 | - run: npm run build 23 | working-directory: packages/yoteams-build-core 24 | env: 25 | CI: true 26 | -------------------------------------------------------------------------------- /.github/workflows/build-tools-publish.yaml: -------------------------------------------------------------------------------- 1 | name: Publish yoteams-build-core 2 | on: 3 | release: 4 | types: [created] 5 | jobs: 6 | publish: 7 | # Only do this for releases with the tag generator-teams 8 | if: "contains(github.event.release.tag_name, 'yoteams-build-core')" 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - uses: actions/setup-node@v1 13 | with: 14 | node-version: '16.x' 15 | registry-url: 'https://registry.npmjs.org' 16 | - run: npm install 17 | working-directory: packages/yoteams-build-core 18 | - run: npm run build 19 | working-directory: packages/yoteams-build-core 20 | - name: Publish release 21 | if: "!contains(github.ref, 'preview')" 22 | run: npm publish 23 | working-directory: packages/yoteams-build-core 24 | env: 25 | NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }} 26 | - name: Publish preview 27 | if: contains(github.ref, 'preview') 28 | run: npm publish --tag preview 29 | working-directory: packages/yoteams-build-core 30 | env: 31 | NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }} 32 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ main ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ main ] 20 | schedule: 21 | - cron: '17 22 * * 2' 22 | workflow_dispatch: 23 | 24 | jobs: 25 | analyze: 26 | name: Analyze 27 | runs-on: ubuntu-latest 28 | permissions: 29 | actions: read 30 | contents: read 31 | security-events: write 32 | 33 | strategy: 34 | fail-fast: false 35 | matrix: 36 | language: [ 'javascript' ] 37 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 38 | # Learn more about CodeQL language support at https://git.io/codeql-language-support 39 | 40 | steps: 41 | - name: Checkout repository 42 | uses: actions/checkout@v2 43 | 44 | # Initializes the CodeQL tools for scanning. 45 | - name: Initialize CodeQL 46 | uses: github/codeql-action/init@v2 47 | with: 48 | languages: ${{ matrix.language }} 49 | # If you wish to specify custom queries, you can do so here or in a config file. 50 | # By default, queries listed here will override any specified in a config file. 51 | # Prefix the list here with "+" to use these queries and those in the config file. 52 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 53 | 54 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 55 | # If this step fails, then you should remove it and run the build manually (see below) 56 | # - name: Autobuild 57 | # uses: github/codeql-action/autobuild@v2 58 | 59 | # ℹ️ Command-line programs to run using the OS shell. 60 | # 📚 https://git.io/JvXDl 61 | 62 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 63 | # and modify them (or add more) to build your code if your project 64 | # uses a compiled language 65 | 66 | #- run: | 67 | # make bootstrap 68 | # make release 69 | 70 | - name: Use Node.js 16.x 71 | uses: actions/setup-node@v1 72 | with: 73 | node-version: 16.x 74 | 75 | - run: npm install 76 | working-directory: packages/yoteams-build-core 77 | - run: npm run build 78 | working-directory: packages/yoteams-build-core 79 | env: 80 | CI: true 81 | 82 | - run: npm install 83 | working-directory: packages/yoteams-deploy 84 | - run: npm run build 85 | working-directory: packages/yoteams-deploy 86 | env: 87 | CI: true 88 | 89 | - run: npm install 90 | working-directory: packages/generator-teams 91 | - run: npm run build 92 | working-directory: packages/generator-teams 93 | env: 94 | CI: true 95 | 96 | 97 | - name: Perform CodeQL Analysis 98 | uses: github/codeql-action/analyze@v2 99 | with: 100 | config-file: ./.github/codeql/codeql-config.yml 101 | -------------------------------------------------------------------------------- /.github/workflows/deploy-tools-build.yaml: -------------------------------------------------------------------------------- 1 | name: Build yoteams-deploy 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | strategy: 11 | matrix: 12 | node-version: [16.x, 18.x] 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Use Node.js ${{ matrix.node-version }} 17 | uses: actions/setup-node@v1 18 | with: 19 | node-version: ${{ matrix.node-version }} 20 | - run: npm install 21 | working-directory: packages/yoteams-deploy 22 | - run: npm run build 23 | working-directory: packages/yoteams-deploy 24 | env: 25 | CI: true 26 | -------------------------------------------------------------------------------- /.github/workflows/deploy-tools-publish.yaml: -------------------------------------------------------------------------------- 1 | name: Publish yoteams-deploy 2 | on: 3 | release: 4 | types: [created] 5 | jobs: 6 | publish: 7 | # Only do this for releases with the tag generator-teams 8 | if: "contains(github.event.release.tag_name, 'yoteams-deploy')" 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - uses: actions/setup-node@v1 13 | with: 14 | node-version: '16.x' 15 | registry-url: 'https://registry.npmjs.org' 16 | - run: npm install 17 | working-directory: packages/yoteams-deploy 18 | - run: npm run build 19 | working-directory: packages/yoteams-deploy 20 | - name: Publish release 21 | if: "!contains(github.ref, 'preview')" 22 | run: npm publish 23 | working-directory: packages/yoteams-deploy 24 | env: 25 | NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }} 26 | - name: Publish preview 27 | if: contains(github.ref, 'preview') 28 | run: npm publish --tag preview 29 | working-directory: packages/yoteams-deploy 30 | env: 31 | NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }} 32 | -------------------------------------------------------------------------------- /.github/workflows/docs-publish.yaml: -------------------------------------------------------------------------------- 1 | name: Deploy GitHub Pages website 2 | on: 3 | push: 4 | branches: 5 | - main 6 | jobs: 7 | deploy: 8 | runs-on: ubuntu-latest 9 | 10 | # Only when run from the main repo 11 | if: github.repository == 'pnp/generator-teams' 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: actions/setup-python@v2 16 | with: 17 | python-version: 3.x 18 | - run: | 19 | pip install mkdocs-material 20 | - name: Deploy 21 | working-directory: ./docs 22 | run: mkdocs gh-deploy --force 23 | -------------------------------------------------------------------------------- /.github/workflows/generator-build.yaml: -------------------------------------------------------------------------------- 1 | name: Build generator-teams 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | strategy: 11 | matrix: 12 | node-version: [16.x, 18.x] 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Use Node.js ${{ matrix.node-version }} 17 | uses: actions/setup-node@v1 18 | with: 19 | node-version: ${{ matrix.node-version }} 20 | - run: npm install 21 | working-directory: packages/generator-teams 22 | - run: npm run test 23 | working-directory: packages/generator-teams 24 | - run: npm run build 25 | working-directory: packages/generator-teams 26 | env: 27 | CI: true 28 | -------------------------------------------------------------------------------- /.github/workflows/generator-integration-test.yaml: -------------------------------------------------------------------------------- 1 | name: Integration test for generator-teams 2 | 3 | on: workflow_dispatch 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | timeout-minutes: 600 10 | strategy: 11 | matrix: 12 | node-version: [16.x, 18.x] 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Use Node.js ${{ matrix.node-version }} 17 | uses: actions/setup-node@v1 18 | with: 19 | node-version: ${{ matrix.node-version }} 20 | - run: npm install 21 | working-directory: packages/generator-teams 22 | - run: npm run test-integration 23 | working-directory: packages/generator-teams 24 | env: 25 | CI: true 26 | -------------------------------------------------------------------------------- /.github/workflows/generator-publish.yaml: -------------------------------------------------------------------------------- 1 | name: Publish generator-teams 2 | on: 3 | release: 4 | types: [created] 5 | jobs: 6 | publish: 7 | # Only do this for releases with the tag generator-teams 8 | if: "contains(github.event.release.tag_name, 'generator-teams')" 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - uses: actions/setup-node@v1 13 | with: 14 | node-version: '16.x' 15 | registry-url: 'https://registry.npmjs.org' 16 | - run: npm install 17 | working-directory: packages/generator-teams 18 | - run: npm run build 19 | working-directory: packages/generator-teams 20 | - name: Publish release 21 | if: "!contains(github.ref, 'preview')" 22 | run: npm publish 23 | working-directory: packages/generator-teams 24 | env: 25 | NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }} 26 | - name: Publish preview 27 | if: "contains(github.ref, 'preview')" 28 | run: npm publish --tag preview 29 | working-directory: packages/generator-teams 30 | env: 31 | NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }} 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | node_modules 3 | generators 4 | temp-templates 5 | *.tgz 6 | dist 7 | 8 | # generated docs 9 | docs/site 10 | 11 | # VS env folder 12 | .vs/ -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Wictor Wilén. All rights reserved. 4 | Copyright (c) Microsoft Corporation. All rights reserved. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE 23 | -------------------------------------------------------------------------------- /docs/assets/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/assets/demo.gif -------------------------------------------------------------------------------- /docs/assets/pnp-teams-generator-ocean.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/assets/pnp-teams-generator-ocean.png -------------------------------------------------------------------------------- /docs/docs/about/halloffame.md: -------------------------------------------------------------------------------- 1 | # yo Teams Hall of fame 2 | 3 | * [Wictor Wilén](https://github.com/wictorwilen) - Original author and coordinator 4 | 5 | ## Project coordinators & maintainers 6 | * [Rick Van Rousselt](https://github.com/rickvanrousselt) 7 | * [Albert-Jan Schot](https://github.com/appieschot) 8 | * [Thomas Gölles](https://github.com/thomyg) 9 | * [Stephan Bisser](https://github.com/stephanbisser) 10 | 11 | ## Contributors 12 | * [Bill Bliss](https://github.com/billbliss) 13 | * [Richard DiZerega](https://github.com/richdizz) 14 | * [Elaine van Bergen](https://github.com/laneyvb) 15 | * [Daniel Laskewitz](https://github.com/Laskewitz) 16 | * [Paul Schaeflein](https://github.com/pschaeflein) 17 | * [Cagdas Davulcu](https://github.com/cagdasdavulcu) 18 | * [Stefan Bauer](https://github.com/StfBauer) 19 | * [Andrew Connell](https://github.com/andrewconnell) 20 | * [Ralf Brennscheidt](https://github.com/RalfBrennscheidt) 21 | * [Felipe Plets](https://github.com/felipeplets) 22 | * [Brendan Andrade](https://github.com/BrendanAndrade) 23 | * [Jason Merino](https://github.com/jasonmerino) 24 | * [Orta Therox](https://github.com/orta) 25 | * [Oleksandr Fediashov](https://github.com/layershifter) 26 | * [Paolo Pialorsi](https://github.com/PaoloPia) 27 | * [Nanddeep Nachan](https://github.com/nanddeepn) 28 | * [Sergei Sergeev](https://github.com/s-KaiNet) 29 | * [Elia](https://github.com/twopill) 30 | * [Luise Freese](https://github.com/LuiseFreese) 31 | * [Robert Pocklington](https://github.com/rpocklin) 32 | -------------------------------------------------------------------------------- /docs/docs/about/license.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 4 | 5 | MIT License 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining 8 | a copy of this software and associated documentation files (the 9 | "Software"), to deal in the Software without restriction, including 10 | without limitation the rights to use, copy, modify, merge, publish, 11 | distribute, sublicense, and/or sell copies of the Software, and to 12 | permit persons to whom the Software is furnished to do so, subject to 13 | the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /docs/docs/about/telemetry.md: -------------------------------------------------------------------------------- 1 | # Telemetry 2 | 3 | The `generator-teams` package and the build packages (`yoteams-build-core` and `yoteams-deploy`) includes a telemetry feature that collects usage data and exception information when the generator or build package tasks crashes. It's important that the `generator-teams` team understands how the generator and the build packages are used so they can be improved. 4 | 5 | ## Scope 6 | 7 | `generator-teams` *is collecting* telemetry about what configuration is used when scaffolding a new project. 8 | 9 | The build packages *is collecting* telemetry about what tasks are being used when building or debugging a project. 10 | 11 | Telemetry *isn't* collected from using the generated project or when the generated project is running. 12 | 13 | ## How to opt out 14 | 15 | Telemetry is enabled by default. To opt out of the telemetry feature set the `YOTEAMS_TELEMETRY_OPTOUT` environment variable to ´1´ or ´true´. To opt out of telemetry feature when scaffolding a project use the `-no-telemetry` flag in combination with the `yo teams` command. 16 | 17 | ## Data points 18 | 19 | The telemetry feature doesn't collect personal data, such as usernames or email addresses. It does not scan your code and does not extract project-level data, such as name, repository, or author. The data is sent and secured securely to Microsoft servers using the Azure Monitor technology. 20 | 21 | The following data is collected: 22 | 23 | * Name and arguments of Gulp tasks used 24 | * Version of `generator-teams` used 25 | * Version of `yoteams-*` build and helper packages used 26 | * Client type and operating system 27 | * Location (city, province and country) 28 | * Configuration options (excluding any values) when scaffolding a project 29 | 30 | Protecting your privacy is important to us. If you suspect telemetry is collecting sensitive data or the data is being insecurely or inappropriately handled, file an issue in the [pnp/generator-teams](https://aka.ms/yoteams) repository. 31 | -------------------------------------------------------------------------------- /docs/docs/about/why-generator.md: -------------------------------------------------------------------------------- 1 | # Why Yo Teams - the Microsoft Teams app generator? 2 | 3 | A [Yeoman Generator](http://yeoman.io/) for [Microsoft Teams](https://teams.microsoft.com) Apps projects. **The most comprehensive and complete development toolkit for Microsoft Teams development**. Yo Teams allows you to build Microsoft Teams applications on any platform based on TypeScript and node.js on your terms, in your editor of choice, without any external or online dependencies. 4 | 5 | This generator is built and maintained by the community, for the community. 6 | 7 | ## Supportability and SLA 8 | This library is open-source and community provided library with active community providing support for it. This is not Microsoft provided module so there's no SLA or direct support for this open-source component from Microsoft. Please report any issues using the [issues list](https://github.com/pnp/generator-teams/issues). -------------------------------------------------------------------------------- /docs/docs/concepts/ngrok.md: -------------------------------------------------------------------------------- 1 | # Using ngrok for local development and hosting 2 | 3 | In order to make development locally a great experience it is recommended to use [ngrok](https://ngrok.io), which allows you to publish the localhost on a public DNS, so that you can consume the bot and the other resources in Microsoft Teams. 4 | 5 | Microsoft Teams is a cloud-based product and requires that your tab content is available from the cloud using HTTPS endpoints. Teams doesn't allow local hosting. Publish your tab to a public URL or use a proxy that exposes your local port to an internet-facing URL. 6 | 7 | To use ngrok, it is recommended to use the `gulp ngrok-serve` command, which will read your ngrok settings from the `.env` file and automatically create a correct manifest file and finally start a local development server using the ngrok settings. 8 | 9 | ## Note on ngrok and WSL2 10 | 11 | When running a Yo Teams generated solution on WSL2 with ngrok you might experience issues like *Failed to complete tunnel connection*. Try running ngrok as a separate process pointing to your local IP instead of localhost. For more information see: https://github.com/pnp/generator-teams/issues/189 -------------------------------------------------------------------------------- /docs/docs/concepts/project-structure.md: -------------------------------------------------------------------------------- 1 | # Project Structure 2 | 3 | The generated project from the Microsoft Teams Apps generator has the following project and file/folder structure. 4 | 5 | ## The root folder 6 | 7 | The root folder contains all the required files you need to build and run your project. 8 | 9 | - `gulpfile.js` contains configuration for the Gulp tasks. See [`yoteams-core-build`](https://github.com/pnp/generator-teams/tree/master/packages/yoteams-build-core) for more information 10 | - `README-XXX.md` automatically generated readme file to help you get started 11 | - `.env` contains replaceable strings that will be used throughout your project and make it easier for you to move projects between environments. 12 | - `Dockerfile` a pre-configured Docker file 13 | 14 | ## The `src` folder 15 | 16 | The source folder contains your code and is divided into the following folders: 17 | 18 | - `client`: contains clients side TypeScript code 19 | - `manifest`: contains the Microsoft Teams App manifest and icons 20 | - `public`: contains static web site files 21 | - `server`: contains server side code 22 | - `test`: contains test setup files. *Note*: only added if tests are chosen during initial scaffolding 23 | 24 | ### The `manifest` folder 25 | 26 | The `manifest` folder contains the manifest file (`manifest.json`) as well as the two required logos. The manifest file uses replaceable tokens. 27 | 28 | ### The `client` folder 29 | 30 | The `client` folder contains the client side React components. All scaffolded client side components are automatically added to `client.ts`. 31 | 32 | ### The `server` folder 33 | 34 | The `server` folder contains the server side [Express](https://www.npmjs.com/package/express) application, defined in `server.ts`. 35 | 36 | The `TeamsAppsComponents.ts` file is used to export all classes for automatic Express routing detection, that uses the [express-msteams-host](https://www.npmjs.com/package/express-msteams-host) npm package for the routing setup. 37 | 38 | ### The `public` folder 39 | 40 | The folder called `public` contains all the files required for the web application, such as html, assets and css files. -------------------------------------------------------------------------------- /docs/docs/contributing/contribution-guidelines.md: -------------------------------------------------------------------------------- 1 | # Contribution guidelines 2 | 3 | **#yoTeams** is a community-driven initiative which is open for new ideas as well as for volunteers helping us make it even better and better. Therefore, if you are interested in contributing to this project, we'd like you to read through the contribution guidelines so you know how to contribute to this project. 4 | 5 | ## You have a new idea for #yoTeams 6 | 7 | Great, we always love to add more features and capabilities to #yoTeams! But in case that there is already the same or a similar idea listed in our [issue list](https://github.com/pnp/generator-teams/issues) we would like you to check that list before you submit a new issue. If your ideas is not in that list, then please add a new issue in the issue list of type `Feature request` and fill out the form with your idea (please describe it as precise as possible so we have a clear understanding what you want to achieve with it). 8 | 9 | > NOTE: If you want to add the idea or feature to the project yourself, please just state that in the issue form. 10 | 11 | ## You have found a bug in #yoTeams 12 | 13 | We try our best to avoid any bugs, but sometimes they do happen though. So if you encounter a bug while using #yoTeams please check if that bug is already part of our [issue list](https://github.com/pnp/generator-teams/issues) and if not we would like to know what problems you encountered. So feel free to add a new issue of type `Bug report` in the [issue list](https://github.com/pnp/generator-teams/issues) along with information about the bug itself and how to reproduce it. If there is anything unclear to us or we cannot reproduce the bug, we will ask you for clarification to get things sorted out. 14 | 15 | ## Fixing typos 16 | 17 | Typos are embarrassing! Most PR's that fix typos will be accepted immediately. In order to make it easier to review the PR, please narrow the focus instead of sending a huge PR of fixes. 18 | 19 | ## DO's & DON'Ts 20 | 21 | - **DO** follow the same project and test structure as the existing project. 22 | - **DO** include tests when adding new functionality and features. When fixing bugs, start with adding a test that highlights how the current behavior is broken. 23 | - **DO** keep discussions focused. When a new or related topic comes up it's often better to create new issue than to side track the conversation. 24 | - **DO NOT** submit PR's for coding style changes. 25 | - **DO NOT** surprise us with big PR's. Instead file an issue & start a discussion so we can agree on a direction before you invest a large amount of time. 26 | - **DO NOT** commit code you didn't write. 27 | - **DO NOT** submit PR's that refactor existing code without a discussion first. -------------------------------------------------------------------------------- /docs/docs/contributing/getting-started.md: -------------------------------------------------------------------------------- 1 | If you are not currently familiar with Yeoman generator development you will find some helpful links. 2 | 3 | ## Basic technologies to know 4 | 5 | * [Write your own Yeoman generator](http://yeoman.io/authoring/) - This article provides you with basic insights on how to write a custom Yeoman generator 6 | 7 | * [Mocha getting started](https://mochajs.org/#getting-started) - To test that the Yeoman generator is working properly a testing framework named Mocha is used 8 | 9 | * [Embedded JavaScript templating](http://ejs.co) - EJS is the core engine used in Yeoman to deploy template files and dynamically embed custom settings in the files getting deployed 10 | 11 | ## Places to look for help 12 | The JavaScript a Yeoman generator uses is based on Node.js and many things are treated differently than in the JavaScript that exists in browsers. 13 | 14 | * [Yeoman API Documentation](http://yeoman.io/generator/) - 15 | Besides the getting started guide, there is official documentation available that covers the complete Yeoman API 16 | 17 | * [Node.js documentation](https://nodejs.org/en/docs/) - 18 | In general, a Yeoman generator is written in JavaScript but it is based on Node.js where the development patterns are slightly different. In some cases it might be handy to consult the Node.js documentation. 19 | -------------------------------------------------------------------------------- /docs/docs/images/contributing-didchanges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/contributing-didchanges.png -------------------------------------------------------------------------------- /docs/docs/images/contributing-dochanges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/contributing-dochanges.png -------------------------------------------------------------------------------- /docs/docs/images/contributing-nochanges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/contributing-nochanges.png -------------------------------------------------------------------------------- /docs/docs/images/contributing-npminstall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/contributing-npminstall.png -------------------------------------------------------------------------------- /docs/docs/images/contributing-npmrunbuild.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/contributing-npmrunbuild.png -------------------------------------------------------------------------------- /docs/docs/images/contributing-run-docs-container-interactive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/contributing-run-docs-container-interactive.png -------------------------------------------------------------------------------- /docs/docs/images/contributing-run-docs-container.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/contributing-run-docs-container.png -------------------------------------------------------------------------------- /docs/docs/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/favicon.ico -------------------------------------------------------------------------------- /docs/docs/images/parker-teams-300.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/parker-teams-300.png -------------------------------------------------------------------------------- /docs/docs/images/pnp-teams-generator-ocean.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/pnp-teams-generator-ocean.png -------------------------------------------------------------------------------- /docs/docs/images/teams-bot-sso-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-bot-sso-1.png -------------------------------------------------------------------------------- /docs/docs/images/teams-bot-sso-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-bot-sso-10.png -------------------------------------------------------------------------------- /docs/docs/images/teams-bot-sso-11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-bot-sso-11.png -------------------------------------------------------------------------------- /docs/docs/images/teams-bot-sso-12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-bot-sso-12.png -------------------------------------------------------------------------------- /docs/docs/images/teams-bot-sso-13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-bot-sso-13.png -------------------------------------------------------------------------------- /docs/docs/images/teams-bot-sso-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-bot-sso-2.png -------------------------------------------------------------------------------- /docs/docs/images/teams-bot-sso-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-bot-sso-3.png -------------------------------------------------------------------------------- /docs/docs/images/teams-bot-sso-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-bot-sso-4.png -------------------------------------------------------------------------------- /docs/docs/images/teams-bot-sso-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-bot-sso-5.png -------------------------------------------------------------------------------- /docs/docs/images/teams-bot-sso-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-bot-sso-6.png -------------------------------------------------------------------------------- /docs/docs/images/teams-bot-sso-7-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-bot-sso-7-1.png -------------------------------------------------------------------------------- /docs/docs/images/teams-bot-sso-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-bot-sso-7.png -------------------------------------------------------------------------------- /docs/docs/images/teams-bot-sso-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-bot-sso-8.png -------------------------------------------------------------------------------- /docs/docs/images/teams-bot-sso-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-bot-sso-9.png -------------------------------------------------------------------------------- /docs/docs/images/teams-connector-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-connector-1.png -------------------------------------------------------------------------------- /docs/docs/images/teams-connector-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-connector-10.png -------------------------------------------------------------------------------- /docs/docs/images/teams-connector-11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-connector-11.png -------------------------------------------------------------------------------- /docs/docs/images/teams-connector-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-connector-2.png -------------------------------------------------------------------------------- /docs/docs/images/teams-connector-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-connector-3.png -------------------------------------------------------------------------------- /docs/docs/images/teams-connector-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-connector-4.png -------------------------------------------------------------------------------- /docs/docs/images/teams-connector-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-connector-5.png -------------------------------------------------------------------------------- /docs/docs/images/teams-connector-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-connector-6.png -------------------------------------------------------------------------------- /docs/docs/images/teams-connector-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-connector-7.png -------------------------------------------------------------------------------- /docs/docs/images/teams-connector-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-connector-8.png -------------------------------------------------------------------------------- /docs/docs/images/teams-connector-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-connector-9.png -------------------------------------------------------------------------------- /docs/docs/images/teams-first-app-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-first-app-1.png -------------------------------------------------------------------------------- /docs/docs/images/teams-first-app-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-first-app-2.png -------------------------------------------------------------------------------- /docs/docs/images/teams-first-app-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-first-app-3.png -------------------------------------------------------------------------------- /docs/docs/images/teams-first-app-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-first-app-4.png -------------------------------------------------------------------------------- /docs/docs/images/teams-first-app-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-first-app-5.png -------------------------------------------------------------------------------- /docs/docs/images/teams-tab-sso-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-tab-sso-1.jpg -------------------------------------------------------------------------------- /docs/docs/images/teams-tab-sso-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-tab-sso-2.jpg -------------------------------------------------------------------------------- /docs/docs/images/teams-tab-sso-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-tab-sso-3.jpg -------------------------------------------------------------------------------- /docs/docs/images/teams-tab-sso-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-tab-sso-4.jpg -------------------------------------------------------------------------------- /docs/docs/images/teams-tab-sso-5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-tab-sso-5.jpg -------------------------------------------------------------------------------- /docs/docs/images/teams-tab-sso-6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-tab-sso-6.jpg -------------------------------------------------------------------------------- /docs/docs/images/teams-tab-sso-7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-tab-sso-7.jpg -------------------------------------------------------------------------------- /docs/docs/images/teams-tab-sso-8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-tab-sso-8.jpg -------------------------------------------------------------------------------- /docs/docs/images/teams-webpart-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-webpart-1.png -------------------------------------------------------------------------------- /docs/docs/images/teams-webpart-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-webpart-2.png -------------------------------------------------------------------------------- /docs/docs/images/teams-webpart-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-webpart-3.png -------------------------------------------------------------------------------- /docs/docs/images/teams-webpart-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/teams-webpart-4.png -------------------------------------------------------------------------------- /docs/docs/images/upgrade-project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/docs/docs/images/upgrade-project.png -------------------------------------------------------------------------------- /docs/docs/releases/release-notes.md: -------------------------------------------------------------------------------- 1 | # Release notes 2 | 3 | For Release notes please refer to the [Yo Teams Github Releases page](https://github.com/pnp/generator-teams/releases) -------------------------------------------------------------------------------- /docs/docs/releases/releases.md: -------------------------------------------------------------------------------- 1 | # Release overview 2 | 3 | This is the release history of the Microsoft Teams Apps generator(`generator-teams`). 4 | 5 | Detailed descriptions can always be found in the [CHANGELOG.md](https://github.com/pnp/generator-teams/blob/master/packages/generator-teams/CHANGELOG.md) file or each branch. 6 | 7 | ## Release notes 8 | 9 | | Version | Release date | Upgrades | Tag | Notes | 10 | | - | - | -| - | - | 11 | | [2.7.1](./Release-notes-2.7.1) | 2019-05-08 | n/a ||| 12 | | [2.8.0](./Release-notes-2.8.0) | 2019-05-20 | n/a ||| 13 | | [2.9.0](./Release-notes-2.9.0) | 2019-06-14 | to 2.11.1 || *deprecated* | 14 | | [2.9.1](./Release-notes-2.9.1) | 2019-06-15 | to 2.11.1 ||| 15 | | [2.10.0](./Release-notes-2.10.0) | 2019-07-18 | to 2.11.1 ||| 16 | | [2.11.0](./Release-notes-2.11.0) | 2019-08-06 | to 2.11.1 || *deprecated* | 17 | | [2.11.1](./Release-notes-2.11.1) | 2019-08-14 | n/a | || 18 | | [2.12.0](./Release-notes-2.12.0) | 2020-03-15 | to 2.14.0 ||| 19 | | [2.13.0](./Release-notes-2.13.0) | 2020-04-16 | to 2.14.0 ||| 20 | | [2.14.0](./Release-notes-2.14.0) | 2020-05-18| to 2.16.0 ||| 21 | | [2.15.0](./Release-notes-2.15.0) | 2020-06-20| to 2.16.0 ||| 22 | | [2.16.0](./Release-notes-2.16.0) | 2020-10-28| ||| 23 | | [2.17.1](./Release-notes-2.16.0) | 2020-12-07| n/a ||| 24 | | [3.0.0](./Release-notes-3.0.0) | 2021-02-07| to 3.3.0 ||| 25 | | [3.0.1](https://github.com/pnp/generator-teams/releases/tag/generator-teams%403.0.1) | 2021-02-11| to 3.3.0 ||| 26 | | [3.0.2](https://github.com/pnp/generator-teams/releases/tag/generator-teams%403.0.2) | 2021-02-24| to 3.3.0 ||| 27 | | [3.0.3](https://github.com/pnp/generator-teams/releases/tag/generator-teams%403.0.3) | 2021-03-02| to 3.3.0 ||| 28 | | [3.1.0](https://github.com/pnp/generator-teams/releases/tag/generator-teams%403.1.0) | 2021-05-21| to 3.3.0 ||| 29 | | [3.2.0](https://github.com/pnp/generator-teams/releases/tag/generator-teams%403.2.0) | 2021-06-07| to 3.3.0 ||| 30 | | [3.3.0](https://github.com/pnp/generator-teams/releases/tag/generator-teams%403.3.0) | 2021-09-30| to 3.5.0 ||| 31 | | [3.3.0](https://github.com/pnp/generator-teams/releases/tag/generator-teams%403.4.0) | 2021-10-28| to 3.5.0 ||| 32 | | [3.3.0](https://github.com/pnp/generator-teams/releases/tag/generator-teams%403.5.0) | 2021-11-18| to 3.5.0 ||| 33 | | [4.0.0](https://github.com/pnp/generator-teams/releases/tag/generator-teams%404.0.0) | 2022-05-25| n/a ||| 34 | 35 | 36 | ## How to install a specific version 37 | 38 | Execute the following using the version or tag of your choice 39 | 40 | Using version: 41 | 42 | ``` bash 43 | npm install -g generator-teams@3.2.0 44 | ``` 45 | 46 | Using tag: 47 | 48 | ``` bash 49 | npm install -g generator-teams@latest 50 | ``` 51 | 52 | ## How to run a specific version using npx 53 | 54 | Install `npx` globally 55 | 56 | ``` bash 57 | npm install -g npx 58 | ``` 59 | 60 | Execute the following using the version of your choice 61 | 62 | ``` bash 63 | npx -p yo -p generator-teams@3.2.0 -- yo teams 64 | ``` 65 | -------------------------------------------------------------------------------- /docs/docs/user-guide/bot-configuration.md: -------------------------------------------------------------------------------- 1 | # Bots and Messaging Extension Configuration 2 | 3 | If you are creating a bot or a messaging extension as a part of your Microsoft Teams app you also need to register an *Azure Bot* service in *Microsoft Azure*, before you configure and run your application. 4 | 5 | ## How to prepare and create resources in Microsoft Azure 6 | 7 | * Create or use an existing Resource Group 8 | * Add a new *Azure Bot* resource to the resource group 9 | * Fill in the basic details 10 | * In the *Type of App* you must choose *Multi-tenant* or *Single-tenant*. Using a *Managed Identity* is currently not supported 11 | * After the resource has been created you need to update the *Messaging endpoint* in the Bot *Configuration*. It should contain the URL of where you are hosting your Teams Application and point to the `/api/messages`endpoint.Example: `https://0cbfd984d7b1.ngrok.io/api/messages` 12 | * Ensure to configure the *Microsoft Teams* channel under *Channels*. If you're building a Messaging Extension for Outlook you also need to enable the *Outlook* channel. 13 | * Under *Configuration* you should take a note of the *Microsoft App ID* and also click on *Manage* and under *Certificate & secrets* create a new secret. 14 | * Copy the App ID and secret and update your `.env` file as follows: 15 | 16 | ``` 17 | MICROSOFT_APP_ID= 18 | MICROSOFT_APP_PASSWORD= 19 | ``` 20 | -------------------------------------------------------------------------------- /docs/docs/user-guide/codespaces.md: -------------------------------------------------------------------------------- 1 | # Development with Github Codespaces 2 | 3 | The Yo Teams Gulp tasks will automatically detect if you're running a Yo Teams project inside a Github Codespace. 4 | 5 | The `PUBLIC_HOSTNAME` will automatically be set as the public DNS name of your Github Codespace when running `gulp codespace-serve`. The only manual step is to make the exposed port (3007 by default) Public (default is private). 6 | -------------------------------------------------------------------------------- /docs/docs/user-guide/connector-configuration.md: -------------------------------------------------------------------------------- 1 | # Connector Configuration 2 | 3 | The Yo Teams generator supports scaffolding [custom Office 365 Connectors](https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/connectors-creating) as a part of the Teams Application. 4 | 5 | ## Configuration 6 | 7 | All Connectors has to be registered in the [Connectors Developer Dashboard](https://outlook.office.com/connectors/publish), which you have to log in to using a Microsoft Organizational Account, a Microsoft Account (MSA) will not work. If you're building an internal enterprise connector you only have to register it, and not publish it. 8 | 9 | You need to specify the following details: 10 | 11 | - **Connector Name** 12 | - **Configuration Page** - must contain the full URL to the `/config.html` for your scaffolded connector. 13 | - **Valid domains** -- must contain the domain name of your Teams App 14 | - **Enable Actions on your Connector Cards** - must be set to *Yes* 15 | - **Action URL** - must contain the full URL to the `/api/connector` endpoint 16 | 17 | After creating or updating the Connector you must copy the Guid of the Connector (found in the URL when configuring the Connector) and paste that into your `.env` file: 18 | 19 | ``` 20 | CONNECTOR_ID= 21 | ``` 22 | 23 | ## Scaffolded files details 24 | 25 | The following files will be scaffolded for a Connector 26 | 27 | * `./src/server//.ts` - implementation of the Connector that manages registering new subscriptions (`Connect()`) as well as a sample method to send a message to all subscribers (`Ping()`). 28 | * `./src/public//config.html` - the configuration of the Connector . 29 | * `./src/client/Config.tsx` - React component of the configuration page page. 30 | 31 | ## Connector API end-points 32 | 33 | For the Connector you will have two generated end-points, defined in `./src/server/server.ts`. 34 | 35 | * `/api/connector/connect` - this is the end-point that is connected to the `Connect` method of the Connector implementation and is used when registering a Connector. 36 | * `/api/connector/ping` - this is a test end-point to demonstrate how to invoke the Connector using a simple HTTP GET operation. It is highly recommended that you remove this end-point and implement your own logic for invoking the connector. 37 | -------------------------------------------------------------------------------- /docs/docs/user-guide/deploy-to-teams.md: -------------------------------------------------------------------------------- 1 | # Deployment to Teams App Store 2 | 3 | > **NOTE**: requires `generator-teams` 3.2.0 or later 4 | 5 | In order to streamline the development cycle Yo Teams 3.2.0 and later includes a plugin to make deployment to Teams App store easier. The `yoteams-deploy` package is a plugin for the Yo Teams Gulp tasks that adds a new flag to the `serve` and `*-serve` tasks, called `--publish`. 6 | 7 | When this flag is used the Gulp task will after building the manifest automatically upload the package to the Microsoft Teams App Store. You will the first time this is used be asked to sign in to the tenant of your choice. -------------------------------------------------------------------------------- /docs/docs/user-guide/outgoing-webhook-configuration.md: -------------------------------------------------------------------------------- 1 | # Outgoing Webhook Configuration 2 | 3 | # How to add the Outgoing Webhook to a Teams team 4 | 5 | To add the Outgoing Webhook to a Microsoft Teams team, choose *View Team* and then choose the *Bots* tab. In the lower right corner click on *Create a outgoing webhook*. Then fill in the name, the URL (`https:///api/webhook`) and a description and click ok. Once the outgoing webhook is registered you will receive a _Security token_. Save this token in a secure place for future use, and you wil not be able to retrieve it again. 6 | 7 | ### Security token usage 8 | 9 | The security token must be added as an environment variable. For development purposes it can be added to the `.env` file with the property name `SECURITY_TOKEN` and for Azure you should add it as a new environment variable called `SECURITY_TOKEN`. These can be configured in the Azure Web App under *Application Settings > App Settings*. 10 | 11 | ## Notes 12 | 13 | You might receive an error the first time you send a message to the bot, if you just deployed the solution. Outgoing webhooks must answer within 5 seconds. 14 | -------------------------------------------------------------------------------- /docs/docs/user-guide/setup-machine.md: -------------------------------------------------------------------------------- 1 | # Setup and prepare your machine 2 | 3 | You need to install the following on your machine before starting to use the Teams Generator. 4 | 5 | ## Install Node 6 | 7 | You need to have NodeJS installed on your machine. You should use the latest [LTS version](https://nodejs.org/en/download/). 8 | 9 | ## Install a code editor 10 | 11 | You also need a code editor, feel free to use whatever text editor you prefer. However most of this documentation and screenshots refer to using [Visual Studio Code](https://code.visualstudio.com). 12 | 13 | ## Install Yeoman and Gulp CLI 14 | 15 | To be able to scaffold projects using the Teams generator you need to install the Yeoman tool as well as the Gulp CLI task manager. 16 | 17 | Open up a command prompt and type the following: 18 | 19 | ``` bash 20 | npm install yo gulp-cli --global 21 | ``` 22 | 23 | ## Install the Microsoft Teams Apps generator - Yo Teams 24 | 25 | The Yeoman generator for Microsoft Teams apps are installed with the following command: 26 | 27 | ``` bash 28 | npm install generator-teams --global 29 | ``` 30 | 31 | ### Install preview versions 32 | 33 | If you want to install preview versions of the Teams generator with this command: 34 | 35 | ``` bash 36 | npm install generator-teams@preview --global 37 | ``` 38 | ## Possible issues 39 | If you get this error: 40 | > This generator (teams:app) requires yeoman-environment at least 3.0.0, current version is 2.10.3, try reinstalling latest version of 'yo' or use '--ignore-version-check' option 41 | 42 | and you run `npm -g outdated` you will notice that you are not running the latest version of the yeoman generator. You need to update your global package of yo using `npm install yo@latest -g`. To update to the latest version of the gulp CLI use `npm install gulp-cli@latest -g` -------------------------------------------------------------------------------- /docs/docs/user-guide/setup-tenant.md: -------------------------------------------------------------------------------- 1 | # Set up your Office 365 tenant 2 | 3 | In order to build applications for Microsoft Teams you need to have access to an Office 365 tenant with either global administrator permissions or have a global administrator allow you to do side-loading of apps. 4 | 5 | Your Microsoft 365 developer program membership entitles you to a special free Microsoft 365 E5 subscription with all the latest Microsoft 365 apps, Enterprise Mobility + Security, Azure AD, and more. You can use this subscription to build a developer sandbox where you can test your apps created with the Teams generator. 6 | 7 | [Set up your free Microsoft 365 E5 developer subscription now!](https://developer.microsoft.com/en-us/microsoft-365/dev-program) 8 | 9 | ## Enabling Microsoft Teams Apps and sideloading 10 | 11 | To enable Teams for your tenant and for more invormation see [enabling Teams for your organization](https://docs.microsoft.com/en-us/microsoftteams/enable-features-office-365) 12 | 13 | Enabling custom apps for Microsoft Teams is done via the [Microsoft Teams admin center, https://admin.teams.microsoft.com](https://admin.teams.microsoft.com). Use the navigation on the left hand side and choose *Teams apps* > *Setup policies*. To allow all users to sideload applications you should modify the *Global* policy to allow **Upload custom apps**. To only allow a subset of users to use side loading, then create a new policy with the setting and then assign that policy to the users you want to be able to sideload. 14 | 15 | For a more detailed guide on this see [enable custom Teams apps and turn on custom app uploading](https://docs.microsoft.com/en-us/microsoftteams/platform/concepts/build-and-test/prepare-your-o365-tenant#enable-custom-teams-apps-and-turn-on-custom-app-uploading) 16 | -------------------------------------------------------------------------------- /docs/docs/user-guide/upgrading-projects.md: -------------------------------------------------------------------------------- 1 | # Upgrading projects 2 | 3 | Starting with Yo Teams version 2.11.0 upgrading from earlier versions of the generator is possible. An upgrade of a project includes the option of upgrading the core build files, which are required by certain features introduced in new versions. 4 | 5 | Projects can be updated either automatically using the generator or manually. 6 | 7 | > Note: See release notes for upgrade compatibility 8 | 9 | ![](../images/upgrade-project.png) 10 | 11 | ## Automatically upgrade a project 12 | 13 | Microsoft Teams apps projects created by Yo Teams (from version 2.9) can automatically be updated by running the generator once again over the project (starting from version 2.11). 14 | 15 | Always check release notes for individual versions for upgrades, as some does not support upgrading. 16 | 17 | ### Upgrade notes 18 | 19 | > NOTE: See each specific [release note](./Releases) for more details. 20 | 21 | When running the generator using `yo teams` on an existing project you will be prompted if you want to continue - select *Yes* to continue the upgrade. If the project can be updated the next question asks you if you want to update the *yo teams core files* - select *Yes* to allow the generator to update the necessary files. Before you do this you should ensure that you have your source files under source control, so that you can inspect and potentially revert any changes being done by the generator. 22 | 23 | Once the generator is finished it will update the core files as required. 24 | 25 | > Note: you will only be asked once for updating the core build files. Subsequent executions of the generator on the project will assume an upgrade has been done. 26 | 27 | ## Manually upgrading a project 28 | 29 | Manually upgrading a project might be required if you have made changes to the core build files or the automatic upgrade fails. 30 | 31 | To manually upgrade a project the best way is to create a new blank project using the latest generator. Then use a file comparison tool and compare the new project with the one you want to upgrade. 32 | -------------------------------------------------------------------------------- /docs/docs/user-guide/using-multiple-manifests.md: -------------------------------------------------------------------------------- 1 | # Using multiple manifest files 2 | 3 | > **NOTE**: requires `yoteams-build-core` 1.1.0 or later 4 | 5 | Yo Teams supports having multiple manifests files. There are many scenarios where you would like to have multiple manifest files and Teams App packages generated, for instance: 6 | 7 | * Having one application but multiple Teams Apps, such as one for users and one for admins, but still using the same hosting and solution 8 | * Working with multiple environments and you want to build different manifests for different environments, including different icons 9 | * Working with multiple developer environments where `validDomains` and Tab SSO App Id URI's are different. 10 | 11 | ## How to use multiple manifests with Yo Teams 12 | 13 | By default, the scaffolded project will contain one `manifest.json`, located in `./src/manifest/`. To add additional manifest files you either create a new `.json` file and manually create the manifest, or copy the existing manifest into a new file in the same folder. 14 | 15 | The build tasks `manifest` and `validate-manifest` will now work cross all those defined manifest.json files and validate and create a separate package for each of them. The package name will be the name of the `packageName` in the manifest and only referenced icons and localization files will be included in the package. 16 | 17 | ## Add this feature to an existing project 18 | 19 | If you scaffolded a project using Yo Teams v 3.0.x then you might need to update the build tools to support this feature. This is done using the following command 20 | 21 | ``` bash 22 | npm install yoteams-build-core@preview --save-dev 23 | ``` 24 | 25 | ## A word of caution 26 | 27 | When working with multiple manifests there are a couple of things to keep in mind. 28 | 29 | If you create a new manifest from a copy of an existing one it is important that you change the `packageName` so that they are different - as that is used to create the package zip file. 30 | 31 | If you intend to use the multiple packages in the same environment it is required that you have different `id`'s in your manifest files. 32 | -------------------------------------------------------------------------------- /docs/docs/user-guide/vscode.md: -------------------------------------------------------------------------------- 1 | # Debugging with Visual Studio Code 2 | 3 | If the application is started with the `--debug` flag, then you can use the built-in debug configurations to step through your code. 4 | 5 | * **Debug (Edge)** will open up Edge in remote debugging mode and also connect to the server side for debugging 6 | * **Debug (Chrome)** will open up Chrome in remote debugging mode and also connect to the server side for debugging 7 | * **Attach to server** will only connect to the server side for debugging 8 | * **Launch client (Edge)** will only open up Edge in remote debugging mode 9 | * **Launch client (Chrome)** will only open up Chrome in remote debugging mode -------------------------------------------------------------------------------- /docs/mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: Yo Teams - Microsoft Teams Apps generator 2 | site_url: https://pnp.github.io/generator-teams/ 3 | nav: 4 | - Home: 'index.md' 5 | - User guide: 6 | - 'Setup a developer tenant': 'user-guide/setup-tenant.md' 7 | - 'Setup your developer environment': 'user-guide/setup-machine.md' 8 | - 'Deploy to teams': 'user-guide/deploy-to-teams.md' 9 | - 'Upgrading projects': 'user-guide/upgrading-projects.md' 10 | - 'Debugging': 'user-guide/vscode.md' 11 | - 'Using GitHub code spaces': 'user-guide/codespaces.md' 12 | - 'Using multiple manifests': 'user-guide/using-multiple-manifests.md' 13 | - 'Bots and Messaging Extension configuration': 'user-guide/bot-configuration.md' 14 | - 'Connector configuration': 'user-guide/connector-configuration.md' 15 | - 'Outgoing Webhook configuration': 'user-guide/outgoing-webhook-configuration.md' 16 | - Concepts: 17 | - 'Tunneling': 'concepts/ngrok.md' 18 | - 'Project structure': 'concepts/project-structure.md' 19 | - Tutorials: 20 | - 'Your first Microsoft Teams App': 'tutorials/build-your-first-microsoft-teams-app.md' 21 | - 'Build a SharePoint Online Provider hosted web parts': 'tutorials/build-a-sharepoint-online-provider-hosted-web-part.md' 22 | - 'Build a connector for Microsoft Teams': 'tutorials/build-a-connector-for-microsoft-teams.md' 23 | - 'Build a tab with SSO': 'tutorials/build-a-tab-with-sso-support.md' 24 | - Releases: 25 | - 'Releases': 'releases/releases.md' 26 | - 'Release notes': 'releases/release-notes.md' 27 | - Contributing: 28 | - 'Guidelines': 'contributing/contribution-guidelines.md' 29 | - 'Getting started': 'contributing/getting-started.md' 30 | - 'Minimal Path to Awesome': 'contributing/minimal-path.md' 31 | - About: 32 | - 'Why this generator': 'about/why-generator.md' 33 | - 'Telemetry': 'about/telemetry.md' 34 | - 'License': 'about/license.md' 35 | - 'Hall of Fame': 'about/halloffame.md' 36 | 37 | theme: 38 | name: 'material' 39 | logo: images/pnp-teams-generator-white.svg 40 | favicon: images/favicon.ico 41 | icon: 42 | repo: fontawesome/brands/git-alt 43 | palette: 44 | - scheme: default 45 | primary: deep purple 46 | toggle: 47 | icon: material/toggle-switch-off-outline 48 | name: Switch to dark mode 49 | - scheme: slate 50 | primary: deep purple 51 | toggle: 52 | icon: material/toggle-switch 53 | name: Switch to light mode 54 | features: 55 | - navigation.tabs 56 | - navigation.instant 57 | - search.share 58 | markdown_extensions: 59 | - admonition 60 | - codehilite: 61 | guess_lang: false 62 | - toc: 63 | permalink: true 64 | - pymdownx.superfences 65 | - pymdownx.snippets 66 | - def_list 67 | extra: 68 | social: 69 | - icon: fontawesome/brands/twitter 70 | link: 'https://twitter.com/m365pnp' 71 | - icon: fontawesome/brands/youtube 72 | link: 'http://aka.ms/sppnp-videos' 73 | - icon: fontawesome/solid/link 74 | link: 'https://aka.ms/sppnp' 75 | analytics: 76 | provider: google 77 | property: G-55J8T06W37 78 | repo_url: https://github.com/pnp/generator-teams 79 | edit_uri: tree/master/docs/docs 80 | -------------------------------------------------------------------------------- /generator-teams.yml: -------------------------------------------------------------------------------- 1 | page_type: sample 2 | products: 3 | - office-365 4 | languages: 5 | - typescript 6 | extensions: 7 | contentType: tools 8 | createdDate: '3/3/2017 2:24:18 AM' 9 | title: Yo Teams 10 | description: A Yeoman Generator for Microsoft Teams 11 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "lockfileVersion": 1 3 | } 4 | -------------------------------------------------------------------------------- /packages/README.md: -------------------------------------------------------------------------------- 1 | # Different packages 2 | 3 | To keep an a seperation of concern, we opted into having 3 seperate packages that when combined create the Microsoft Teams Apps generator 4 | 5 | ## Generator-teams 6 | 7 | This is the yeoman generator that creates the scaffolding for you. Most functionality can be found in this package. 8 | More information can be found in the specific [readme file](https://github.com/pnp/generator-teams/tree/master/packages/generator-teams) 9 | 10 | ## Yoteams-build-core 11 | 12 | This package consists of a set of Gulp tasks that is used by the Generator. They make sure the generator works as designed but hold no scaffolding logic. These are for example the build tasks. 13 | More information can be found in the specific [readme file](https://github.com/pnp/generator-teams/tree/master/packages/yoteams-build-core) 14 | 15 | ## Yoteams-deploy 16 | 17 | This package consists of a set of Gulp tasks that will help you deploy the Teams application you created to your environment. 18 | More information can be found in the specific [readme file](https://github.com/pnp/generator-teams/tree/master/packages/yoteams-deploy) 19 | -------------------------------------------------------------------------------- /packages/generator-teams/.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | indent_style = space 8 | indent_size = 4 9 | -------------------------------------------------------------------------------- /packages/generator-teams/.npmignore: -------------------------------------------------------------------------------- 1 | # Folders 2 | .vscode 3 | node_modules 4 | src 5 | .github 6 | tests 7 | temp-templates 8 | 9 | # Files 10 | *.csproj 11 | .git* 12 | .yo-rc.json 13 | webpack.config.js 14 | gulpfile.js 15 | tsconfig.json 16 | .travis.yml 17 | .editorconfig -------------------------------------------------------------------------------- /packages/generator-teams/src/app/EmptyGuid.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. 4 | 5 | export const empty = "00000000-0000-0000-0000-000000000000"; -------------------------------------------------------------------------------- /packages/generator-teams/src/app/USAGE: -------------------------------------------------------------------------------- 1 | More information: 2 | See https://aka.ms/yoteams -------------------------------------------------------------------------------- /packages/generator-teams/src/app/coreFilesUpdater/BaseCoreFilesUpdater.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. 4 | 5 | import { GeneratorTeamsAppOptions } from "../GeneratorTeamsAppOptions"; 6 | import { Editor } from 'mem-fs-editor'; 7 | import * as Generator from 'yeoman-generator'; 8 | import { Logger } from "yeoman-environment"; 9 | 10 | export abstract class BaseCoreFilesUpdater { 11 | public updateCoreFiles(options: GeneratorTeamsAppOptions, fs: Editor, log?: Logger): boolean { 12 | // nop 13 | return true; 14 | } 15 | } -------------------------------------------------------------------------------- /packages/generator-teams/src/app/coreFilesUpdater/CoreFilesUpdaterFactory.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. 4 | 5 | import { BaseCoreFilesUpdater } from "./BaseCoreFilesUpdater"; 6 | import * as semver from "semver"; 7 | import { CoreFilesUpdater_3 } from "./coreFilesUpdaters/CoreFilesUpdater_3"; 8 | 9 | export class CoreFilesUpdaterFactory { 10 | public static createCoreFilesUpdater(currentVersion: string): BaseCoreFilesUpdater | undefined { 11 | // < 4.0.0 cannot be updated 12 | if(semver.lt(currentVersion, "4.0.0")) { 13 | return new CoreFilesUpdater_3(currentVersion); 14 | } 15 | return undefined; 16 | } 17 | } -------------------------------------------------------------------------------- /packages/generator-teams/src/app/coreFilesUpdater/coreFilesUpdaters/CoreFilesUpdaterNop.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. 4 | 5 | import { GeneratorTeamsAppOptions } from "../../GeneratorTeamsAppOptions"; 6 | import { BaseCoreFilesUpdater } from "../BaseCoreFilesUpdater"; 7 | import * as Generator from 'yeoman-generator'; 8 | import { Editor } from 'mem-fs-editor'; 9 | import { Project, PropertyAssignment } from 'ts-morph'; 10 | import * as ts from 'typescript'; 11 | 12 | export class CoreFilesUpdaterNop extends BaseCoreFilesUpdater { 13 | public updateCoreFiles(options: GeneratorTeamsAppOptions, fs: Editor): boolean { 14 | return true; 15 | } 16 | } -------------------------------------------------------------------------------- /packages/generator-teams/src/app/coreFilesUpdater/coreFilesUpdaters/CoreFilesUpdater_3.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. 4 | 5 | import { GeneratorTeamsAppOptions } from "../../GeneratorTeamsAppOptions"; 6 | import { BaseCoreFilesUpdater } from "../BaseCoreFilesUpdater"; 7 | import { Editor } from 'mem-fs-editor'; 8 | import * as Generator from 'yeoman-generator'; 9 | import * as semver from "semver"; 10 | import { Project, PropertyAssignment, SyntaxKind } from 'ts-morph'; 11 | import { Logger } from "yeoman-environment"; 12 | import * as chalk from "chalk"; 13 | 14 | export class CoreFilesUpdater_3 extends BaseCoreFilesUpdater { 15 | public constructor(private currentVersion: string) { 16 | super(); 17 | }; 18 | 19 | public updateCoreFiles(options: GeneratorTeamsAppOptions, fs: Editor, log?: Logger): boolean { 20 | 21 | if (log) { 22 | log(chalk.red("Projects generated with earlier versions than 4.0 cannot be updated.")); 23 | } 24 | 25 | return false; 26 | } 27 | } -------------------------------------------------------------------------------- /packages/generator-teams/src/app/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. 4 | 5 | import {GeneratorTeamsApp} from './GeneratorTeamsApp'; 6 | 7 | module.exports = GeneratorTeamsApp 8 | -------------------------------------------------------------------------------- /packages/generator-teams/src/app/manifestGeneration/IManifestUpdater.ts: -------------------------------------------------------------------------------- 1 | import { GeneratorTeamsAppOptions } from "../GeneratorTeamsAppOptions"; 2 | 3 | // Copyright (c) Wictor Wilén. All rights reserved. 4 | // Copyright (c) Microsoft Corporation. All rights reserved. 5 | // Licensed under the MIT license. 6 | 7 | export interface IManifestUpdater { 8 | updateManifest(manifest: any, options?: GeneratorTeamsAppOptions): void; 9 | } -------------------------------------------------------------------------------- /packages/generator-teams/src/app/manifestGeneration/ManifestVersions.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. 4 | 5 | export enum ManifestVersions { 6 | v18 = "v1.8", 7 | v19 = "v1.9", 8 | v110 = "v1.10", 9 | v111 = "v1.11", 10 | v112 = "v1.12", 11 | v113 = "v1.13", 12 | v114 = "v1.14", 13 | v115 = "v1.15", 14 | devPreview = "devPreview", 15 | m365DevPreview = "m365DevPreview" 16 | } -------------------------------------------------------------------------------- /packages/generator-teams/src/app/manifestGeneration/manifestGenerators/generator110/ManifestGenerator.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. 4 | 5 | import { BaseManifestGenerator } from "../../BaseManifestGenerator"; 6 | import { TabManifestUpdater } from "../generator19/TabManifestUpdater"; 7 | import { BotManifestUpdater } from "../generator18/BotManifestUpdater"; 8 | import { ConnectorManifestUpdater } from "../generator18/ConnectorManifestUpdater"; 9 | import { MessageExtensionManifestUpdater } from "../generator18/MessageExtensionManifestUpdater"; 10 | import { GeneratorTeamsAppOptions } from "../../../GeneratorTeamsAppOptions"; 11 | import { LocalizationManifestUpdater } from "../generator18/LocalizationManifestUpdater"; 12 | import * as chalk from 'chalk'; 13 | 14 | 15 | export class ManifestGenerator extends BaseManifestGenerator { 16 | constructor() { 17 | super(); 18 | this.tabUpdater = new TabManifestUpdater(); 19 | this.botUpdater = new BotManifestUpdater(); 20 | this.connectorUpdater = new ConnectorManifestUpdater(); 21 | this.messageExtensionUpdater = new MessageExtensionManifestUpdater(); 22 | this.localizationUpdater = new LocalizationManifestUpdater(); 23 | } 24 | 25 | public generateManifest(options: GeneratorTeamsAppOptions): any { 26 | const manifest = super.generateManifest(options); 27 | manifest["$schema"] = "https://developer.microsoft.com/en-us/json-schemas/teams/v1.10/MicrosoftTeams.schema.json"; 28 | manifest.manifestVersion = "1.10"; 29 | 30 | return manifest; 31 | } 32 | 33 | public supportsUpdateManifest(from: string): boolean { 34 | return from === "1.8" || from === "1.9"; 35 | } 36 | 37 | public updateManifest(manifest: any, log?: (message?: string, context?: any) => void): any { 38 | switch (manifest.manifestVersion) { 39 | case "1.8": 40 | case "1.9": 41 | manifest["$schema"] = "https://developer.microsoft.com/en-us/json-schemas/teams/v1.10/MicrosoftTeams.schema.json"; 42 | manifest.manifestVersion = "1.10"; 43 | return manifest; 44 | default: 45 | throw "Unable to update manifest"; 46 | 47 | } 48 | }; 49 | } 50 | -------------------------------------------------------------------------------- /packages/generator-teams/src/app/manifestGeneration/manifestGenerators/generator111/ManifestGenerator.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. 4 | 5 | import { BaseManifestGenerator } from "../../BaseManifestGenerator"; 6 | import { TabManifestUpdater } from "../generator19/TabManifestUpdater"; 7 | import { BotManifestUpdater } from "../generator18/BotManifestUpdater"; 8 | import { ConnectorManifestUpdater } from "../generator18/ConnectorManifestUpdater"; 9 | import { MessageExtensionManifestUpdater } from "../generator18/MessageExtensionManifestUpdater"; 10 | import { GeneratorTeamsAppOptions } from "../../../GeneratorTeamsAppOptions"; 11 | import { LocalizationManifestUpdater } from "../generator18/LocalizationManifestUpdater"; 12 | import * as chalk from 'chalk'; 13 | 14 | 15 | export class ManifestGenerator extends BaseManifestGenerator { 16 | constructor() { 17 | super(); 18 | this.tabUpdater = new TabManifestUpdater(); 19 | this.botUpdater = new BotManifestUpdater(); 20 | this.connectorUpdater = new ConnectorManifestUpdater(); 21 | this.messageExtensionUpdater = new MessageExtensionManifestUpdater(); 22 | this.localizationUpdater = new LocalizationManifestUpdater(); 23 | } 24 | 25 | public generateManifest(options: GeneratorTeamsAppOptions): any { 26 | const manifest = super.generateManifest(options); 27 | manifest["$schema"] = "https://developer.microsoft.com/en-us/json-schemas/teams/v1.11/MicrosoftTeams.schema.json"; 28 | manifest.manifestVersion = "1.11"; 29 | 30 | return manifest; 31 | } 32 | 33 | public supportsUpdateManifest(from: string): boolean { 34 | return from === "1.8" || from === "1.9" || from === "1.10"; 35 | } 36 | 37 | public updateManifest(manifest: any, log?: (message?: string, context?: any) => void): any { 38 | switch (manifest.manifestVersion) { 39 | case "1.8": 40 | case "1.9": 41 | case "1.10": 42 | manifest["$schema"] = "https://developer.microsoft.com/en-us/json-schemas/teams/v1.11/MicrosoftTeams.schema.json"; 43 | manifest.manifestVersion = "1.11"; 44 | return manifest; 45 | default: 46 | throw "Unable to update manifest"; 47 | 48 | } 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /packages/generator-teams/src/app/manifestGeneration/manifestGenerators/generator112/ManifestGenerator.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. 4 | 5 | import { BaseManifestGenerator } from "../../BaseManifestGenerator"; 6 | import { TabManifestUpdater } from "../generator19/TabManifestUpdater"; 7 | import { BotManifestUpdater } from "../generator18/BotManifestUpdater"; 8 | import { ConnectorManifestUpdater } from "../generator18/ConnectorManifestUpdater"; 9 | import { MessageExtensionManifestUpdater } from "../generator18/MessageExtensionManifestUpdater"; 10 | import { GeneratorTeamsAppOptions } from "../../../GeneratorTeamsAppOptions"; 11 | import { LocalizationManifestUpdater } from "../generator18/LocalizationManifestUpdater"; 12 | import * as chalk from 'chalk'; 13 | 14 | 15 | export class ManifestGenerator extends BaseManifestGenerator { 16 | constructor() { 17 | super(); 18 | this.tabUpdater = new TabManifestUpdater(); 19 | this.botUpdater = new BotManifestUpdater(); 20 | this.connectorUpdater = new ConnectorManifestUpdater(); 21 | this.messageExtensionUpdater = new MessageExtensionManifestUpdater(); 22 | this.localizationUpdater = new LocalizationManifestUpdater(); 23 | } 24 | 25 | public generateManifest(options: GeneratorTeamsAppOptions): any { 26 | const manifest = super.generateManifest(options); 27 | manifest["$schema"] = "https://developer.microsoft.com/en-us/json-schemas/teams/v1.12/MicrosoftTeams.schema.json"; 28 | manifest.manifestVersion = "1.12"; 29 | 30 | return manifest; 31 | } 32 | 33 | public supportsUpdateManifest(from: string): boolean { 34 | return from === "1.8" || from === "1.9" || from === "1.10" || from === "1.11"; 35 | } 36 | 37 | public updateManifest(manifest: any, log?: (message?: string, context?: any) => void): any { 38 | switch (manifest.manifestVersion) { 39 | case "1.8": 40 | case "1.9": 41 | case "1.10": 42 | case "1.11": 43 | manifest["$schema"] = "https://developer.microsoft.com/en-us/json-schemas/teams/v1.12/MicrosoftTeams.schema.json"; 44 | manifest.manifestVersion = "1.12"; 45 | return manifest; 46 | default: 47 | throw "Unable to update manifest"; 48 | 49 | } 50 | }; 51 | } 52 | -------------------------------------------------------------------------------- /packages/generator-teams/src/app/manifestGeneration/manifestGenerators/generator113/ManifestGenerator.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. 4 | 5 | import { BaseManifestGenerator } from "../../BaseManifestGenerator"; 6 | import { TabManifestUpdater } from "../generator19/TabManifestUpdater"; 7 | import { BotManifestUpdater } from "../generator18/BotManifestUpdater"; 8 | import { ConnectorManifestUpdater } from "../generator18/ConnectorManifestUpdater"; 9 | import { MessageExtensionManifestUpdater } from "../generator18/MessageExtensionManifestUpdater"; 10 | import { GeneratorTeamsAppOptions } from "../../../GeneratorTeamsAppOptions"; 11 | import { LocalizationManifestUpdater } from "../generator18/LocalizationManifestUpdater"; 12 | import * as chalk from 'chalk'; 13 | 14 | 15 | export class ManifestGenerator extends BaseManifestGenerator { 16 | constructor() { 17 | super(); 18 | this.tabUpdater = new TabManifestUpdater(); 19 | this.botUpdater = new BotManifestUpdater(); 20 | this.connectorUpdater = new ConnectorManifestUpdater(); 21 | this.messageExtensionUpdater = new MessageExtensionManifestUpdater(); 22 | this.localizationUpdater = new LocalizationManifestUpdater(); 23 | } 24 | 25 | public generateManifest(options: GeneratorTeamsAppOptions): any { 26 | const manifest = super.generateManifest(options); 27 | manifest["$schema"] = "https://developer.microsoft.com/en-us/json-schemas/teams/v1.13/MicrosoftTeams.schema.json"; 28 | manifest.manifestVersion = "1.13"; 29 | 30 | return manifest; 31 | } 32 | 33 | public supportsUpdateManifest(from: string): boolean { 34 | return from === "1.8" || from === "1.9" || from === "1.10" || from === "1.11" || from === "1.12"; 35 | } 36 | 37 | public updateManifest(manifest: any, log?: (message?: string, context?: any) => void): any { 38 | switch (manifest.manifestVersion) { 39 | case "1.8": 40 | case "1.9": 41 | case "1.10": 42 | case "1.11": 43 | case "1.12": 44 | manifest["$schema"] = "https://developer.microsoft.com/en-us/json-schemas/teams/v1.13/MicrosoftTeams.schema.json"; 45 | manifest.manifestVersion = "1.13"; 46 | return manifest; 47 | default: 48 | throw "Unable to update manifest"; 49 | 50 | } 51 | }; 52 | } 53 | -------------------------------------------------------------------------------- /packages/generator-teams/src/app/manifestGeneration/manifestGenerators/generator114/ManifestGenerator.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. 4 | 5 | import { BaseManifestGenerator } from "../../BaseManifestGenerator"; 6 | import { TabManifestUpdater } from "../generator19/TabManifestUpdater"; 7 | import { BotManifestUpdater } from "../generator18/BotManifestUpdater"; 8 | import { ConnectorManifestUpdater } from "../generator18/ConnectorManifestUpdater"; 9 | import { MessageExtensionManifestUpdater } from "../generator18/MessageExtensionManifestUpdater"; 10 | import { GeneratorTeamsAppOptions } from "../../../GeneratorTeamsAppOptions"; 11 | import { LocalizationManifestUpdater } from "../generator18/LocalizationManifestUpdater"; 12 | import * as chalk from 'chalk'; 13 | 14 | 15 | export class ManifestGenerator extends BaseManifestGenerator { 16 | constructor() { 17 | super(); 18 | this.tabUpdater = new TabManifestUpdater(); 19 | this.botUpdater = new BotManifestUpdater(); 20 | this.connectorUpdater = new ConnectorManifestUpdater(); 21 | this.messageExtensionUpdater = new MessageExtensionManifestUpdater(); 22 | this.localizationUpdater = new LocalizationManifestUpdater(); 23 | } 24 | 25 | public generateManifest(options: GeneratorTeamsAppOptions): any { 26 | const manifest = super.generateManifest(options); 27 | manifest["$schema"] = "https://developer.microsoft.com/en-us/json-schemas/teams/v1.14/MicrosoftTeams.schema.json"; 28 | manifest.manifestVersion = "1.14"; 29 | 30 | return manifest; 31 | } 32 | 33 | public supportsUpdateManifest(from: string): boolean { 34 | return from === "1.8" || from === "1.9" || from === "1.10" || from === "1.11" || from === "1.12" || from === "1.13"; 35 | } 36 | 37 | public updateManifest(manifest: any, log?: (message?: string, context?: any) => void): any { 38 | switch (manifest.manifestVersion) { 39 | case "1.8": 40 | case "1.9": 41 | case "1.10": 42 | case "1.11": 43 | case "1.12": 44 | case "1.13": 45 | manifest["$schema"] = "https://developer.microsoft.com/en-us/json-schemas/teams/v1.14/MicrosoftTeams.schema.json"; 46 | manifest.manifestVersion = "1.14"; 47 | return manifest; 48 | default: 49 | throw "Unable to update manifest"; 50 | 51 | } 52 | }; 53 | } 54 | -------------------------------------------------------------------------------- /packages/generator-teams/src/app/manifestGeneration/manifestGenerators/generator115/ManifestGenerator.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. 4 | 5 | import { BaseManifestGenerator } from "../../BaseManifestGenerator"; 6 | import { TabManifestUpdater } from "../generator19/TabManifestUpdater"; 7 | import { BotManifestUpdater } from "../generator18/BotManifestUpdater"; 8 | import { ConnectorManifestUpdater } from "../generator18/ConnectorManifestUpdater"; 9 | import { MessageExtensionManifestUpdater } from "../generator18/MessageExtensionManifestUpdater"; 10 | import { GeneratorTeamsAppOptions } from "../../../GeneratorTeamsAppOptions"; 11 | import { LocalizationManifestUpdater } from "../generator18/LocalizationManifestUpdater"; 12 | import * as chalk from 'chalk'; 13 | 14 | 15 | export class ManifestGenerator extends BaseManifestGenerator { 16 | constructor() { 17 | super(); 18 | this.tabUpdater = new TabManifestUpdater(); 19 | this.botUpdater = new BotManifestUpdater(); 20 | this.connectorUpdater = new ConnectorManifestUpdater(); 21 | this.messageExtensionUpdater = new MessageExtensionManifestUpdater(); 22 | this.localizationUpdater = new LocalizationManifestUpdater(); 23 | } 24 | 25 | public generateManifest(options: GeneratorTeamsAppOptions): any { 26 | const manifest = super.generateManifest(options); 27 | manifest["$schema"] = "https://developer.microsoft.com/en-us/json-schemas/teams/v1.15/MicrosoftTeams.schema.json"; 28 | manifest.manifestVersion = "1.15"; 29 | 30 | return manifest; 31 | } 32 | 33 | public supportsUpdateManifest(from: string): boolean { 34 | return from === "1.8" || from === "1.9" || from === "1.10" || from === "1.11" || from === "1.12" || from === "1.13" || from === "1.14"; 35 | } 36 | 37 | public updateManifest(manifest: any, log?: (message?: string, context?: any) => void): any { 38 | switch (manifest.manifestVersion) { 39 | case "1.8": 40 | case "1.9": 41 | case "1.10": 42 | case "1.11": 43 | case "1.12": 44 | case "1.13": 45 | case "1.14": 46 | manifest["$schema"] = "https://developer.microsoft.com/en-us/json-schemas/teams/v1.15/MicrosoftTeams.schema.json"; 47 | manifest.manifestVersion = "1.15"; 48 | return manifest; 49 | default: 50 | throw "Unable to update manifest"; 51 | 52 | } 53 | }; 54 | } 55 | -------------------------------------------------------------------------------- /packages/generator-teams/src/app/manifestGeneration/manifestGenerators/generator18/BotManifestUpdater.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. 4 | 5 | import { IManifestUpdater } from "../../IManifestUpdater"; 6 | import { GeneratorTeamsAppOptions } from "../../../GeneratorTeamsAppOptions"; 7 | import { v1 as uuid } from 'uuid'; 8 | 9 | export class BotManifestUpdater implements IManifestUpdater { 10 | 11 | public updateManifest(manifest: any, options: GeneratorTeamsAppOptions): void { 12 | var newbot: any = { 13 | botId: `{{${options.botidEnv}}}`, 14 | needsChannelSelector: true, 15 | isNotificationOnly: false, 16 | scopes: ["team", "personal", "groupchat"], 17 | commandLists: [ 18 | { 19 | "scopes": [ 20 | "team", 21 | "personal" 22 | ], 23 | "commands": [ 24 | { 25 | "title": "Help", 26 | "description": "Shows help information" 27 | }, 28 | { 29 | "title": "Who am I?", 30 | "description": "Shows information about your Teams user" 31 | }, 32 | { 33 | "title": "Mention me", 34 | "description": "Let the bot @mention you" 35 | } 36 | ] 37 | } 38 | ] 39 | }; 40 | 41 | if (options.staticTab) { 42 | manifest.staticTabs.push({ 43 | entityId: uuid(), 44 | name: options.staticTabTitle, 45 | contentUrl: `https://{{PUBLIC_HOSTNAME}}/${options.botName}/${options.staticTabName}.html`, 46 | scopes: ["personal"] 47 | }); 48 | } 49 | 50 | if (options.botFilesEnabled) { 51 | newbot.supportsFiles = true; 52 | } 53 | 54 | (manifest.bots).push(newbot); 55 | } 56 | } -------------------------------------------------------------------------------- /packages/generator-teams/src/app/manifestGeneration/manifestGenerators/generator18/ConnectorManifestUpdater.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. 4 | 5 | import { IManifestUpdater } from "../../IManifestUpdater"; 6 | import { GeneratorTeamsAppOptions } from "../../../GeneratorTeamsAppOptions"; 7 | 8 | export class ConnectorManifestUpdater implements IManifestUpdater { 9 | 10 | public updateManifest(manifest: any, options: GeneratorTeamsAppOptions): void { 11 | manifest.connectors.push({ 12 | connectorId: "{{CONNECTOR_ID}}", 13 | configurationUrl: `https://{{PUBLIC_HOSTNAME}}/${options.connectorName}/config.html?name={loginHint}&tenant={tid}&group={groupId}&theme={theme}`, 14 | scopes: ["team"], 15 | }); 16 | } 17 | } -------------------------------------------------------------------------------- /packages/generator-teams/src/app/manifestGeneration/manifestGenerators/generator18/LocalizationManifestUpdater.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. 4 | 5 | import { IManifestUpdater } from "../../IManifestUpdater"; 6 | import { GeneratorTeamsAppOptions } from "../../../GeneratorTeamsAppOptions"; 7 | 8 | export class LocalizationManifestUpdater implements IManifestUpdater { 9 | 10 | public updateManifest(manifest: any, options: GeneratorTeamsAppOptions): void { 11 | if (options.localization) { 12 | if(!options.defaultLanguage) { 13 | throw "Localization requires a default language tag"; 14 | } 15 | if (!manifest.localizationInfo) { 16 | manifest.localizationInfo = {}; 17 | } 18 | // Set the default language 19 | if (options.defaultLanguage) { 20 | manifest.localizationInfo.defaultLanguageTag = options.defaultLanguage; 21 | } 22 | // Additional language 23 | if (options.additionalLanguage) { 24 | const lang = { 25 | languageTag: options.additionalLanguage, 26 | file: `${options.additionalLanguage}.json` 27 | }; 28 | if (!manifest.localizationInfo.additionalLanguages) { 29 | manifest.localizationInfo.additionalLanguages = []; 30 | } 31 | manifest.localizationInfo.additionalLanguages.push(lang); 32 | } 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /packages/generator-teams/src/app/manifestGeneration/manifestGenerators/generator18/ManifestGenerator.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. 4 | 5 | import { BaseManifestGenerator } from "../../BaseManifestGenerator"; 6 | import { TabManifestUpdater } from "../generator18/TabManifestUpdater"; 7 | import { BotManifestUpdater } from "../generator18/BotManifestUpdater"; 8 | import { ConnectorManifestUpdater } from "../generator18/ConnectorManifestUpdater"; 9 | import { MessageExtensionManifestUpdater } from "../generator18/MessageExtensionManifestUpdater"; 10 | import { GeneratorTeamsAppOptions } from "../../../GeneratorTeamsAppOptions"; 11 | import { LocalizationManifestUpdater } from "../generator18/LocalizationManifestUpdater"; 12 | import * as chalk from 'chalk'; 13 | 14 | 15 | export class ManifestGenerator extends BaseManifestGenerator { 16 | constructor() { 17 | super(); 18 | this.tabUpdater = new TabManifestUpdater(); 19 | this.botUpdater = new BotManifestUpdater(); 20 | this.connectorUpdater = new ConnectorManifestUpdater(); 21 | this.messageExtensionUpdater = new MessageExtensionManifestUpdater(); 22 | this.localizationUpdater = new LocalizationManifestUpdater(); 23 | } 24 | 25 | public generateManifest(options: GeneratorTeamsAppOptions): any { 26 | const manifest = super.generateManifest(options); 27 | return manifest; 28 | } 29 | 30 | public supportsUpdateManifest(from: string): boolean { 31 | return false; 32 | } 33 | 34 | public updateManifest(manifest: any, log?: (message?: string, context?: any) => void): any { 35 | throw "Unable to update manifest"; 36 | }; 37 | } 38 | -------------------------------------------------------------------------------- /packages/generator-teams/src/app/manifestGeneration/manifestGenerators/generator18/TabManifestUpdater.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. 4 | 5 | import { IManifestUpdater } from "../../IManifestUpdater"; 6 | import { GeneratorTeamsAppOptions } from "../../../GeneratorTeamsAppOptions"; 7 | import { v4 as uuid } from 'uuid'; 8 | 9 | export class TabManifestUpdater implements IManifestUpdater { 10 | 11 | public updateManifest(manifest: any, options: GeneratorTeamsAppOptions): void { 12 | if (options.tabType == "static") { 13 | (manifest.staticTabs).push({ 14 | entityId: uuid(), 15 | name: options.tabTitle, 16 | contentUrl: `https://{{PUBLIC_HOSTNAME}}/${options.tabName}/?name={loginHint}&tenant={tid}&theme={theme}`, 17 | scopes: ["personal"] 18 | }); 19 | } 20 | else { 21 | const tab: any = { 22 | configurationUrl: `https://{{PUBLIC_HOSTNAME}}/${options.tabName}/config.html?name={loginHint}&tenant={tid}&group={groupId}&theme={theme}`, 23 | canUpdateConfiguration: true, 24 | scopes: options.tabScopes 25 | }; 26 | if (options.tabSharePoint) { 27 | tab.sharePointPreviewImage = `https://{{PUBLIC_HOSTNAME}}/assets/${options.tabName}-preview.png`; 28 | tab.supportedSharePointHosts = options.tabSharePointHosts; 29 | } 30 | (manifest.configurableTabs).push(tab); 31 | } 32 | if (options.tabSSO && manifest.webApplicationInfo === undefined) { 33 | // only add SSO the first time 34 | manifest.webApplicationInfo = { 35 | id: `{{TAB_APP_ID}}`, 36 | resource: `{{TAB_APP_URI}}` 37 | } 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /packages/generator-teams/src/app/manifestGeneration/manifestGenerators/generator19/ManifestGenerator.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. 4 | 5 | import { BaseManifestGenerator } from "../../BaseManifestGenerator"; 6 | import { TabManifestUpdater } from "./TabManifestUpdater"; 7 | import { BotManifestUpdater } from "../generator18/BotManifestUpdater"; 8 | import { ConnectorManifestUpdater } from "../generator18/ConnectorManifestUpdater"; 9 | import { MessageExtensionManifestUpdater } from "../generator18/MessageExtensionManifestUpdater"; 10 | import { GeneratorTeamsAppOptions } from "../../../GeneratorTeamsAppOptions"; 11 | import { LocalizationManifestUpdater } from "../generator18/LocalizationManifestUpdater"; 12 | import * as chalk from 'chalk'; 13 | 14 | 15 | export class ManifestGenerator extends BaseManifestGenerator { 16 | constructor() { 17 | super(); 18 | this.tabUpdater = new TabManifestUpdater(); 19 | this.botUpdater = new BotManifestUpdater(); 20 | this.connectorUpdater = new ConnectorManifestUpdater(); 21 | this.messageExtensionUpdater = new MessageExtensionManifestUpdater(); 22 | this.localizationUpdater = new LocalizationManifestUpdater(); 23 | } 24 | 25 | public generateManifest(options: GeneratorTeamsAppOptions): any { 26 | const manifest = super.generateManifest(options); 27 | manifest["$schema"] = "https://developer.microsoft.com/en-us/json-schemas/teams/v1.9/MicrosoftTeams.schema.json"; 28 | manifest.manifestVersion = "1.9"; 29 | 30 | return manifest; 31 | } 32 | 33 | public supportsUpdateManifest(from: string): boolean { 34 | return from === "1.8" ; 35 | } 36 | 37 | public updateManifest(manifest: any, log?: (message?: string, context?: any) => void): any { 38 | switch (manifest.manifestVersion) { 39 | case "1.8": 40 | manifest["$schema"] = "https://developer.microsoft.com/en-us/json-schemas/teams/v1.9/MicrosoftTeams.schema.json"; 41 | manifest.manifestVersion = "1.9"; 42 | return manifest; 43 | default: 44 | throw "Unable to update manifest"; 45 | 46 | } 47 | }; 48 | } 49 | -------------------------------------------------------------------------------- /packages/generator-teams/src/app/manifestGeneration/manifestGenerators/generator19/TabManifestUpdater.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. 4 | 5 | import { IManifestUpdater } from "../../IManifestUpdater"; 6 | import { GeneratorTeamsAppOptions } from "../../../GeneratorTeamsAppOptions"; 7 | import { v4 as uuid } from 'uuid'; 8 | import { OFFICE_GUID } from "../../../../tab/TabGenerator"; 9 | import { parse } from "tldjs"; 10 | 11 | export class TabManifestUpdater implements IManifestUpdater { 12 | 13 | public updateManifest(manifest: any, options: GeneratorTeamsAppOptions): void { 14 | if (options.tabType == "static") { 15 | (manifest.staticTabs).push({ 16 | entityId: uuid(), 17 | name: options.tabTitle, 18 | contentUrl: `https://{{PUBLIC_HOSTNAME}}/${options.tabName}/?name={loginHint}&tenant={tid}&theme={theme}`, 19 | scopes: ["personal"] 20 | }); 21 | } 22 | else if (options.tabType == "configurable") { 23 | const tab: any = { 24 | configurationUrl: `https://{{PUBLIC_HOSTNAME}}/${options.tabName}/config.html?name={loginHint}&tenant={tid}&group={groupId}&theme={theme}`, 25 | canUpdateConfiguration: true, 26 | scopes: options.tabScopes 27 | }; 28 | if (options.tabSharePoint) { 29 | tab.sharePointPreviewImage = `https://{{PUBLIC_HOSTNAME}}/assets/${options.tabName}-preview.png`; 30 | tab.supportedSharePointHosts = options.tabSharePointHosts; 31 | } 32 | (manifest.configurableTabs).push(tab); 33 | } 34 | if (options.tabSSO && manifest.webApplicationInfo === undefined) { 35 | // only add SSO the first time 36 | manifest.webApplicationInfo = { 37 | id: `{{TAB_APP_ID}}`, 38 | resource: `{{TAB_APP_URI}}` 39 | } 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /packages/generator-teams/src/app/manifestGeneration/manifestGenerators/generatorDevPreview/ManifestGenerator.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. 4 | 5 | import { BaseManifestGenerator } from "../../BaseManifestGenerator"; 6 | import { TabManifestUpdater } from "../generator19/TabManifestUpdater"; 7 | import { BotManifestUpdater } from "../generator18/BotManifestUpdater"; 8 | import { ConnectorManifestUpdater } from "../generator18/ConnectorManifestUpdater"; 9 | import { MessageExtensionManifestUpdater } from "../generator18/MessageExtensionManifestUpdater"; 10 | import { GeneratorTeamsAppOptions } from "../../../GeneratorTeamsAppOptions"; 11 | import { LocalizationManifestUpdater } from "../generator18/LocalizationManifestUpdater"; 12 | 13 | import * as chalk from 'chalk'; 14 | 15 | export class ManifestGenerator extends BaseManifestGenerator { 16 | constructor() { 17 | super(); 18 | this.tabUpdater = new TabManifestUpdater(); 19 | this.botUpdater = new BotManifestUpdater(); 20 | this.connectorUpdater = new ConnectorManifestUpdater(); 21 | this.messageExtensionUpdater = new MessageExtensionManifestUpdater(); 22 | this.localizationUpdater = new LocalizationManifestUpdater(); 23 | 24 | } 25 | 26 | public generateManifest(options: GeneratorTeamsAppOptions): any { 27 | const manifest = super.generateManifest(options); 28 | manifest["$schema"] = "https://raw.githubusercontent.com/OfficeDev/microsoft-teams-app-schema/preview/DevPreview/MicrosoftTeams.schema.json"; 29 | manifest.manifestVersion = "devPreview"; 30 | 31 | return manifest; 32 | } 33 | 34 | public supportsUpdateManifest(from: string): boolean { 35 | return from === "1.11" || 36 | from === "m365devPreview" || 37 | from === "1.12" || 38 | from === "1.13"; 39 | } 40 | 41 | public updateManifest(manifest: any, log?: (message?: string, context?: any) => void): any { 42 | switch (manifest.manifestVersion) { 43 | case "1.11": 44 | case "1.12": 45 | case "1.13": 46 | case "m365DevPreview": 47 | manifest["$schema"] = "https://raw.githubusercontent.com/OfficeDev/microsoft-teams-app-schema/preview/DevPreview/MicrosoftTeams.schema.json"; 48 | manifest.manifestVersion = "devPreview"; 49 | return manifest; 50 | default: 51 | throw "Unable to update manifest"; 52 | 53 | } 54 | }; 55 | } 56 | -------------------------------------------------------------------------------- /packages/generator-teams/src/app/manifestGeneration/manifestGenerators/generatorM365DevPreview/ManifestGenerator.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. 4 | 5 | import { BaseManifestGenerator } from "../../BaseManifestGenerator"; 6 | import { TabManifestUpdater } from "../generator19/TabManifestUpdater"; 7 | import { BotManifestUpdater } from "../generator18/BotManifestUpdater"; 8 | import { ConnectorManifestUpdater } from "../generator18/ConnectorManifestUpdater"; 9 | import { MessageExtensionManifestUpdater } from "../generator18/MessageExtensionManifestUpdater"; 10 | import { GeneratorTeamsAppOptions } from "../../../GeneratorTeamsAppOptions"; 11 | import { LocalizationManifestUpdater } from "../generator18/LocalizationManifestUpdater"; 12 | import * as chalk from 'chalk'; 13 | 14 | export class ManifestGenerator extends BaseManifestGenerator { 15 | constructor() { 16 | super(); 17 | this.tabUpdater = new TabManifestUpdater(); 18 | this.botUpdater = new BotManifestUpdater(); 19 | this.connectorUpdater = new ConnectorManifestUpdater(); 20 | this.messageExtensionUpdater = new MessageExtensionManifestUpdater(); 21 | this.localizationUpdater = new LocalizationManifestUpdater(); 22 | 23 | } 24 | 25 | public generateManifest(options: GeneratorTeamsAppOptions): any { 26 | const manifest = super.generateManifest(options); 27 | manifest["$schema"] = "https://raw.githubusercontent.com/OfficeDev/microsoft-teams-app-schema/preview/DevPreview/MicrosoftTeams.schema.json"; 28 | manifest.manifestVersion = "m365DevPreview"; 29 | 30 | return manifest; 31 | } 32 | 33 | public supportsUpdateManifest(from: string): boolean { 34 | return from === "devPreview" || 35 | from === "1.11"; 36 | } 37 | 38 | public updateManifest(manifest: any, log?: (message?: string, context?: any) => void): any { 39 | switch (manifest.manifestVersion) { 40 | case "1.11": 41 | case "devPreview": 42 | manifest["$schema"] = "https://raw.githubusercontent.com/OfficeDev/microsoft-teams-app-schema/preview/DevPreview/MicrosoftTeams.schema.json"; 43 | manifest.manifestVersion = "m365DevPreview"; 44 | return manifest; 45 | default: 46 | throw "Unable to update manifest"; 47 | 48 | } 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /packages/generator-teams/src/app/templates/.env: -------------------------------------------------------------------------------- 1 | # The public domain name of where you host your application 2 | PUBLIC_HOSTNAME=<%=hostname%> 3 | 4 | # Id of the Microsoft Teams application 5 | APPLICATION_ID=<%=id%> 6 | # Package name of the Microsoft Teams application 7 | PACKAGE_NAME=<%=packageName%> 8 | 9 | # App Id and App Password for the Bot Framework bot 10 | <%= botidEnv %>=<%= botid ? botid : '' %> 11 | <%= botidEnvSecret %>= 12 | 13 | # Port for local debugging 14 | PORT=3007 15 | 16 | # Security token for the default outgoing webhook 17 | SECURITY_TOKEN= 18 | 19 | # ID of the Outlook Connector 20 | CONNECTOR_ID=<%= connectorId ? connectorId : '' %> 21 | 22 | # Application Insights instrumentation key 23 | APPINSIGHTS_INSTRUMENTATIONKEY=<%= azureAppInsightsKey %> 24 | 25 | # NGROK configuration for development 26 | # NGROK authentication token (leave empty for anonymous) 27 | NGROK_AUTH= 28 | # NGROK sub domain. ex "myapp" or (leave empty for random) 29 | NGROK_SUBDOMAIN= 30 | # NGROK region. (us, eu, au, ap - default is us) 31 | NGROK_REGION= 32 | 33 | # Debug settings, default logging "msteams" 34 | DEBUG=msteams 35 | -------------------------------------------------------------------------------- /packages/generator-teams/src/app/templates/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:14 AS BUILD_IMAGE 2 | 3 | RUN curl -sfL https://install.goreleaser.com/github.com/tj/node-prune.sh | bash -s -- -b /usr/local/bin 4 | 5 | WORKDIR /app 6 | 7 | COPY . /app/ 8 | 9 | # install 10 | RUN npm install 11 | 12 | # build 13 | RUN npm run build 14 | 15 | # remove development dependencies 16 | RUN npm prune --production 17 | 18 | # run node prune 19 | RUN /usr/local/bin/node-prune 20 | 21 | FROM node:14-alpine 22 | 23 | WORKDIR /app 24 | 25 | # copy from build image 26 | COPY --from=BUILD_IMAGE /app/dist ./dist 27 | COPY --from=BUILD_IMAGE /app/node_modules ./node_modules 28 | 29 | ENV DEBUG msteams 30 | 31 | EXPOSE 3007 32 | 33 | CMD [ "node", "dist/server.js" ] -------------------------------------------------------------------------------- /packages/generator-teams/src/app/templates/_eslintignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | **/*.spec.* -------------------------------------------------------------------------------- /packages/generator-teams/src/app/templates/_eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "standard" 4 | ], 5 | "globals": { 6 | "Atomics": "readonly", 7 | "SharedArrayBuffer": "readonly" 8 | }, 9 | "parser": "@typescript-eslint/parser", 10 | "plugins": [ 11 | "@typescript-eslint" 12 | ], 13 | "rules": { 14 | "object-shorthand": 2, 15 | "quotes": [ 16 | "error", 17 | "double" 18 | ], 19 | "semi": [ 20 | "error", 21 | "always" 22 | ], 23 | "indent": [ 24 | "error", 25 | 4, 26 | { 27 | "SwitchCase": 1 28 | } 29 | ], 30 | "space-before-function-paren": "off", 31 | "space-in-parens": "off", 32 | "padded-blocks": "off", 33 | "no-unused-vars": "off" 34 | } 35 | } -------------------------------------------------------------------------------- /packages/generator-teams/src/app/templates/_gitignore: -------------------------------------------------------------------------------- 1 | # do not include the node modules in Git 2 | node_modules 3 | 4 | # do not include the package in Git 5 | package 6 | 7 | # do not include the local environment files 8 | .env 9 | 10 | # do not include the Connectors Json Db file 11 | connectors.json 12 | 13 | # do not include dist files 14 | dist 15 | -------------------------------------------------------------------------------- /packages/generator-teams/src/app/templates/_vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 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 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Attach to server", 9 | "type": "pwa-node", 10 | "request": "attach", 11 | "skipFiles": [ 12 | "/**" 13 | ], 14 | "restart": true, 15 | "outFiles": [ 16 | "${workspaceFolder}/dist/**/*.js", 17 | "!**/node_modules/**" 18 | ] 19 | }, 20 | { 21 | "name": "Launch client (Edge)", 22 | "type": "pwa-msedge", 23 | "request": "launch", 24 | "url": "https://teams.microsoft.com/", 25 | "outFiles": [ 26 | "${workspaceFolder}/dist/web/**/*.js", 27 | "!**/node_modules/**" 28 | ] 29 | }, 30 | { 31 | "name": "Launch client (Chrome)", 32 | "type": "pwa-chrome", 33 | "request": "launch", 34 | "url": "https://teams.microsoft.com/", 35 | "outFiles": [ 36 | "${workspaceFolder}/dist/web/**/*.js", 37 | "!**/node_modules/**" 38 | ] 39 | } 40 | ], 41 | "compounds": [ 42 | { 43 | "name": "Debug (Edge)", 44 | "configurations": [ 45 | "Attach to server", 46 | "Launch client (Edge)" 47 | ], 48 | "presentation": { 49 | "group": "all", 50 | "order": 1 51 | }, 52 | "stopAll": true 53 | }, 54 | { 55 | "name": "Debug (Chrome)", 56 | "configurations": [ 57 | "Attach to server", 58 | "Launch client (Chrome)" 59 | ], 60 | "presentation": { 61 | "group": "all", 62 | "order": 2 63 | }, 64 | "stopAll": true 65 | } 66 | ] 67 | } -------------------------------------------------------------------------------- /packages/generator-teams/src/app/templates/gulpfile.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. 4 | 5 | const gulp = require("gulp"); 6 | const package = require("./package.json"); 7 | const argv = require("yargs").argv; 8 | const log = require("fancy-log"); 9 | const path = require("path"); 10 | 11 | const config = {}; 12 | 13 | // Set environment variables 14 | const env = argv.env; 15 | if (env === undefined) { 16 | require("dotenv").config(); 17 | } else { 18 | log(`Using custom .env: ${env}`); 19 | require("dotenv").config({ path: path.resolve(process.cwd(), env) }); 20 | } 21 | process.env.VERSION = package.version; 22 | 23 | const core = require("yoteams-build-core"); 24 | 25 | // Initialize core build 26 | core.setup(gulp, config); 27 | 28 | // Add your custom or override tasks below 29 | -------------------------------------------------------------------------------- /packages/generator-teams/src/app/templates/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "<%= packageName %>", 3 | "version": "0.0.1", 4 | "description": "Generated by yoteams, https://aka.ms/yoteams", 5 | "scripts": { 6 | "start": "node dist/server.js", 7 | "build": "gulp build", 8 | "manifest": "gulp manifest", 9 | "debug": "gulp serve --debug" 10 | }, 11 | "dependencies": { 12 | "@fluentui/react-northstar": "~0.62.0", 13 | "@microsoft/teams-js": "^2.0.0", 14 | "axios": "^0.27.2", 15 | "botbuilder": "~4.16.0", 16 | "botbuilder-dialogs": "~4.16.0", 17 | "compression": "1.7.4", 18 | "debug": "~4.3.4", 19 | "dotenv": "^16.0.0", 20 | "express": "~4.18.1", 21 | "express-msteams-host": "1.9.1", 22 | "express-session": "~1.17.2", 23 | "morgan": "~1.10.0", 24 | "react": "^16.8.6", 25 | "react-dom": "^16.8.6" 26 | }, 27 | "devDependencies": { 28 | "@types/debug": "4.1.7", 29 | "@types/express": "^4.17.13", 30 | "@types/express-session": "~1.17.4", 31 | "@types/morgan": "~1.9.3", 32 | "@types/react": "16.8.10", 33 | "@types/react-dom": "^16.9.7", 34 | "browser-sync": "^2.26.5", 35 | "fancy-log": "^2.0.0", 36 | "file-loader": "~6.2.0", 37 | "fork-ts-checker-webpack-plugin": "~7.2.11", 38 | "gulp": "4.0.2", 39 | "ts-loader": "~9.3.0", 40 | "typescript": "~4.6.4", 41 | "typestyle": "~2.3.0", 42 | "vinyl": "2.2.1", 43 | "webpack": "^5.72.0", 44 | "yargs": "^17.5.1", 45 | "yoteams-build-core": "^1.8.0", 46 | "yoteams-deploy": "^1.4.0", 47 | "webpack-node-externals": "^3.0.0" 48 | }, 49 | "browserslist": [ 50 | "> 1%", 51 | "last 2 versions", 52 | "Firefox ESR" 53 | ], 54 | "engines": { 55 | "node": ">=12" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /packages/generator-teams/src/app/templates/src/client/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "settings": { 3 | "react": { 4 | "version": "detect" 5 | } 6 | }, 7 | "env": { 8 | "browser": true, 9 | "es6": true 10 | }, 11 | "extends": [ 12 | "plugin:react/recommended" 13 | ], 14 | "plugins": [ 15 | "react", 16 | "react-hooks" 17 | ], 18 | "rules": { 19 | "react-hooks/rules-of-hooks": "error", // Checks rules of Hooks 20 | "react-hooks/exhaustive-deps": "warn", // Checks effect dependencies 21 | "no-use-before-define": "off", 22 | "@typescript-eslint/no-use-before-define": ["error"] 23 | } 24 | } -------------------------------------------------------------------------------- /packages/generator-teams/src/app/templates/src/client/client.ts: -------------------------------------------------------------------------------- 1 | // Default entry point for client scripts 2 | // Automatically generated 3 | // Please avoid from modifying to much... 4 | import * as ReactDOM from "react-dom"; 5 | import * as React from "react"; 6 | 7 | export const render = (type: any, element: HTMLElement) => { 8 | ReactDOM.render(React.createElement(type, {}), element); 9 | }; 10 | -------------------------------------------------------------------------------- /packages/generator-teams/src/app/templates/src/client/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | displayName: "client", 3 | rootDir: "./../../", 4 | globals: { 5 | "ts-jest": { 6 | tsconfig: "/src/client/tsconfig.json", 7 | diagnostics: { 8 | ignoreCodes: [151001] 9 | } 10 | } 11 | }, 12 | testEnvironment: "jsdom", 13 | preset: "ts-jest/presets/js-with-ts", 14 | snapshotSerializers: [ 15 | "enzyme-to-json/serializer" 16 | ], 17 | setupFiles: [ 18 | "/src/test/test-shim.js", 19 | "/src/test/test-setup.js" 20 | ], 21 | testMatch: [ 22 | "/src/client/**/__tests__/*.(ts|tsx|js)" 23 | ], 24 | collectCoverageFrom: [ 25 | "src/client/**/*.{js,jsx,ts,tsx}", 26 | "!/node_modules/" 27 | ], 28 | coverageReporters: [ 29 | "text" 30 | ] 31 | }; 32 | -------------------------------------------------------------------------------- /packages/generator-teams/src/app/templates/src/client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "es6", 4 | "target": "es5", 5 | "moduleResolution": "node", 6 | "noImplicitAny": false, 7 | "strictNullChecks": true, 8 | "jsx": "react", 9 | "sourceMap": true, 10 | "allowJs": true 11 | }, 12 | "exclude": [ 13 | "node_modules" 14 | ] 15 | } -------------------------------------------------------------------------------- /packages/generator-teams/src/app/templates/src/manifest/icon-color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/packages/generator-teams/src/app/templates/src/manifest/icon-color.png -------------------------------------------------------------------------------- /packages/generator-teams/src/app/templates/src/manifest/icon-outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/packages/generator-teams/src/app/templates/src/manifest/icon-outline.png -------------------------------------------------------------------------------- /packages/generator-teams/src/app/templates/src/public/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/packages/generator-teams/src/app/templates/src/public/assets/icon.png -------------------------------------------------------------------------------- /packages/generator-teams/src/app/templates/src/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | <%= title %> 8 | 9 | 10 | 11 | 12 | <% if (useAzureAppInsights) { %><% } %> 21 | 22 | 23 | 24 | 34 |
35 |

<%= description %>

36 | <% if (botType == 'botframework') { %> 37 |

Add the bot to Microsoft Teams: 38 | Add bot to Teams

39 | <% } %> 40 |
41 | 42 | 43 | -------------------------------------------------------------------------------- /packages/generator-teams/src/app/templates/src/public/privacy.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | <%= title %> - Privacy 8 | 9 | 10 | 11 | 12 | <% if (useAzureAppInsights) { %><% } %> 21 | 22 | 23 | 24 | 34 |
35 |

36 | Add your privacy statement here... 37 |

38 |
39 | 40 | 41 | -------------------------------------------------------------------------------- /packages/generator-teams/src/app/templates/src/public/styles/main.scss: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: sans-serif; 3 | 4 | max-width: 80vw; 5 | margin: 0 auto; 6 | } 7 | 8 | .l-header { 9 | display: flex; 10 | 11 | box-sizing: border-box; 12 | width: 100%; 13 | min-height: 5em; 14 | padding: 1em; 15 | 16 | align-content: space-between; 17 | align-items: stretch; 18 | justify-content: space-between; 19 | 20 | h1 { 21 | box-sizing: border-box; 22 | padding-left: 1.5em; 23 | padding-right: 1.5em; 24 | 25 | } 26 | } 27 | 28 | .l-article { 29 | margin: 3em 2em; 30 | } 31 | 32 | .l-title { 33 | flex-grow: 1; 34 | } 35 | 36 | img.logo { 37 | width: 100%; 38 | min-width: 76px; 39 | height: auto; 40 | } -------------------------------------------------------------------------------- /packages/generator-teams/src/app/templates/src/public/tou.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | <%= title %> - Terms of Use 8 | 9 | 10 | 11 | 12 | <% if (useAzureAppInsights) { %><% } %> 21 | 22 | 23 | 24 | 34 |
35 |

36 | Add your Terms of Use here... 37 |

38 |
39 | 40 | 41 | -------------------------------------------------------------------------------- /packages/generator-teams/src/app/templates/src/server/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true, 4 | "es2020": true 5 | }, 6 | "parserOptions": { 7 | "ecmaFeatures": { 8 | "jsx": true 9 | }, 10 | "ecmaVersion": 2018 11 | }, 12 | "rules": { 13 | "no-process-env": "off" 14 | } 15 | } -------------------------------------------------------------------------------- /packages/generator-teams/src/app/templates/src/server/TeamsAppsComponents.ts: -------------------------------------------------------------------------------- 1 | // Components will be added here 2 | 3 | export const nonce = {}; // Do not remove! 4 | -------------------------------------------------------------------------------- /packages/generator-teams/src/app/templates/src/server/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | displayName: "server", 3 | rootDir: "./../../", 4 | globals: { 5 | "ts-jest": { 6 | tsconfig: "/src/server/tsconfig.json" 7 | } 8 | }, 9 | preset: "ts-jest/presets/js-with-ts", 10 | testMatch: [ 11 | "/src/server/**/__tests__/*.(ts|js)" 12 | ], 13 | collectCoverageFrom: [ 14 | "src/server/**/*.{js,ts}", 15 | "!/node_modules/" 16 | ], 17 | coverageReporters: [ 18 | "text" 19 | ] 20 | }; 21 | -------------------------------------------------------------------------------- /packages/generator-teams/src/app/templates/src/server/server.ts: -------------------------------------------------------------------------------- 1 | import * as Express from "express"; 2 | import * as http from "http"; 3 | import * as path from "path"; 4 | import * as morgan from "morgan"; 5 | import { MsTeamsApiRouter, MsTeamsPageRouter } from "express-msteams-host"; 6 | import * as debug from "debug"; 7 | import * as compression from "compression"; 8 | <% if (useAzureAppInsights) { %>import * as appInsights from "applicationinsights";<% } %> 9 | // Initialize debug logging module 10 | const log = debug("msteams"); 11 | 12 | log("Initializing Microsoft Teams Express hosted App..."); 13 | 14 | // Initialize dotenv, to use .env file settings if existing 15 | require("dotenv").config(); 16 | 17 | <% if (useAzureAppInsights) { %>// Set up app insights 18 | appInsights.setup(process.env.APPINSIGHTS_INSTRUMENTATIONKEY).start(); 19 | <% } 20 | 21 | %>// The import of components has to be done AFTER the dotenv config 22 | // eslint-disable-next-line import/first 23 | import * as allComponents from "./TeamsAppsComponents"; 24 | 25 | // Create the Express webserver 26 | const express = Express(); 27 | const port = process.env.port || process.env.PORT || 3007; 28 | 29 | // Inject the raw request body onto the request object 30 | express.use(Express.json({ 31 | verify: (req, res, buf: Buffer, encoding: string): void => { 32 | (req as any).rawBody = buf.toString(); 33 | } 34 | })); 35 | express.use(Express.urlencoded({ extended: true })); 36 | 37 | // Express configuration 38 | express.set("views", path.join(__dirname, "/")); 39 | 40 | // Add simple logging 41 | express.use(morgan("tiny")); 42 | 43 | // Add compression - uncomment to remove compression 44 | express.use(compression()); 45 | 46 | // Add /scripts and /assets as static folders 47 | express.use("/scripts", Express.static(path.join(__dirname, "web/scripts"))); 48 | express.use("/assets", Express.static(path.join(__dirname, "web/assets"))); 49 | 50 | // routing for bots, connectors and incoming web hooks - based on the decorators 51 | // For more information see: https://www.npmjs.com/package/express-msteams-host 52 | express.use(MsTeamsApiRouter(allComponents)); 53 | 54 | // routing for pages for tabs and connector configuration 55 | // For more information see: https://www.npmjs.com/package/express-msteams-host 56 | express.use(MsTeamsPageRouter({ 57 | root: path.join(__dirname, "web/"), 58 | components: allComponents 59 | })); 60 | 61 | // Set default web page 62 | express.use("/", Express.static(path.join(__dirname, "web/"), { 63 | index: "index.html" 64 | })); 65 | 66 | // Set the port 67 | express.set("port", port); 68 | 69 | // Start the webserver 70 | http.createServer(express).listen(port, () => { 71 | log(`Server running on ${port}`); 72 | }); 73 | -------------------------------------------------------------------------------- /packages/generator-teams/src/app/templates/src/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "moduleResolution": "node", 6 | "noImplicitAny": false, 7 | "strictNullChecks": true, 8 | "jsx": "react", 9 | "sourceMap": true, 10 | "experimentalDecorators": true, 11 | "resolveJsonModule": true, 12 | }, 13 | "exclude": [ 14 | "node_modules" 15 | ] 16 | } -------------------------------------------------------------------------------- /packages/generator-teams/src/app/templates/src/test/test-setup.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Defines the React 16 Adapter for Enzyme. 3 | * 4 | * @link http://airbnb.io/enzyme/docs/installation/#working-with-react-16 5 | * @copyright 2017 Airbnb, Inc. 6 | */ 7 | const enzyme = require("enzyme"); 8 | const Adapter = require("enzyme-adapter-react-16"); 9 | 10 | enzyme.configure({ adapter: new Adapter() }); 11 | -------------------------------------------------------------------------------- /packages/generator-teams/src/app/templates/src/test/test-shim.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Get rids of the missing requestAnimationFrame polyfill warning. 3 | * 4 | * @link https://reactjs.org/docs/javascript-environment-requirements.html 5 | * @copyright 2004-present Facebook. All Rights Reserved. 6 | */ 7 | global.requestAnimationFrame = function(callback) { 8 | setTimeout(callback, 0); 9 | }; 10 | -------------------------------------------------------------------------------- /packages/generator-teams/src/bot/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. 4 | 5 | import {BotGenerator} from './BotGenerator'; 6 | 7 | module.exports = BotGenerator 8 | -------------------------------------------------------------------------------- /packages/generator-teams/src/bot/templates/src/client/{botName}/__tests__/{staticTabClassName}Tab.spec.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { shallow, mount } from "enzyme"; 3 | import toJson from "enzyme-to-json"; 4 | import { Header } from "@fluentui/react-northstar"; 5 | 6 | import { <%=staticTabClassName%>Tab } from "../<%=staticTabClassName%>Tab"; 7 | 8 | describe("<%=staticTabClassName%> Component", () => { 9 | // Snapshot Test Sample 10 | it("should match the snapshot", () => { 11 | const wrapper = shallow(<<%=staticTabClassName%>Tab />); 12 | expect(toJson(wrapper)).toMatchSnapshot(); 13 | }); 14 | 15 | // Component Test Sample 16 | it("should render the tab", () => { 17 | const component = shallow(<<%=staticTabClassName%>Tab />); 18 | const divResult = component.containsMatchingElement(
); 19 | expect(divResult).toBeTruthy(); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /packages/generator-teams/src/bot/templates/src/client/{botName}/{staticTabClassName}Tab.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { Provider, Flex, Text, Button, Header } from "@fluentui/react-northstar"; 3 | import { useTeams } from "msteams-react-base-component"; 4 | import { useState, useEffect } from "react"; 5 | import { app } from "@microsoft/teams-js"; 6 | 7 | /** 8 | * Implementation of the <%= staticTabName %> content page 9 | */ 10 | export const <%=staticTabClassName%>Tab = () => { 11 | const [{ inTeams, theme }] = useTeams(); 12 | 13 | useEffect(() => { 14 | if (inTeams === true) { 15 | app.notifySuccess(); 16 | } 17 | }, [inTeams]); 18 | 19 | return ( 20 | 21 | 24 | 25 |
26 | 27 | 28 |
29 | 30 |
31 |
32 | 35 | 36 | 37 | 38 | 39 | ); 40 | }; 41 | -------------------------------------------------------------------------------- /packages/generator-teams/src/bot/templates/src/public/{botName}/{staticTabName}.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%= staticTabTitle %> 7 | 8 | 9 | 10 | <% if (useAzureAppInsights) { %><% } %> 19 | 20 | 21 | 22 |
23 | Loading... 24 |
25 | 26 | 27 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /packages/generator-teams/src/bot/templates/src/server/{botName}/cards/welcomeCard.ts: -------------------------------------------------------------------------------- 1 | const WelcomeCard = require("./welcomeCard.json"); 2 | export default WelcomeCard; 3 | -------------------------------------------------------------------------------- /packages/generator-teams/src/bot/templates/src/server/{botName}/dialogBot.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ConversationState, 3 | UserState, 4 | TeamsActivityHandler, 5 | TurnContext 6 | } from "botbuilder"; 7 | import { MainDialog } from "./dialogs/mainDialog"; 8 | 9 | export class DialogBot extends TeamsActivityHandler { 10 | public dialogState: any; 11 | 12 | constructor(public conversationState: ConversationState, public userState: UserState, public dialog: MainDialog) { 13 | super(); 14 | this.conversationState = conversationState; 15 | this.userState = userState; 16 | this.dialog = dialog; 17 | this.dialogState = this.conversationState.createProperty("DialogState"); 18 | 19 | this.onMessage(async (context, next) => { 20 | // Run the MainDialog with the new message Activity. 21 | await this.dialog.run(context, this.dialogState); 22 | await next(); 23 | }); 24 | } 25 | 26 | public async run(context: TurnContext) { 27 | await super.run(context); 28 | // Save any state changes. The load happened during the execution of the Dialog. 29 | await this.conversationState.saveChanges(context, false); 30 | await this.userState.saveChanges(context, false); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/generator-teams/src/bot/templates/src/server/{botName}/dialogs/__tests__/HelpDialog.spec.ts: -------------------------------------------------------------------------------- 1 | import { HelpDialog } from "../helpDialog"; 2 | import { DialogTestClient } from "botbuilder-testing"; 3 | 4 | describe("bot:HelpDialog", () => { 5 | const helpDialog = new HelpDialog(); 6 | const testClient = new DialogTestClient("msteams", helpDialog); 7 | 8 | it("Should receive an answer", async () => { 9 | const reply = await testClient.sendActivity(""); 10 | expect(reply.text).toEqual("I am terribly sorry, but my developer hasn't trained me to do anything yet 😂. Please refer to [this link](https://docs.microsoft.com/en-us/microsoftteams/platform/bots/what-are-bots) to see how to develop bots for Teams"); 11 | }); 12 | 13 | }); 14 | -------------------------------------------------------------------------------- /packages/generator-teams/src/bot/templates/src/server/{botName}/dialogs/helpDialog.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ComponentDialog, 3 | DialogSet, 4 | DialogState, 5 | DialogTurnResult, 6 | DialogTurnStatus, 7 | TextPrompt, 8 | WaterfallDialog, 9 | WaterfallStepContext 10 | } from "botbuilder-dialogs"; 11 | import { 12 | MessageFactory, 13 | StatePropertyAccessor, 14 | TurnContext 15 | } from "botbuilder"; 16 | 17 | const HELP_DIALOG_ID = "helpDialog"; 18 | const HELP_WATERFALL_DIALOG_ID = "helpWaterfallDialog"; 19 | 20 | export class HelpDialog extends ComponentDialog { 21 | constructor() { 22 | super(HELP_DIALOG_ID); 23 | this.addDialog(new TextPrompt("TextPrompt")) 24 | .addDialog(new WaterfallDialog(HELP_WATERFALL_DIALOG_ID, [ 25 | this.introStep.bind(this) 26 | ])); 27 | this.initialDialogId = HELP_WATERFALL_DIALOG_ID; 28 | } 29 | 30 | public async run(context: TurnContext, accessor: StatePropertyAccessor) { 31 | const dialogSet = new DialogSet(accessor); 32 | dialogSet.add(this); 33 | const dialogContext = await dialogSet.createContext(context); 34 | const results = await dialogContext.continueDialog(); 35 | if (results.status === DialogTurnStatus.empty) { 36 | await dialogContext.beginDialog(this.id); 37 | } 38 | } 39 | 40 | private async introStep(stepContext: WaterfallStepContext): Promise { 41 | const message = MessageFactory.text("I am terribly sorry, but my developer hasn't trained me to do anything yet 😂. Please refer to [this link](https://docs.microsoft.com/en-us/microsoftteams/platform/bots/what-are-bots) to see how to develop bots for Teams"); 42 | await stepContext.context.sendActivity(message); 43 | return await stepContext.endDialog(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/generator-teams/src/bot/templates/src/server/{botName}/dialogs/mentionUserDialog.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ComponentDialog, 3 | DialogSet, 4 | DialogState, 5 | DialogTurnResult, 6 | DialogTurnStatus, 7 | TextPrompt, 8 | WaterfallDialog, 9 | WaterfallStepContext 10 | } from "botbuilder-dialogs"; 11 | import { 12 | MessageFactory, 13 | StatePropertyAccessor, 14 | TurnContext 15 | } from "botbuilder"; 16 | 17 | const MENTION_DIALOG_ID = "mentionUserDialog"; 18 | const MENTION_WATERFALL_DIALOG_ID = "mentionUserWaterfallDialog"; 19 | 20 | export class MentionUserDialog extends ComponentDialog { 21 | constructor() { 22 | super(MENTION_DIALOG_ID); 23 | this.addDialog(new TextPrompt("TextPrompt")) 24 | .addDialog(new WaterfallDialog(MENTION_WATERFALL_DIALOG_ID, [ 25 | this.introStep.bind(this) 26 | ])); 27 | this.initialDialogId = MENTION_WATERFALL_DIALOG_ID; 28 | } 29 | 30 | public async run(context: TurnContext, accessor: StatePropertyAccessor) { 31 | const dialogSet = new DialogSet(accessor); 32 | dialogSet.add(this); 33 | const dialogContext = await dialogSet.createContext(context); 34 | const results = await dialogContext.continueDialog(); 35 | if (results.status === DialogTurnStatus.empty) { 36 | await dialogContext.beginDialog(this.id); 37 | } 38 | } 39 | 40 | private async introStep(stepContext: WaterfallStepContext): Promise { 41 | const mention = { 42 | mentioned: stepContext.context.activity.from, 43 | text: `${new TextEncoder().encode(stepContext.context.activity.from.name)}`, 44 | type: "mention" 45 | }; 46 | const replyActivity = MessageFactory.text(`Hi ${mention.text}`); 47 | replyActivity.entities = [mention]; 48 | await stepContext.context.sendActivity(replyActivity); 49 | return await stepContext.endDialog(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /packages/generator-teams/src/bot/templates/src/server/{botName}/dialogs/teamsInfoDialog.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ComponentDialog, 3 | DialogSet, 4 | DialogState, 5 | DialogTurnResult, 6 | DialogTurnStatus, 7 | TextPrompt, 8 | WaterfallDialog, 9 | WaterfallStepContext 10 | } from "botbuilder-dialogs"; 11 | import { 12 | MessageFactory, 13 | StatePropertyAccessor, 14 | TeamsInfo, 15 | TurnContext 16 | } from "botbuilder"; 17 | 18 | const TEAMS_INFO_DIALOG_ID = "teamsInfoDialog"; 19 | const TEAMS_INFO_WATERFALL_DIALOG_ID = "teamsInfoWaterfallDialog"; 20 | let teamsUserInfo; 21 | 22 | export class TeamsInfoDialog extends ComponentDialog { 23 | constructor() { 24 | super(TEAMS_INFO_DIALOG_ID); 25 | this.addDialog(new TextPrompt("TextPrompt")) 26 | .addDialog(new WaterfallDialog(TEAMS_INFO_WATERFALL_DIALOG_ID, [ 27 | this.introStep.bind(this), 28 | this.actStep.bind(this), 29 | this.finalStep.bind(this) 30 | ])); 31 | this.initialDialogId = TEAMS_INFO_WATERFALL_DIALOG_ID; 32 | } 33 | 34 | public async run(context: TurnContext, accessor: StatePropertyAccessor) { 35 | const dialogSet = new DialogSet(accessor); 36 | dialogSet.add(this); 37 | const dialogContext = await dialogSet.createContext(context); 38 | const results = await dialogContext.continueDialog(); 39 | if (results.status === DialogTurnStatus.empty) { 40 | await dialogContext.beginDialog(this.id); 41 | } 42 | } 43 | 44 | private async introStep(stepContext: WaterfallStepContext): Promise { 45 | const promptMessage = MessageFactory.text("Do you want me to send you the current Teams member info?\n\nSay **yes** if you do like to see the context or **no** if you don't"); 46 | return await stepContext.prompt("TextPrompt", { prompt: promptMessage }); 47 | } 48 | 49 | private async actStep(stepContext: WaterfallStepContext): Promise { 50 | if (stepContext.result) { 51 | const result = stepContext.result; 52 | switch (result) { 53 | case "yes": { 54 | teamsUserInfo = await TeamsInfo.getMember(stepContext.context, stepContext.context.activity.from.id); 55 | await stepContext.context.sendActivity(`Your name: **${teamsUserInfo.name}**\n\nYour Teams ID: **${teamsUserInfo.id}**\n\nYour email: **${teamsUserInfo.email}**`); 56 | break; 57 | } 58 | default: { 59 | await stepContext.context.sendActivity("Ok, maybe next time 😉"); 60 | return await stepContext.next(); 61 | } 62 | } 63 | } 64 | return await stepContext.next(); 65 | } 66 | 67 | private async finalStep(stepContext: WaterfallStepContext): Promise { 68 | await stepContext.context.sendActivity("Now it's time to add more functionality to your bot, so head over to the [docs](https://aka.ms/yoTeams) and start building"); 69 | return await stepContext.endDialog(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /packages/generator-teams/src/connector/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. 4 | 5 | import { ConnectorGenerator } from './ConnectorGenerator'; 6 | 7 | module.exports = ConnectorGenerator 8 | -------------------------------------------------------------------------------- /packages/generator-teams/src/connector/templates/src/client/{connectorName}/__tests__/{connectorComponentName}Config.spec.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { shallow } from "enzyme"; 3 | import toJson from "enzyme-to-json"; 4 | import { Header } from "@fluentui/react-northstar"; 5 | 6 | import { <%=connectorComponentName%>Config } from "../<%=connectorComponentName%>Config"; 7 | 8 | describe("<%=connectorComponentName%>Config Component", () => { 9 | // Snapshot Test Sample 10 | it("should match the snapshot", () => { 11 | window.alert = jest.fn(); 12 | const wrapper = shallow(<<%=connectorComponentName%>Config />); 13 | expect(toJson(wrapper)).toMatchSnapshot(); 14 | }); 15 | 16 | // Component Test Sample 17 | it("should render the tab", () => { 18 | const component = shallow(<<%=connectorComponentName%>Config />); 19 | const divResult = component.containsMatchingElement(
); 20 | 21 | expect(divResult).toBeTruthy(); 22 | }); 23 | }); 24 | 25 | -------------------------------------------------------------------------------- /packages/generator-teams/src/connector/templates/src/public/{connectorName}/config.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%= connectorTitle %> 7 | 8 | 9 | 10 | <% if (useAzureAppInsights) { %><% } %> 19 | 20 | 21 | 22 |
23 | Loading... 24 |
25 | 26 | 27 | 28 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /packages/generator-teams/src/custombot/CustomBotGenerator.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. 4 | 5 | import * as Generator from 'yeoman-generator'; 6 | import * as lodash from 'lodash'; 7 | import { GeneratorTeamsAppOptions } from './../app/GeneratorTeamsAppOptions'; 8 | import { Yotilities } from './../app/Yotilities'; 9 | 10 | export class CustomBotGenerator extends Generator { 11 | options: GeneratorTeamsAppOptions; 12 | 13 | public constructor(args: any, opts: any) { 14 | super(args, opts); 15 | opts.force = true; 16 | this.options = opts.options; 17 | this.desc('Adds an outgoing webhook to a Teams project.'); 18 | } 19 | public prompting() { 20 | if (this.options.customBot) { 21 | const generatorPrefix = "[custombot]"; 22 | 23 | return this.prompt( 24 | [ 25 | { 26 | type: 'input', 27 | name: 'title', 28 | message: 'What is the name of your outgoing webhook?', 29 | prefix: generatorPrefix, 30 | default: this.options.title + ' Outgoing Webhook', 31 | validate: (input) => { 32 | if(! (/^[a-zA-Z].*/.test(input))) { 33 | return "Must start with an alphabetical character"; 34 | } 35 | return input.length > 0; 36 | } 37 | }, 38 | ] 39 | ).then((answers: any) => { 40 | this.options.customBotTitle = answers.title; 41 | this.options.customBotName = lodash.camelCase(answers.title); 42 | this.options.customBotClassName = this.options.customBotName.charAt(0).toUpperCase() + this.options.customBotName.slice(1); 43 | if (!this.options.customBotName.endsWith('OutgoingWebhook')) { 44 | this.options.customBotName = this.options.customBotName + 'OutgoingWebhook'; 45 | } 46 | }); 47 | } 48 | } 49 | public writing() { 50 | if (this.options.customBot) { 51 | let templateFiles = [ 52 | "src/server/{customBotName}/{customBotClassName}.ts" 53 | ]; 54 | 55 | this.sourceRoot() 56 | 57 | templateFiles.forEach(t => { 58 | this.fs.copyTpl( 59 | this.templatePath(t), 60 | Yotilities.fixFileNames(t, this.options), 61 | this.options); 62 | }); 63 | 64 | Yotilities.insertTsExportDeclaration( 65 | "src/server/TeamsAppsComponents.ts", 66 | `./${this.options.customBotName}/${this.options.customBotClassName}`, 67 | `Automatically added for the ${this.options.customBotName} outgoing webhook`, 68 | this.fs 69 | ); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /packages/generator-teams/src/custombot/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. 4 | 5 | import {CustomBotGenerator} from './CustomBotGenerator'; 6 | 7 | module.exports = CustomBotGenerator -------------------------------------------------------------------------------- /packages/generator-teams/src/custombot/templates/src/server/{customBotName}/{customBotClassName}.ts: -------------------------------------------------------------------------------- 1 | import * as builder from "botbuilder"; 2 | import * as express from "express"; 3 | import * as crypto from "crypto"; 4 | import { OutgoingWebhookDeclaration, IOutgoingWebhook } from "express-msteams-host"; 5 | 6 | /** 7 | * Implementation for <%= customBotTitle %> 8 | */ 9 | @OutgoingWebhookDeclaration("/api/webhook") 10 | export class <%= customBotClassName %> implements IOutgoingWebhook { 11 | 12 | /** 13 | * Implement your outgoing webhook logic here 14 | * @param req the Request 15 | * @param res the Response 16 | * @param next 17 | */ 18 | public requestHandler(req: express.Request, res: express.Response, next: express.NextFunction) { 19 | // parse the incoming message 20 | const incoming = req.body as builder.Activity; 21 | 22 | // create the response, any Teams compatible responses can be used 23 | const message: Partial = { 24 | type: builder.ActivityTypes.Message 25 | }; 26 | 27 | const securityToken = process.env.SECURITY_TOKEN; 28 | if (securityToken && securityToken.length > 0) { 29 | // There is a configured security token 30 | const auth = req.headers.authorization; 31 | const msgBuf = Buffer.from((req as any).rawBody, "utf8"); 32 | const msgHash = "HMAC " + crypto 33 | .createHmac("sha256", Buffer.from(securityToken as string, "base64")) 34 | .update(msgBuf) 35 | .digest("base64"); 36 | 37 | if (msgHash === auth) { 38 | // Message was ok and verified 39 | message.text = `Echo ${incoming.text}`; 40 | } else { 41 | // Message could not be verified 42 | message.text = "Error: message sender cannot be verified"; 43 | } 44 | } else { 45 | // There is no configured security token 46 | message.text = "Error: outgoing webhook is not configured with a security token"; 47 | } 48 | 49 | // send the message 50 | res.send(JSON.stringify(message)); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /packages/generator-teams/src/localization/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. 4 | 5 | import { LocalizationGenerator } from './LocalizationGenerator'; 6 | 7 | module.exports = LocalizationGenerator -------------------------------------------------------------------------------- /packages/generator-teams/src/messageExtension/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. 4 | 5 | import { MessageExtensionGenerator } from './MessageExtensionGenerator'; 6 | 7 | module.exports = MessageExtensionGenerator 8 | -------------------------------------------------------------------------------- /packages/generator-teams/src/messageExtension/templates/src/client/{messageExtensionName}/__tests__/{messageExtensionClassName}Config.spec.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { shallow } from "enzyme"; 3 | import toJson from "enzyme-to-json"; 4 | import { Header } from "@fluentui/react-northstar"; 5 | 6 | import { <%=messageExtensionClassName%>Config } from "../<%=messageExtensionClassName%>Config"; 7 | 8 | describe("<%=messageExtensionClassName%>Config Component", () => { 9 | // Snapshot Test Sample 10 | it("should match the snapshot", () => { 11 | const wrapper = shallow(<<%=messageExtensionClassName%>Config />); 12 | expect(toJson(wrapper)).toMatchSnapshot(); 13 | }); 14 | 15 | // Component Test Sample 16 | it("should render the tab", () => { 17 | const component = shallow(<<%=messageExtensionClassName%>Config />); 18 | const divResult = component.containsMatchingElement(
); 19 | 20 | expect(divResult).toBeTruthy(); 21 | }); 22 | }); 23 | 24 | 25 | -------------------------------------------------------------------------------- /packages/generator-teams/src/messageExtension/templates/src/client/{messageExtensionName}/{messageExtensionClassName}Action.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { Provider, Flex, Header, Input, Button, Text } from "@fluentui/react-northstar"; 3 | import { useState, useEffect } from "react"; 4 | import { useTeams } from "msteams-react-base-component"; 5 | import { ap, dialogp } from "@microsoft/teams-js"; 6 | 7 | /** 8 | * Implementation of the <%= messageExtensionTitle %> Task Module page 9 | */ 10 | export const <%=messageExtensionClassName %>Action = () => { 11 | 12 | const [{ inTeams, theme }] = useTeams(); 13 | const [email, setEmail] = useState(); 14 | 15 | useEffect(() => { 16 | if (inTeams === true) { 17 | app.notifySuccess(); 18 | } 19 | }, [inTeams]); 20 | 21 | return ( 22 | 23 | 26 | 27 |
28 |
29 | 30 | { 36 | if (data) { 37 | setEmail(data.value); 38 | } 39 | }} 40 | required /> 41 | 44 |
45 |
46 |
47 |
48 | ); 49 | }; 50 | -------------------------------------------------------------------------------- /packages/generator-teams/src/messageExtension/templates/src/client/{messageExtensionName}/{messageExtensionClassName}Config.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { Provider, Flex, Header, Checkbox, Button } from "@fluentui/react-northstar"; 3 | import { app } from "@microsoft/teams-js"; 4 | import { useState, useEffect } from "react"; 5 | import { useTeams } from "msteams-react-base-component"; 6 | 7 | /** 8 | * Implementation of the <%= messageExtensionTitle %> configuration page 9 | */ 10 | export const <%=messageExtensionClassName%>Config = () => { 11 | 12 | const [{ inTeams, theme }] = useTeams(); 13 | const [onOrOff, setOnOrOff] = useState(); 14 | 15 | useEffect(() => { 16 | if (inTeams === true) { 17 | app.notifySuccess(); 18 | setOnOrOff(true); 19 | } 20 | }, [inTeams]); 21 | 22 | return ( 23 | 24 | 25 | 26 |
27 |
28 | { setOnOrOff(!onOrOff); }} /> 33 | 34 |
35 |
36 |
37 |
38 | ); 39 | }; 40 | -------------------------------------------------------------------------------- /packages/generator-teams/src/messageExtension/templates/src/public/{messageExtensionName}/action.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%= messageExtensionTitle %> Action 7 | 8 | 9 | 10 | <% if (useAzureAppInsights) { %><% } %> 19 | 20 | 21 | 22 |
23 | Loading... 24 |
25 | 26 | 27 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /packages/generator-teams/src/messageExtension/templates/src/public/{messageExtensionName}/config.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%= messageExtensionTitle %> Configuration 7 | 8 | 9 | 10 | <% if (useAzureAppInsights) { %><% } %> 19 | 20 | 21 | 22 |
23 | Loading... 24 |
25 | 26 | 27 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /packages/generator-teams/src/messageExtension/templates/src/server/{messageExtensionName}/__tests__/{messageExtensionClassName}.spec.ts: -------------------------------------------------------------------------------- 1 | import <%=messageExtensionClassName%> from "../<%=messageExtensionClassName%>"; 2 | 3 | describe("<%=messageExtensionClassName%>", () => { 4 | it("Should create the middleware", () => { 5 | const middleware = new <%=messageExtensionClassName%>(); 6 | expect(middleware).toBeDefined(); 7 | }); 8 | }); -------------------------------------------------------------------------------- /packages/generator-teams/src/tab/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the MIT license. 4 | 5 | import {TabGenerator} from './TabGenerator'; 6 | 7 | module.exports = TabGenerator -------------------------------------------------------------------------------- /packages/generator-teams/src/tab/templates/src/client/{tabName}/__tests__/{tabReactComponentName}.spec.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { shallow, mount } from "enzyme"; 3 | import toJson from "enzyme-to-json"; 4 | import { Header } from "@fluentui/react-northstar"; 5 | import { act } from "react-dom/test-utils"; 6 | 7 | 8 | import { <%=tabReactComponentName%> } from "../<%=tabReactComponentName%>"; 9 | 10 | describe("<%=tabReactComponentName%> Component", () => { 11 | // Snapshot Test Sample 12 | it("should match the snapshot", () => { 13 | const wrapper = shallow(<<%=tabReactComponentName%> />); 14 | expect(toJson(wrapper)).toMatchSnapshot(); 15 | }); 16 | 17 | // Component Test Sample 18 | it("should render the tab", () => { 19 | const component = shallow(<<%=tabReactComponentName%> />); 20 | const divResult = component.containsMatchingElement(
); 21 | 22 | expect(divResult).toBeTruthy(); 23 | }); 24 | 25 | // Mocking Sample 26 | it("should show alert on button click", async () => { 27 | window.alert = jest.fn(); 28 | 29 | let component; 30 | await act (async () => { 31 | component = mount(<<%=tabReactComponentName%> />); 32 | }); 33 | 34 | const button = component.find("button"); 35 | 36 | button.simulate("click"); 37 | 38 | expect(window.alert).toHaveBeenCalledWith("It worked!"); 39 | 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /packages/generator-teams/src/tab/templates/src/client/{tabName}/__tests__/{tabReactComponentName}Config.spec.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { shallow } from "enzyme"; 3 | import toJson from "enzyme-to-json"; 4 | import { Header } from "@fluentui/react-northstar"; 5 | 6 | import { <%=tabReactComponentName%>Config } from "../<%=tabReactComponentName%>Config"; 7 | 8 | describe("<%=tabReactComponentName%>Config Component", () => { 9 | // Snapshot Test Sample 10 | it("should match the snapshot", () => { 11 | const wrapper = shallow(<<%=tabReactComponentName%>Config />); 12 | expect(toJson(wrapper)).toMatchSnapshot(); 13 | }); 14 | 15 | // Component Test Sample 16 | it("should render the tab", () => { 17 | const component = shallow(<<%=tabReactComponentName%>Config />); 18 | const divResult = component.containsMatchingElement(
); 19 | 20 | expect(divResult).toBeTruthy(); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /packages/generator-teams/src/tab/templates/src/client/{tabName}/__tests__/{tabReactComponentName}Remove.spec.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { shallow } from "enzyme"; 3 | import toJson from "enzyme-to-json"; 4 | import { Header } from "@fluentui/react-northstar"; 5 | 6 | import { <%=tabReactComponentName%>Remove } from "../<%=tabReactComponentName%>Remove"; 7 | 8 | describe("<%=tabReactComponentName%>Remove Component", () => { 9 | // Snapshot Test Sample 10 | it("should match the snapshot", () => { 11 | const wrapper = shallow(<<%=tabReactComponentName%>Remove />); 12 | expect(toJson(wrapper)).toMatchSnapshot(); 13 | }); 14 | 15 | // Component Test Sample 16 | it("should render the tab", () => { 17 | const component = shallow(<<%=tabReactComponentName%>Remove />); 18 | const divResult = component.containsMatchingElement(
); 19 | 20 | expect(divResult).toBeTruthy(); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /packages/generator-teams/src/tab/templates/src/client/{tabName}/{tabReactComponentName}.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { Provider, Flex, Text, Button, Header } from "@fluentui/react-northstar"; 3 | import { useState, useEffect } from "react"; 4 | import { useTeams } from "msteams-react-base-component"; 5 | import { app<% if (tabSSO) { %>, authentication<% } %> } from "@microsoft/teams-js";<% if (tabSSO) { %> 6 | import jwtDecode from "jwt-decode";<% } %> 7 | 8 | /** 9 | * Implementation of the <%= tabTitle %> content page 10 | */ 11 | export const <%=tabReactComponentName%> = () => { 12 | 13 | const [{ inTeams, theme, context }] = useTeams(); 14 | const [entityId, setEntityId] = useState();<% if (tabSSO) { %> 15 | const [name, setName] = useState(); 16 | const [error, setError] = useState();<% } %> 17 | 18 | useEffect(() => { 19 | if (inTeams === true) {<% if (tabSSO) { %> 20 | authentication.getAuthToken({ 21 | resources: [process.env.TAB_APP_URI as string], 22 | silent: false 23 | } as authentication.AuthTokenRequestParameters).then(token => { 24 | const decoded: { [key: string]: any; } = jwtDecode(token) as { [key: string]: any; }; 25 | setName(decoded!.name); 26 | app.notifySuccess(); 27 | }).catch(message => { 28 | setError(message); 29 | app.notifyFailure({ 30 | reason: app.FailedReason.AuthFailed, 31 | message 32 | }); 33 | });<% } else { %> 34 | app.notifySuccess();<% } %> 35 | } else { 36 | setEntityId("Not in Microsoft Teams"); 37 | } 38 | }, [inTeams]); 39 | 40 | useEffect(() => { 41 | if (context) { 42 | setEntityId(context.page.id); 43 | } 44 | }, [context]); 45 | 46 | /** 47 | * The render() method to create the UI of the tab 48 | */ 49 | return ( 50 | 51 | 54 | 55 |
56 | 57 | 58 |
<% if (tabSSO) { %> 59 |
60 | 61 |
62 | {error &&
} 63 | <% } else { %> 64 |
65 | 66 |
67 | <% } %> 68 |
69 | 70 |
71 |
72 |
73 | 76 | 77 | 78 | 79 | 80 | ); 81 | }; 82 | -------------------------------------------------------------------------------- /packages/generator-teams/src/tab/templates/src/client/{tabName}/{tabReactComponentName}Config.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { Provider, Flex, Header, Input } from "@fluentui/react-northstar"; 3 | import { useState, useEffect, useRef } from "react"; 4 | import { useTeams } from "msteams-react-base-component"; 5 | import { app, pages } from "@microsoft/teams-js"; 6 | 7 | /** 8 | * Implementation of <%= tabTitle %> configuration page 9 | */ 10 | export const <%=tabReactComponentName%>Config = () => { 11 | 12 | const [{ inTeams, theme, context }] = useTeams({}); 13 | const [text, setText] = useState(); 14 | const entityId = useRef(""); 15 | 16 | const onSaveHandler = (saveEvent: pages.config.SaveEvent) => { 17 | const host = "https://" + window.location.host; 18 | pages.config.setConfig({ 19 | contentUrl: host + "/<%=tabName%>/?name={loginHint}&tenant={tid}&group={groupId}&theme={theme}", 20 | websiteUrl: host + "/<%=tabName%>/?name={loginHint}&tenant={tid}&group={groupId}&theme={theme}", 21 | suggestedDisplayName: "<%=tabTitle%>", 22 | removeUrl: host + "/<%=tabName%>/remove.html?theme={theme}", 23 | entityId: entityId.current 24 | }).then(() => { 25 | saveEvent.notifySuccess(); 26 | }); 27 | }; 28 | 29 | useEffect(() => { 30 | if (context) { 31 | setText(context.page.id); 32 | entityId.current = context.page.id; 33 | pages.config.registerOnSaveHandler(onSaveHandler); 34 | pages.config.setValidityState(true); 35 | app.notifySuccess(); 36 | } 37 | // eslint-disable-next-line react-hooks/exhaustive-deps 38 | }, [context]); 39 | 40 | return ( 41 | 42 | 43 | 44 |
45 |
46 | { 52 | if (data) { 53 | setText(data.value); 54 | entityId.current = data.value; 55 | } 56 | }} 57 | required /> 58 |
59 |
60 |
61 |
62 | ); 63 | }; 64 | -------------------------------------------------------------------------------- /packages/generator-teams/src/tab/templates/src/client/{tabName}/{tabReactComponentName}Remove.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { Provider, Flex, Text, Header } from "@fluentui/react-northstar"; 3 | import { useEffect } from "react"; 4 | import { useTeams } from "msteams-react-base-component"; 5 | import { app } from "@microsoft/teams-js"; 6 | 7 | /** 8 | * Implementation of <%= tabTitle %> remove page 9 | */ 10 | export const <%=tabReactComponentName%>Remove = () => { 11 | 12 | const [{ inTeams, theme }] = useTeams(); 13 | 14 | useEffect(() => { 15 | if (inTeams === true) { 16 | app.notifySuccess(); 17 | } 18 | }, [inTeams]); 19 | 20 | return ( 21 | 22 | 23 | 24 |
25 |
26 | 27 |
28 |
29 |
30 |
31 | ); 32 | }; 33 | -------------------------------------------------------------------------------- /packages/generator-teams/src/tab/templates/src/public/assets/tab-preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnp/generator-teams/f0a7c1398eb3adcced89cf2a97e7039fc22a46f5/packages/generator-teams/src/tab/templates/src/public/assets/tab-preview.png -------------------------------------------------------------------------------- /packages/generator-teams/src/tab/templates/src/public/{tabName}/config.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%= tabTitle %>: Configuration 7 | 8 | 9 | 10 | <% if (useAzureAppInsights) { %><% } %> 19 | 20 | 21 | 22 |
23 | Loading... 24 |
25 | 26 | 27 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /packages/generator-teams/src/tab/templates/src/public/{tabName}/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%= tabTitle %> 7 | 8 | 9 | 10 | <% if (useAzureAppInsights) { %><% } %> 19 | 20 | 21 | 22 |
23 | Loading... 24 |
25 | 26 | 27 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /packages/generator-teams/src/tab/templates/src/public/{tabName}/remove.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%= tabTitle %>: Remove 7 | 8 | 9 | 10 | <% if (useAzureAppInsights) { %><% } %> 19 | 20 | 21 | 22 |
23 | Loading... 24 |
25 | 26 | 27 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /packages/generator-teams/src/tab/templates/src/server/{tabName}/{tabReactComponentName}.ts: -------------------------------------------------------------------------------- 1 | import { PreventIframe } from "express-msteams-host"; 2 | 3 | /** 4 | * Used as place holder for the decorators 5 | */ 6 | @PreventIframe("/<%=tabName%>/index.html")<% if(tabType == "configurable" ) { %> 7 | @PreventIframe("/<%=tabName%>/config.html")<% } %><% if(tabType == "configurable" ) { %> 8 | @PreventIframe("/<%=tabName%>/remove.html")<% } %> 9 | export class <%=tabReactComponentName%> { 10 | } 11 | -------------------------------------------------------------------------------- /packages/generator-teams/tests/ConnectorGenerator.spec.ts: -------------------------------------------------------------------------------- 1 | import * as del from 'del'; 2 | import * as helpers from 'yeoman-test'; 3 | import * as assert from 'yeoman-assert'; 4 | import { describe, it } from 'mocha'; 5 | const tests = require("./tests.json"); 6 | 7 | import * as testHelper from './helpers/TestHelper'; 8 | 9 | describe('teams:connector', function () { 10 | 11 | const CONNECTOR_HTML_FILES = [ 12 | 'src/public/teamsSolutionConnector/config.html' 13 | ]; 14 | 15 | const CONNECTOR_SCRIPT_FILES = [ 16 | 'src/client/teamsSolutionConnector/TeamsSolutionConnectorConfig.tsx' 17 | ]; 18 | 19 | const CONNECTOR_SCRIPT_TEST_FILES = [ 20 | 'src/client/teamsSolutionConnector/__tests__/TeamsSolutionConnectorConfig.spec.tsx' 21 | ]; 22 | 23 | const CONNECTOR_FILES = [ 24 | 'src/server/teamsSolutionConnector/TeamsSolutionConnector.ts' 25 | ]; 26 | 27 | 28 | const CONNECTOR_THEME_URL = "https://{{PUBLIC_HOSTNAME}}/teamsSolutionConnector/config.html?name={loginHint}&tenant={tid}&group={groupId}&theme={theme}"; 29 | 30 | 31 | beforeEach(async () => { 32 | await del([testHelper.TEMP_GENERATOR_PATTERN]); 33 | }); 34 | 35 | 36 | async function connectorTests(prompts: any) { 37 | it("Should have a configuration URL", async () => { 38 | assert.jsonFileContent('src/manifest/manifest.json', { 39 | "connectors": [{ 40 | "configurationUrl": CONNECTOR_THEME_URL 41 | } 42 | ] 43 | }); 44 | }); 45 | it("Should have connector client files", async () => { 46 | assert.file(CONNECTOR_SCRIPT_FILES); 47 | }); 48 | 49 | it("Should have connector app files", async () => { 50 | assert.file(CONNECTOR_FILES); 51 | }); 52 | 53 | it("Should have connector html files", async () => { 54 | assert.file(CONNECTOR_HTML_FILES); 55 | }); 56 | 57 | assert.noFile(CONNECTOR_SCRIPT_TEST_FILES); 58 | 59 | assert.file(CONNECTOR_HTML_FILES); 60 | 61 | 62 | 63 | if (prompts.unitTestsEnabled) { 64 | it("Should have connector unit test files", async () => { 65 | assert.file(CONNECTOR_SCRIPT_TEST_FILES); 66 | }); 67 | } else { 68 | it("Should not have connector unit test files", async () => { 69 | assert.noFile(CONNECTOR_SCRIPT_TEST_FILES); 70 | }); 71 | } 72 | 73 | 74 | } 75 | 76 | 77 | testHelper.runTests("connector", tests.connector, connectorTests); 78 | 79 | }); -------------------------------------------------------------------------------- /packages/generator-teams/tests/generic.spec.ts: -------------------------------------------------------------------------------- 1 | import * as del from "del"; 2 | import * as helpers from "yeoman-test"; 3 | import * as assert from "yeoman-assert"; 4 | import { describe, it } from "mocha"; 5 | 6 | import * as testHelper from "./helpers/TestHelper"; 7 | 8 | const tests = require("./tests.json"); 9 | 10 | 11 | 12 | describe("teams:generic", function () { 13 | beforeEach(async () => { 14 | await del([testHelper.TEMP_GENERATOR_PATTERN]); 15 | }); 16 | 17 | async function genericTests(prompts: any) { 18 | 19 | } 20 | 21 | testHelper.runTests("generic", tests.generic, genericTests); 22 | 23 | }); 24 | -------------------------------------------------------------------------------- /packages/generator-teams/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "lib": ["es6"], 6 | "moduleResolution": "node", 7 | "noImplicitAny": true, 8 | "strictNullChecks": true, 9 | "sourceMap": false, 10 | "skipLibCheck": true 11 | }, 12 | "exclude": [ 13 | "node_modules", 14 | "temp-templates", 15 | "src/app/templates", 16 | "src/bot/templates", 17 | "src/tab/templates", 18 | "src/custombot/templates", 19 | "src/connector/templates", 20 | "src/messageExtension/templates", 21 | "generators" 22 | ] 23 | } -------------------------------------------------------------------------------- /packages/yoteams-build-core/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true 5 | }, 6 | "extends": [ 7 | "standard" 8 | ], 9 | "parser": "@typescript-eslint/parser", 10 | "parserOptions": { 11 | "ecmaVersion": 12, 12 | "sourceType": "module" 13 | }, 14 | "plugins": [ 15 | "@typescript-eslint" 16 | ], 17 | "rules": { 18 | "object-shorthand": 2, 19 | "quotes": [ 20 | "error", 21 | "double" 22 | ], 23 | "semi": [ 24 | "error", 25 | "always" 26 | ], 27 | "indent": [ 28 | "error", 29 | 4, 30 | { 31 | "SwitchCase": 1 32 | } 33 | ], 34 | "space-before-function-paren": "off", 35 | "space-in-parens": "off", 36 | "padded-blocks": "off", 37 | "no-unused-vars": "off" 38 | } 39 | } -------------------------------------------------------------------------------- /packages/yoteams-build-core/.npmignore: -------------------------------------------------------------------------------- 1 | # Folders 2 | .vscode 3 | node_modules 4 | src 5 | .github 6 | tests 7 | 8 | # Files 9 | .git* 10 | .yo-rc.json 11 | tsconfig.json 12 | *.tgz 13 | .eslintrc.json 14 | -------------------------------------------------------------------------------- /packages/yoteams-build-core/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Wictor Wilén. All rights reserved. 4 | Copyright (c) Microsoft Corporation. All rights reserved. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE 23 | -------------------------------------------------------------------------------- /packages/yoteams-build-core/src/buildTasks.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Licensed under the MIT license. 3 | // SPDX-License-Identifier: MIT 4 | 5 | import GulpClient from "gulp"; 6 | import { dependencies } from "."; 7 | import { IBuildCoreConfig } from "./iBuildCoreConfig"; 8 | 9 | /** 10 | * Registers the "build" Gulp task 11 | * @param gulp 12 | * @param config 13 | */ 14 | export const buildTasks = (gulp: GulpClient.Gulp, config: IBuildCoreConfig) => { 15 | gulp.task("build", dependencies(gulp, "build", "webpack", "styles", "static:copy", "static:inject")); 16 | }; 17 | -------------------------------------------------------------------------------- /packages/yoteams-build-core/src/codespacesTasks.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Licensed under the MIT license. 3 | // SPDX-License-Identifier: MIT 4 | 5 | import GulpClient from "gulp"; 6 | import log from "fancy-log"; 7 | import { dependencies } from "."; 8 | import { IBuildCoreConfig } from "./iBuildCoreConfig"; 9 | import fs from "fs-extra"; 10 | import chalk from "chalk"; 11 | 12 | const argv = require("yargs").argv; 13 | 14 | /** 15 | * Registers tasks for Github Codespaces 16 | * @param gulp Gulp object 17 | * @param config configuration - not used 18 | */ 19 | export const codespacesTasks = (gulp: GulpClient.Gulp, config: IBuildCoreConfig) => { 20 | if (process.env.CODESPACES) { 21 | try { 22 | // only register code spaces tasks when in Github Codespaces 23 | 24 | const codespaceEnvConfig = "/workspaces/.codespaces/shared/environment-variables.json"; 25 | const codespaceEnv = fs.readJSONSync(codespaceEnvConfig); 26 | const codespaceName = codespaceEnv.CODESPACE_NAME; 27 | 28 | process.env.PUBLIC_HOSTNAME = `${codespaceName}-${process.env.PORT}.githubpreview.dev`; 29 | log("[Codespace] Public url: " + process.env.PUBLIC_HOSTNAME); 30 | gulp.task("codespaces-serve", dependencies(gulp, "codespaces-serve", "manifest", "serve")); 31 | } catch (ex) { 32 | log(chalk.red(`Unable to set up Codespaces tasks: ${ex}`)); 33 | } 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /packages/yoteams-build-core/src/iBuildCoreConfig.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Copyright (c) Paul Schaeflein. All rights reserved. 3 | // Licensed under the MIT license. 4 | // SPDX-License-Identifier: MIT 5 | 6 | import inject from "gulp-inject"; 7 | 8 | export interface IBuildCoreConfig { 9 | watches: string | string[]; 10 | clientWatches: string | string[]; 11 | staticFiles: string | string[]; 12 | injectSources: string | string[]; 13 | injectOptions: inject.IOptions; 14 | htmlFiles: string | string[]; 15 | } 16 | -------------------------------------------------------------------------------- /packages/yoteams-build-core/src/loadPlugins.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Licensed under the MIT license. 3 | // SPDX-License-Identifier: MIT 4 | 5 | import GulpClient from "gulp"; 6 | import log from "fancy-log"; 7 | import findupSync from "findup-sync"; 8 | 9 | /** 10 | * Loads additional plugins for yoteams 11 | * @param gulp 12 | * @param config 13 | */ 14 | export const loadPlugins = (gulp: GulpClient.Gulp, config: any) => { 15 | if (require.main) { 16 | const packagePath = findupSync("package.json"); 17 | if (packagePath) { 18 | const pkg = require(packagePath); 19 | if (pkg) { 20 | const modules = Object.keys(pkg.devDependencies); 21 | for (const module of modules) { 22 | if (module.startsWith("yoteams-") && module !== "yoteams-build-core") { 23 | log(`Found additional Yo Teams plugin: ${module}`); 24 | const plugin = require(module); 25 | plugin.setup(gulp, config); 26 | } 27 | } 28 | } 29 | } 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /packages/yoteams-build-core/src/ngrokTasks.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Licensed under the MIT license. 3 | // SPDX-License-Identifier: MIT 4 | 5 | import GulpClient from "gulp"; 6 | import log from "fancy-log"; 7 | import ngrok from "ngrok"; 8 | import { dependencies } from "."; 9 | import { IBuildCoreConfig } from "./iBuildCoreConfig"; 10 | import PluginError from "plugin-error"; 11 | 12 | export const ngrokTasks = (gulp: GulpClient.Gulp, config: IBuildCoreConfig) => { 13 | 14 | gulp.task("start-ngrok", (cb) => { 15 | log("[NGROK] starting ngrok..."); 16 | const conf = { 17 | subdomain: process.env.NGROK_SUBDOMAIN as string, 18 | region: process.env.NGROK_REGION as "us" | "eu" | "au" | "ap" | "sa" | "jp" | "in", 19 | addr: process.env.PORT as string, 20 | authtoken: process.env.NGROK_AUTH as string 21 | }; 22 | 23 | ngrok.connect(conf).then((url) => { 24 | log("[NGROK] Url: " + url); 25 | if (!conf.authtoken) { 26 | log("[NGROK] You have been assigned a random ngrok URL that will only be available for this session. You will need to re-upload the Teams manifest next time you run this command."); 27 | } 28 | let hostName = url.replace("http://", ""); 29 | hostName = hostName.replace("https://", ""); 30 | 31 | log("[NGROK] PUBLIC_HOSTNAME set to: " + hostName); 32 | process.env.PUBLIC_HOSTNAME = hostName; 33 | 34 | log("[NGROK] Inspect Url: " + ngrok.getUrl()); 35 | cb(); 36 | 37 | }).catch((err) => { 38 | cb(new PluginError("yoteams-build.core", err)); 39 | }); 40 | }); 41 | 42 | gulp.task("ngrok-serve", dependencies(gulp, "ngrok-serve", "start-ngrok", "manifest", "serve")); 43 | 44 | }; 45 | -------------------------------------------------------------------------------- /packages/yoteams-build-core/src/nukeTasks.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Licensed under the MIT license. 3 | // SPDX-License-Identifier: MIT 4 | 5 | import GulpClient from "gulp"; 6 | import del from "del"; 7 | import { trackEvent } from "."; 8 | import { IBuildCoreConfig } from "./iBuildCoreConfig"; 9 | 10 | export const nukeTasks = (gulp: GulpClient.Gulp, config: IBuildCoreConfig) => { 11 | 12 | gulp.task("nuke", () => { 13 | trackEvent("nuke"); 14 | return del(["temp", "dist"]); 15 | }); 16 | 17 | }; 18 | -------------------------------------------------------------------------------- /packages/yoteams-build-core/src/schemas.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "version": "1.3", 4 | "schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.3/MicrosoftTeams.schema.json" 5 | }, 6 | { 7 | "version": "1.4", 8 | "schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.4/MicrosoftTeams.schema.json" 9 | }, 10 | { 11 | "version": "devPreview", 12 | "schema": "https://raw.githubusercontent.com/OfficeDev/microsoft-teams-app-schema/preview/DevPreview/MicrosoftTeams.schema.json" 13 | }, 14 | { 15 | "version": "m365DevPreview", 16 | "schema": "https://raw.githubusercontent.com/OfficeDev/microsoft-teams-app-schema/preview/DevPreview/MicrosoftTeams.schema.json" 17 | }, 18 | { 19 | "version": "1.5", 20 | "schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.5/MicrosoftTeams.schema.json" 21 | }, 22 | { 23 | "version": "1.6", 24 | "schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.6/MicrosoftTeams.schema.json" 25 | }, 26 | { 27 | "version": "1.7", 28 | "schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.7/MicrosoftTeams.schema.json" 29 | }, 30 | { 31 | "version": "1.8", 32 | "schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.8/MicrosoftTeams.schema.json" 33 | }, 34 | { 35 | "version": "1.9", 36 | "schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.9/MicrosoftTeams.schema.json" 37 | }, 38 | { 39 | "version": "1.10", 40 | "schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.10/MicrosoftTeams.schema.json" 41 | }, 42 | { 43 | "version": "1.11", 44 | "schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.11/MicrosoftTeams.schema.json" 45 | }, 46 | { 47 | "version": "1.12", 48 | "schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.12/MicrosoftTeams.schema.json" 49 | }, 50 | { 51 | "version": "1.13", 52 | "schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.13/MicrosoftTeams.schema.json" 53 | }, 54 | { 55 | "version": "1.14", 56 | "schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.14/MicrosoftTeams.schema.json" 57 | }, 58 | { 59 | "version": "1.15", 60 | "schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.15/MicrosoftTeams.schema.json" 61 | } 62 | ] 63 | -------------------------------------------------------------------------------- /packages/yoteams-build-core/src/styleTasks.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Licensed under the MIT license. 3 | // SPDX-License-Identifier: MIT 4 | 5 | import GulpClient from "gulp"; 6 | import Plumber from "gulp-plumber"; 7 | import autoprefixer from "autoprefixer"; 8 | import gulpif from "gulp-if"; 9 | import sourcemaps from "gulp-sourcemaps"; 10 | import dartSass from "sass"; 11 | import gulpSass from "gulp-sass"; 12 | import postcss from "gulp-postcss"; 13 | import { trackEvent } from "."; 14 | import { IBuildCoreConfig } from "./iBuildCoreConfig"; 15 | 16 | const argv = require("yargs").argv; 17 | 18 | const sass = gulpSass(dartSass); 19 | 20 | export const styleTasks = (gulp: GulpClient.Gulp, config: IBuildCoreConfig) => { 21 | const debug = argv.debug !== undefined; 22 | const styles = () => { 23 | trackEvent("styles"); 24 | return gulp.src("src/public/**/*.scss") 25 | .pipe(Plumber()) 26 | .pipe(gulpif(debug, sourcemaps.init())) 27 | .pipe(sass.sync({ 28 | outputStyle: debug ? "expanded" : "compressed", 29 | includePaths: ["."] 30 | }).on("error", sass.logError)) 31 | .pipe(postcss([ 32 | autoprefixer() 33 | ])) 34 | .pipe(gulpif(debug, sourcemaps.write())) 35 | .pipe(gulp.dest("dist/web")); 36 | }; 37 | 38 | gulp.task("styles", styles); 39 | 40 | }; 41 | -------------------------------------------------------------------------------- /packages/yoteams-build-core/src/types.d.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Licensed under the MIT license. 3 | // SPDX-License-Identifier: MIT 4 | 5 | // Terrible, but working fix to get around this missing typings for gulp-token-replace 6 | declare module "gulp-token-replace"; 7 | -------------------------------------------------------------------------------- /packages/yoteams-build-core/src/webTasks.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Licensed under the MIT license. 3 | // SPDX-License-Identifier: MIT 4 | 5 | import GulpClient from "gulp"; 6 | import replace from "gulp-token-replace"; 7 | import inject from "gulp-inject"; 8 | import { trackEvent } from "."; 9 | import { IBuildCoreConfig } from "./iBuildCoreConfig"; 10 | 11 | export const injectSources = (gulp: GulpClient.Gulp, config: IBuildCoreConfig) => () => { 12 | trackEvent("static:inject"); 13 | const htmlFiles = [ 14 | "./src/public/**/*.html", 15 | "./src/public/**/*.ejs" 16 | ]; 17 | const injectSourceFiles = [ 18 | "./dist/web/scripts/**/*.js", 19 | "./dist/web/styles/**/*.css" 20 | ]; 21 | const injectSrc = gulp.src(config.injectSources ? injectSourceFiles.concat(config.injectSources) : injectSourceFiles); 22 | 23 | const defaultInjectOptions = { 24 | relative: false, 25 | ignorePath: "dist/web", 26 | addRootSlash: true 27 | }; 28 | return gulp.src(config.htmlFiles ? htmlFiles.concat(config.htmlFiles) : htmlFiles) 29 | .pipe(replace({ 30 | tokens: { 31 | ...process.env 32 | } 33 | })) 34 | .pipe( 35 | inject(injectSrc, config.injectOptions || defaultInjectOptions) 36 | ) 37 | .pipe( 38 | gulp.dest("./dist/web") 39 | ); 40 | 41 | }; 42 | 43 | export const webTasks = (gulp: GulpClient.Gulp, config: IBuildCoreConfig) => { 44 | const staticFiles = [ 45 | "./src/public/**/*.html", 46 | "./src/public/**/*.ejs", 47 | "./src/public/assets/**/*" 48 | ]; 49 | 50 | gulp.task("static:inject", injectSources(gulp, config)); 51 | 52 | gulp.task("static:copy", () => { 53 | trackEvent("static:copy"); 54 | return gulp.src(config.staticFiles ? staticFiles.concat(config.staticFiles) : staticFiles, { 55 | base: "./src/public" 56 | }) 57 | .pipe( 58 | gulp.dest("./dist/web") 59 | ); 60 | }); 61 | 62 | }; 63 | -------------------------------------------------------------------------------- /packages/yoteams-build-core/src/webpackServe.ts: -------------------------------------------------------------------------------- 1 | // Licensed under the MIT license. 2 | // SPDX-License-Identifier: MIT 3 | 4 | import WebpackDevServer from "webpack-dev-server"; 5 | import Webpack from "webpack"; 6 | import log from "fancy-log"; 7 | import path from "path"; 8 | 9 | (async () => { 10 | const webpackConfig = require(path.join(process.cwd(), "webpack.config")); 11 | const clientConfig = webpackConfig[1]; 12 | const compiler = Webpack(clientConfig); 13 | 14 | compiler.hooks.beforeCompile.tap("webpackServe", () => { 15 | log.info("webpack compiling"); 16 | }); 17 | 18 | const defaultDevServerConfig = { 19 | hot: false, 20 | host: "localhost", 21 | port: 9000, 22 | allowedHosts: "all", 23 | client: { 24 | overlay: { 25 | warnings: false, 26 | errors: true 27 | } 28 | }, 29 | devMiddleware: { 30 | writeToDisk: true, 31 | stats: { 32 | all: false, 33 | colors: true, 34 | errors: true, 35 | warnings: true, 36 | timings: true, 37 | entrypoints: true 38 | } 39 | } 40 | }; 41 | const server = new WebpackDevServer(clientConfig.devServer || defaultDevServerConfig, compiler); 42 | 43 | try { 44 | await server.start(); 45 | } catch (error) { 46 | log.error(error); 47 | throw error; 48 | } 49 | })(); 50 | -------------------------------------------------------------------------------- /packages/yoteams-build-core/src/webpackTasks.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Licensed under the MIT license. 3 | // SPDX-License-Identifier: MIT 4 | 5 | import webpack from "webpack"; 6 | import path from "path"; 7 | import PluginError from "plugin-error"; 8 | import log from "fancy-log"; 9 | import GulpClient from "gulp"; 10 | import { dependenciesP, trackEvent } from "."; 11 | import { IBuildCoreConfig } from "./iBuildCoreConfig"; 12 | import chalk from "chalk"; 13 | 14 | /** 15 | * Defines the two webpack tasks 16 | */ 17 | export const webpackTasks = (gulp: GulpClient.Gulp, config: IBuildCoreConfig) => { 18 | 19 | const webpackTask = (idx: number, callback: Function) => { 20 | const webpackConfig = require( 21 | path.join(process.cwd(), "webpack.config") 22 | ); 23 | 24 | webpack(webpackConfig[idx], (err, stats) => { 25 | 26 | if (err) { 27 | return callback(new PluginError("webpack", err)); 28 | } 29 | if (stats) { 30 | const jsonStats = stats.toJson(); 31 | if (jsonStats) { 32 | if (jsonStats.warnings && jsonStats.warnings.length > 0) { 33 | chalk.yellow("[Webpack warnings]:" + chalk.reset()); 34 | // eslint-disable-next-line array-callback-return 35 | jsonStats.warnings.map((e) => { 36 | log(e.message); 37 | }); 38 | } 39 | if (jsonStats.errors && jsonStats.errors.length > 0) { 40 | log(chalk.red("[Webpack errors]:") + chalk.reset()); 41 | // eslint-disable-next-line array-callback-return 42 | jsonStats.errors.map((e) => { 43 | log(e.message); 44 | }); 45 | return callback(new PluginError("webpack", `${jsonStats.errorsCount} webpack error(s) found.`)); 46 | } 47 | } 48 | } 49 | callback(); 50 | }); 51 | }; 52 | 53 | gulp.task("webpack:client", callback => { 54 | trackEvent("webpack:client"); 55 | webpackTask(1, callback); 56 | }); 57 | 58 | gulp.task("webpack:server", callback => { 59 | trackEvent("webpack:server"); 60 | webpackTask(0, callback); 61 | }); 62 | 63 | gulp.task("webpack", dependenciesP(gulp, "webpack", "webpack:client", "webpack:server")); 64 | 65 | }; 66 | -------------------------------------------------------------------------------- /packages/yoteams-deploy/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "env": { 4 | "browser": true, 5 | "es2021": true 6 | }, 7 | "extends": [ 8 | "standard" 9 | ], 10 | "parser": "@typescript-eslint/parser", 11 | "parserOptions": { 12 | "ecmaVersion": 12, 13 | "sourceType": "module" 14 | }, 15 | "plugins": [ 16 | "@typescript-eslint" 17 | ], 18 | "rules": { 19 | "object-shorthand": 2, 20 | "quotes": [ 21 | "error", 22 | "double" 23 | ], 24 | "semi": [ 25 | "error", 26 | "always" 27 | ], 28 | "indent": [ 29 | "error", 30 | 4, 31 | { 32 | "SwitchCase": 1 33 | } 34 | ], 35 | "space-before-function-paren": "off", 36 | "space-in-parens": "off", 37 | "padded-blocks": "off", 38 | "no-unused-vars": "off" 39 | } 40 | } -------------------------------------------------------------------------------- /packages/yoteams-deploy/.npmignore: -------------------------------------------------------------------------------- 1 | # Folders 2 | .vscode 3 | node_modules 4 | src 5 | .github 6 | tests 7 | 8 | # Files 9 | .git* 10 | .yo-rc.json 11 | tsconfig.json 12 | *.tgz 13 | .eslintrc.json 14 | -------------------------------------------------------------------------------- /packages/yoteams-deploy/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 6 | and this project adheres to [Semantic Versioning](http://semver.org/). 7 | 8 | ## [*1.4.0*]- <*2023-01-19*> 9 | 10 | ### Changes 11 | 12 | * Dependency package updates 13 | 14 | ## [*1.3.0*]- <*2022-06-23*> 15 | 16 | ### Changes 17 | 18 | * Requires Node 16 or later 19 | * Dependency package updates 20 | 21 | ## [*1.2.0*]- <*2022-06-23*> 22 | 23 | ### Changes 24 | 25 | * Requires Node 14 or later 26 | * Dependency package updates 27 | 28 | ## [*1.1.0*]- <*2021-11-18*> 29 | 30 | ### Added 31 | 32 | * Added telemetry, see https://github.com/pnp/generator-teams/blob/master/docs/docs/about/telemetry.md 33 | 34 | ## [*1.0.1*]- <*2021-09-14*> 35 | 36 | ### Fixes 37 | 38 | * Fix #228 39 | 40 | ## [*1.0.0*]- <*2021-06-07*> 41 | 42 | ### Added 43 | 44 | * Initial release 45 | 46 | # Template 47 | ## [*MAJOR.MINOR.PATCH*] - <*DATE*> 48 | ### Added 49 | ### Changed 50 | ### Fixed 51 | ### Removed 52 | -------------------------------------------------------------------------------- /packages/yoteams-deploy/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Wictor Wilén. All rights reserved. 4 | Copyright (c) Microsoft Corporation. All rights reserved. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE 23 | -------------------------------------------------------------------------------- /packages/yoteams-deploy/README.md: -------------------------------------------------------------------------------- 1 | # yoteams-deploy 2 | 3 | [![npm version](https://badge.fury.io/js/yoteams-deploy.svg)](https://www.npmjs.com/package/yoteams-deploy) 4 | [![npm](https://img.shields.io/npm/dt/yoteams-deploy.svg)](https://www.npmjs.com/package/yoteams-deploy) 5 | [![MIT](https://img.shields.io/npm/l/generator-teams.svg)](https://github.com/PnP/generator-teams/blob/master/LICENSE.md) 6 | 7 | Library with deployment Gulp task for the Microsoft Teams Apps generator [**yo teams**](https://aka.ms/yoteams) 8 | 9 | ## Documentation 10 | 11 | The `yoteams-deploy` package is set of Gulp tasks that is used for deployment operations, by projects scaffolded using the [**yo teams**](https://aka.ms/yoteams) Microsoft Teams Apps generator, based on the [Microsoft 365 CLI](https://pnp.github.io/cli-microsoft365/). 12 | 13 | ### Using the package 14 | 15 | The package is installed when scaffolding a project using the Microsoft Teams Apps generator. It will update the existing `serve` `codespaces-server`and `ngrok-serve` tasks with an additional flag `--publish`. When this flag is specified the Gulp tasks will automatically publish the application to the Teams App store. 16 | 17 | The first time the `--publish` flag is used the user will be asked to sign in to the tenant using the Azure AD device code flow. Follow the instructions in the terminal window. After the first sign-in, the credentials will be locally cached and reused the next time the Gulp task is performed. 18 | 19 | To force a sign-out, for instance if switching to a new tenant, use the `tenant:logout` Gulp task. 20 | 21 | The package can safely be removed from any Yo Teams application - with only the loss of the new and modified tasks, which will be restored to its default. 22 | 23 | ### Default Gulp tasks 24 | 25 | * **`serve`** - Modified default `serve` task to accept the `--publish` flag 26 | * **`ngrok-serve`** - Modified default `ngrok-serve` task to accept the `--publish` flag 27 | * **`codespaces-serve`** - Modified default `codespaces-serve` task to accept the `--publish` flag 28 | * **`tenant:deploy`** - Builds the manifest and publishes the app to the Teams app store 29 | * **`tenant:publish`** - Publishes the app to the Teams apps tore 30 | * **`tenant:logout`** - Removes the current cached credentials 31 | 32 | ## Contributing 33 | 34 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 35 | -------------------------------------------------------------------------------- /packages/yoteams-deploy/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yoteams-deploy", 3 | "version": "1.4.0-preview", 4 | "description": "Deployment Gulp tasks for Yo Teams, Microsoft Teams Apps Generator (https://aka.ms/yoteams)", 5 | "main": "dist/index.js", 6 | "scripts": { 7 | "prepublishOnly": "npm run clean && npm run build", 8 | "build": "tsc -p tsconfig.json", 9 | "clean": "rimraf lib", 10 | "lint": "eslint ./src/**/*.{js,ts,tsx}" 11 | }, 12 | "author": "Wictor Wilén (wictor@wictorwilen.se)", 13 | "maintainers": [ 14 | { 15 | "name": "Wictor Wilén", 16 | "email": "wictor@wictorwilen.se", 17 | "url": "http://www.wictorwilen.se" 18 | }, 19 | { 20 | "name": "Rick Van Rousselt", 21 | "email": "rick.vanrousselt@outlook.com", 22 | "url": "http://www.rickvanrousselt.com" 23 | }, 24 | { 25 | "name": "Thomas Gölles", 26 | "email": "thomy@outlook.at", 27 | "url": "https://thomy.tech" 28 | }, 29 | { 30 | "name": "Albert-Jan Schot", 31 | "email": "albert.jan.schot@digiwijs.nl", 32 | "url": "https://www.cloudappie.nl/" 33 | }, 34 | { 35 | "name": "Stephan Bisser", 36 | "email": "stephan@bisser.at", 37 | "url": "https://bisser.io" 38 | } 39 | ], 40 | "repository": { 41 | "type": "git", 42 | "url": "https://github.com/pnp/generator-teams.git" 43 | }, 44 | "bugs": { 45 | "url": "https://github.com/pnp/generator-teams/issues" 46 | }, 47 | "homepage": "https://github.com/pnp/generator-teams/tree/master/packages/yoteams-deploy", 48 | "license": "MIT", 49 | "dependencies": { 50 | "@pnp/cli-microsoft365": "^5.9.0", 51 | "applicationinsights": "^2.3.1", 52 | "chalk": "^4.1.2", 53 | "fancy-log": "^2.0.0", 54 | "fs-extra": "^10.0.0", 55 | "gulp": "^4.0.2", 56 | "jszip": "^3.6.0", 57 | "plugin-error": "^1.0.1", 58 | "through2": "^4.0.2", 59 | "vinyl": "^2.2.1", 60 | "yargs": "^17.0.1" 61 | }, 62 | "devDependencies": { 63 | "@types/fancy-log": "^1.3.1", 64 | "@types/fs-extra": "^9.0.11", 65 | "@types/gulp": "^4.0.8", 66 | "@types/node": "^14.14.16", 67 | "@types/through2": "^2.0.36", 68 | "@typescript-eslint/eslint-plugin": "^5.22.0", 69 | "@typescript-eslint/parser": "^5.22.0", 70 | "eslint": "^8.15.0", 71 | "eslint-config-standard": "^17.0.0", 72 | "eslint-plugin-import": "^2.22.1", 73 | "eslint-plugin-node": "^11.1.0", 74 | "eslint-plugin-promise": "^6.0.0", 75 | "rimraf": "^3.0.2", 76 | "typescript": "^4.0.3" 77 | }, 78 | "engines": { 79 | "node": ">=16" 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /packages/yoteams-deploy/src/execute.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Licensed under the MIT license. 3 | // SPDX-License-Identifier: MIT 4 | 5 | import chalk from "chalk"; 6 | import { spawn } from "child_process"; 7 | import log from "fancy-log"; 8 | 9 | export const execute = ( 10 | args: readonly string[], 11 | out: (chunk: any) => void, 12 | err: (chunk: any) => void, 13 | close?: (code: number | null) => void | undefined): void => { 14 | 15 | const cmd = spawn("./node_modules/.bin/m365", args); 16 | cmd.on("error", (err: any) => { 17 | if (err.code === "ENOENT") { 18 | log(chalk.red("Unable to start m365 CLI, ensure that @pnp/cli-microsoft365 is installed!")); 19 | } 20 | }); 21 | 22 | cmd.stdout.on("data", (data) => { 23 | out(data); 24 | }); 25 | 26 | cmd.stderr.on("data", (data) => { 27 | err(data); 28 | }); 29 | 30 | cmd.on("close", (code) => { 31 | if (close) { 32 | close(code); 33 | } 34 | }); 35 | 36 | }; 37 | -------------------------------------------------------------------------------- /packages/yoteams-deploy/src/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Wictor Wilén. All rights reserved. 2 | // Licensed under the MIT license. 3 | // SPDX-License-Identifier: MIT 4 | 5 | import GulpClient from "gulp"; 6 | import Undertaker from "undertaker"; 7 | import { deployTask } from "./deployTask"; 8 | import * as appInsights from "applicationinsights"; 9 | 10 | /** 11 | * Run the dependencies in series 12 | * @param gulp the gulp client 13 | * @param eventName name of event to track 14 | * @param tasks the tasks 15 | */ 16 | export const dependencies = (gulp: GulpClient.Gulp, eventName: string, ...tasks: Undertaker.Task[]) => { 17 | return (done: any) => { 18 | trackEvent(eventName); 19 | gulp.series(...tasks)(done); 20 | }; 21 | }; 22 | 23 | /** 24 | * Run the dependencies in parallel 25 | * @param gulp the gulp client 26 | * @param eventName name of event to track 27 | * @param tasks the tasks 28 | */ 29 | export const dependenciesP = (gulp: GulpClient.Gulp, eventName: string, ...tasks: Undertaker.Task[]) => { 30 | return (done: any) => { 31 | trackEvent(eventName); 32 | gulp.parallel(...tasks)(done); 33 | }; 34 | }; 35 | 36 | export const trackEvent = (eventName: string) => { 37 | if (appInsights && appInsights.defaultClient) { 38 | appInsights.defaultClient.trackEvent({ name: "yoteams-build-core:" + eventName }); 39 | appInsights.defaultClient.flush(); 40 | } 41 | }; 42 | 43 | export const setup = (gulp: GulpClient.Gulp, config: any): void => { 44 | deployTask(gulp, config); 45 | 46 | }; 47 | --------------------------------------------------------------------------------