diff --git a/.eslintignore b/.eslintignore index 6ea478bc2bf7..f5df2d394adf 100644 --- a/.eslintignore +++ b/.eslintignore @@ -18,8 +18,7 @@ target # plugin overrides /src/core/lib/osd_internal_native_observable /src/legacy/plugin_discovery/plugin_pack/__tests__/fixtures/plugins/broken -/src/plugins/data/common/opensearch_query/kuery/ast/_generated_/** -/src/plugins/vis_type_timeline/public/_generated_/** +/src/plugins/**/_generated_/** # package overrides /packages/opensearch-eslint-config-opensearch-dashboards diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index fd4e2984aa07..5649019bd089 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @ananzh @kavilla @seanneumann @AMoo-Miki @ashwin-pc @joshuarrrr @abbyhu2000 @zengyan-amazon @kristenTian @zhongnansu @manasvinibs @ZilongX @Flyingliuhub @BSFishy @curq +* @ananzh @kavilla @AMoo-Miki @ashwin-pc @joshuarrrr @abbyhu2000 @zengyan-amazon @kristenTian @zhongnansu @manasvinibs @ZilongX @Flyingliuhub @BSFishy @curq @bandinib-amzn @SuZhou-Joe diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 2a9e814b5fe3..3cb6f172b119 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -24,7 +24,6 @@ - [ ] All tests pass - [ ] `yarn test:jest` - [ ] `yarn test:jest_integration` - - [ ] `yarn test:ftr` - [ ] New functionality includes testing. - [ ] New functionality has been documented. - [ ] Update [CHANGELOG.md](./../CHANGELOG.md) diff --git a/.github/workflows/build_and_test_workflow.yml b/.github/workflows/build_and_test_workflow.yml index 2eb77832b90f..16c2d3f4011b 100644 --- a/.github/workflows/build_and_test_workflow.yml +++ b/.github/workflows/build_and_test_workflow.yml @@ -25,7 +25,7 @@ env: TEST_OPENSEARCH_TRANSPORT_PORT: 9403 TEST_OPENSEARCH_PORT: 9400 OSD_SNAPSHOT_SKIP_VERIFY_CHECKSUM: true - NODE_OPTIONS: "--max-old-space-size=6144 --dns-result-order=ipv4first" + NODE_OPTIONS: '--max-old-space-size=6144 --dns-result-order=ipv4first' jobs: build-lint-test: @@ -45,6 +45,14 @@ jobs: run: | git config --global core.autocrlf false + - name: Configure pagefile size (Windows only) + if: matrix.os == 'windows-latest' + uses: al-cheb/configure-pagefile-action@v1.3 + with: + minimum-size: 16GB + maximum-size: 64GB + disk-root: 'C:' + - name: Checkout code uses: actions/checkout@v3 @@ -142,6 +150,14 @@ jobs: run: | git config --global core.autocrlf false + - name: Configure pagefile size (Windows only) + if: matrix.os == 'windows-latest' + uses: al-cheb/configure-pagefile-action@v1.3 + with: + minimum-size: 16GB + maximum-size: 64GB + disk-root: 'C:' + - name: Checkout code uses: actions/checkout@v3 @@ -208,6 +224,95 @@ jobs: test/*/failure_debug/ test/*/screenshots/ + plugin-functional-tests: + name: Run plugin functional tests on ${{ matrix.name }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + include: + - os: ubuntu-latest + name: Linux + - os: windows-latest + name: Windows + runs-on: ${{ matrix.os }} + steps: + - run: echo Running plugin functional tests + + - name: Configure git's autocrlf (Windows only) + if: matrix.os == 'windows-latest' + run: | + git config --global core.autocrlf false + + - name: Configure pagefile size (Windows only) + if: matrix.os == 'windows-latest' + uses: al-cheb/configure-pagefile-action@v1.3 + with: + minimum-size: 16GB + maximum-size: 64GB + disk-root: 'C:' + + - name: Checkout code + uses: actions/checkout@v3 + + - name: Setup JDK (Windows only) + if: matrix.os == 'windows-latest' + uses: actions/setup-java@v3 + with: + java-version: '11' + distribution: 'adopt' + + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version-file: '.nvmrc' + registry-url: 'https://registry.npmjs.org' + + - name: Setup Yarn + run: | + npm uninstall -g yarn + npm i -g yarn@1.22.10 + yarn config set network-timeout 1000000 -g + + - name: Configure Yarn Cache (Linux) + if: matrix.os != 'windows-latest' + run: echo "YARN_CACHE_LOCATION=$(yarn cache dir)" >> $GITHUB_ENV + + - name: Initialize Yarn Cache + uses: actions/cache@v3 + if: matrix.os != 'windows-latest' + with: + path: ${{ env.YARN_CACHE_LOCATION }} + key: yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + yarn- + + - name: Setup chromedriver + run: node scripts/upgrade_chromedriver.js + + - name: Run bootstrap (Linux) + if: matrix.os != 'windows-latest' + run: yarn osd bootstrap + + - name: Run bootstrap (Windows) + if: matrix.os == 'windows-latest' + run: yarn osd bootstrap || yarn osd bootstrap + + - name: Build plugins + run: node scripts/build_opensearch_dashboards_platform_plugins --no-examples --workers 10 --scan-dir "./test/plugin_functional/plugins" + + - name: Run functional plugin tests + id: plugin-ftr-tests + run: node scripts/functional_tests.js --config test/plugin_functional/config.ts + + - uses: actions/upload-artifact@v3 + if: failure() + with: + name: failure-artifacts-plugin-functional-${{ matrix.os }} + path: | + test/*/failure_debug/ + test/*/screenshots/ + build-min-artifact-tests: name: Build min release artifacts on ${{ matrix.name }} strategy: @@ -234,6 +339,14 @@ jobs: git config --global core.autocrlf false working-directory: . + - name: Configure pagefile size (Windows only) + if: matrix.os == 'windows-latest' + uses: al-cheb/configure-pagefile-action@v1.3 + with: + minimum-size: 16GB + maximum-size: 64GB + disk-root: 'C:' + - name: Checkout code uses: actions/checkout@v3 with: @@ -310,7 +423,19 @@ jobs: working-directory: ./artifacts strategy: matrix: - version: [osd-2.0.0, osd-2.1.0, osd-2.2.0, osd-2.3.0, osd-2.4.0, osd-2.5.0, osd-2.6.0, osd-2.7.0, osd-2.8.0, osd-2.9.0] + version: + [ + osd-2.0.0, + osd-2.1.0, + osd-2.2.0, + osd-2.3.0, + osd-2.4.0, + osd-2.5.0, + osd-2.6.0, + osd-2.7.0, + osd-2.8.0, + osd-2.9.0, + ] steps: - name: Checkout code uses: actions/checkout@v3 @@ -344,7 +469,7 @@ jobs: id: verify-opensearch-exists run: | if curl -I -L ${{ env.OPENSEARCH_URL }}; then - echo "::set-output name=version-exists::true" + echo "name=version-exists::true" >> $GITHUB_OUTPUT fi - name: Skipping tests diff --git a/.github/workflows/cypress_workflow.yml b/.github/workflows/cypress_workflow.yml index 42894281fe4f..ec904ffdfcd4 100644 --- a/.github/workflows/cypress_workflow.yml +++ b/.github/workflows/cypress_workflow.yml @@ -3,23 +3,53 @@ name: Run cypress tests # trigger on every PR for all branches on: pull_request: - branches: [ '**' ] + branches: ['**'] paths-ignore: - '**/*.md' + workflow_dispatch: + inputs: + test_repo: + description: 'Cypress test repo' + default: 'opensearch-project/opensearch-dashboards-functional-test' + required: true + type: string + test_branch: + description: 'Cypress test branch (default: source branch)' + # remove this default value + default: 'workspace' + required: false + type: string + specs: + description: 'Tests to run (default: osd:ciGroup)' + required: false + type: string + pr_number: + description: 'PR Number (optional)' + required: false + type: number env: + TEST_REPO: ${{ inputs.test_repo != '' && inputs.test_repo || 'opensearch-project/opensearch-dashboards-functional-test' }} + TEST_BRANCH: "${{ inputs.test_branch != '' && inputs.test_branch || github.base_ref }}" FTR_PATH: 'ftr' - START_CMD: 'node ../scripts/opensearch_dashboards --dev --no-base-path --no-watch' - OPENSEARCH_SNAPSHOT_CMD: 'node ../scripts/opensearch snapshot' - SPEC: 'cypress/integration/core-opensearch-dashboards/opensearch-dashboards/**/*.js,' + START_CMD: 'node ../scripts/opensearch_dashboards --dev --no-base-path --no-watch --savedObjects.maxImportPayloadBytes=10485760 --server.maxPayloadBytes=1759977 --logging.json=false --data.search.aggs.shardDelay.enabled=true' + OPENSEARCH_SNAPSHOT_CMD: 'node ../scripts/opensearch snapshot -E cluster.routing.allocation.disk.threshold_enabled=false' CYPRESS_BROWSER: 'chromium' CYPRESS_VISBUILDER_ENABLED: true CYPRESS_DATASOURCE_MANAGEMENT_ENABLED: false OSD_SNAPSHOT_SKIP_VERIFY_CHECKSUM: true + NODE_OPTIONS: '--max-old-space-size=6144 --dns-result-order=ipv4first' + COMMENT_TAG: '[MANUAL CYPRESS TEST RUN RESULTS]' + COMMENT_SUCCESS_MSG: ':white_check_mark: Cypress test run succeeded!' + COMMENT_FAILURE_MSG: ':x: Cypress test run failed!' jobs: cypress-tests: runs-on: arc-runner-set + strategy: + fail-fast: false + matrix: + group: [1, 2, 3, 4, 5, 6, 7, 8, 9] container: image: docker://opensearchstaging/ci-runner:ci-runner-rockylinux8-opensearch-dashboards-integtest-v2 options: --user 1001 @@ -28,10 +58,33 @@ jobs: CI: 1 # avoid warnings like "tput: No value for $TERM and no -T specified" TERM: xterm - name: Run cypress tests + name: Run cypress tests (osd:ciGroup${{ matrix.group }}) steps: + - name: Get source information from PR number + if: ${{ github.event_name == 'workflow_dispatch' && inputs.pr_number != '' }} + id: get_pr_info + uses: actions/github-script@v6 + with: + script: | + const { data: result } = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: ${{ inputs.pr_number }} + }); + core.setOutput('head_name', result.head.repo.full_name); + core.setOutput('head_ref', result.head.ref); + + - name: Set source repo from PR number + if: ${{ github.event_name == 'workflow_dispatch' && inputs.pr_number != '' }} + run: | + echo "SOURCE_REPO=${{ steps.get_pr_info.outputs.head_name }}" >> $GITHUB_ENV + echo "SOURCE_BRANCH=${{ steps.get_pr_info.outputs.head_ref }}" >> $GITHUB_ENV + - name: Checkout code uses: actions/checkout@v2 + with: + repository: ${{ env.SOURCE_REPO }} + ref: '${{ env.SOURCE_BRANCH }}' - name: Setup Node uses: actions/setup-node@v2 @@ -48,20 +101,36 @@ jobs: run: yarn osd bootstrap - name: Build plugins - run: node scripts/build_opensearch_dashboards_platform_plugins --no-examples --workers 10 + run: node scripts/build_opensearch_dashboards_platform_plugins --no-examples --workers 12 - name: Checkout uses: actions/checkout@v2 with: path: ${{ env.FTR_PATH }} - repository: opensearch-project/opensearch-dashboards-functional-test - # revert this to '${{ github.base_ref }}' - ref: 'workspace' + repository: ${{ env.TEST_REPO }} + ref: '${{ env.TEST_BRANCH }}' + + - name: Setup spec files by input + if: ${{ inputs.specs != '' }} + run: | + echo "SPEC=${{ inputs.specs }}" >> $GITHUB_ENV + + - name: Setup spec files + if: ${{ inputs.specs == '' }} + working-directory: ${{ env.FTR_PATH }} + shell: bash + run: | + IFS="," read -a SPEC_ARRAY <<< $(yarn --silent osd:ciGroup${{ matrix.group }}) + FORMATTED_SPEC='' + for i in "${SPEC_ARRAY[@]}"; do + FORMATTED_SPEC+="cypress/integration/core-opensearch-dashboards/opensearch-dashboards/${i}," + done + echo "SPEC=${FORMATTED_SPEC}" >> $GITHUB_ENV - name: Get Cypress version id: cypress_version run: | - echo "::set-output name=cypress_version::$(cat ./${{ env.FTR_PATH }}/package.json | jq '.devDependencies.cypress' | tr -d '"')" + echo "name=cypress_version::$(cat ./${{ env.FTR_PATH }}/package.json | jq '.devDependencies.cypress' | tr -d '"')" >> $GITHUB_OUTPUT - name: Cache Cypress id: cache-cypress @@ -103,3 +172,41 @@ jobs: name: ftr-cypress-results path: ${{ env.FTR_PATH }}/cypress/results retention-days: 1 + + add-comment: + needs: [cypress-tests] + if: ${{ always() && github.event_name == 'workflow_dispatch' && inputs.pr_number != '' }} + permissions: + pull-requests: write + runs-on: ubuntu-latest + steps: + - name: Find Comment + uses: peter-evans/find-comment@v2 + id: fc + with: + issue-number: ${{ inputs.pr_number }} + comment-author: 'github-actions[bot]' + body-includes: '${{ env.COMMENT_TAG }}' + + - name: Add comment on the PR + uses: peter-evans/create-or-update-comment@v3 + with: + comment-id: ${{ steps.fc.outputs.comment-id }} + issue-number: ${{ inputs.pr_number }} + body: | + ### ${{ env.COMMENT_TAG }} + + #### ${{ needs.cypress-tests.result == 'success' && env.COMMENT_SUCCESS_MSG || env.COMMENT_FAILURE_MSG }} + + #### Inputs: + ``` + Test repo: '${{ env.TEST_REPO }}' + Test branch: '${{ env.TEST_BRANCH }}' + + Test spec: + '${{ env.SPEC }}' + ``` + + #### Link to results: + ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + edit-mode: replace diff --git a/.github/workflows/dashboards_cypress_workflow.yml b/.github/workflows/dashboards_cypress_workflow.yml new file mode 100644 index 000000000000..ce578970b477 --- /dev/null +++ b/.github/workflows/dashboards_cypress_workflow.yml @@ -0,0 +1,143 @@ +name: Orchestrator cypress workflow +run-name: dashboards_cypress_workflow ${{ inputs.UNIQUE_ID != '' && inputs.UNIQUE_ID || '' }} # Unique id number appended to the workflow run-name to reference the run within the orchestrator. +# Trigger on dispatch event sent from FT repo orchestrator +on: + workflow_dispatch: + inputs: + test_repo: + description: 'Cypress test repo' + default: '' + required: false + type: string + test_branch: + description: 'Cypress test branch (default: source branch)' + required: false + type: string + specs: + description: 'Test group to run' + required: false + type: string + build_id: + description: 'Build Id' + required: false + type: string + OS_URL: + description: 'OpenSearch release artifact' + required: false + type: string + OSD_URL: + description: 'OpenSearch Dashboards release artifact' + required: false + type: string + UNIQUE_ID: + description: 'Unique Id for the workflow execution' + required: true + type: string + SECURITY_ENABLED: + required: false + type: string + +env: + TEST_REPO: ${{ inputs.test_repo != '' && inputs.test_repo || github.repository }} + TEST_BRANCH: "${{ inputs.test_branch != '' && inputs.test_branch || github.base_ref }}" + OSD_PATH: 'osd' + CYPRESS_BROWSER: 'chromium' + JOB_ID: ${{ inputs.UNIQUE_ID}} + OPENSEARCH: ${{ inputs.OS_URL != '' && inputs.OS_URL || 'https://ci.opensearch.org/ci/dbc/distribution-build-opensearch/$VERSION/latest/linux/x64/tar/dist/opensearch/opensearch-$VERSION-linux-x64.tar.gz' }} + DASHBOARDS: ${{ inputs.OSD_URL != '' && inputs.OSD_URL || 'https://ci.opensearch.org/ci/dbc/distribution-build-opensearch-dashboards/$VERSION/latest/linux/x64/tar/dist/opensearch-dashboards/opensearch-dashboards-$VERSION-linux-x64.tar.gz' }} + OPENSEARCH_DIR: 'cypress/opensearch' + DASHBOARDS_DIR: 'cypress/opensearch-dashboards' + SECURITY_ENABLED: ${{ inputs.SECURITY_ENABLED != '' && inputs.SECURITY_ENABLED || 'false' }} + SPEC: 'cypress/integration/core_opensearch_dashboards/*' + +jobs: + cypress-tests: + runs-on: ubuntu-latest + container: + image: docker://opensearchstaging/ci-runner:ci-runner-rockylinux8-opensearch-dashboards-integtest-v2 + options: --user 1001 + env: + # prevents extra Cypress installation progress messages + CI: 1 + # avoid warnings like "tput: No value for $TERM and no -T specified" + TERM: xterm + name: Run cypress tests ${{ inputs.UNIQUE_ID}} + steps: + - name: Checkout code + uses: actions/checkout@v2 + with: + path: ./${{ env.OSD_PATH }} + repository: ${{ env.TEST_REPO }} + ref: '${{ env.TEST_BRANCH }}' + + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version-file: './${{ env.OSD_PATH }}/.nvmrc' + registry-url: 'https://registry.npmjs.org' + + - name: Setup Yarn + run: | + npm uninstall -g yarn + npm i -g yarn@1.22.10 + yarn config set network-timeout 1000000 -g + + - name: Get Cypress version + id: cypress_version + run: | + echo "name=cypress_version::$(cat ./${{ env.OSD_PATH }}/package.json | jq '.devDependencies.cypress' | tr -d '"')" >> $GITHUB_OUTPUT + + - name: Cache Cypress + id: cache-cypress + uses: actions/cache@v1 + with: + path: ~/.cache/Cypress + key: cypress-cache-v2-${{ runner.os }}-${{ hashFiles('**/package.json') }} + env: + CYPRESS_INSTALL_BINARY: ${{ steps.cypress_version.outputs.cypress_version }} + - run: npx cypress cache list + - run: npx cypress cache path + + - name: Get package version (Linux) + run: | + cd ${{ env.OSD_PATH }} + echo "VERSION=$(yarn --silent pkg-version)" >> $GITHUB_ENV + + - name: Run bootstrap + run: | + cd ${{ env.OSD_PATH }} + yarn osd bootstrap + + - name: Download and extract Opensearch artifacts + run: | + CWD=$(pwd) + mkdir -p $CWD/${{ env.OPENSEARCH_DIR }} + source ${{ env.OSD_PATH }}/scripts/common/utils.sh + open_artifact $CWD/${{ env.OPENSEARCH_DIR }} ${{ env.OPENSEARCH }} + + - name: Download and extract Opensearch Dashboards artifacts + run: | + CWD=$(pwd) + mkdir -p $CWD/${{ env.DASHBOARDS_DIR }} + source ${{ env.OSD_PATH }}/scripts/common/utils.sh + open_artifact $CWD/${{ env.DASHBOARDS_DIR }} ${{ env.DASHBOARDS }} + + - name: Run Cypress tests + run: | + source ${{ env.OSD_PATH }}/scripts/cypress_tests.sh + run_dashboards_cypress_tests + + # Screenshots are only captured on failures + - uses: actions/upload-artifact@v3 + if: failure() + with: + name: osd-cypress-screenshots + path: ${{ env.OSD_PATH }}/cypress/screenshots + retention-days: 1 + + - uses: actions/upload-artifact@v3 + if: always() + with: + name: osd-cypress-videos + path: ${{ env.OSD_PATH }}/cypress/videos + retention-days: 1 diff --git a/.node-version b/.node-version index 6d80269a4f04..a9d087399d71 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -18.16.0 +18.19.0 diff --git a/.nvmrc b/.nvmrc index 6d80269a4f04..a9d087399d71 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -18.16.0 +18.19.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index 8abc72aeb779..d49655b69e70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,93 +8,296 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### Deprecations +- Rename `withLongNumerals` to `withLongNumeralsSupport` in `HttpFetchOptions` [#5592](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5592) + ### 🛡 Security -- [CVE-2022-37599] Bump loader-utils from `2.0.3` to `2.0.4` ([#3031](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3031)). Backwards-compatible fixes included in v2.6.0 and v1.3.7 releases. -- [CVE-2022-37603] Bump loader-utils from `2.0.3` to `2.0.4` ([#3031](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3031)). Backwards-compatible fixes included in v2.6.0 and v1.3.7 releases. - [WS-2021-0638] Bump mocha from `7.2.0` to `10.1.0` ([#2711](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2711)) -- [CVE-2023-26115] Bump `word-wrap` from `1.2.3` to `1.2.4` ([#4589](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4589)) -- Bump `node-sass` to a version that uses a newer `libsass` ([#4649](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4649)) -- [CVE-2019-11358] Bump version of tinygradient from 0.4.3 to 1.1.5 ([#4742](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4742)) -- [CVE-2021-3520] Bump `lmdb` from `2.8.0` to `2.8.5` ([#4804](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4804)) -- Remove examples and other unwanted artifacts from installed dependencies ([#4896](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4896)) +- Add support for TLS v1.3 ([#5133](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5133)) +- [CVE-2023-45133] Bump all babel dependencies from `7.16.x` to `7.22.9` to fix upstream vulnerability ([#5428](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5428)) +- [CVE-2023-45857] Bump `axios` from `0.27.2` to `1.6.1` ([#5470](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5470)) +- [CVE-2023-26159] Bump `follow-redirects` from `1.15.2` to `1.15.4` ([#5669](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5669)) +- [CVE-2023-52079] Bump `msgpackr` from `1.9.7` to `1.10.1` ([#5803](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5803)) +- [CVE-2020-8203] Bump `cheerio` from `0.22.0` to `1.0.0-rc.1` to fix vulnerable `lodash` dependency ([#5797](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5797)) ### 📈 Features/Enhancements -- Enable theme-switching via Advanced Settings to preview the Next theme ([#4475](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4475)) -- Optimize `augment-vis` saved obj searching by adding arg to saved obj client ([#4595](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4595)) -- Add resource ID filtering in fetch `augment-vis` obj queries ([#4608](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4608)) -- Reduce the amount of comments in compiled CSS ([#4648](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4648)) -- [Saved Object Service] Customize saved objects service status ([#4696](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4696)) -- Remove minimum constraint on opensearch hosts to allow empty host ([#4701](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4701)) -- [Discover] Update styles to compatible with OUI `next` theme ([#4644](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4644)) -- [Home] Add modal to introduce the `next` theme ([#4715](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4715)) -- [Home] Add new theme sample dashboard screenshots ([#4906](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4906)) -- Remove visualization editor sidebar background ([#4719](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4719)) -- [Vis Colors] Remove customized colors from sample visualizations and dashboards ([#4741](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4741)) -- [Vis Colors] Update color mapper to prioritize unique colors per visualization rather than across entire dashboard ([#4890](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4890)) -- [Decouple] Allow plugin manifest config to define semver compatible OpenSearch plugin and verify if it is installed on the cluster([#4612](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4612)) -- [Advanced Settings] Consolidate settings into new "Appearance" category and add category IDs ([#4845](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4845)) -- Adds Data explorer framework and implements Discover using it ([#4806](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4806)) -- [Theme] Use themes' definitions to render the initial view ([#4936](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4936/)) -- [Theme] Make `next` theme the default ([#4854](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4854/)) -- [Workspace] Setup workspace skeleton and implement basic CRUD API ([#5075](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5075/)) +- Add support for read-only mode through tenants ([#4498](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4498)) +- Replace OuiSelect component with OuiSuperSelect in data-source plugin ([#5315](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5315)) +- [Workspace] Add core workspace service module to enable the implementation of workspace features within OSD plugins ([#5092](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5092)) +- [Workspace] Setup workspace skeleton and implement basic CRUD API ([#5075](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5075)) +- [Decouple] Add new cross compatibility check core service which export functionality for plugins to verify if their OpenSearch plugin counterpart is installed on the cluster or has incompatible version to configure the plugin behavior([#4710](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4710)) +- [Discover] Add long numerals support [#5592](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5592) +- [Discover] Display inner properties in the left navigation bar [#5429](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5429) +- [Discover] Added customizable pagination options based on Discover UI settings [#5610](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5610) +- [Chrome] Introduce registerCollapsibleNavHeader to allow plugins to customize the rendering of nav menu header ([#5244](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5244)) +- [PM] Enhance single version requirements imposed during bootstrapping ([#5675](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5675)) +- [Custom Branding] Relative URL should be allowed for logos ([#5572](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5572)) +- Revert to legacy discover table and add toggle to new discover table ([#5789](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5789)) +- [Discover] Add collapsible and resizeable sidebar ([#5789](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5789)) +- [Discover] Enhanced the data source selector with added sorting functionality ([#5609](https://github.com/opensearch-project/OpenSearch-Dashboards/issues/5609)) +- [Multiple Datasource] Add datasource picker component and use it in devtools and tutorial page when multiple datasource is enabled ([#5756](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5756)) +- [Multiple Datasource] Add datasource picker to import saved object flyout when multiple data source is enabled ([#5781](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5781)) +- [Multiple Datasource] Add interfaces to register add-on authentication method from plug-in module ([#5851](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5851)) +- [Multiple Datasource] Able to Hide "Local Cluster" option from datasource DropDown ([#5827](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5827)) +- [Multiple Datasource] Add api registry and allow it to be added into client config in data source plugin ([#5895](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5895)) ### 🐛 Bug Fixes - [Chore] Update deprecated url methods (url.parse(), url.format()) ([#2910](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2910)) - Cleanup unused url ([#3847](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3847)) -- Fix Node.js download link ([#4556](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4556)) - [TSVB, Dashboards] Fix inconsistent dark mode code editor themes ([#4609](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4609)) -- [Table Visualization] Fix width of multiple tables when rendered in column view ([#4638](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4638)) -- [Legacy Maps] Fix dark mode style overrides ([#4658](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4658)) -- [BUG] Fix management overview page duplicate rendering ([#4636](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4636)) -- [Table Vis] Fix filter actions on data table vis cells ([#4837](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4837)) -- Fix broken app when management is turned off ([#4891](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4891)) -- Correct the generated path for downloading plugins by their names on Windows ([#4953](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4953)) -- [BUG] Fix buildPointSeriesData unit test fails due to local timezone ([#4992](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4992)) +- Fix `maps.proxyOpenSearchMapsServiceInMaps` config definition so it can be set ([#5170](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5170)) +- [Discover] Fix inactive state on 'Discover' tab in side navigation menu ([#5432](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5432)) +- [BUG] Add platform "darwin-arm64" to unit test ([#5290](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5290)) +- [BUG][Dev Tool] Add dev tool documentation link to dev tool's help menu [#5166](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5166) +- Fix missing border for header navigation control on right ([#5450](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5450)) +- [BUG] Fix filtering issue in data source selector ([5484](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5484)) +- [BUG][Data] Support for custom filters with heterogeneous data fields ([5577](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5577)) +- [BUG][Data] Fix empty suggestion history when querying in search bar [#5349](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5349) +- [BUG][Discover] Fix what is displayed in `selected fields` when removing columns from canvas [#5537](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5537) +- [BUG][Discover] Fix advanced setting `discover:modifyColumnsOnSwitch` ([#5508](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5508)) +- [BUG][Discover] Show 0 filters when there are no active filters ([#5508](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5508)) +- [Discover] Fix missing index pattern field from breaking Discover [#5626](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5626) +- [BUG][Discover] Fix Discover table panel not adjusting its size automatically when the time range changes ([#5789](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5789)) +- [BUG] Fix issue where changing from a search with few results to a search with more results keeps the number of rows from the previous search ([#5789](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5789)) +- [BUG] Fix copying data from columns in Discover including extra data ([#5789](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5789)) +- [BUG] Fix no line wrapping when displaying fields in Discover datagrid ([#5789](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5789)) +- [BUG] Fix 'truncate:maxHeight' not working in Discover since 2.10.0 ([#5789](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5789)) +- [BUG] Fix UI glitch when mouseover Discover datagrid element ([#5789](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5789)) +- [BUG] Remove duplicate sample data as id 90943e30-9a47-11e8-b64d-95841ca0b247 ([5668](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5668)) +- [BUG][Multiple Datasource] Fix datasource testing connection unexpectedly passed with wrong endpoint [#5663](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5663) +- [Table Visualization] Fix filter action buttons for split table aggregations ([#5619](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5619)) ### 🚞 Infrastructure - Re-enable CI workflows for feature branches ([#2908](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2908)) - Upgrade yarn version to be compatible with @opensearch-project/opensearch ([#3443](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3443)) - Add an achievement badger to the PR ([#3721](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3721)) +- [CI] Enable inputs for manually triggered Cypress test jobs ([#5134](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5134)) +- [CI] Replace usage of deprecated `set-output` in workflows ([#5340](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5340)) +- [Chore] Add `--security` for `opensearch snapshot` and `opensearch_dashboards` to configure local setup with the security plugin ([#5451](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5451)) +- [Tests] Add Github workflow for Test Orchestrator in FT Repo to run cypress tests within Dashboards repo ([#5725](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5725)) +- [Chore] Updates default dev environment security credentials ([#5736](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5736)) ### 📝 Documentation - [Doc] Add COMMUNICATIONS.md with info about Slack, forum, office hours ([#3837](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3837)) - Add plugin development section in DEVELOPER_GUIDE.md ([#3989](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3989)) -- [Vis Augmenter] Add documentation to `vis_augmenter` plugin ([#4527](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4527)) +- Remove ftr test step from PR template ([#5217](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5217)) +- [Doc] Update EUI doc site links to point to OUI doc site ([#5293](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5293)) ### 🛠 Maintenance -- Remove angular html extractor ([#4680](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4680)) - Removes `minimatch` manual resolution ([#3019](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3019)) - Upgrade `vega-lite` dependency from `4.17.0` to `^5.6.0` ([#3076](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3076)). Backwards-compatible version included in v2.5.0 release. - Bump `js-yaml` from `3.14.0` to `4.1.0` ([#3770](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3770)) -- [@osd/pm] Automate multi-target bootstrap and build ([#4650](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4650)) -- Update webpack environment targets ([#4649](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4649)) -- Add @curq as maintainer ([#4760](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4760)) -- Bump `oui` to `1.3.0` ([#4941](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4941)) +- Replace `node-sass` with `sass-embedded` ([#5338](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5338)) +- Bump `chromedriver` from `107.0.3` to `119.0.1` ([#5465](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5465)) +- Bump `typescript` resolution from `4.0.2` to `4.6.4` ([#5470](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5470)) +- Bump `OUI` to `1.5.1` ([#5862](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5862)) +- Add @SuZhou-Joe as a maintainer ([#5594](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5594)) +- Move @seanneumann to emeritus maintainer ([#5634](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5634)) +- Remove `ui-select` dev dependency ([#5660](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5660)) ### 🪛 Refactoring - [Console] Remove unused ul element and its custom styling ([#3993](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3993)) -- Fix EUI/OUI type errors ([#3798](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3798)) - Remove unused Sass in `tile_map` plugin ([#4110](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4110)) -- [Maps Legacy] Removed KUI usage in `maps_legacy` plugin([#3998](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3998)) -- [Markdown] Replace custom CSS styles and HTML markup with OUI components ([#4390](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4390)) -- [Vis Colors] [VisLib] Update legend colors to use OUI color palette ([#4365](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4365)) -- [Vis colors] Update legacy mapped colors in charts plugin to use `ouiPaletteColorBlind()`, Update default color in legacy visualizations to use `ouiPaletteColorBlind()[0]` ([#4398](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4398)) +- [Home] Remove unused tutorials ([#5212](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5212)) +- [UiSharedDeps] Standardize theme JSON imports to be light/dark-mode aware ([#5662](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5662)) + +### 🔩 Tests + +- Update caniuse to `1.0.30001587` to fix failed integration tests ([#5886](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5886)) +- [Home] Add more unit tests for other complications of overview ([#5418](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5418)) + +## [2.11.1 - 2023-11-21](https://github.com/opensearch-project/OpenSearch-Dashboards/releases/tag/2.11.1) + +### 🛡 Security + +- [CVE-2023-45133] Add package resolution for `@babel/traverse` to `7.23.2` to fix vulnerability ([#5309](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5309)) +- [CVE-2023-46234] Bump `eslint-import-resolver-webpack` from `0.11.1` to `0.13.8` and `browserify-sign` from `4.2.1` to `4.2.2` ([#5414](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5414/)) + +### 📈 Features/Enhancements + +### 🐛 Bug Fixes + +- Fix navigation issue across dashboards ([#5435](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5435)) +- [Discover] Fix table panel auto-sizing ([#5441](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5441)) + +### 🚞 Infrastructure + +- [CI][Test] Add plugin functional tests on GitHub Actions ([#5383](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5383)) + +### 📝 Documentation + +- Add Release Notes and update CHANGELOG.md for 2.11.1 ([#5486](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5486)) + +### 🔩 Tests + +### 🛠 Maintenance + +## [2.11.0 - 2023-10-18](https://github.com/opensearch-project/OpenSearch-Dashboards/releases/tag/2.11.0) + +### 🛡 Security + +- [CVE-2022-25869] Remove AngularJS `1.8` ([#5086](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5086)) + +### 📈 Features/Enhancements + +- [Console] Add support for JSON with long numerals ([#4562](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4562)) +- [Data] Add `DataSource` service and `DataSourceSelector` for multiple datasource support ([#5167](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5167)) +- [Discover] Update embeddable for saved searches ([#5081](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5081)) + +### 🐛 Bug Fixes + +- Bump `agentkeepalive` to `4.5.0` to solve a problem preventing the use `https://ip` in `opensearch.hosts` ([#4949](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4949)) +- [Data Explorer][Discover] Add `onQuerySubmit` to top nav and allow force update to embeddable ([#5160](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5160)) +- [Data Explorer][Discover] Automatically load default index pattern ([#5171](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5171)) +- [Data Explorer][Discover] Fix total hits issue for no time based data ([#5087](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5087)) +- [Data Explorer][Discover] Allow data grid to auto adjust size based on fetched data count ([#5191](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5191)) +- [Data Explorer][Discover] Allow filter and query persist when refresh page or paste url to a new tab ([#5206](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5206)) +- [Data Explorer][Discover] Fix misc navigation issues ([#5168](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5168)) +- [Data Explorer][Discover] Fix mobile view ([#5168](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5168)) +- [Table Visualization] Fix width of multiple tables when rendered in column view ([#4638](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4638)) +- [Table Visualization] Fix filter actions on data table vis cells ([#4837](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4837)) +- [Vis Augmenter] Fix errors in conditions for activating `vizAugmenter` ([#5213](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5213)) +- [Vis Augmenter] Fix `visAugmenter` forming empty key-value pairs in its calls to the `SavedObject` API ([#5190](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5190)) +- [Data Explorer] Remove the `X` icon in data source selection field ([#5238](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5238)) +- [BUG][Fuctional Test] Make setDefaultAbsoluteRange more robust and update doc views tests ([#5242](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5242)) + +### 🚞 Infrastructure + +- [CI] Add `NODE_OPTIONS` and disable disk allocation threshold ([#5172](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5172)) +- [CI] Supprt CI Groups for Cypress test jobs ([#5298](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5298)) + +### 🛠 Maintenance + +- [Version] Version increment from 2.10 to 2.11 ([#4975](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4975)) +- Remove angular html extractor ([#4680](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4680)) +- Add @bandinib-amzn as maintainer ([#5113](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5113)) +- Add @bandinib-amzn to CODEOWNERS file. ([#5456](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5456)) + +### 🔩 Tests + +- [Functional][Doc Views] Remove angular code from `plugin_functional` and update tests ([#5221](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5221)) +- [Unit][Data Explorer][Discover] Fix wrong test due to time conversion ([#5174](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5174)) +- [Unit][Data Explorer][Discover]Fix `buildPointSeriesData` unit test fails due to local timezone ([#4992](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4992)) + +## [2.10.0 - 2023-09-25](https://github.com/opensearch-project/OpenSearch-Dashboards/releases/tag/2.10.0) + +### 🛡 Security + +- Bump word-wrap from 1.2.3 to 1.2.4 ([#4589](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4589)) +- Bump version of tinygradient from 0.4.3 to 1.1.5 ([#4742](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4742)) +- Bump lmdb from 2.8.0 to 2.8.5 ([#4804](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4804)) +- Alias and bump mocha ([#4874](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4874)) +- Remove examples and other unwanted artifacts from installed dependencies ([#4896](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4896)) + +### 📈 Features/Enhancements + +- [Vis colors] Update legacy mapped colors in charts plugin to use ouiPaletteColorBlind() ([#4398](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4398)) - [Saved Objects Management] Add new or remove extra tags and styles ([#4069](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4069)) -- [Console] Migrate `/lib/autocomplete/` module to TypeScript ([#4148](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4148)) -- [Console] Migrate `/lib/!autocomplete/` module to TypeScript ([#4150](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4150)) -- [Dashboard] Restructure the `Dashboard` plugin folder to be more cohesive with the project ([#4575](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4575)) -- Refactor logo usage to centralize and optimize assets and improve tests ([#4702](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4702)) +- Chore (home): Update visual consistency dashboard TSVB colors ([#4501](http://github.com/opensearch-project/OpenSearch-Dashboards/pull/4501)) +- Feature (home): Update visual consistency sample dashboard with more vis ([#4581](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4581)) +- Add resource ID filtering in fetch augment-vis obj queries ([#4608](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4608)) +- Enable theme-switching via Advanced Settings to preview the Next theme ([#4475](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4475)) +- Feat (home): Add remaining vis type examples ([#4619](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4619)) +- Feat (Discover): Update styles to be compatible with next theme ([#4644](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4644)) +- Update webpack environment targets ([#4649](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4649)) +- [Table Visualization] Replace div containers with OuiFlex components ([#4272](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4272)) +- Reduce the amount of comments in compiled CSS ([#4648](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4648)) +- Feat (home): Remove color customizations from sample dashboards ([#4741](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4741)) +- Remove visualization editor background ([#4719](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4719)) +- Add saved objects service status api ([#4696](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4696)) +- Allow plugin manifest config to define semver compatible OpenSearch plugin and verify if it is installed on the cluster ([#4612](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4612)) +- Eliminate duplicate dashboard breadcrumb text ID ([#4805](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4805)) +- [@osd/pm] Automate multi-target bootstrap and build ([#4650](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4650)) +- [Home] Add modal to introduce the `next` theme ([#4715](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4715)) +- [Home] Add new theme sample dashboard screenshots ([#4906](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4906)) +- Change color fn used to calculate icon colors for search typeahead suggestions ([#4884](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4884)) +- [Next Theme] Make next theme the default ([#4854](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4854)) +- [Vis Colors] Update color mapper to prioritize unique colors per vis ([#4890](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4890)) +- [Advanced Settings] Consolidate settings into new "Appearance" category and add category IDs ([#4845](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4845)) +- Adds Data explorer framework and implements Discover using it ([#4806](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4806)) +- Use themes' definitions to render the initial view. This impacts the loading screen font and colors ([#4936](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4936)) + +### 🐛 Bug Fixes + +- [VisLib] Replace legend color palette with OUI color palette ([#4365](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4365)) +- Fix (styles): Make ace code editor themes consistent ([#4609](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4609)) +- [i18n] fix generation scripts ([#4252](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4252)) +- Fix (Legacy Maps): Add necessary specificity for dark mode style overrides ([#4658](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4658)) +- Fix --font-text CSS var usage and add more leaflet font overrides ([#4674](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4674)) +- Fix snapshots that didn't get updated between PRs ([#4863](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4863)) +- [BUG] Fix management overview page duplicate rendering ([#4636](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4636)) +- Fixes broken app when management is turned off ([#4891](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4891)) +- [CCI] Fix EUI/OUI type errors ([#3798](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3798)) +- Correct the generated path for downloading plugins by their names on Windows ([#4953](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4953)) + +### 📝 Documentation + +- Add missing 1.3.x patch release notes to 2.x branch ([#4771](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4771)) +- [Vis Augmenter] Add documentation to `vis_augmenter` plugin ([#4527](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4527)) + +### 🛠 Maintenance + +- Version increment from 2.9 to 2.10 ([#4545](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4545)) +- Bump OpenSearch-Dashboards 2.10.0 to use nodejs 18.16.0 version ([#4948](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4948)) +- Bump `oui` to `1.3.0` ([#4941](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4941)) +- Add @curq as maintainer ([#4760](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4760)) +- Bump OpenSearch Dashboards to use nodejs v18.19.0 ([#4948](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5830)) + +### 🪛 Refactoring + +- [Markdown] Replace custom css styles and native html with OUI ([#4390](http://github.com/opensearch-project/OpenSearch-Dashboards/pull/4390)) +- Removed KUI usage in `maps_legacy` plugin ([#3998](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3998)) +- [Console] Converted all `/lib/autocomplete/**/*.js` files to typescript ([#4148](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4148)) +- [Console] Convert all non-autocomplete lib files to typescript ([#4150](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4150)) +- Refactor/remove breadcrumb styling main ([#4621](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4621)) +- Bump `node-sass` to a version that uses a newer `libsass` ([#4651](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4651)) +- [Dashboards] restructure folder to be more cohesive with the project ([#4575](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4575)) +- Remove minimum constraint on opensearch hosts ([#4701](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4701)) +- [CCI] Remove unused tags in the navigation plugin ([#3964](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3964)) +- Refactor logo usage ([#4702](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4702)) +- [Markdown] Replace custom CSS styles and HTML markup with OUI components ([#4390](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4390)) +- Fix EUI/OUI type errors ([#3798](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3798)) ### 🔩 Tests -- [Tests] Add BWC tests for 2.9 and 2.10 ([#4762](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4762)) +- [CI] Fix BWC related CI failures by swapping dist url with snapshot url ([#4828](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4828)) +- [Dashboard De-Angular] Add unit tests for `dashboard_listing` and `dashboard_top_nav` ([#4640](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4640)) +- [Tests] Add BWC tests for 2.9 and 2.10 versions ([#4762](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4762)) +- [Stylelint] Add `no_restricted_values` linter rule ([#4413](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4413)) +- Units test for utils folder ([#4641](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4641)) +- Test (linkchecker): Exclude checking dead link ([#4720](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4720)) +- Update baseline images for functional tests ([#4879](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4879)) + +## [1.3.13 - 2023-09-21](https://github.com/opensearch-project/OpenSearch-Dashboards/releases/tag/1.3.13) + +### 🛡 Security + +- [CVE-2019-11358] Bump version of `tinygradient` from `0.4.3` to `1.1.5` ([#4571](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4751)) +- [CVE-2023-26136] Bump `word-wrap` from `1.2.3` to `1.2.4` ([#5002](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5002)) +- [CVE-2022-21670] Bump `markdown-it` from `10.0.0` to `12.3.2` ([#5016](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5016)) +- [CVE-2022-33987] Partially fix security issues for `got` by bumping `@elastic/makelogs` from `6.0.0` to `6.1.1` and updating yarn.lock ([#5006](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5006)) +- Bump `yo` from `2.0.6` to `3.1.1` ([#5005]( https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5005)) +- [CVE-2023-0842] Bump `xml2js` from `0.4.22` to `0.6.2` ([#5024](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5024)) + +### 📈 Features/Enhancements + +### 🐛 Bug Fixes + +### 🚞 Infrastructure + +### 📝 Documentation + +### 🛠 Maintenance + +- [Version] Increment version to 1.3.13 ([#4721](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4721)) +- [Chore] Add company.net to exclusion list in linkchecker ([#4704](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4704)) +- [Chore] Exclude checking dead link in linkchecker ([#4868](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4868)) ## [1.3.12 - 2023-08-10](https://github.com/opensearch-project/OpenSearch-Dashboards/releases/tag/1.3.12) @@ -116,6 +319,8 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### 📝 Documentation +- added js documentation and a readme file to files in utils folder ([#5540])(https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5540/) + ### 🛠 Maintenance - Adding @ZilongX and @Flyingliuhub as maintainers. ([#4137](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4137)) @@ -154,6 +359,8 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - [Vis Augmenter] Update base vis height in view events flyout ([#4535](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4535)) - [Dashboard De-Angular] Add more unit tests for utils folder ([#4641](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4641)) - [Dashboard De-Angular] Add unit tests for dashboard_listing and dashboard_top_nav ([#4640](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4640)) +- Optimize `augment-vis` saved obj searching by adding arg to saved obj client ([#4595](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4595)) +- Change SavedObjects' Import API to allow selecting a data source when uploading files ([#5777](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5777)) ### 🐛 Bug Fixes @@ -164,6 +371,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Update main menu to display 'Dashboards' for consistency ([#4453](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4453)) - [Multiple DataSource] Retain the original sample data API ([#4526](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4526)) - Remove `lmdb-store` to fix backport issue ([#4266](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4266)) +- Fix Node.js download link ([#4556](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4556)) ### 🚞 Infrastructure @@ -349,6 +557,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - [VisBuilder] Fix Firefox legend selection issue ([#3732](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3732)) - [VisBuilder] Fix type errors ([#3732](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3732)) - [VisBuilder] Fix indexpattern selection in filter bar ([#3751](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3751)) +- [Console] Fix dev tool console autocomplete not loading issue for aliases ([#5568](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5568)) ### 🚞 Infrastructure @@ -457,6 +666,8 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - [Search Telemetry] Fix search telemetry's observable object that won't be GC-ed([#3390](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3390)) - [Region Maps] Add ui setting to configure custom vector map's size parameter([#3399](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/3399)) +- [Import API] Fix import saved objects always display overwritten issue([#5861](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5871)) + ### 🚞 Infrastructure @@ -658,6 +869,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### 📈 Features/Enhancements +- Add DataSource service and DataSourceSelector for multiple datasource support ([#5167](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5167)) - Add extension point in saved object management to register namespaces and show filter ([#2656](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2656)) - Add updated_at column to Saved Objects' tables ([#1218](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/1218)) - Change the links in the visualize plugin to use `href` rather than `onClick` ([#2395](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2395)) diff --git a/DEVELOPER_GUIDE.md b/DEVELOPER_GUIDE.md index ac26f4bcd8b2..366445b4513b 100644 --- a/DEVELOPER_GUIDE.md +++ b/DEVELOPER_GUIDE.md @@ -62,18 +62,6 @@ We recommend using [Node Version Manager (nvm)](https://github.com/nvm-sh/nvm) t If it's the only version of node installed, it will automatically be set to the `default` alias. Otherwise, use `nvm list` to see all installed `node` versions, and `nvm use` to select the node version required by OpenSearch Dashboards. -#### Install `yarn` - -Take a look at the [latest Yarn release](https://github.com/yarnpkg/berry/releases/latest), note the version number, and run: - -```bash -$ npm i -g corepack - -$ corepack prepare yarn@ --activate -``` - -(See the [Yarn installation documentation](https://yarnpkg.com/getting-started/install) for more information.) - ### Fork and clone OpenSearch Dashboards All local development should be done in a [forked repository](https://docs.github.com/en/get-started/quickstart/fork-a-repo). @@ -85,6 +73,20 @@ Clone your forked version of OpenSearch Dashboards to your local machine (replac $ git clone git@github.com:opensearch-project/OpenSearch-Dashboards.git ``` +#### Install `yarn` + +OpenSearch Dashboards is set up using yarn, which can be installed through corepack. To install yarn, run: + +```bash +$ # Update corepack to the latest version +$ npm i -g corepack + +$ # Install the correct version of yarn +$ corepack install +``` + +(See the [corepack documentation](https://github.com/nodejs/corepack#-corepack) for more information.) + ### Bootstrap OpenSearch Dashboards If you haven't already, change directories to your cloned repository directory: @@ -177,6 +179,7 @@ For windows: $ wsl -d docker-desktop $ sysctl -w vm.max_map_count=262144 ``` + ### Next Steps Now that you have a development environment to play with, there are a number of different paths you may take next. @@ -233,6 +236,7 @@ $ yarn opensearch snapshot --P https://repo1.maven.org/maven2/org/opensearch/plu Note - if you add the [`security` plugin](https://github.com/opensearch-project/security), you'll also need to [configure OpenSearch Dashboards for security](#configure-opensearch-dashboards-for-security). ### Plugin development + The osd-plugin-generator tool makes it easier to create a plugin for OpenSearch Dashboards. It sets up the basic structure of the project and provides scripts to build it. Refer to [osd-plugin-generator](https://github.com/opensearch-project/OpenSearch-Dashboards/tree/main/packages/osd-plugin-generator) for more details. #### Other snapshot configuration options @@ -250,12 +254,19 @@ Options: -E Additional key=value settings to pass to OpenSearch --download-only Download the snapshot but don't actually start it --ssl Sets up SSL on OpenSearch + --security Installs and sets up OpenSearch Security plugin on the cluster --P OpenSearch plugin artifact URL to install it on the cluster. ```bash -$ yarn opensearch snapshot --version 2.2.0 -E cluster.name=test -E path.data=/tmp/opensearch-data --P org.opensearch.plugin:test-plugin:2.2.0.0 --P file:/home/user/opensearch-test-plugin-2.2.0.0.zip +$ yarn opensearch snapshot --version 2.2.0 -E cluster.name=test -E path.data=/tmp/opensearch-data --P org.opensearch.plugin:test-plugin:2.2.0.0 --P file:/home/user/opensearch-test-plugin-2.2.0.0.zip --security ``` +#### Read Only capabilities + +_This feature will only work if you have the [`security` plugin](https://github.com/opensearch-project/security) installed on your OpenSearch cluster with https/authentication enabled._ + +Please follow the design described in [the docs](https://github.com/opensearch-project/OpenSearch/blob/main/docs/capabilities/read_only_mode.md#design) + ### Alternative - Run OpenSearch from tarball If you would like to run OpenSearch from the tarball, you'll need to download the minimal distribution, install it, and then run the executable. (You'll also need Java installed and the `JAVA_HOME` environmental variable set - see [OpenSearch developer guide](https://github.com/opensearch-project/OpenSearch/blob/main/DEVELOPER_GUIDE.md#install-prerequisites) for details). @@ -271,17 +282,26 @@ This method can also be used to develop against the [full distribution of OpenSe ### Configure OpenSearch Dashboards for security -_This step is only mandatory if you have the [`security` plugin](https://github.com/opensearch-project/security) installed on your OpenSearch cluster with https/authentication enabled._ +_This step is only needed if you want your dev environment to also start with security. To do so both the OpenSearch node and OpenSearch Dashboards cluster need to have the security plugin installed. Follow the steps below to get setup correctly._ -Once the bootstrap of OpenSearch Dashboards is finished, you need to apply some -changes to the default [`opensearch_dashboards.yml`](https://github.com/opensearch-project/OpenSearch-Dashboards/blob/main/config/opensearch_dashboards.yml#L25-L72) in order to connect to OpenSearch. +To startup the OpenSearch snapshot with security -```yml -opensearch.hosts: ["https://localhost:9200"] -opensearch.username: "admin" # Default username on the docker image -opensearch.password: "admin" # Default password on the docker image -opensearch.ssl.verificationMode: none -``` +> OpenSearch has strong password requirements and will fail to bootstrap if the password requirements are not met. e.g. myStrongPassword123! + +1. Run `export OPENSEARCH_INITIAL_ADMIN_PASSWORD=` since it's needed by the configuration script +2. Run `yarn opensearch snapshot --security` +3. Wait a few seconds while the plugin is installed, configured, and OpenSearch starts up. + +Then within another window you can start OpenSearch Dashboards: + +_First make sure to clone the https://github.com/opensearch-project/security-dashboards-plugin repo into the plugins folder and build it (Using `yarn build`). You can follow the instructions here https://github.com/opensearch-project/security-dashboards-plugin/blob/main/DEVELOPER_GUIDE.md#install-opensearch-dashboards-with-security-dashboards-plugin._ + +> You do not have to edit the `config/opensearch-dashboards.yml` file since the `yarn start:security` command sets up the default overrides automatically + +Then do the following: + +1. Run `yarn start:security` +2. Navigate to OpenSearch Dashboards and login with the username `admin` and password ``. For more detailed documentation, see [Configure TLS for OpenSearch Dashboards](https://opensearch.org/docs/latest/install-and-configure/install-dashboards/tls). @@ -460,7 +480,7 @@ You can also use this service outside of React. When writing a new component, create a sibling SASS file of the same name and import directly into the **top** of the JS/TS component file. Doing so ensures the styles are never separated or lost on import and allows for better modularization (smaller individual plugin asset footprint). -All SASS (.scss) files will automatically build with the [EUI](https://elastic.github.io/eui/#/guidelines/sass) & OpenSearch Dashboards invisibles (SASS variables, mixins, functions) from the [`globals_[theme].scss` file](src/core/public/core_app/styles/_globals_v7light.scss). +All SASS (.scss) files will automatically build with the [OUI](https://oui.opensearch.org/#/guidelines/sass) & OpenSearch Dashboards invisibles (SASS variables, mixins, functions) from the [`globals_[theme].scss` file](src/core/public/core_app/styles/_globals_v7light.scss). While the styles for this component will only be loaded if the component exists on the page, the styles **will** be global and so it is recommended to use a three letter prefix on your @@ -927,30 +947,6 @@ license. The following developer guide rules are specific for working with the React framework. -#### Prefer reactDirective over react-component - -When using `ngReact` to embed your react components inside Angular HTML, prefer the -`reactDirective` service over the `react-component` directive. -You can read more about these two ngReact methods [here](https://github.com/ngReact/ngReact#features). - -Using `react-component` means adding a bunch of components into angular, while `reactDirective` keeps them isolated, and is also a more succinct syntax. - -**Good:** - -```html - -``` - -**Bad:** - -```html - -``` - #### Name action functions and prop functions appropriately Name action functions in the form of a strong verb and passed properties in the form of on. E.g: diff --git a/Dockerfile b/Dockerfile index 9337b827810e..960321ef1051 100755 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -ARG NODE_VERSION=18.16.0 +ARG NODE_VERSION=18.19.0 FROM node:${NODE_VERSION} AS base ENV HOME '.' @@ -13,7 +13,7 @@ RUN apt-get update && \ # Specify the version of Chrome that matches the version of chromedriver in the package.json. # A list of Chrome versions can be found here: # https://www.ubuntuupdates.org/package/google_chrome/stable/main/base/google-chrome-stable -ARG CHROME_VERSION=107.0.5304.121-1 +ARG CHROME_VERSION=119.0.6045.123-1 RUN curl -sSL https://dl.google.com/linux/linux_signing_key.pub | apt-key add - \ && wget -O /tmp/chrome.deb https://dl.google.com/linux/chrome/deb/pool/main/g/google-chrome-stable/google-chrome-stable_${CHROME_VERSION}_amd64.deb \ && apt-get update \ diff --git a/MAINTAINERS.md b/MAINTAINERS.md index a7ee5ce2fcdc..83709bd6209a 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -8,7 +8,6 @@ This document contains a list of maintainers in this repo. See [opensearch-proje | ------------------------- | --------------------------------------------------- | ----------- | | Anan Zhuang | [ananzh](https://github.com/ananzh) | Amazon | | Kawika (Rocky) Avilla | [kavilla](https://github.com/kavilla) | Amazon | -| Sean Neumann | [seanneumann](https://github.com/seanneumann) | Amazon | | Miki Barahmand | [AMoo-Miki](https://github.com/AMoo-Miki) | Amazon | | Ashwin P Chandran | [ashwin-pc](https://github.com/ashwin-pc) | Amazon | | Josh Romero | [joshuarrrr](https://github.com/joshuarrrr) | Amazon | @@ -21,11 +20,14 @@ This document contains a list of maintainers in this repo. See [opensearch-proje | Zilong Xia | [ZilongX](https://github.com/ZilongX) | Amazon | | Matt Provost | [BSFishy](https://github.com/BSFishy) | Amazon | | Sirazh Gabdullin | [curq](https://github.com/curq) | External contributor | +| Bandini Bhopi | [bandinib-amzn](https://github.com/bandinib-amzn) | Amazon | +| Su Zhou | [SuZhou-Joe](https://github.com/SuZhou-Joe) | Amazon | ## Emeritus -| Maintainer | GitHub ID | Affiliation | -| ------------- | ----------------------------------------- | ----------- | -| Tommy Markley | [tmarkley](https://github.com/tmarkley) | Amazon | -| Mihir Soni | [mihirsoni](https://github.com/mihirsoni) | Amazon | -| Bishoy Boktor | [boktorbb](https://github.com/boktorbb) | Amazon | +| Maintainer | GitHub ID | Affiliation | +| ------------- |-----------------------------------------------|-------------| +| Tommy Markley | [tmarkley](https://github.com/tmarkley) | Amazon | +| Mihir Soni | [mihirsoni](https://github.com/mihirsoni) | Amazon | +| Bishoy Boktor | [boktorbb](https://github.com/boktorbb) | Amazon | +| Sean Neumann | [seanneumann](https://github.com/seanneumann) | Contributor | diff --git a/README.md b/README.md index 5bafaf4c7c1a..e8e2da025df7 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ We aim to be an exceptional community-driven platform and to foster open partici You can [contribute to this project](https://github.com/opensearch-project/OpenSearch-Dashboards/issues/CONTRIBUTING.md) by [opening issues](https://github.com/opensearch-project/OpenSearch-Dashboards/issues/new/choose) to give feedback, share ideas, identify bugs, and contribute code. -Set up your [OpenSearch Dashboards development environment](ttps://github.com/opensearch-project/OpenSearch-Dashboards/blob/main/DEVELOPER_GUIDE.md#getting-started-guide) today! The project team looks forward to your contributions. +Set up your [OpenSearch Dashboards development environment](https://github.com/opensearch-project/OpenSearch-Dashboards/blob/main/DEVELOPER_GUIDE.md#getting-started-guide) today! The project team looks forward to your contributions. ## Code Summary diff --git a/TESTING.md b/TESTING.md index 95dc9495309a..64f10127538b 100644 --- a/TESTING.md +++ b/TESTING.md @@ -24,7 +24,7 @@ In general, we recommend four tiers of tests: # Requirements * Install the latest NodeJS, [NPM](https://www.npmjs.com/get-npm) and [Yarn](https://classic.yarnpkg.com/en/docs/install/#mac-stable) - * `nvm install v14.20.1` + * `nvm install v18.19.0` * `npm install -g yarn` # Running tests diff --git a/config/opensearch_dashboards.yml b/config/opensearch_dashboards.yml index 80baeb5b4ae0..0e5beac120c0 100644 --- a/config/opensearch_dashboards.yml +++ b/config/opensearch_dashboards.yml @@ -237,6 +237,8 @@ # Set the value of this setting to true to enable multiple data source feature. #data_source.enabled: false +# Set the value of this setting to true to hide local cluster in data source feature. +#data_source.hideLocalCluster: false # Set the value of these settings to customize crypto materials to encryption saved credentials # in data sources. #data_source.encryption.wrappingKeyName: 'changeme' @@ -278,4 +280,4 @@ # workspace.enabled: false # Set the value to false to disable permission check on workspace # Permission check depends on OpenSearch Dashboards has authentication enabled, set it to false if no authentication is configured -# workspace.permission.enabled: true \ No newline at end of file +# workspace.permission.enabled: true diff --git a/cypress.config.js b/cypress.config.js new file mode 100644 index 000000000000..6312b14e8777 --- /dev/null +++ b/cypress.config.js @@ -0,0 +1,28 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +const { defineConfig } = require('cypress'); + +module.exports = defineConfig({ + defaultCommandTimeout: 60000, + requestTimeout: 60000, + responseTimeout: 60000, + baseUrl: 'http://localhost:5601', + viewportWidth: 2000, + viewportHeight: 1320, + env: { + openSearchUrl: 'http://localhost:9200', + SECURITY_ENABLED: false, + AGGREGATION_VIEW: false, + username: 'admin', + password: 'myStrongPassword123!', + ENDPOINT_WITH_PROXY: false, + MANAGED_SERVICE_ENDPOINT: false, + VISBUILDER_ENABLED: true, + DATASOURCE_MANAGEMENT_ENABLED: false, + ML_COMMONS_DASHBOARDS_ENABLED: true, + WAIT_FOR_LOADER_BUFFER_MS: 0, + }, +}); diff --git a/cypress/integration/core_opensearch_dashboards/dashboard_sanity_test_spec.js b/cypress/integration/core_opensearch_dashboards/dashboard_sanity_test_spec.js new file mode 100644 index 000000000000..98877f9c6dbb --- /dev/null +++ b/cypress/integration/core_opensearch_dashboards/dashboard_sanity_test_spec.js @@ -0,0 +1,164 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + CommonUI, + MiscUtils, +} from '@opensearch-dashboards-test/opensearch-dashboards-test-library'; + +/** + * dashboard_sample_data test suite description: + * 1) Visit the home page of opensearchdashboard, check key UI elements display + * 2) add sample data of eCommerce, flights, web logs from tutorial page + * 3) check each sample data dashboard key UI elements display + */ +export function dashboardSanityTests() { + const commonUI = new CommonUI(cy); + const miscUtils = new MiscUtils(cy); + const baseURL = new URL(Cypress.config().baseUrl); + // remove trailing slash + const path = baseURL.pathname.replace(/\/$/, ''); + + describe('dashboard sample data validation', () => { + before(() => {}); + + after(() => {}); + + describe('checking home page', () => { + before(() => { + // Go to the home page + miscUtils.visitPage('app/home#'); + cy.window().then((win) => win.localStorage.setItem('home:welcome:show', false)); + cy.reload(true); + }); + + after(() => { + cy.window().then((win) => win.localStorage.removeItem('home:welcome:show')); + }); + + it('checking opensearch_dashboards_overview display', () => { + // Check that opensearch_dashboards_overview is visable + commonUI.checkElementExists(`a[href="${path}/app/opensearch_dashboards_overview"]`, 1); + }); + + it('checking tutorial_directory display', () => { + // Check that tutorial_directory is visable + commonUI.checkElementExists(`a[href="${path}/app/home#/tutorial_directory"]`, 2); + }); + + it('checking management display', () => { + // Check that management is visable + commonUI.checkElementExists(`a[href="${path}/app/management"]`, 1); + }); + + it('checking dev_tools display', () => { + // Check that dev_tools is visable + commonUI.checkElementExists(`a[href="${path}/app/dev_tools#/console"]`, 2); + }); + + it('settings display', () => { + // Check that settings is visable + commonUI.checkElementExists( + `a[href="${path}/app/management/opensearch-dashboards/settings#defaultRoute"]`, + 1 + ); + }); + + it('checking feature_directory display', () => { + // Check that feature_directory is visable + commonUI.checkElementExists(`a[href="${path}/app/home#/feature_directory"]`, 1); + }); + + it('checking navigation display', () => { + // Check that navigation is visable + commonUI.checkElementExists('button[data-test-subj="toggleNavButton"]', 1); + }); + + it('checking Help menu display', () => { + // Check that Help menu is visable + commonUI.checkElementExists('button[aria-label="Help menu"]', 1); + }); + }); + + describe('checking Dev Tools', () => { + before(() => { + // Go to the Dev Tools page + miscUtils.visitPage('app/dev_tools#/console'); + }); + + after(() => {}); + + it('checking welcome panel display', () => { + commonUI.checkElementExists('div[data-test-subj="welcomePanel"]', 1); + }); + + it('checking dismiss button display', () => { + commonUI.checkElementExists('button[data-test-subj="help-close-button"]', 1); + }); + + it('checking console input area display', () => { + commonUI.checkElementExists('div[data-test-subj="request-editor"]', 1); + }); + + it('checking console output area display', () => { + commonUI.checkElementExists('div[data-test-subj="response-editor"]', 1); + }); + }); + + describe('adding sample data', () => { + before(() => { + miscUtils.addSampleData(); + }); + + after(() => { + miscUtils.removeSampleData(); + }); + + it('checking ecommerce dashboards displayed', () => { + miscUtils.viewData('ecommerce'); + commonUI.checkElementContainsValue( + 'span[title="[eCommerce] Revenue Dashboard"]', + 1, + '\\[eCommerce\\] Revenue Dashboard' + ); + commonUI.checkElementContainsValue( + 'div[data-test-subj="markdownBody"] > h3', + 1, + 'Sample eCommerce Data' + ); + }); + + it('checking flights dashboards displayed', () => { + miscUtils.viewData('flights'); + commonUI.checkElementContainsValue( + 'span[title="[Flights] Global Flight Dashboard"]', + 1, + '\\[Flights\\] Global Flight Dashboard' + ); + commonUI.checkElementContainsValue( + 'div[data-test-subj="markdownBody"] > h3', + 1, + 'Sample Flight data' + ); + }); + + it('checking web logs dashboards displayed', () => { + miscUtils.viewData('logs'); + commonUI.checkElementContainsValue( + 'span[title="[Logs] Web Traffic"]', + 1, + '\\[Logs\\] Web Traffic' + ); + commonUI.checkElementContainsValue( + 'div[data-test-subj="markdownBody"] > h3', + 1, + 'Sample Logs Data' + ); + }); + }); + }); +} + +dashboardSanityTests(); diff --git a/docs/capabilities/read_only_mode.md b/docs/capabilities/read_only_mode.md new file mode 100644 index 000000000000..a1e14d119cbf --- /dev/null +++ b/docs/capabilities/read_only_mode.md @@ -0,0 +1,80 @@ +# Read-only Mode + +There are two distinct functionalities for "read-only" access in Dashboards. One of them is associated with roles and one is associated with tenants. Regarding the first one, the Dashboards Security plugin contains a feature of hiding all plugin navigation links except Dashboards and Visualizations when the logged-in user has a certain role (more about it in [Read-only Role](#read-only-role)). + +The second one is limiting Dashboards access rights via assigning a specific role to a tenant (therefore, making a tenant read-only). Due to past issues and the deprecation of the first functionality, using read-only tenants is now the recommended way to limit users' access to Dashboards. + +## Design + +Whenever a plugin registers capabilities that should be limited (in other words, set to false) for read-only tenants, such capabilities should be registered through `registerSwitcher` with using method `core.security.readonlyService().hideForReadonly()` + +### Example + +```ts +public setup(core: CoreSetup) { + core.capabilities.registerProvider({ + myAwesomePlugin: { + show: true, + save: true, + delete: true, + } + }); + + core.capabilities.registerSwitcher(async (request, capabilites) => { + return await core.security.readonlyService().hideForReadonly(request, capabilites, { + myAwesomePlugin: { + save: false, + delete: false, + }, + }); + }); +} +``` + +In this case, we might assume that a plugin relies on the `save` and `delete` capabilities to limit changes somewhere in the UI. Therefore, those capabilities are processed through `registerSwitcher`, they will be set to `false` whenever a read-only tenant is accessed. + +If `registerSwitcher` will try to provide or remove capabilites when invoking the switcher will be ignored. + +*In case of a disabled / not installed `security` plugin changes will be never applied to a capabilites.* + +## Requirements + +This feature will only work if you have the [`security` plugin](https://github.com/opensearch-project/security) installed on your OpenSearch cluster with https/authentication enabled. + +## Read-only Role + +The role is called `kibana_read_only` by default, but the name can be changed using the dashboard config option `opensearch_security.readonly_mode.roles`. One big issue with this feature is that the backend site of a Dashboard Security plugin is completely unaware of it. Thus, users in this mode still have write access to the Dashboards saved objects via the API as the implementation effectively hides everything except the Dashboards and Visualization plugins. + +**We highly do not recommend using it!** + +For more context, see [this group issues of problems connected with read-only roles](https://github.com/opensearch-project/security/issues/2701). + +### Usage + +1. Go to `Management > Security > Internal users` +2. Create or select an already existing user +3. Add a new `Backend role` called `kibana_read_only` (or use name used in `opensearch_security.readonly_mode.roles`) +4. Save changes + +## Read-only Tenant (recommended) + +Dashboards Security plugin recognizes the selection of read-only tenant after logging in and sets the capabilities associated with write access or showing write controls to false for a variety of plugins. This can be easily checked for example by trying to re-arrange some visualizations on Dashboards. Such action will be resulting in a 403 error due to limited read-only access. + +### Usage + +1. Prepare tenant: + * Use an existing tenant or create a new one in `Management > Security > Tenants` +2. Prepare role: + * Go to `Management > Security > Roles` + * Use an existing role or create a new one + * Fill **index permissions** with: + * `indices:data/read/search` + * `indices:data/read/get` + * Add new **tenant permission** with: + * your name of the tenant + * read only +3. Assign a role to a user: + * Go to role + * Click the tab `Mapped users` + * Click `Manage mapping` + * In `Users` select the user that will be affected diff --git a/docs/multi-datasource/client_management_design.md b/docs/multi-datasource/client_management_design.md index c83b254fe289..6c5befdb143f 100644 --- a/docs/multi-datasource/client_management_design.md +++ b/docs/multi-datasource/client_management_design.md @@ -10,6 +10,8 @@ This design is part of the OpenSearch Dashboards multi data source project [[RFC 2. How to expose data source clients to callers through clean interfaces? 3. How to maintain backwards compatibility if user turn off this feature? 4. How to manage multiple clients/connection efficiently, and not consume all the memory? +5. Where should we implement the core logic? +6. How to register custom API schema and add into the client during initialization? ## 2. Requirements @@ -87,6 +89,20 @@ Current `opensearch service` exists in core. The module we'll implement has simi 2. We don't mess up with OpenSearch Dashboards core, since this is an experimental feature, the potential risk of breaking existing behavior will be lowered if we use plugin. Worst case, user could just uninstall the plugin. 3. Complexity wise, it's about the same amount of work. +**6.How to register custom API schema and add into the client during initialization?** +Currently, OpenSearch Dashboards plugins uses the following to initialize instance of Cluster client and register the custom API schema via the plugins configuration option. +```ts +core.opensearch.legacy.createClient( + 'exampleName', + { + plugins: [ExamplePlugin], + } + ); +``` +The downside of this approach is the schema is defined inside the plugin and there is no centralized registry for the schema making it not easy to access. This will be resolved by implementing a centralized API schema registry, and consumers can add data source plugin as dependency and be able to consume all the registered schema, eg. `dataSource.registerCustomApiSchema(sqlPlugin)`. + +The schema will be added into the client configuration when multi data source client is initiated. + ### 4.1 Data Source Plugin Create a data source plugin that only has server side code, to hold most core logic of data source feature. Including data service, crypto service, and client management. A plugin will have all setup, start and stop as lifecycle. @@ -146,12 +162,20 @@ context.core.opensearch.legacy.client.callAsCurrentUser; context.core.opensearch.client.asCurrentUser; ``` -Since deprecating legacy client could be a bigger scope of project, multiple data source feature still need to implement a substitute for it as for now. Implementation should be done in a way that's decoupled with data source client as much as possible, for easier deprecation. Similar to [opensearch legacy service](https://github.com/opensearch-project/OpenSearch-Dashboards/tree/main/src/core/server/opensearch/legacy) in core. +Since deprecating legacy client could be a bigger scope of project, multiple data source feature still need to implement a substitute for it as for now. Implementation should be done in a way that's decoupled with data source client as much as possible, for easier deprecation. Similar to [opensearch legacy service](https://github.com/opensearch-project/OpenSearch-Dashboards/tree/main/src/core/server/opensearch/legacy) in core. See how to intialize the data source client below: ```ts context.dataSource.opensearch.legacy.getClient(dataSourceId); ``` +If using Legacy cluster client with asScoped and callAsCurrentUser, the following is the equivalent when using data source client: +```ts +//legacy cluster client +const response = client.asScoped(request).callAsCurrentUser(format, params); +//equivalent when using data source client instead +const response = client.callAPI(format, params); +``` + ### 4.3 Register datasource client to core context This is for plugin to access data source client via request handler. For example, by `core.client.search(params)`. It’s a very common use case for plugin to access cluster while handling request. In fact data plugin uses it in its search module to get client, and I’ll talk about it in details in next section. diff --git a/package.json b/package.json index 0cffb3fcc47e..cb24289e686f 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "bugs": { "url": "http://github.com/opensearch-project/OpenSearch-Dashboards/issues" }, + "packageManager": "yarn@1.22.19", "opensearchDashboards": { "clean": { "extraPatterns": [ @@ -63,6 +64,7 @@ "build": "scripts/use_node scripts/build --all-platforms", "start": "scripts/use_node scripts/opensearch_dashboards --dev", "start:docker": "scripts/use_node scripts/opensearch_dashboards --dev --opensearch.hosts=$OPENSEARCH_HOSTS --opensearch.ignoreVersionMismatch=true --server.host=$SERVER_HOST", + "start:security": "scripts/use_node scripts/opensearch_dashboards --dev --security", "debug": "scripts/use_node --nolazy --inspect scripts/opensearch_dashboards --dev", "debug-break": "scripts/use_node --nolazy --inspect-brk scripts/opensearch_dashboards --dev", "lint": "yarn run lint:es && yarn run lint:style", @@ -75,7 +77,10 @@ "docs:acceptApiChanges": "scripts/use_node --max-old-space-size=6144 scripts/check_published_api_changes.js --accept", "osd:bootstrap": "scripts/use_node scripts/build_ts_refs && scripts/use_node scripts/register_git_hook", "spec_to_console": "scripts/use_node scripts/spec_to_console", - "pkg-version": "scripts/use_node -e \"console.log(require('./package.json').version)\"" + "pkg-version": "scripts/use_node -e \"console.log(require('./package.json').version)\"", + "cypress:run-without-security": "env TZ=America/Los_Angeles NO_COLOR=1 cypress run --headless --env SECURITY_ENABLED=false", + "cypress:run-with-security": "env TZ=America/Los_Angeles NO_COLOR=1 cypress run --headless --env SECURITY_ENABLED=true,openSearchUrl=https://localhost:9200,WAIT_FOR_LOADER_BUFFER_MS=500" + }, "repository": { "type": "git", @@ -86,6 +91,8 @@ "**/ansi-regex": "^5.0.1", "**/async": "^3.2.3", "**/d3-color": "^3.1.0", + "**/elasticsearch/agentkeepalive": "^4.5.0", + "**/follow-redirects": "^1.15.4", "**/glob-parent": "^6.0.0", "**/hoist-non-react-statics": "^3.3.2", "**/json-schema": "^0.4.0", @@ -94,14 +101,15 @@ "**/node-jose": "^2.2.0", "**/nth-check": "^2.0.1", "**/trim": "^0.0.3", - "**/typescript": "4.0.2", + "**/typescript": "4.6.4", "**/unset-value": "^2.0.1", "**/jest-config": "npm:@amoo-miki/jest-config@27.5.1", "**/jest-jasmine2": "npm:@amoo-miki/jest-jasmine2@27.5.1", "**/semver": "^7.5.3", "**/set-value": "^4.1.0", "**/xml2js": "^0.5.0", - "**/yaml": "^2.2.2" + "**/yaml": "^2.2.2", + "**/@babel/traverse": "^7.23.2" }, "workspaces": { "packages": [ @@ -123,9 +131,9 @@ "dependencies": { "@aws-crypto/client-node": "^3.1.1", "@elastic/datemath": "5.0.3", - "@elastic/eui": "npm:@opensearch-project/oui@1.3.0", + "@elastic/eui": "npm:@opensearch-project/oui@1.5.1", "@elastic/good": "^9.0.1-kibana3", - "@elastic/numeral": "^2.5.0", + "@elastic/numeral": "npm:@amoo-miki/numeral@2.6.0", "@elastic/request-crypto": "2.0.0", "@elastic/safer-lodash-set": "0.0.0", "@hapi/accept": "^5.0.2", @@ -158,9 +166,6 @@ "JSONStream": "1.3.5", "abortcontroller-polyfill": "^1.4.0", "ajv": "^8.11.0", - "angular": "^1.8.2", - "angular-elastic": "^2.5.1", - "angular-sanitize": "^1.8.0", "bluebird": "3.5.5", "chalk": "^4.1.0", "chokidar": "^3.4.2", @@ -224,13 +229,15 @@ "type-detect": "^4.0.8", "uuid": "3.3.2", "whatwg-fetch": "^3.0.0", - "yauzl": "^2.10.0" + "yauzl": "^2.10.0", + "@opensearch-dashboards-test/opensearch-dashboards-test-library": "https://github.com/opensearch-project/opensearch-dashboards-test-library/archive/refs/tags/1.0.6.tar.gz" + }, "devDependencies": { - "@babel/core": "^7.16.5", - "@babel/parser": "^7.16.6", - "@babel/register": "^7.16.5", - "@babel/types": "^7.16.0", + "@babel/core": "^7.22.9", + "@babel/parser": "^7.22.9", + "@babel/register": "^7.22.9", + "@babel/types": "^7.22.9", "@elastic/apm-rum": "^5.6.1", "@elastic/charts": "31.1.0", "@elastic/ems-client": "7.10.0", @@ -262,8 +269,6 @@ "@testing-library/jest-dom": "^5.16.2", "@testing-library/react": "^12.1.2", "@testing-library/react-hooks": "^7.0.2", - "@types/angular": "^1.8.4", - "@types/angular-mocks": "^1.7.1", "@types/archiver": "^5.3.1", "@types/babel__core": "^7.1.17", "@types/bluebird": "^3.1.1", @@ -344,10 +349,6 @@ "@types/zen-observable": "^0.8.0", "@typescript-eslint/eslint-plugin": "^3.10.0", "@typescript-eslint/parser": "^3.10.0", - "angular-aria": "^1.8.0", - "angular-mocks": "^1.8.2", - "angular-recursion": "^1.0.5", - "angular-route": "^1.8.0", "archiver": "^5.3.0", "axe-core": "^4.0.2", "babel-eslint": "^10.0.3", @@ -355,10 +356,11 @@ "brace": "0.11.1", "chai": "3.5.0", "chance": "1.0.18", - "cheerio": "0.22.0", - "chromedriver": "^107.0.3", + "cheerio": "1.0.0-rc.1", + "chromedriver": "^119.0.1", "classnames": "2.3.1", "compare-versions": "3.5.1", + "cypress": "9.5.4", "d3": "3.5.17", "d3-cloud": "1.2.5", "dedent": "^0.7.0", @@ -424,7 +426,6 @@ "ms-chromium-edge-driver": "^0.4.3", "murmurhash3js": "3.0.1", "mutation-observer": "^1.0.3", - "ngreact": "^0.5.1", "nock": "12.0.3", "node-stream-zip": "^1.15.0", "normalize-path": "^3.0.0", @@ -461,7 +462,6 @@ "tough-cookie": "^4.1.3", "tree-kill": "^1.2.2", "typescript": "4.0.2", - "ui-select": "0.19.8", "vega": "^5.23.0", "vega-interpreter": "npm:@amoo-miki/vega-forced-csp-compliant-interpreter@1.0.6", "vega-lite": "^5.6.0", diff --git a/packages/osd-analytics/babel.config.js b/packages/osd-analytics/babel.config.js index 06979152709a..31c41164c3ef 100644 --- a/packages/osd-analytics/babel.config.js +++ b/packages/osd-analytics/babel.config.js @@ -30,7 +30,7 @@ // We can't use common OpenSearch Dashboards presets here because of babel versions incompatibility module.exports = { - plugins: ['@babel/plugin-proposal-class-properties'], + plugins: ['@babel/plugin-transform-class-properties'], env: { web: { presets: ['@osd/babel-preset/webpack_preset'], diff --git a/packages/osd-analytics/package.json b/packages/osd-analytics/package.json index 954f6cc51ba4..73fdd2b102cb 100644 --- a/packages/osd-analytics/package.json +++ b/packages/osd-analytics/package.json @@ -14,7 +14,7 @@ "osd:watch": "../../scripts/use_node scripts/build --source-maps --watch" }, "devDependencies": { - "@babel/cli": "^7.16.0", + "@babel/cli": "^7.22.9", "@osd/dev-utils": "1.0.0", "@osd/babel-preset": "1.0.0", "typescript": "4.0.2" diff --git a/packages/osd-babel-preset/common_preset.js b/packages/osd-babel-preset/common_preset.js index 3b0bdf22fec2..dc324f6efd35 100644 --- a/packages/osd-babel-preset/common_preset.js +++ b/packages/osd-babel-preset/common_preset.js @@ -29,30 +29,23 @@ */ const plugins = [ + require.resolve('@babel/plugin-transform-class-properties'), + require.resolve('@babel/plugin-transform-private-methods'), require.resolve('babel-plugin-add-module-exports'), - // The class properties proposal was merged with the private fields proposal - // into the "class fields" proposal which is stage 3. - // See https://github.com/babel/proposals/issues/12 for progress - require.resolve('@babel/plugin-proposal-class-properties'), - // Optional Chaining proposal is stage 4 (https://github.com/tc39/proposal-optional-chaining) // Need this since we are using TypeScript 3.7+ - require.resolve('@babel/plugin-proposal-optional-chaining'), + require.resolve('@babel/plugin-transform-optional-chaining'), // Nullish coalescing proposal is stage 4 (https://github.com/tc39/proposal-nullish-coalescing) // Need this since we are using TypeScript 3.7+ - require.resolve('@babel/plugin-proposal-nullish-coalescing-operator'), + require.resolve('@babel/plugin-transform-nullish-coalescing-operator'), // Proposal is merged into ECMA-262 (https://github.com/tc39/proposal-export-ns-from) // Need this since we are using TypeScript 3.8+ - require.resolve('@babel/plugin-proposal-export-namespace-from'), - - // Proposal is merged into ECMA-262 (https://github.com/tc39/proposal-export-ns-from) - // Need this since we are using TypeScript 3.9+ - require.resolve('@babel/plugin-proposal-private-methods'), + require.resolve('@babel/plugin-transform-export-namespace-from'), // Proposal is on stage 4 (https://github.com/tc39/proposal-logical-assignment) - require.resolve('@babel/plugin-proposal-logical-assignment-operators'), + require.resolve('@babel/plugin-transform-logical-assignment-operators'), ]; module.exports = { diff --git a/packages/osd-babel-preset/package.json b/packages/osd-babel-preset/package.json index b471529bbda0..676afffe7cdc 100644 --- a/packages/osd-babel-preset/package.json +++ b/packages/osd-babel-preset/package.json @@ -7,15 +7,13 @@ "devOnly": true }, "dependencies": { - "@babel/plugin-proposal-class-properties": "^7.16.5", - "@babel/plugin-proposal-export-namespace-from": "^7.16.5", - "@babel/plugin-proposal-logical-assignment-operators": "^7.16.5", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.5", - "@babel/plugin-proposal-optional-chaining": "^7.16.5", - "@babel/plugin-proposal-private-methods": "^7.16.5", - "@babel/preset-env": "^7.16.5", - "@babel/preset-react": "^7.16.5", - "@babel/preset-typescript": "^7.16.5", + "@babel/plugin-transform-export-namespace-from": "^7.22.9", + "@babel/plugin-transform-logical-assignment-operators": "^7.22.9", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.9", + "@babel/plugin-transform-optional-chaining": "^7.22.9", + "@babel/preset-env": "^7.22.9", + "@babel/preset-react": "^7.22.9", + "@babel/preset-typescript": "^7.22.9", "babel-plugin-add-module-exports": "^1.0.4", "babel-plugin-styled-components": "^2.0.2", "babel-plugin-transform-react-remove-prop-types": "^0.4.24", diff --git a/packages/osd-cross-platform/src/repo_root.ts b/packages/osd-cross-platform/src/repo_root.ts index a7ffc19a7f7c..ea2975d19eb5 100644 --- a/packages/osd-cross-platform/src/repo_root.ts +++ b/packages/osd-cross-platform/src/repo_root.ts @@ -41,7 +41,7 @@ const readOpenSearchDashboardsPkgJson = (dir: string) => { return json; } } catch (error) { - if (error && error.code === 'ENOENT') { + if (error?.code === 'ENOENT') { return; } diff --git a/packages/osd-dev-utils/package.json b/packages/osd-dev-utils/package.json index 6a7a4fd9d647..f35b795bb907 100644 --- a/packages/osd-dev-utils/package.json +++ b/packages/osd-dev-utils/package.json @@ -13,11 +13,11 @@ "devOnly": true }, "dependencies": { - "@babel/core": "^7.16.5", + "@babel/core": "^7.22.9", "@osd/utils": "1.0.0", - "axios": "^0.27.2", + "axios": "^1.6.1", "chalk": "^4.1.0", - "cheerio": "0.22.0", + "cheerio": "1.0.0-rc.1", "dedent": "^0.7.0", "execa": "^4.0.2", "exit-hook": "^2.2.0", diff --git a/packages/osd-dev-utils/src/osd_client/osd_client_requester.ts b/packages/osd-dev-utils/src/osd_client/osd_client_requester.ts index 807ec64b0e5a..1519497705bb 100644 --- a/packages/osd-dev-utils/src/osd_client/osd_client_requester.ts +++ b/packages/osd-dev-utils/src/osd_client/osd_client_requester.ts @@ -36,7 +36,7 @@ import { ToolingLog } from '../tooling_log'; const isConcliftOnGetError = (error: any) => { return ( - isAxiosResponseError(error) && error.config.method === 'GET' && error.response.status === 409 + isAxiosResponseError(error) && error.config?.method === 'GET' && error.response.status === 409 ); }; diff --git a/packages/osd-eslint-import-resolver-opensearch-dashboards/package.json b/packages/osd-eslint-import-resolver-opensearch-dashboards/package.json index 1112cb1ed223..2dfa00a516ba 100755 --- a/packages/osd-eslint-import-resolver-opensearch-dashboards/package.json +++ b/packages/osd-eslint-import-resolver-opensearch-dashboards/package.json @@ -15,7 +15,7 @@ "dependencies": { "debug": "^2.6.9", "eslint-import-resolver-node": "0.3.2", - "eslint-import-resolver-webpack": "0.11.1", + "eslint-import-resolver-webpack": "0.13.8", "glob-all": "^3.2.1", "lru-cache": "^4.1.5", "resolve": "^1.7.1", diff --git a/packages/osd-i18n/GUIDELINE.md b/packages/osd-i18n/GUIDELINE.md index ae5b2b5ca298..ac2fb0047c9b 100644 --- a/packages/osd-i18n/GUIDELINE.md +++ b/packages/osd-i18n/GUIDELINE.md @@ -92,17 +92,6 @@ The long term plan is to rely on using `FormattedMessage` and `i18n.translate()` Currently, we support the following ReactJS `i18n` tools, but they will be removed in future releases: - Usage of `props.intl.formatmessage()` (where `intl` is passed to `props` by `injectI18n` HOC). -#### In AngularJS - -The long term plan is to rely on using `i18n.translate()` by statically importing `i18n` from the `@osd/i18n` package. **Avoid using the `i18n` filter and the `i18n` service injected in controllers, directives, services.** - -- Call JS function `i18n.translate()` from the `@osd/i18n` package. -- Use `i18nId` directive in template. - -Currently, we support the following AngluarJS `i18n` tools, but they will be removed in future releases: -- Usage of `i18n` service in controllers, directives, services by injecting it. -- Usage of `i18n` filter in template for attribute translation. Note: Use one-time binding ("{{:: ... }}") in filters wherever it's possible to prevent unnecessary expression re-evaluation. - #### In JavaScript - Use `i18n.translate()` in NodeJS or any other framework agnostic code, where `i18n` is the I18n engine from `@osd/i18n` package. diff --git a/packages/osd-i18n/README.md b/packages/osd-i18n/README.md index 16e740e925d5..a58a15ab4fd9 100644 --- a/packages/osd-i18n/README.md +++ b/packages/osd-i18n/README.md @@ -1,14 +1,13 @@ # I18n -OpenSearch Dashboards relies on several UI frameworks (ReactJS and AngularJS) and +OpenSearch Dashboards relies on UI frameworks (ReactJS) and requires localization in different environments (browser and NodeJS). Internationalization engine is framework agnostic and consumable in -all parts of OpenSearch Dashboards (ReactJS, AngularJS and NodeJS). In order to simplify +all parts of OpenSearch Dashboards (ReactJS and NodeJS). In order to simplify internationalization in UI frameworks, the additional abstractions are -built around the I18n engine: `react-intl` for React and custom -components for AngularJS. [React-intl](https://github.com/yahoo/react-intl) +built around the I18n engine: `react-intl` for React. [React-intl](https://github.com/yahoo/react-intl) is built around [intl-messageformat](https://github.com/yahoo/intl-messageformat), -so both React and AngularJS frameworks use the same engine and the same +so the React framework uses the same engine and the same message syntax. ## Localization files @@ -343,98 +342,6 @@ export const MyComponent = injectI18n( ); ``` -## AngularJS - -The long term plan is to rely on using `i18n.translate()` by statically importing `i18n` from the `@osd/i18n` package. **Avoid using the `i18n` filter and the `i18n` service injected in controllers, directives, services.** - -AngularJS wrapper has 4 entities: translation `provider`, `service`, `directive` -and `filter`. Both the directive and the filter use the translation `service` -with i18n engine under the hood. - -The translation `provider` is used for `service` configuration and -has the following methods: -- `addMessages(messages: Map, [locale: string])` - provides a way to register -translations with the library -- `setLocale(locale: string)` - tells the library which language to use by given -language key -- `getLocale()` - returns the current locale -- `setDefaultLocale(locale: string)` - tells the library which language to fallback -when missing translations -- `getDefaultLocale()` - returns the default locale -- `setFormats(formats: object)` - supplies a set of options to the underlying formatter -- `getFormats()` - returns current formats -- `getRegisteredLocales()` - returns array of locales having translations -- `init(messages: Map)` - initializes the engine - -The translation `service` provides only one method: -- `i18n(id: string, { values: object, defaultMessage: string, description: string })` – -translate message by id - -The translation `filter` is used for attributes translation and has -the following syntax: -``` -{{ ::'translationId' | i18n: { values: object, defaultMessage: string, description: string } }} -``` - -Where: -- `translationId` - translation id to be translated -- `values` - values to pass into translation -- `defaultMessage` - will be used unless translation was successful (the final - fallback in english, will be used for generating `en.json`) -- `description` - optional context comment that will be extracted by i18n tools -and added as a comment next to translation message at `defaultMessages.json` - -The translation `directive` has the following syntax: -```html - -``` - -Where: -- `i18n-id` - translation id to be translated -- `i18n-default-message` - will be used unless translation was successful -- `i18n-values` - values to pass into translation -- `i18n-description` - optional context comment that will be extracted by i18n tools -and added as a comment next to translation message at `defaultMessages.json` - -If HTML rendering in `i18n-values` is required then value key in `i18n-values` object -should have `html_` prefix. Otherwise the value will be inserted to the message without -HTML rendering.\ -Example: -```html -

-``` - -Angular `I18n` module is placed into `autoload` module, so it will be -loaded automatically. After that we can use i18n directive in Angular templates: -```html - -``` - -In order to translate attributes in AngularJS we should use `i18nFilter`: -```html - -``` - ## I18n tools In order to simplify localization process, some additional tools were implemented: diff --git a/packages/osd-i18n/angular/package.json b/packages/osd-i18n/angular/package.json deleted file mode 100644 index 1979e988fa7c..000000000000 --- a/packages/osd-i18n/angular/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "browser": "../target/web/angular", - "main": "../target/node/angular", - "types": "../target/types/angular/index.d.ts" -} diff --git a/packages/osd-i18n/package.json b/packages/osd-i18n/package.json index cda6b10c428b..ca15c98cf0fb 100644 --- a/packages/osd-i18n/package.json +++ b/packages/osd-i18n/package.json @@ -7,8 +7,8 @@ "license": "Apache-2.0", "private": true, "devDependencies": { - "@babel/cli": "^7.16.0", - "@babel/core": "^7.16.5", + "@babel/cli": "^7.22.9", + "@babel/core": "^7.22.9", "@osd/babel-preset": "1.0.0", "@osd/dev-utils": "1.0.0", "@types/intl-relativeformat": "^2.1.0", diff --git a/packages/osd-i18n/src/angular/__snapshots__/directive.test.ts.snap b/packages/osd-i18n/src/angular/__snapshots__/directive.test.ts.snap deleted file mode 100644 index 44bb96399432..000000000000 --- a/packages/osd-i18n/src/angular/__snapshots__/directive.test.ts.snap +++ /dev/null @@ -1,69 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`i18nDirective doesn't render html in result message with text-only values 1`] = ` -
- Default <span onclick=alert(1) >Press</span> message -
-`; - -exports[`i18nDirective doesn't render html in text-only value 1`] = ` -
- Default <strong>message</strong> -
-`; - -exports[`i18nDirective inserts correct translation html content with values 1`] = `"default-message word"`; - -exports[`i18nDirective inserts correct translation html content with values 2`] = `"default-message anotherWord"`; - -exports[`i18nDirective sanitizes message before inserting it to DOM 1`] = ` -
' }" -> - Default message, -
-
-`; - -exports[`i18nDirective sanitizes onclick attribute 1`] = ` -
- Default - - Press - - message -
-`; - -exports[`i18nDirective sanitizes onmouseover attribute 1`] = ` -
Press' }" -> - Default - - Press - - message -
-`; diff --git a/packages/osd-i18n/src/angular/directive.test.ts b/packages/osd-i18n/src/angular/directive.test.ts deleted file mode 100644 index 35dd43001335..000000000000 --- a/packages/osd-i18n/src/angular/directive.test.ts +++ /dev/null @@ -1,172 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import angular from 'angular'; -import 'angular-mocks'; -import 'angular-sanitize'; - -import { i18nDirective } from './directive'; -import { I18nProvider } from './provider'; - -angular - .module('app', ['ngSanitize']) - .provider('i18n', I18nProvider) - .directive('i18nId', i18nDirective); - -describe('i18nDirective', () => { - let compile: angular.ICompileService; - let scope: angular.IRootScopeService & { word?: string }; - - beforeEach(angular.mock.module('app')); - beforeEach( - angular.mock.inject( - ($compile: angular.ICompileService, $rootScope: angular.IRootScopeService) => { - compile = $compile; - scope = $rootScope.$new(); - scope.word = 'word'; - } - ) - ); - - test('inserts correct translation html content', () => { - const id = 'id'; - const defaultMessage = 'default-message'; - - const element = angular.element( - `
` - ); - - compile(element)(scope); - scope.$digest(); - - expect(element.html()).toEqual(defaultMessage); - }); - - test('inserts correct translation html content with values', () => { - const id = 'id'; - const defaultMessage = 'default-message {word}'; - - const element = angular.element( - `
` - ); - - compile(element)(scope); - scope.$digest(); - - expect(element.html()).toMatchSnapshot(); - - scope.word = 'anotherWord'; - scope.$digest(); - - expect(element.html()).toMatchSnapshot(); - }); - - test('sanitizes message before inserting it to DOM', () => { - const element = angular.element( - `
` - ); - - compile(element)(scope); - scope.$digest(); - - expect(element[0]).toMatchSnapshot(); - }); - - test(`doesn't render html in result message with text-only values`, () => { - const element = angular.element( - `
` - ); - - compile(element)(scope); - scope.$digest(); - - expect(element[0]).toMatchSnapshot(); - }); - - test('sanitizes onclick attribute', () => { - const element = angular.element( - `
` - ); - - compile(element)(scope); - scope.$digest(); - - expect(element[0]).toMatchSnapshot(); - }); - - test('sanitizes onmouseover attribute', () => { - const element = angular.element( - `
` - ); - - compile(element)(scope); - scope.$digest(); - - expect(element[0]).toMatchSnapshot(); - }); - - test(`doesn't render html in text-only value`, () => { - const element = angular.element( - `
` - ); - - compile(element)(scope); - scope.$digest(); - - expect(element[0]).toMatchSnapshot(); - }); -}); diff --git a/packages/osd-i18n/src/angular/directive.ts b/packages/osd-i18n/src/angular/directive.ts deleted file mode 100644 index 790357da7d46..000000000000 --- a/packages/osd-i18n/src/angular/directive.ts +++ /dev/null @@ -1,126 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { IDirective, IRootElementService, IScope } from 'angular'; - -import { I18nServiceType } from './provider'; - -interface I18nScope extends IScope { - values?: Record; - defaultMessage: string; - id: string; -} - -const HTML_KEY_PREFIX = 'html_'; -const PLACEHOLDER_SEPARATOR = '@I18N@'; - -export const i18nDirective: [string, string, typeof i18nDirectiveFn] = [ - 'i18n', - '$sanitize', - i18nDirectiveFn, -]; - -function i18nDirectiveFn( - i18n: I18nServiceType, - $sanitize: (html: string) => string -): IDirective { - return { - restrict: 'A', - scope: { - id: '@i18nId', - defaultMessage: '@i18nDefaultMessage', - values: ' { - setContent($element, $scope, $sanitize, i18n); - }); - } else { - setContent($element, $scope, $sanitize, i18n); - } - }, - }; -} - -function setContent( - $element: IRootElementService, - $scope: I18nScope, - $sanitize: (html: string) => string, - i18n: I18nServiceType -) { - const originalValues = $scope.values; - const valuesWithPlaceholders = {} as Record; - let hasValuesWithPlaceholders = false; - - // If we have values with the keys that start with HTML_KEY_PREFIX we should replace - // them with special placeholders that later on will be inserted as HTML - // into the DOM, the rest of the content will be treated as text. We don't - // sanitize values at this stage as some of the values can be excluded from - // the translated string (e.g. not used by ICU conditional statements). - if (originalValues) { - for (const [key, value] of Object.entries(originalValues)) { - if (key.startsWith(HTML_KEY_PREFIX)) { - valuesWithPlaceholders[ - key.slice(HTML_KEY_PREFIX.length) - ] = `${PLACEHOLDER_SEPARATOR}${key}${PLACEHOLDER_SEPARATOR}`; - - hasValuesWithPlaceholders = true; - } else { - valuesWithPlaceholders[key] = value; - } - } - } - - const label = i18n($scope.id, { - values: valuesWithPlaceholders, - defaultMessage: $scope.defaultMessage, - }); - - // If there are no placeholders to replace treat everything as text, otherwise - // insert label piece by piece replacing every placeholder with corresponding - // sanitized HTML content. - if (!hasValuesWithPlaceholders) { - $element.text(label); - } else { - $element.empty(); - for (const contentOrPlaceholder of label.split(PLACEHOLDER_SEPARATOR)) { - if (!contentOrPlaceholder) { - continue; - } - - $element.append( - originalValues!.hasOwnProperty(contentOrPlaceholder) - ? $sanitize(originalValues![contentOrPlaceholder]) - : document.createTextNode(contentOrPlaceholder) - ); - } - } -} diff --git a/packages/osd-i18n/src/angular/filter.test.ts b/packages/osd-i18n/src/angular/filter.test.ts deleted file mode 100644 index bc236e711d4c..000000000000 --- a/packages/osd-i18n/src/angular/filter.test.ts +++ /dev/null @@ -1,68 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -jest.mock('../core/i18n', () => ({ - translate: jest.fn().mockImplementation(() => 'translation'), -})); - -import angular from 'angular'; -import 'angular-mocks'; - -import * as i18n from '../core/i18n'; -import { i18nFilter as angularI18nFilter } from './filter'; -import { I18nProvider, I18nServiceType } from './provider'; - -angular.module('app', []).provider('i18n', I18nProvider).filter('i18n', angularI18nFilter); - -describe('i18nFilter', () => { - let filter: I18nServiceType; - - beforeEach(angular.mock.module('app')); - beforeEach( - angular.mock.inject((i18nFilter) => { - filter = i18nFilter; - }) - ); - afterEach(() => { - jest.resetAllMocks(); - }); - - test('provides wrapper around i18n engine', () => { - const id = 'id'; - const defaultMessage = 'default-message'; - const values = {}; - - const result = filter(id, { defaultMessage, values }); - - expect(result).toEqual('translation'); - expect(i18n.translate).toHaveBeenCalledTimes(1); - expect(i18n.translate).toHaveBeenCalledWith(id, { defaultMessage, values }); - }); -}); diff --git a/packages/osd-i18n/src/angular/filter.ts b/packages/osd-i18n/src/angular/filter.ts deleted file mode 100644 index 4ffa5dd3ef4e..000000000000 --- a/packages/osd-i18n/src/angular/filter.ts +++ /dev/null @@ -1,42 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { I18nServiceType } from './provider'; - -export const i18nFilter: [string, typeof i18nFilterFn] = ['i18n', i18nFilterFn]; - -function i18nFilterFn(i18n: I18nServiceType) { - return (id: string, { defaultMessage = '', values = {} } = {}) => { - return i18n(id, { - values, - defaultMessage, - }); - }; -} diff --git a/packages/osd-i18n/src/angular/index.ts b/packages/osd-i18n/src/angular/index.ts deleted file mode 100644 index 04f7d66eb12a..000000000000 --- a/packages/osd-i18n/src/angular/index.ts +++ /dev/null @@ -1,38 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export { I18nProvider } from './provider'; - -export { i18nFilter } from './filter'; -export { i18nDirective } from './directive'; - -// re-export types: https://github.com/babel/babel-loader/issues/603 -import { I18nServiceType as _I18nServiceType } from './provider'; -export type I18nServiceType = _I18nServiceType; diff --git a/packages/osd-i18n/src/angular/provider.test.ts b/packages/osd-i18n/src/angular/provider.test.ts deleted file mode 100644 index e612e0bef3d8..000000000000 --- a/packages/osd-i18n/src/angular/provider.test.ts +++ /dev/null @@ -1,69 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import angular from 'angular'; -import 'angular-mocks'; - -import * as i18nCore from '../core/i18n'; -import { I18nProvider, I18nServiceType } from './provider'; - -angular.module('app', []).provider('i18n', I18nProvider); - -describe('i18nProvider', () => { - let provider: I18nProvider; - let service: I18nServiceType; - - beforeEach( - angular.mock.module('app', [ - 'i18nProvider', - (i18n: I18nProvider) => { - provider = i18n; - }, - ]) - ); - beforeEach( - angular.mock.inject((i18n: I18nServiceType) => { - service = i18n; - }) - ); - - test('provides wrapper around i18n engine', () => { - expect(service).toEqual(i18nCore.translate); - }); - - test('provides service wrapper around i18n engine', () => { - const serviceMethodNames = Object.keys(provider); - const pluginMethodNames = Object.keys(i18nCore); - - expect([...serviceMethodNames, 'translate'].sort()).toEqual( - [...pluginMethodNames, '$get'].sort() - ); - }); -}); diff --git a/packages/osd-i18n/src/angular/provider.ts b/packages/osd-i18n/src/angular/provider.ts deleted file mode 100644 index bd02a30cef45..000000000000 --- a/packages/osd-i18n/src/angular/provider.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import * as i18n from '../core'; - -export type I18nServiceType = ReturnType; - -export class I18nProvider implements angular.IServiceProvider { - public addTranslation = i18n.addTranslation; - public getTranslation = i18n.getTranslation; - public setLocale = i18n.setLocale; - public getLocale = i18n.getLocale; - public setDefaultLocale = i18n.setDefaultLocale; - public getDefaultLocale = i18n.getDefaultLocale; - public setFormats = i18n.setFormats; - public getFormats = i18n.getFormats; - public getRegisteredLocales = i18n.getRegisteredLocales; - public init = i18n.init; - public load = i18n.load; - public $get = () => i18n.translate; -} diff --git a/packages/osd-interpreter/package.json b/packages/osd-interpreter/package.json index 5667f0661e1f..34bcad460244 100644 --- a/packages/osd-interpreter/package.json +++ b/packages/osd-interpreter/package.json @@ -9,16 +9,16 @@ "osd:watch": "../../scripts/use_node scripts/build --dev --watch" }, "dependencies": { - "@babel/runtime": "^7.16.5", + "@babel/runtime": "^7.22.9", "@osd/i18n": "1.0.0", "lodash": "^4.17.21", "uuid": "3.3.2" }, "devDependencies": { - "@babel/cli": "^7.16.0", - "@babel/core": "^7.16.5", - "@babel/plugin-transform-modules-commonjs": "^7.16.5", - "@babel/plugin-transform-runtime": "^7.16.5", + "@babel/cli": "^7.22.9", + "@babel/core": "^7.22.9", + "@babel/plugin-transform-modules-commonjs": "^7.22.9", + "@babel/plugin-transform-runtime": "^7.22.9", "@osd/babel-preset": "1.0.0", "@osd/dev-utils": "1.0.0", "babel-loader": "^8.2.3", @@ -27,7 +27,7 @@ "del": "^6.1.1", "getopts": "^2.2.5", "pegjs": "0.10.0", - "sass-loader": "npm:@amoo-miki/sass-loader@10.4.1-node-sass-9.0.0-libsass-3.6.5", + "sass-loader": "npm:@amoo-miki/sass-loader@10.4.1-node-sass-9.0.0-libsass-3.6.5-with-sass-embedded.rc1", "style-loader": "^1.1.3", "supports-color": "^7.0.0", "url-loader": "^2.2.0", diff --git a/packages/osd-opensearch-archiver/src/cli.ts b/packages/osd-opensearch-archiver/src/cli.ts index e8606ed26645..78df5a2c6407 100644 --- a/packages/osd-opensearch-archiver/src/cli.ts +++ b/packages/osd-opensearch-archiver/src/cli.ts @@ -239,7 +239,7 @@ export function runCli() { output: process.stdout, }); - await new Promise((resolveInput) => { + await new Promise((resolveInput) => { rl.question(`Press enter when you're done`, () => { rl.close(); resolveInput(); diff --git a/packages/osd-opensearch/package.json b/packages/osd-opensearch/package.json index 1675b0ef134a..cff646e7e403 100644 --- a/packages/osd-opensearch/package.json +++ b/packages/osd-opensearch/package.json @@ -29,7 +29,7 @@ }, "devDependencies": { "@osd/babel-preset": "1.0.0", - "@babel/cli": "^7.16.0", + "@babel/cli": "^7.22.9", "del": "^6.1.1" } } diff --git a/packages/osd-opensearch/src/cli_commands/snapshot.js b/packages/osd-opensearch/src/cli_commands/snapshot.js index 3cf8701856bd..ff21dbe851c8 100644 --- a/packages/osd-opensearch/src/cli_commands/snapshot.js +++ b/packages/osd-opensearch/src/cli_commands/snapshot.js @@ -49,6 +49,7 @@ exports.help = (defaults = {}) => { -E Additional key=value settings to pass to OpenSearch --download-only Download the snapshot but don't actually start it --ssl Sets up SSL on OpenSearch + --security Installs and sets up the OpenSearch Security plugin on the cluster --P OpenSearch plugin artifact URL to install it on the cluster. We can use the flag multiple times to install multiple plugins on the cluster snapshot. The argument value can be url to zip file, maven coordinates of the plugin or for local zip files, use file:. @@ -74,6 +75,8 @@ exports.run = async (defaults = {}) => { boolean: ['download-only'], + boolean: ['security'], + default: defaults, }); @@ -91,6 +94,10 @@ exports.run = async (defaults = {}) => { await cluster.installOpenSearchPlugins(installPath, options.opensearchPlugins); } + if (options.security) { + await cluster.setupSecurity(installPath, options.version ?? defaults.version); + } + options.bundledJDK = true; await cluster.run(installPath, options); diff --git a/packages/osd-opensearch/src/cluster.js b/packages/osd-opensearch/src/cluster.js index 3527668eed05..455a1e5f919f 100644 --- a/packages/osd-opensearch/src/cluster.js +++ b/packages/osd-opensearch/src/cluster.js @@ -34,7 +34,7 @@ const execa = require('execa'); const chalk = require('chalk'); const path = require('path'); const { downloadSnapshot, installSnapshot, installSource, installArchive } = require('./install'); -const { OPENSEARCH_BIN, OPENSEARCH_PLUGIN } = require('./paths'); +const { OPENSEARCH_BIN, OPENSEARCH_PLUGIN, OPENSEARCH_SECURITY_INSTALL } = require('./paths'); const { log: defaultLog, parseOpenSearchLog, extractConfigFiles, decompress } = require('./utils'); const { createCliError } = require('./errors'); const { promisify } = require('util'); @@ -42,6 +42,19 @@ const treeKillAsync = promisify(require('tree-kill')); const { parseSettings, SettingsFilter } = require('./settings'); const { CA_CERT_PATH, OPENSEARCH_P12_PATH, OPENSEARCH_P12_PASSWORD } = require('@osd/dev-utils'); const readFile = util.promisify(fs.readFile); +const chmodAsync = util.promisify(fs.chmod); + +const LATEST_ENGINE_PLUGIN_BASE_URL = + 'https://ci.opensearch.org/ci/dbc/distribution-build-opensearch'; + +function generateEnginePluginUrl(version, plugin) { + const legacyVersion = `${version}.0`; + const [platform, type] = + process.platform === 'win32' ? ['windows', 'zip'] : [process.platform, 'tar']; + const arch = process.arch === 'arm64' ? 'arm64' : 'x64'; + + return `${LATEST_ENGINE_PLUGIN_BASE_URL}/${version}/latest/${platform}/${arch}/${type}/builds/opensearch/plugins/${plugin}-${legacyVersion}.zip`; +} // listen to data on stream until map returns anything but undefined const first = (stream, map) => @@ -57,9 +70,10 @@ const first = (stream, map) => }); exports.Cluster = class Cluster { - constructor({ log = defaultLog, ssl = false } = {}) { + constructor({ log = defaultLog, ssl = false, security = false } = {}) { this._log = log; this._ssl = ssl; + this._security = security; this._caCertPromise = ssl ? readFile(CA_CERT_PATH) : undefined; } @@ -193,6 +207,23 @@ exports.Cluster = class Cluster { } } + /** + * Setups cluster with security demo configuration + * + * @param {string} installPath + * @property {String} version - version of OpenSearch + */ + async setupSecurity(installPath, version) { + const pluginUrl = generateEnginePluginUrl(version, 'opensearch-security'); + await this.installOpenSearchPlugins(installPath, pluginUrl); + this._log.info('Setting up security'); + const pluginPath = path.resolve(installPath, OPENSEARCH_SECURITY_INSTALL); + if (pluginPath) { + await chmodAsync(pluginPath, '755'); + await execa(OPENSEARCH_SECURITY_INSTALL, ['-y', '-i', '-s'], { cwd: installPath }); + } + } + /** * Starts OpenSearch and returns resolved promise once started * diff --git a/packages/osd-opensearch/src/paths.js b/packages/osd-opensearch/src/paths.js index 93bb80e97ff1..d316f7cd41bf 100644 --- a/packages/osd-opensearch/src/paths.js +++ b/packages/osd-opensearch/src/paths.js @@ -35,6 +35,10 @@ function maybeUseBat(bin) { return os.platform().startsWith('win') ? `${bin}.bat` : bin; } +function maybeUseBatOrShell(bin) { + return os.platform().startsWith('win') ? `${bin}.bat` : `${bin}.sh`; +} + const tempDir = os.tmpdir(); exports.BASE_PATH = path.resolve(tempDir, 'osd-opensearch'); @@ -45,3 +49,6 @@ exports.OPENSEARCH_CONFIG = 'config/opensearch.yml'; exports.OPENSEARCH_KEYSTORE_BIN = maybeUseBat('./bin/opensearch-keystore'); exports.OPENSEARCH_PLUGIN = maybeUseBat('./bin/opensearch-plugin'); +exports.OPENSEARCH_SECURITY_INSTALL = maybeUseBatOrShell( + './plugins/opensearch-security/tools/install_demo_configuration' +); diff --git a/packages/osd-optimizer/package.json b/packages/osd-optimizer/package.json index d1e0edbe59e6..5e68f36a1098 100644 --- a/packages/osd-optimizer/package.json +++ b/packages/osd-optimizer/package.json @@ -10,8 +10,8 @@ "osd:watch": "yarn build --watch" }, "dependencies": { - "@babel/cli": "^7.16.0", - "@babel/core": "^7.16.5", + "@babel/cli": "^7.22.9", + "@babel/core": "^7.22.9", "@osd/babel-preset": "1.0.0", "@osd/cross-platform": "1.0.0", "@osd/dev-utils": "1.0.0", @@ -51,10 +51,10 @@ "css-loader": "^5.2.7", "file-loader": "^6.2.0", "loader-utils": "^2.0.4", - "node-sass": "npm:@amoo-miki/node-sass@9.0.0-libsass-3.6.5", + "sass-embedded": "1.66.1", "postcss-loader": "^4.2.0", "raw-loader": "^4.0.2", - "sass-loader": "npm:@amoo-miki/sass-loader@10.4.1-node-sass-9.0.0-libsass-3.6.5", + "sass-loader": "npm:@amoo-miki/sass-loader@10.4.1-node-sass-9.0.0-libsass-3.6.5-with-sass-embedded.rc1", "style-loader": "^1.1.3", "url-loader": "^2.2.0", "val-loader": "^2.1.2", diff --git a/packages/osd-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap b/packages/osd-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap index b08793cdae2f..dbbb5bad9bf3 100644 --- a/packages/osd-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap +++ b/packages/osd-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap @@ -76,7 +76,7 @@ OptimizerConfig { } `; -exports[`prepares assets for distribution: bar bundle 1`] = `"(function(modules){var installedModules={};function __webpack_require__(moduleId){if(installedModules[moduleId]){return installedModules[moduleId].exports}var module=installedModules[moduleId]={i:moduleId,l:false,exports:{}};modules[moduleId].call(module.exports,module,module.exports,__webpack_require__);module.l=true;return module.exports}__webpack_require__.m=modules;__webpack_require__.c=installedModules;__webpack_require__.d=function(exports,name,getter){if(!__webpack_require__.o(exports,name)){Object.defineProperty(exports,name,{enumerable:true,get:getter})}};__webpack_require__.r=function(exports){if(typeof Symbol!==\\"undefined\\"&&Symbol.toStringTag){Object.defineProperty(exports,Symbol.toStringTag,{value:\\"Module\\"})}Object.defineProperty(exports,\\"__esModule\\",{value:true})};__webpack_require__.t=function(value,mode){if(mode&1)value=__webpack_require__(value);if(mode&8)return value;if(mode&4&&typeof value===\\"object\\"&&value&&value.__esModule)return value;var ns=Object.create(null);__webpack_require__.r(ns);Object.defineProperty(ns,\\"default\\",{enumerable:true,value:value});if(mode&2&&typeof value!=\\"string\\")for(var key in value)__webpack_require__.d(ns,key,function(key){return value[key]}.bind(null,key));return ns};__webpack_require__.n=function(module){var getter=module&&module.__esModule?function getDefault(){return module[\\"default\\"]}:function getModuleExports(){return module};__webpack_require__.d(getter,\\"a\\",getter);return getter};__webpack_require__.o=function(object,property){return Object.prototype.hasOwnProperty.call(object,property)};__webpack_require__.p=\\"\\";return __webpack_require__(__webpack_require__.s=3)})([function(module,exports,__webpack_require__){\\"use strict\\";module.exports=function(cssWithMappingToString){var list=[];list.toString=function toString(){return this.map((function(item){var content=cssWithMappingToString(item);if(item[2]){return\\"@media \\".concat(item[2],\\" {\\").concat(content,\\"}\\")}return content})).join(\\"\\")};list.i=function(modules,mediaQuery,dedupe){if(typeof modules===\\"string\\"){modules=[[null,modules,\\"\\"]]}var alreadyImportedModules={};if(dedupe){for(var i=0;i`, to all `package.json` files that declared the dependency. + +``` +yarn osd bootstrap --single-version=ignore +``` +In `ignore` mode, bootstrapping behaves very similar to the `strict` mode by showing errors when different ranges of a +package are marked as dependencies, but without terminating. + ### Running scripts Some times you want to run the same script across multiple packages and plugins, diff --git a/packages/osd-pm/dist/index.js b/packages/osd-pm/dist/index.js index da3a27746bf7..0301d50fdb26 100644 --- a/packages/osd-pm/dist/index.js +++ b/packages/osd-pm/dist/index.js @@ -131,18 +131,12 @@ Object.defineProperty(exports, "run", { return _cli.run; } }); - var _cli = __webpack_require__(1); - var _production = __webpack_require__(563); - var _projects = __webpack_require__(147); - var _project = __webpack_require__(165); - -var _workspaces = __webpack_require__(329); - -var _config = __webpack_require__(330); +var _workspaces = __webpack_require__(330); +var _config = __webpack_require__(331); /***/ }), /* 1 */ @@ -155,23 +149,14 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.run = run; - var _dedent = _interopRequireDefault(__webpack_require__(2)); - var _getopts = _interopRequireDefault(__webpack_require__(3)); - var _path = __webpack_require__(4); - var _tooling_log = __webpack_require__(5); - var _commands = __webpack_require__(128); - var _run = __webpack_require__(558); - var _log = __webpack_require__(145); - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - /* * SPDX-License-Identifier: Apache-2.0 * @@ -201,11 +186,12 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de * specific language governing permissions and limitations * under the License. */ + function help() { _log.log.info((0, _dedent.default)` usage: osd [] - By default commands are run for OpenSearch Dashboards itself, all packages in the 'packages/' + By default, commands are run for OpenSearch Dashboards itself, all packages in the 'packages/' folder and for all plugins in './plugins' and '../opensearch-dashboards-extra'. Available commands: @@ -218,27 +204,25 @@ function help() { -i, --include Include only specified projects. If left unspecified, it defaults to including all projects. --skip-opensearch-dashboards-plugins Filter all plugins in ./plugins and ../opensearch-dashboards-extra when running command. --no-cache Disable the bootstrap cache + --single-version Set single version validation method: 'strict', 'loose', 'ignore', or 'brute-force' --verbose Set log level to verbose --debug Set log level to debug --quiet Set log level to error --silent Disable log output ` + '\n'); } - async function run(argv) { _log.log.setLogLevel((0, _tooling_log.pickLevelFromFlags)((0, _getopts.default)(argv, { boolean: ['verbose', 'debug', 'quiet', 'silent'] - }))); // We can simplify this setup (and remove this extra handling) once Yarn + }))); + + // We can simplify this setup (and remove this extra handling) once Yarn // starts forwarding the `--` directly to this script, see // https://github.com/yarnpkg/yarn/blob/b2d3e1a8fe45ef376b716d597cc79b38702a9320/src/cli/index.js#L174-L182 - - if (argv.includes('--')) { _log.log.error(`Using "--" is not allowed, as it doesn't work with 'yarn osd'.`); - process.exit(1); } - const options = (0, _getopts.default)(argv, { alias: { e: 'exclude', @@ -248,17 +232,17 @@ async function run(argv) { default: { cache: true }, - boolean: ['prefer-offline', 'frozen-lockfile', 'cache'] + boolean: ['prefer-offline', 'frozen-lockfile', 'cache'], + string: ['single-version'] }); const args = options._; - if (options.help || args.length === 0) { help(); return; - } // This `rootPath` is relative to `./dist/` as that's the location of the - // built version of this tool. - + } + // This `rootPath` is relative to `./dist/` as that's the location of the + // built version of this tool. const rootPath = (0, _path.resolve)(__dirname, '../../../'); const commandName = args[0]; const extraArgs = args.slice(1); @@ -268,13 +252,10 @@ async function run(argv) { rootPath }; const command = _commands.commands[commandName]; - if (command === undefined) { _log.log.error(`[${commandName}] is not a valid command, see 'osd --help'`); - process.exit(1); } - await (0, _run.runCommand)(command, commandOptions); } @@ -555,6 +536,7 @@ module.exports = require("path"); "use strict"; + /* * SPDX-License-Identifier: Apache-2.0 * @@ -565,7 +547,6 @@ module.exports = require("path"); * Any modifications Copyright OpenSearch Contributors. See * GitHub history for details. */ - Object.defineProperty(exports, "__esModule", { value: true }); @@ -588,27 +569,21 @@ exports.ToolingLogCollectingWriter = exports.parseLogLevel = exports.pickLevelFr * specific language governing permissions and limitations * under the License. */ - var tooling_log_1 = __webpack_require__(6); - Object.defineProperty(exports, "ToolingLog", { enumerable: true, get: function () { return tooling_log_1.ToolingLog; } }); - var tooling_log_text_writer_1 = __webpack_require__(111); - Object.defineProperty(exports, "ToolingLogTextWriter", { enumerable: true, get: function () { return tooling_log_text_writer_1.ToolingLogTextWriter; } }); - var log_levels_1 = __webpack_require__(126); - Object.defineProperty(exports, "pickLevelFromFlags", { enumerable: true, get: function () { @@ -621,9 +596,7 @@ Object.defineProperty(exports, "parseLogLevel", { return log_levels_1.parseLogLevel; } }); - var tooling_log_collecting_writer_1 = __webpack_require__(127); - Object.defineProperty(exports, "ToolingLogCollectingWriter", { enumerable: true, get: function () { @@ -637,6 +610,7 @@ Object.defineProperty(exports, "ToolingLogCollectingWriter", { "use strict"; + /* * SPDX-License-Identifier: Apache-2.0 * @@ -647,12 +621,10 @@ Object.defineProperty(exports, "ToolingLogCollectingWriter", { * Any modifications Copyright OpenSearch Contributors. See * GitHub history for details. */ - Object.defineProperty(exports, "__esModule", { value: true }); exports.ToolingLog = void 0; - const tslib_1 = __webpack_require__(7); /* * Licensed to Elasticsearch B.V. under one or more contributor @@ -672,64 +644,48 @@ const tslib_1 = __webpack_require__(7); * specific language governing permissions and limitations * under the License. */ - - const Rx = tslib_1.__importStar(__webpack_require__(8)); - const tooling_log_text_writer_1 = __webpack_require__(111); - class ToolingLog { constructor(writerConfig) { this.identWidth = 0; this.writers = writerConfig ? [new tooling_log_text_writer_1.ToolingLogTextWriter(writerConfig)] : []; this.written$ = new Rx.Subject(); } - indent(delta = 0) { this.identWidth = Math.max(this.identWidth + delta, 0); return this.identWidth; } - verbose(...args) { this.sendToWriters('verbose', args); } - debug(...args) { this.sendToWriters('debug', args); } - info(...args) { this.sendToWriters('info', args); } - success(...args) { this.sendToWriters('success', args); } - warning(...args) { this.sendToWriters('warning', args); } - error(error) { this.sendToWriters('error', [error]); } - write(...args) { this.sendToWriters('write', args); } - getWriters() { return this.writers.slice(0); } - setWriters(writers) { this.writers = [...writers]; } - getWritten$() { return this.written$.asObservable(); } - sendToWriters(type, args) { const msg = { type, @@ -737,20 +693,16 @@ class ToolingLog { args }; let written = false; - for (const writer of this.writers) { if (writer.write(msg)) { written = true; } } - if (written) { this.written$.next(msg); } } - } - exports.ToolingLog = ToolingLog; /***/ }), @@ -6816,6 +6768,7 @@ var ZipBufferIterator = /*@__PURE__*/ (function (_super) { "use strict"; + /* * SPDX-License-Identifier: Apache-2.0 * @@ -6826,12 +6779,10 @@ var ZipBufferIterator = /*@__PURE__*/ (function (_super) { * Any modifications Copyright OpenSearch Contributors. See * GitHub history for details. */ - Object.defineProperty(exports, "__esModule", { value: true }); exports.ToolingLogTextWriter = void 0; - const tslib_1 = __webpack_require__(7); /* * Licensed to Elasticsearch B.V. under one or more contributor @@ -6851,14 +6802,9 @@ const tslib_1 = __webpack_require__(7); * specific language governing permissions and limitations * under the License. */ - - const util_1 = __webpack_require__(112); - const chalk_1 = tslib_1.__importDefault(__webpack_require__(113)); - const log_levels_1 = __webpack_require__(126); - const { magentaBright, yellow, @@ -6876,72 +6822,56 @@ const MSG_PREFIXES = { warning: ` ${yellow('warn')} `, error: `${red('ERROR')} ` }; - const has = (obj, key) => obj.hasOwnProperty(key); - function shouldWriteType(level, type) { if (type === 'write') { return level.name !== 'silent'; } - return Boolean(level.flags[type === 'success' ? 'info' : type]); } - function stringifyError(error) { if (typeof error !== 'string' && !(error instanceof Error)) { error = new Error(`"${error}" thrown`); } - if (typeof error === 'string') { return error; } - return error.stack || error.message || error; } - class ToolingLogTextWriter { constructor(config) { - this.level = log_levels_1.parseLogLevel(config.level); + this.level = (0, log_levels_1.parseLogLevel)(config.level); this.writeTo = config.writeTo; - if (!this.writeTo || typeof this.writeTo.write !== 'function') { throw new Error('ToolingLogTextWriter requires the `writeTo` option be set to a stream (like process.stdout)'); } } - write(msg) { if (!shouldWriteType(this.level, msg.type)) { return false; } - const prefix = has(MSG_PREFIXES, msg.type) ? MSG_PREFIXES[msg.type] : ''; ToolingLogTextWriter.write(this.writeTo, prefix, msg); return true; } - static write(writeTo, prefix, msg) { - const txt = msg.type === 'error' ? stringifyError(msg.args[0]) : util_1.format(msg.args[0], ...msg.args.slice(1)); + const txt = msg.type === 'error' ? stringifyError(msg.args[0]) : (0, util_1.format)(msg.args[0], ...msg.args.slice(1)); (prefix + txt).split('\n').forEach((line, i) => { let lineIndent = ''; - if (msg.indent > 0) { // if we are indenting write some spaces followed by a symbol lineIndent += ' '.repeat(msg.indent - 1); lineIndent += line.startsWith('-') ? '└' : '│'; } - if (line && prefix && i > 0) { // apply additional indentation to lines after // the first if this message gets a prefix lineIndent += PREFIX_INDENT; } - writeTo.write(`${lineIndent}${line}\n`); }); } - } - exports.ToolingLogTextWriter = ToolingLogTextWriter; /***/ }), @@ -8941,6 +8871,7 @@ module.exports = (chalk, temporary) => { "use strict"; + /* * SPDX-License-Identifier: Apache-2.0 * @@ -8951,13 +8882,11 @@ module.exports = (chalk, temporary) => { * Any modifications Copyright OpenSearch Contributors. See * GitHub history for details. */ - Object.defineProperty(exports, "__esModule", { value: true }); exports.parseLogLevel = exports.pickLevelFromFlags = void 0; const LEVELS = ['silent', 'error', 'warning', 'info', 'debug', 'verbose']; - function pickLevelFromFlags(flags, options = {}) { if (flags.verbose) return 'verbose'; if (flags.debug) return 'debug'; @@ -8965,17 +8894,13 @@ function pickLevelFromFlags(flags, options = {}) { if (flags.silent) return 'silent'; return options.default || 'info'; } - exports.pickLevelFromFlags = pickLevelFromFlags; - function parseLogLevel(name) { const i = LEVELS.indexOf(name); - if (i === -1) { const msg = `Invalid log level "${name}" ` + `(expected one of ${LEVELS.join(',')})`; throw new Error(msg); } - const flags = {}; LEVELS.forEach((level, levelI) => { flags[level] = levelI <= i; @@ -8985,7 +8910,6 @@ function parseLogLevel(name) { flags: flags }; } - exports.parseLogLevel = parseLogLevel; /***/ }), @@ -8994,6 +8918,7 @@ exports.parseLogLevel = parseLogLevel; "use strict"; + /* * SPDX-License-Identifier: Apache-2.0 * @@ -9004,7 +8929,6 @@ exports.parseLogLevel = parseLogLevel; * Any modifications Copyright OpenSearch Contributors. See * GitHub history for details. */ - Object.defineProperty(exports, "__esModule", { value: true }); @@ -9027,9 +8951,7 @@ exports.ToolingLogCollectingWriter = void 0; * specific language governing permissions and limitations * under the License. */ - const tooling_log_text_writer_1 = __webpack_require__(111); - class ToolingLogCollectingWriter extends tooling_log_text_writer_1.ToolingLogTextWriter { constructor(level = 'verbose') { super({ @@ -9043,9 +8965,7 @@ class ToolingLogCollectingWriter extends tooling_log_text_writer_1.ToolingLogTex }); this.messages = []; } - } - exports.ToolingLogCollectingWriter = ToolingLogCollectingWriter; /***/ }), @@ -9059,15 +8979,10 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.commands = void 0; - var _bootstrap = __webpack_require__(129); - -var _clean = __webpack_require__(344); - +var _clean = __webpack_require__(345); var _run = __webpack_require__(457); - var _watch = __webpack_require__(458); - /* * SPDX-License-Identifier: Apache-2.0 * @@ -9097,13 +9012,13 @@ var _watch = __webpack_require__(458); * specific language governing permissions and limitations * under the License. */ -const commands = { + +const commands = exports.commands = { bootstrap: _bootstrap.BootstrapCommand, clean: _clean.CleanCommand, run: _run.RunCommand, watch: _watch.WatchCommand }; -exports.commands = commands; /***/ }), /* 129 */ @@ -9116,23 +9031,14 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.BootstrapCommand = void 0; - var _link_project_executables = __webpack_require__(130); - var _log = __webpack_require__(145); - var _parallelize = __webpack_require__(146); - var _projects = __webpack_require__(147); - -var _project_checksums = __webpack_require__(331); - -var _bootstrap_cache_file = __webpack_require__(341); - -var _yarn_lock = __webpack_require__(333); - -var _validate_dependencies = __webpack_require__(342); - +var _project_checksums = __webpack_require__(332); +var _bootstrap_cache_file = __webpack_require__(342); +var _yarn_lock = __webpack_require__(334); +var _validate_dependencies = __webpack_require__(343); /* * SPDX-License-Identifier: Apache-2.0 * @@ -9162,28 +9068,26 @@ var _validate_dependencies = __webpack_require__(342); * specific language governing permissions and limitations * under the License. */ -const BootstrapCommand = { + +const BootstrapCommand = exports.BootstrapCommand = { description: 'Install dependencies and crosslink projects', name: 'bootstrap', - async run(projects, projectGraph, { options, osd }) { + var _options$singleVersi, _options$singleVersi$; const batchedProjectsByWorkspace = (0, _projects.topologicallyBatchProjects)(projects, projectGraph, { batchByWorkspace: true }); const batchedProjects = (0, _projects.topologicallyBatchProjects)(projects, projectGraph); const extraArgs = [...(options['frozen-lockfile'] === true ? ['--frozen-lockfile'] : []), ...(options['prefer-offline'] === true ? ['--prefer-offline'] : [])]; - for (const batch of batchedProjectsByWorkspace) { for (const project of batch) { if (project.isWorkspaceProject) { _log.log.verbose(`Skipping workspace project: ${project.name}`); - continue; } - if (project.hasDependencies()) { await project.installDependencies({ extraArgs @@ -9191,10 +9095,10 @@ const BootstrapCommand = { } } } - const yarnLock = await (0, _yarn_lock.readYarnLock)(osd); - await (0, _validate_dependencies.validateDependencies)(osd, yarnLock); + await (0, _validate_dependencies.validateDependencies)(osd, yarnLock, (_options$singleVersi = options['single-version']) === null || _options$singleVersi === void 0 || (_options$singleVersi$ = _options$singleVersi.toLowerCase) === null || _options$singleVersi$ === void 0 ? void 0 : _options$singleVersi$.call(_options$singleVersi)); await (0, _link_project_executables.linkProjectExecutables)(projects, projectGraph); + /** * At the end of the bootstrapping process we call all `osd:bootstrap` scripts * in the list of projects. We do this because some projects need to be @@ -9205,61 +9109,47 @@ const BootstrapCommand = { const checksums = await (0, _project_checksums.getAllChecksums)(osd, _log.log, yarnLock); const caches = new Map(); let cachedProjectCount = 0; - for (const project of projects.values()) { if (project.hasScript('osd:bootstrap') || project.hasBuildTargets()) { const file = new _bootstrap_cache_file.BootstrapCacheFile(osd, project, checksums); const valid = options.cache && file.isValid(); - if (valid) { _log.log.debug(`[${project.name}] cache up to date`); - cachedProjectCount += 1; } - caches.set(project, { file, valid }); } } - if (cachedProjectCount > 0) { _log.log.success(`${cachedProjectCount} bootstrap builds are cached`); } - await (0, _parallelize.parallelizeBatches)(batchedProjects, async project => { const cache = caches.get(project); - if (cache && !cache.valid) { // Explicitly defined targets override any bootstrap scripts if (project.hasBuildTargets()) { if (project.hasScript('osd:bootstrap')) { _log.log.debug(`[${project.name}] ignoring [osd:bootstrap] script since build targets are provided`); } - _log.log.info(`[${project.name}] running [osd:bootstrap] build targets`); - cache.file.delete(); await project.buildForTargets({ sourceMaps: true }); } else { _log.log.info(`[${project.name}] running [osd:bootstrap] script`); - cache.file.delete(); await project.runScriptStreaming('osd:bootstrap'); } - cache.file.write(); - _log.log.success(`[${project.name}] bootstrap complete`); } }); } - }; -exports.BootstrapCommand = BootstrapCommand; /***/ }), /* 130 */ @@ -9272,13 +9162,9 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.linkProjectExecutables = linkProjectExecutables; - var _path = __webpack_require__(4); - var _fs = __webpack_require__(131); - var _log = __webpack_require__(145); - /* * SPDX-License-Identifier: Apache-2.0 * @@ -9319,28 +9205,24 @@ var _log = __webpack_require__(145); */ async function linkProjectExecutables(projectsByName, projectGraph) { _log.log.debug(`Linking package executables`); - for (const [projectName, projectDeps] of projectGraph) { const project = projectsByName.get(projectName); const binsDir = (0, _path.resolve)(project.nodeModulesLocation, '.bin'); - for (const projectDep of projectDeps) { const executables = projectDep.getExecutables(); - for (const name of Object.keys(executables)) { - const srcPath = executables[name]; // existing logic from lerna -- ensure that the bin we are going to - // point to exists or ignore it + const srcPath = executables[name]; + // existing logic from lerna -- ensure that the bin we are going to + // point to exists or ignore it if (!(await (0, _fs.isFile)(srcPath))) { continue; } + const dest = (0, _path.resolve)(binsDir, name); - const dest = (0, _path.resolve)(binsDir, name); // Get relative project path with normalized path separators. - + // Get relative project path with normalized path separators. const projectRelativePath = (0, _path.relative)(project.path, srcPath).split(_path.sep).join('/'); - _log.log.debug(`[${project.name}] ${name} -> ${projectRelativePath}`); - await (0, _fs.mkdirp)((0, _path.dirname)(dest)); await (0, _fs.createSymlink)(srcPath, dest, 'exec'); await (0, _fs.chmod)(dest, '755'); @@ -9389,19 +9271,12 @@ Object.defineProperty(exports, "writeFile", { return _promises.writeFile; } }); - var _cmdShim = _interopRequireDefault(__webpack_require__(132)); - var _promises = __webpack_require__(143); - var _ncp = __webpack_require__(144); - var _path = __webpack_require__(4); - var _util = __webpack_require__(112); - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - /* * SPDX-License-Identifier: Apache-2.0 * @@ -9431,16 +9306,13 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de * specific language governing permissions and limitations * under the License. */ -const cmdShim = (0, _util.promisify)(_cmdShim.default); +const cmdShim = (0, _util.promisify)(_cmdShim.default); const mkdirp = async path => await (0, _promises.mkdir)(path, { recursive: true }); - exports.mkdirp = mkdirp; -const copyDirectory = (0, _util.promisify)(_ncp.ncp); -exports.copyDirectory = copyDirectory; - +const copyDirectory = exports.copyDirectory = (0, _util.promisify)(_ncp.ncp); async function statTest(path, block) { try { return block(await (0, _promises.lstat)(path)); @@ -9448,37 +9320,34 @@ async function statTest(path, block) { if (e.code === 'ENOENT') { return false; } - throw e; } } + /** * Test if a path points to a symlink. * @param path */ - - async function isSymlink(path) { return await statTest(path, stats => stats.isSymbolicLink()); } + /** * Test if a path points to a directory. * @param path */ - - async function isDirectory(path) { return await statTest(path, stats => stats.isDirectory()); } + /** * Test if a path points to a regular file. * @param path */ - - async function isFile(path) { return await statTest(path, stats => stats.isFile()); } + /** * Create a symlink at dest that points to src. Adapted from * https://github.com/lerna/lerna/blob/2f1b87d9e2295f587e4ac74269f714271d8ed428/src/FileSystemUtilities.js#L103. @@ -9489,8 +9358,6 @@ async function isFile(path) { * windows will use the `cmd-shim` module since symlinks can't be used * for executable files on windows. */ - - async function createSymlink(src, dest, type) { if (process.platform === 'win32') { if (type === 'exec') { @@ -9504,7 +9371,6 @@ async function createSymlink(src, dest, type) { await forceCreate(relativeSource, dest, posixType); } } - async function forceCreate(src, dest, type) { try { // If something exists at `dest` we need to remove it first. @@ -9514,7 +9380,6 @@ async function forceCreate(src, dest, type) { throw error; } } - await (0, _promises.symlink)(src, dest, type); } @@ -11221,20 +11086,42 @@ Object.defineProperty(exports, "LogLevel", { } }); exports.log = void 0; - var _tooling_log = __webpack_require__(5); - -function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - +function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } +function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } +function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } /* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Any modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ /* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ class Log extends _tooling_log.ToolingLog { constructor() { super(); - _defineProperty(this, "logLevel", void 0); - this.setLogLevel('info'); } - setLogLevel(level) { this.logLevel = (0, _tooling_log.parseLogLevel)(level); this.setWriters([new _tooling_log.ToolingLogTextWriter({ @@ -11242,16 +11129,12 @@ class Log extends _tooling_log.ToolingLog { writeTo: process.stdout })]); } - wouldLogLevel(level) { return this.logLevel.flags[level]; } - } - exports.Log = Log; -const log = new Log(); -exports.log = log; +const log = exports.log = new Log(); /***/ }), /* 146 */ @@ -11265,7 +11148,92 @@ Object.defineProperty(exports, "__esModule", { }); exports.parallelize = parallelize; exports.parallelizeBatches = parallelizeBatches; +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Any modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +async function parallelizeBatches(batches, fn) { + for (const batch of batches) { + // We need to make sure the entire batch has completed before we can move on + // to the next batch + await parallelize(batch, fn); + } +} +async function parallelize(items, fn, concurrency = 4) { + if (items.length === 0) { + return; + } + return new Promise((resolve, reject) => { + let activePromises = 0; + const values = items.slice(0); + async function scheduleItem(item) { + activePromises++; + try { + await fn(item); + activePromises--; + if (values.length > 0) { + // We have more work to do, so we schedule the next promise + scheduleItem(values.shift()); + } else if (activePromises === 0) { + // We have no more values left, and all items have completed, so we've + // completed all the work. + resolve(); + } + } catch (error) { + reject(error); + } + } + values.splice(0, concurrency).map(scheduleItem); + }); +} + +/***/ }), +/* 147 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.buildProjectGraph = buildProjectGraph; +exports.getProjects = getProjects; +exports.includeTransitiveProjects = includeTransitiveProjects; +exports.topologicallyBatchProjects = topologicallyBatchProjects; +var _glob = _interopRequireDefault(__webpack_require__(148)); +var _path = _interopRequireDefault(__webpack_require__(4)); +var _util = __webpack_require__(112); +var _errors = __webpack_require__(164); +var _project = __webpack_require__(165); +var _workspaces = __webpack_require__(330); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /* * SPDX-License-Identifier: Apache-2.0 * @@ -11295,106 +11263,9 @@ exports.parallelizeBatches = parallelizeBatches; * specific language governing permissions and limitations * under the License. */ -async function parallelizeBatches(batches, fn) { - for (const batch of batches) { - // We need to make sure the entire batch has completed before we can move on - // to the next batch - await parallelize(batch, fn); - } -} - -async function parallelize(items, fn, concurrency = 4) { - if (items.length === 0) { - return; - } - - return new Promise((resolve, reject) => { - let activePromises = 0; - const values = items.slice(0); - - async function scheduleItem(item) { - activePromises++; - - try { - await fn(item); - activePromises--; - - if (values.length > 0) { - // We have more work to do, so we schedule the next promise - scheduleItem(values.shift()); - } else if (activePromises === 0) { - // We have no more values left, and all items have completed, so we've - // completed all the work. - resolve(); - } - } catch (error) { - reject(error); - } - } - - values.splice(0, concurrency).map(scheduleItem); - }); -} - -/***/ }), -/* 147 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.buildProjectGraph = buildProjectGraph; -exports.getProjects = getProjects; -exports.includeTransitiveProjects = includeTransitiveProjects; -exports.topologicallyBatchProjects = topologicallyBatchProjects; - -var _glob = _interopRequireDefault(__webpack_require__(148)); - -var _path = _interopRequireDefault(__webpack_require__(4)); - -var _util = __webpack_require__(112); - -var _errors = __webpack_require__(164); - -var _project = __webpack_require__(165); - -var _workspaces = __webpack_require__(329); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ const glob = (0, _util.promisify)(_glob.default); + /** a Map of project names to Project instances */ async function getProjects(rootPath, projectsPathsPatterns, { @@ -11403,44 +11274,33 @@ async function getProjects(rootPath, projectsPathsPatterns, { } = {}) { const projects = new Map(); const workspaceProjectsPaths = await (0, _workspaces.workspacePackagePaths)(rootPath); - for (const pattern of projectsPathsPatterns) { const pathsToProcess = await packagesFromGlobPattern({ pattern, rootPath }); - for (const filePath of pathsToProcess) { const projectConfigPath = normalize(filePath); - const projectDir = _path.default.dirname(projectConfigPath); - const project = await _project.Project.fromPath(projectDir); - if (workspaceProjectsPaths.indexOf(filePath) >= 0) { project.isWorkspaceProject = true; } - const excludeProject = exclude.includes(project.name) || include.length > 0 && !include.includes(project.name); - if (excludeProject) { continue; } - if (projects.has(project.name)) { throw new _errors.CliError(`There are multiple projects with the same name [${project.name}]`, { name: project.name, paths: [project.path, projects.get(project.name).path] }); } - projects.set(project.name, project); } } - return projects; } - function packagesFromGlobPattern({ pattern, rootPath @@ -11456,22 +11316,19 @@ function packagesFromGlobPattern({ noglobstar: true }; return glob(_path.default.join(pattern, 'package.json'), globOptions); -} // https://github.com/isaacs/node-glob/blob/master/common.js#L104 +} + +// https://github.com/isaacs/node-glob/blob/master/common.js#L104 // glob always returns "\\" as "/" in windows, so everyone // gets normalized because we can't have nice things. - - function normalize(dir) { return _path.default.normalize(dir); } - function buildProjectGraph(projects) { const projectGraph = new Map(); - for (const project of projects.values()) { const projectDeps = []; const dependencies = project.allDependencies; - for (const depName of Object.keys(dependencies)) { if (projects.has(depName)) { const dep = projects.get(depName); @@ -11480,83 +11337,69 @@ function buildProjectGraph(projects) { projectDeps.push(dep); } } - projectGraph.set(project.name, projectDeps); } - return projectGraph; } - function topologicallyBatchProjects(projectsToBatch, projectGraph, { batchByWorkspace = false } = {}) { // We're going to be chopping stuff out of this list, so copy it. const projectsLeftToBatch = new Set(projectsToBatch.keys()); const batches = []; - if (batchByWorkspace) { const workspaceRootProject = Array.from(projectsToBatch.values()).find(p => p.isWorkspaceRoot); - if (!workspaceRootProject) { throw new _errors.CliError(`There was no yarn workspace root found.`); - } // Push in the workspace root first. - + } + // Push in the workspace root first. batches.push([workspaceRootProject]); - projectsLeftToBatch.delete(workspaceRootProject.name); // In the next batch, push in all workspace projects. + projectsLeftToBatch.delete(workspaceRootProject.name); + // In the next batch, push in all workspace projects. const workspaceBatch = []; - for (const projectName of projectsLeftToBatch) { const project = projectsToBatch.get(projectName); - if (project.isWorkspaceProject) { workspaceBatch.push(project); projectsLeftToBatch.delete(projectName); } } - batches.push(workspaceBatch); } - while (projectsLeftToBatch.size > 0) { // Get all projects that have no remaining dependencies within the repo // that haven't yet been picked. const batch = []; - for (const projectName of projectsLeftToBatch) { const projectDeps = projectGraph.get(projectName); const needsDependenciesBatched = projectDeps.some(dep => projectsLeftToBatch.has(dep.name)); - if (!needsDependenciesBatched) { batch.push(projectsToBatch.get(projectName)); } - } // If we weren't able to find a project with no remaining dependencies, - // then we've encountered a cycle in the dependency graph. - + } + // If we weren't able to find a project with no remaining dependencies, + // then we've encountered a cycle in the dependency graph. const hasCycles = batch.length === 0; - if (hasCycles) { const cycleProjectNames = [...projectsLeftToBatch]; const message = 'Encountered a cycle in the dependency graph. Projects in cycle are:\n' + cycleProjectNames.join(', '); throw new _errors.CliError(message); } - batches.push(batch); batch.forEach(project => projectsLeftToBatch.delete(project.name)); } - return batches; } - function includeTransitiveProjects(subsetOfProjects, allProjects, { onlyProductionDependencies = false } = {}) { - const projectsWithDependents = new Map(); // the current list of packages we are expanding using breadth-first-search + const projectsWithDependents = new Map(); + // the current list of packages we are expanding using breadth-first-search const toProcess = [...subsetOfProjects]; - while (toProcess.length > 0) { const project = toProcess.shift(); const dependencies = onlyProductionDependencies ? project.productionDependencies : project.allDependencies; @@ -11567,7 +11410,6 @@ function includeTransitiveProjects(subsetOfProjects, allProjects, { }); projectsWithDependents.set(project.name, project); } - return projectsWithDependents; } @@ -14963,7 +14805,6 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.CliError = void 0; - /* * SPDX-License-Identifier: Apache-2.0 * @@ -14993,14 +14834,13 @@ exports.CliError = void 0; * specific language governing permissions and limitations * under the License. */ + class CliError extends Error { constructor(message, meta = {}) { super(message); this.meta = meta; } - } - exports.CliError = CliError; /***/ }), @@ -15014,107 +14854,115 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.Project = void 0; - -var _fs = _interopRequireDefault(__webpack_require__(134)); - -var _path = _interopRequireDefault(__webpack_require__(4)); - +var _fs = __webpack_require__(134); +var _path = __webpack_require__(4); var _util = __webpack_require__(112); - var _errors = __webpack_require__(164); - var _log = __webpack_require__(145); - var _package_json = __webpack_require__(166); - var _scripts = __webpack_require__(282); - -var _targeted_build = __webpack_require__(328); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - +var _targeted_build = __webpack_require__(329); +function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } +function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } +function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } /* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Any modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ /* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ class Project { static async fromPath(path) { const pkgJson = await (0, _package_json.readPackageJson)(path); return new Project(pkgJson, path); } - /** parsed package.json */ + /** parsed package.json */ constructor(packageJson, projectPath) { _defineProperty(this, "json", void 0); - + /** absolute path to the package.json file in the project */ _defineProperty(this, "packageJsonLocation", void 0); - + /** absolute path to the node_modules in the project (might not actually exist) */ _defineProperty(this, "nodeModulesLocation", void 0); - + /** absolute path to the target directory in the project (might not actually exist) */ _defineProperty(this, "targetLocation", void 0); - + /** absolute path to the directory containing the project */ _defineProperty(this, "path", void 0); - + /** the version of the project */ _defineProperty(this, "version", void 0); - + /** merged set of dependencies of the project, [name => version range] */ _defineProperty(this, "allDependencies", void 0); - + /** regular dependencies of the project, [name => version range] */ _defineProperty(this, "productionDependencies", void 0); - + /** development dependencies of the project, [name => version range] */ _defineProperty(this, "devDependencies", void 0); - + /** scripts defined in the package.json file for the project [name => body] */ _defineProperty(this, "scripts", void 0); - + /** custom definitions for the project, @osd/pm: { key: value } */ _defineProperty(this, "customDefinitions", void 0); - + /** build targets from the custom definitions, @osd/pm: { node: true, web: true } */ _defineProperty(this, "buildTargets", void 0); - _defineProperty(this, "isWorkspaceRoot", false); - _defineProperty(this, "isWorkspaceProject", false); - this.json = Object.freeze(packageJson); this.path = projectPath; - this.packageJsonLocation = _path.default.resolve(this.path, 'package.json'); - this.nodeModulesLocation = _path.default.resolve(this.path, 'node_modules'); - this.targetLocation = _path.default.resolve(this.path, 'target'); + this.packageJsonLocation = (0, _path.resolve)(this.path, 'package.json'); + this.nodeModulesLocation = (0, _path.resolve)(this.path, 'node_modules'); + this.targetLocation = (0, _path.resolve)(this.path, 'target'); this.version = this.json.version; this.productionDependencies = this.json.dependencies || {}; this.devDependencies = this.json.devDependencies || {}; - this.allDependencies = { ...this.devDependencies, + this.allDependencies = { + ...this.devDependencies, ...this.productionDependencies }; this.isWorkspaceRoot = this.json.hasOwnProperty('workspaces'); this.scripts = this.json.scripts || {}; this.customDefinitions = this.json['@osd/pm'] || {}; this.buildTargets = []; - for (const target of _targeted_build.BuildTargets) { if (this.customDefinitions[target]) this.buildTargets.push(target); } } - get name() { return this.json.name; } - ensureValidProjectDependency(project, dependentProjectIsInWorkspace) { const versionInPackageJson = this.allDependencies[project.name]; let expectedVersionInPackageJson; - if (dependentProjectIsInWorkspace) { expectedVersionInPackageJson = project.json.version; } else { - const relativePathToProject = normalizePath(_path.default.relative(this.path, project.path)); + const relativePathToProject = normalizePath((0, _path.relative)(this.path, project.path)); expectedVersionInPackageJson = `link:${relativePathToProject}`; - } // No issues! - + } + // No issues! if (versionInPackageJson === expectedVersionInPackageJson) { return; } - let problemMsg; - if ((0, _package_json.isLinkDependency)(versionInPackageJson) && dependentProjectIsInWorkspace) { problemMsg = `but should be using a workspace`; } else if ((0, _package_json.isLinkDependency)(versionInPackageJson)) { @@ -15122,79 +14970,62 @@ class Project { } else { problemMsg = `but it's not using the local package`; } - throw new _errors.CliError(`[${this.name}] depends on [${project.name}] ${problemMsg}. Update its package.json to the expected value below.`, { actual: `"${project.name}": "${versionInPackageJson}"`, expected: `"${project.name}": "${expectedVersionInPackageJson}"`, package: `${this.name} (${this.packageJsonLocation})` }); } - getBuildConfig() { return this.json.opensearchDashboards && this.json.opensearchDashboards.build || {}; } + /** * Returns the directory that should be copied into the OpenSearch Dashboards build artifact. * This config can be specified to only include the project's build artifacts * instead of everything located in the project directory. */ - - getIntermediateBuildDirectory() { - return _path.default.resolve(this.path, this.getBuildConfig().intermediateBuildDirectory || '.'); + return (0, _path.resolve)(this.path, this.getBuildConfig().intermediateBuildDirectory || '.'); } - getCleanConfig() { return this.json.opensearchDashboards && this.json.opensearchDashboards.clean || {}; } - isFlaggedAsDevOnly() { return !!(this.json.opensearchDashboards && this.json.opensearchDashboards.devOnly); } - hasScript(name) { return name in this.scripts; } - hasBuildTargets() { return this.buildTargets.length > 0; } - getExecutables() { const raw = this.json.bin; - if (!raw) { return {}; } - if (typeof raw === 'string') { return { - [this.name]: _path.default.resolve(this.path, raw) + [this.name]: (0, _path.resolve)(this.path, raw) }; } - if (typeof raw === 'object') { const binsConfig = {}; - for (const binName of Object.keys(raw)) { - binsConfig[binName] = _path.default.resolve(this.path, raw[binName]); + binsConfig[binName] = (0, _path.resolve)(this.path, raw[binName]); } - return binsConfig; } - throw new _errors.CliError(`[${this.name}] has an invalid "bin" field in its package.json, ` + `expected an object or a string`, { binConfig: (0, _util.inspect)(raw), package: `${this.name} (${this.packageJsonLocation})` }); } - async runScript(scriptName, args = []) { _log.log.info(`Running script [${scriptName}] in [${this.name}]:`); - return (0, _scripts.runScriptInPackage)(scriptName, args, this); } - runScriptStreaming(scriptName, options = {}) { return (0, _scripts.runScriptInPackageStreaming)({ script: scriptName, @@ -15203,82 +15034,89 @@ class Project { debug: options.debug }); } - buildForTargets(options = {}) { if (!this.hasBuildTargets()) { _log.log.warning(`There are no build targets defined for [${this.name}]`); - return false; } - return (0, _targeted_build.buildTargetedPackage)({ pkg: this, sourceMaps: options.sourceMaps }); } - hasDependencies() { return Object.keys(this.allDependencies).length > 0; } - async installDependencies({ extraArgs }) { _log.log.info(`[${this.name}] running yarn`); - _log.log.write(''); - await (0, _scripts.installInDir)(this.path, extraArgs); - _log.log.write(''); + await this.removeExtraneousNodeModules(); + } + /** + * Install a specific version of a dependency and update the package.json. + * When a range is not specified, ^ is used. The range is then + * placed in the package.json with intentionally no validation. + */ + async installDependencyVersion(depName, version, dev = false, range) { + _log.log.info(`[${this.name}] running yarn to install ${depName}@${version}`); + _log.log.write(''); + const rangeToUse = range || `^${version}`; + const extraArgs = [`${depName}@${version}`]; + if (dev) extraArgs.push('--dev'); + if (this.isWorkspaceProject) { + await (0, _scripts.installInDir)(this.path); + } else { + await (0, _scripts.installInDir)(this.path, extraArgs, true); + } + _log.log.info(`[${this.name}] updating manifests with ${depName}@${rangeToUse}`); + await (0, _scripts.patchFile)(this.packageJsonLocation, `"${depName}": "${version}"`, `"${depName}": "${rangeToUse}"`); + // The lock-file of workspace packages are symlinked to the root project's and editing the one in the project suffices + await (0, _scripts.patchFile)((0, _path.resolve)(this.path, 'yarn.lock'), `${depName}@${version}`, `${depName}@${rangeToUse}`); + _log.log.write(''); await this.removeExtraneousNodeModules(); } + /** * Yarn workspaces symlinks workspace projects to the root node_modules, even * when there is no depenency on the project. This results in unnecicary, and * often duplicated code in the build archives. */ - - async removeExtraneousNodeModules() { // this is only relevant for the root workspace if (!this.isWorkspaceRoot) { return; } - const workspacesInfo = await (0, _scripts.yarnWorkspacesInfo)(this.path); - const unusedWorkspaces = new Set(Object.keys(workspacesInfo)); // check for any cross-project dependency + const unusedWorkspaces = new Set(Object.keys(workspacesInfo)); + // check for any cross-project dependency for (const name of Object.keys(workspacesInfo)) { const workspace = workspacesInfo[name]; workspace.workspaceDependencies.forEach(w => unusedWorkspaces.delete(w)); } - unusedWorkspaces.forEach(name => { const { dependencies, devDependencies } = this.json; - - const nodeModulesPath = _path.default.resolve(this.nodeModulesLocation, name); - + const nodeModulesPath = (0, _path.resolve)(this.nodeModulesLocation, name); const isDependency = dependencies && dependencies.hasOwnProperty(name); const isDevDependency = devDependencies && devDependencies.hasOwnProperty(name); - - if (!isDependency && !isDevDependency && _fs.default.existsSync(nodeModulesPath)) { + if (!isDependency && !isDevDependency && (0, _fs.existsSync)(nodeModulesPath)) { _log.log.debug(`No dependency on ${name}, removing link in node_modules`); - - _fs.default.unlinkSync(nodeModulesPath); + (0, _fs.unlinkSync)(nodeModulesPath); } }); } +} -} // We normalize all path separators to `/` in generated files - - +// We normalize all path separators to `/` in generated files exports.Project = Project; - function normalizePath(path) { return path.replace(/[\\\/]+/g, '/'); } @@ -15296,13 +15134,9 @@ Object.defineProperty(exports, "__esModule", { exports.isLinkDependency = void 0; exports.readPackageJson = readPackageJson; exports.writePackageJson = writePackageJson; - var _readPkg = _interopRequireDefault(__webpack_require__(167)); - var _writePkg = _interopRequireDefault(__webpack_require__(270)); - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - /* * SPDX-License-Identifier: Apache-2.0 * @@ -15332,19 +15166,17 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de * specific language governing permissions and limitations * under the License. */ + function readPackageJson(cwd) { return (0, _readPkg.default)({ cwd, normalize: false }); } - function writePackageJson(path, json) { return (0, _writePkg.default)(path, json); } - const isLinkDependency = depVersion => depVersion.startsWith('link:'); - exports.isLinkDependency = isLinkDependency; /***/ }), @@ -25446,12 +25278,13 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.installInDir = installInDir; +exports.patchFile = patchFile; exports.runScriptInPackage = runScriptInPackage; exports.runScriptInPackageStreaming = runScriptInPackageStreaming; exports.yarnWorkspacesInfo = yarnWorkspacesInfo; - -var _child_process = __webpack_require__(283); - +var _fs = __webpack_require__(134); +var _readline = __webpack_require__(283); +var _child_process = __webpack_require__(284); /* * SPDX-License-Identifier: Apache-2.0 * @@ -25481,35 +25314,62 @@ var _child_process = __webpack_require__(283); * specific language governing permissions and limitations * under the License. */ -const YARN_EXEC = process.env.npm_execpath || 'yarn'; +const YARN_EXEC = process.env.npm_execpath || 'yarn'; /** * Install all dependencies in the given directory */ -async function installInDir(directory, extraArgs = []) { - const options = ['install', '--non-interactive', ...extraArgs]; // We pass the mutex flag to ensure only one instance of yarn runs at any - // given time (e.g. to avoid conflicts). +async function installInDir(directory, extraArgs = [], useAdd = false) { + const options = [useAdd ? 'add' : 'install', '--non-interactive', ...extraArgs]; + // We pass the mutex flag to ensure only one instance of yarn runs at any + // given time (e.g. to avoid conflicts). await (0, _child_process.spawn)(YARN_EXEC, options, { cwd: directory }); } + /** - * Run script in the given directory + * Patch a file by replacing a given string */ +function patchFile(filePath, searchValue, replacement) { + return new Promise(async (resolve, reject) => { + const patchWriter = (0, _fs.createWriteStream)(`${filePath}.patched`, { + flags: 'w' + }); + const fileReader = (0, _readline.createInterface)({ + input: (0, _fs.createReadStream)(filePath), + crlfDelay: Infinity + }); + for await (const line of fileReader) { + if (line.includes(searchValue)) { + patchWriter.write(line.replace(searchValue, replacement) + '\n', 'utf8'); + } else { + patchWriter.write(line + '\n', 'utf8'); + } + } + patchWriter.on('finish', () => resolve()); + patchWriter.on('error', reject); + fileReader.close(); + patchWriter.end(); + (0, _fs.unlinkSync)(filePath); + (0, _fs.renameSync)(`${filePath}.patched`, filePath); + }); +} - +/** + * Run script in the given directory + */ async function runScriptInPackage(script, args, pkg) { const execOpts = { cwd: pkg.path }; await (0, _child_process.spawn)(YARN_EXEC, ['run', script, ...args], execOpts); } + /** * Run script in the given directory */ - - function runScriptInPackageStreaming({ script, args, @@ -25524,7 +25384,6 @@ function runScriptInPackageStreaming({ debug }); } - async function yarnWorkspacesInfo(directory) { const { stdout @@ -25532,7 +25391,6 @@ async function yarnWorkspacesInfo(directory) { cwd: directory, stdio: 'pipe' }); - try { return JSON.parse(JSON.parse(stdout).data); } catch (error) { @@ -25542,6 +25400,12 @@ async function yarnWorkspacesInfo(directory) { /***/ }), /* 283 */ +/***/ (function(module, exports) { + +module.exports = require("readline"); + +/***/ }), +/* 284 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -25552,19 +25416,12 @@ Object.defineProperty(exports, "__esModule", { }); exports.spawn = spawn; exports.spawnStreaming = spawnStreaming; - var _stream = __webpack_require__(138); - var _chalk = _interopRequireDefault(__webpack_require__(113)); - -var _execa = _interopRequireDefault(__webpack_require__(284)); - -var _strongLogTransformer = _interopRequireDefault(__webpack_require__(320)); - +var _execa = _interopRequireDefault(__webpack_require__(285)); +var _strongLogTransformer = _interopRequireDefault(__webpack_require__(321)); var _log = __webpack_require__(145); - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - /* * SPDX-License-Identifier: Apache-2.0 * @@ -25594,14 +25451,13 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de * specific language governing permissions and limitations * under the License. */ -const colorWheel = [_chalk.default.cyan, _chalk.default.magenta, _chalk.default.blue, _chalk.default.yellow, _chalk.default.green]; +const colorWheel = [_chalk.default.cyan, _chalk.default.magenta, _chalk.default.blue, _chalk.default.yellow, _chalk.default.green]; const getColor = () => { const color = colorWheel.shift(); colorWheel.push(color); return color; }; - function spawn(command, args, opts) { return (0, _execa.default)(command, args, { stdio: 'inherit', @@ -25609,24 +25465,19 @@ function spawn(command, args, opts) { ...opts }); } - function streamToLog(debug = true) { return new _stream.Writable({ objectMode: true, - write(line, _, cb) { if (line.endsWith('\n')) { _log.log[debug ? 'debug' : 'write'](line.slice(0, -1)); } else { _log.log[debug ? 'debug' : 'write'](line); } - cb(); } - }); } - function spawnStreaming(command, args, opts, { prefix, debug @@ -25650,23 +25501,23 @@ function spawnStreaming(command, args, opts, { } /***/ }), -/* 284 */ +/* 285 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const childProcess = __webpack_require__(285); -const crossSpawn = __webpack_require__(286); -const stripFinalNewline = __webpack_require__(299); -const npmRunPath = __webpack_require__(300); -const onetime = __webpack_require__(301); -const makeError = __webpack_require__(303); -const normalizeStdio = __webpack_require__(308); -const {spawnedKill, spawnedCancel, setupTimeout, setExitHandler} = __webpack_require__(309); -const {handleInput, getSpawnedResult, makeAllStream, validateInputSync} = __webpack_require__(310); -const {mergePromise, getSpawnedPromise} = __webpack_require__(318); -const {joinCommand, parseCommand} = __webpack_require__(319); +const childProcess = __webpack_require__(286); +const crossSpawn = __webpack_require__(287); +const stripFinalNewline = __webpack_require__(300); +const npmRunPath = __webpack_require__(301); +const onetime = __webpack_require__(302); +const makeError = __webpack_require__(304); +const normalizeStdio = __webpack_require__(309); +const {spawnedKill, spawnedCancel, setupTimeout, setExitHandler} = __webpack_require__(310); +const {handleInput, getSpawnedResult, makeAllStream, validateInputSync} = __webpack_require__(311); +const {mergePromise, getSpawnedPromise} = __webpack_require__(319); +const {joinCommand, parseCommand} = __webpack_require__(320); const DEFAULT_MAX_BUFFER = 1000 * 1000 * 100; @@ -25917,21 +25768,21 @@ module.exports.node = (scriptPath, args, options = {}) => { /***/ }), -/* 285 */ +/* 286 */ /***/ (function(module, exports) { module.exports = require("child_process"); /***/ }), -/* 286 */ +/* 287 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const cp = __webpack_require__(285); -const parse = __webpack_require__(287); -const enoent = __webpack_require__(298); +const cp = __webpack_require__(286); +const parse = __webpack_require__(288); +const enoent = __webpack_require__(299); function spawn(command, args, options) { // Parse the arguments @@ -25969,16 +25820,16 @@ module.exports._enoent = enoent; /***/ }), -/* 287 */ +/* 288 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const resolveCommand = __webpack_require__(288); -const escape = __webpack_require__(294); -const readShebang = __webpack_require__(295); +const resolveCommand = __webpack_require__(289); +const escape = __webpack_require__(295); +const readShebang = __webpack_require__(296); const isWin = process.platform === 'win32'; const isExecutableRegExp = /\.(?:com|exe)$/i; @@ -26067,15 +25918,15 @@ module.exports = parse; /***/ }), -/* 288 */ +/* 289 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const which = __webpack_require__(289); -const getPathKey = __webpack_require__(293); +const which = __webpack_require__(290); +const getPathKey = __webpack_require__(294); function resolveCommandAttempt(parsed, withoutPathExt) { const env = parsed.options.env || process.env; @@ -26126,7 +25977,7 @@ module.exports = resolveCommand; /***/ }), -/* 289 */ +/* 290 */ /***/ (function(module, exports, __webpack_require__) { const isWindows = process.platform === 'win32' || @@ -26135,7 +25986,7 @@ const isWindows = process.platform === 'win32' || const path = __webpack_require__(4) const COLON = isWindows ? ';' : ':' -const isexe = __webpack_require__(290) +const isexe = __webpack_require__(291) const getNotFoundError = (cmd) => Object.assign(new Error(`not found: ${cmd}`), { code: 'ENOENT' }) @@ -26257,15 +26108,15 @@ which.sync = whichSync /***/ }), -/* 290 */ +/* 291 */ /***/ (function(module, exports, __webpack_require__) { var fs = __webpack_require__(134) var core if (process.platform === 'win32' || global.TESTING_WINDOWS) { - core = __webpack_require__(291) -} else { core = __webpack_require__(292) +} else { + core = __webpack_require__(293) } module.exports = isexe @@ -26320,7 +26171,7 @@ function sync (path, options) { /***/ }), -/* 291 */ +/* 292 */ /***/ (function(module, exports, __webpack_require__) { module.exports = isexe @@ -26368,7 +26219,7 @@ function sync (path, options) { /***/ }), -/* 292 */ +/* 293 */ /***/ (function(module, exports, __webpack_require__) { module.exports = isexe @@ -26415,7 +26266,7 @@ function checkMode (stat, options) { /***/ }), -/* 293 */ +/* 294 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -26438,7 +26289,7 @@ module.exports.default = pathKey; /***/ }), -/* 294 */ +/* 295 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -26490,14 +26341,14 @@ module.exports.argument = escapeArgument; /***/ }), -/* 295 */ +/* 296 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(134); -const shebangCommand = __webpack_require__(296); +const shebangCommand = __webpack_require__(297); function readShebang(command) { // Read the first 150 bytes from the file @@ -26520,12 +26371,12 @@ module.exports = readShebang; /***/ }), -/* 296 */ +/* 297 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const shebangRegex = __webpack_require__(297); +const shebangRegex = __webpack_require__(298); module.exports = (string = '') => { const match = string.match(shebangRegex); @@ -26546,7 +26397,7 @@ module.exports = (string = '') => { /***/ }), -/* 297 */ +/* 298 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -26555,7 +26406,7 @@ module.exports = /^#!(.*)/; /***/ }), -/* 298 */ +/* 299 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -26621,7 +26472,7 @@ module.exports = { /***/ }), -/* 299 */ +/* 300 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -26644,13 +26495,13 @@ module.exports = input => { /***/ }), -/* 300 */ +/* 301 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const pathKey = __webpack_require__(293); +const pathKey = __webpack_require__(294); const npmRunPath = options => { options = { @@ -26698,12 +26549,12 @@ module.exports.env = options => { /***/ }), -/* 301 */ +/* 302 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const mimicFn = __webpack_require__(302); +const mimicFn = __webpack_require__(303); const calledFunctions = new WeakMap(); @@ -26749,7 +26600,7 @@ module.exports.callCount = function_ => { /***/ }), -/* 302 */ +/* 303 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -26769,12 +26620,12 @@ module.exports.default = mimicFn; /***/ }), -/* 303 */ +/* 304 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const {signalsByName} = __webpack_require__(304); +const {signalsByName} = __webpack_require__(305); const getErrorPrefix = ({timedOut, timeout, errorCode, signal, signalDescription, exitCode, isCanceled}) => { if (timedOut) { @@ -26862,14 +26713,14 @@ module.exports = makeError; /***/ }), -/* 304 */ +/* 305 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports,"__esModule",{value:true});exports.signalsByNumber=exports.signalsByName=void 0;var _os=__webpack_require__(121); -var _signals=__webpack_require__(305); -var _realtime=__webpack_require__(307); +var _signals=__webpack_require__(306); +var _realtime=__webpack_require__(308); @@ -26939,14 +26790,14 @@ const signalsByNumber=getSignalsByNumber();exports.signalsByNumber=signalsByNumb //# sourceMappingURL=main.js.map /***/ }), -/* 305 */ +/* 306 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports,"__esModule",{value:true});exports.getSignals=void 0;var _os=__webpack_require__(121); -var _core=__webpack_require__(306); -var _realtime=__webpack_require__(307); +var _core=__webpack_require__(307); +var _realtime=__webpack_require__(308); @@ -26980,7 +26831,7 @@ return{name,number,description,supported,action,forced,standard}; //# sourceMappingURL=signals.js.map /***/ }), -/* 306 */ +/* 307 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -27259,7 +27110,7 @@ standard:"other"}];exports.SIGNALS=SIGNALS; //# sourceMappingURL=core.js.map /***/ }), -/* 307 */ +/* 308 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -27284,7 +27135,7 @@ const SIGRTMAX=64;exports.SIGRTMAX=SIGRTMAX; //# sourceMappingURL=realtime.js.map /***/ }), -/* 308 */ +/* 309 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -27343,7 +27194,7 @@ module.exports.node = opts => { /***/ }), -/* 309 */ +/* 310 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -27462,14 +27313,14 @@ module.exports = { /***/ }), -/* 310 */ +/* 311 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const isStream = __webpack_require__(311); -const getStream = __webpack_require__(312); -const mergeStream = __webpack_require__(317); +const isStream = __webpack_require__(312); +const getStream = __webpack_require__(313); +const mergeStream = __webpack_require__(318); // `input` option const handleInput = (spawned, input) => { @@ -27566,7 +27417,7 @@ module.exports = { /***/ }), -/* 311 */ +/* 312 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -27601,14 +27452,14 @@ module.exports = isStream; /***/ }), -/* 312 */ +/* 313 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const {constants: BufferConstants} = __webpack_require__(313); -const pump = __webpack_require__(314); -const bufferStream = __webpack_require__(316); +const {constants: BufferConstants} = __webpack_require__(314); +const pump = __webpack_require__(315); +const bufferStream = __webpack_require__(317); class MaxBufferError extends Error { constructor() { @@ -27668,17 +27519,17 @@ module.exports.MaxBufferError = MaxBufferError; /***/ }), -/* 313 */ +/* 314 */ /***/ (function(module, exports) { module.exports = require("buffer"); /***/ }), -/* 314 */ +/* 315 */ /***/ (function(module, exports, __webpack_require__) { var once = __webpack_require__(163) -var eos = __webpack_require__(315) +var eos = __webpack_require__(316) var fs = __webpack_require__(134) // we only need fs to get the ReadStream and WriteStream prototypes var noop = function () {} @@ -27762,7 +27613,7 @@ module.exports = pump /***/ }), -/* 315 */ +/* 316 */ /***/ (function(module, exports, __webpack_require__) { var once = __webpack_require__(163); @@ -27862,7 +27713,7 @@ module.exports = eos; /***/ }), -/* 316 */ +/* 317 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -27921,7 +27772,7 @@ module.exports = options => { /***/ }), -/* 317 */ +/* 318 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -27969,7 +27820,7 @@ module.exports = function (/*streams...*/) { /***/ }), -/* 318 */ +/* 319 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -28022,7 +27873,7 @@ module.exports = { /***/ }), -/* 319 */ +/* 320 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -28061,7 +27912,7 @@ module.exports = { /***/ }), -/* 320 */ +/* 321 */ /***/ (function(module, exports, __webpack_require__) { // Copyright IBM Corp. 2014,2018. All Rights Reserved. @@ -28069,12 +27920,12 @@ module.exports = { // This file is licensed under the Apache License 2.0. // License text available at https://opensource.org/licenses/Apache-2.0 -module.exports = __webpack_require__(321); -module.exports.cli = __webpack_require__(325); +module.exports = __webpack_require__(322); +module.exports.cli = __webpack_require__(326); /***/ }), -/* 321 */ +/* 322 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -28089,9 +27940,9 @@ var stream = __webpack_require__(138); var util = __webpack_require__(112); var fs = __webpack_require__(134); -var through = __webpack_require__(322); -var duplexer = __webpack_require__(323); -var StringDecoder = __webpack_require__(324).StringDecoder; +var through = __webpack_require__(323); +var duplexer = __webpack_require__(324); +var StringDecoder = __webpack_require__(325).StringDecoder; module.exports = Logger; @@ -28280,7 +28131,7 @@ function lineMerger(host) { /***/ }), -/* 322 */ +/* 323 */ /***/ (function(module, exports, __webpack_require__) { var Stream = __webpack_require__(138) @@ -28394,7 +28245,7 @@ function through (write, end, opts) { /***/ }), -/* 323 */ +/* 324 */ /***/ (function(module, exports, __webpack_require__) { var Stream = __webpack_require__(138) @@ -28487,13 +28338,13 @@ function duplex(writer, reader) { /***/ }), -/* 324 */ +/* 325 */ /***/ (function(module, exports) { module.exports = require("string_decoder"); /***/ }), -/* 325 */ +/* 326 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -28504,11 +28355,11 @@ module.exports = require("string_decoder"); -var minimist = __webpack_require__(326); +var minimist = __webpack_require__(327); var path = __webpack_require__(4); -var Logger = __webpack_require__(321); -var pkg = __webpack_require__(327); +var Logger = __webpack_require__(322); +var pkg = __webpack_require__(328); module.exports = cli; @@ -28562,7 +28413,7 @@ function usage($0, p) { /***/ }), -/* 326 */ +/* 327 */ /***/ (function(module, exports) { module.exports = function (args, opts) { @@ -28817,13 +28668,13 @@ function isConstructorOrProto (obj, key) { /***/ }), -/* 327 */ +/* 328 */ /***/ (function(module) { module.exports = JSON.parse("{\"name\":\"strong-log-transformer\",\"version\":\"2.1.0\",\"description\":\"Stream transformer that prefixes lines with timestamps and other things.\",\"author\":\"Ryan Graham \",\"license\":\"Apache-2.0\",\"repository\":{\"type\":\"git\",\"url\":\"git://github.com/strongloop/strong-log-transformer\"},\"keywords\":[\"logging\",\"streams\"],\"bugs\":{\"url\":\"https://github.com/strongloop/strong-log-transformer/issues\"},\"homepage\":\"https://github.com/strongloop/strong-log-transformer\",\"directories\":{\"test\":\"test\"},\"bin\":{\"sl-log-transformer\":\"bin/sl-log-transformer.js\"},\"main\":\"index.js\",\"scripts\":{\"test\":\"tap --100 test/test-*\"},\"dependencies\":{\"duplexer\":\"^0.1.1\",\"minimist\":\"^1.2.0\",\"through\":\"^2.3.4\"},\"devDependencies\":{\"tap\":\"^12.0.1\"},\"engines\":{\"node\":\">=4\"}}"); /***/ }), -/* 328 */ +/* 329 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -28834,51 +28685,41 @@ Object.defineProperty(exports, "__esModule", { }); exports.BuildTargets = void 0; exports.buildTargetedPackage = buildTargetedPackage; - var _promises = __webpack_require__(143); - var _path = __webpack_require__(4); - -var _child_process = __webpack_require__(283); - +var _child_process = __webpack_require__(284); var _log = __webpack_require__(145); - /* * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 */ + const BuildTargetPresets = { web: '@osd/babel-preset/webpack_preset', node: '@osd/babel-preset/node_preset' }; -const BuildTargets = Object.keys(BuildTargetPresets); +const BuildTargets = exports.BuildTargets = Object.keys(BuildTargetPresets); + /** * Run script in the given directory */ - -exports.BuildTargets = BuildTargets; - async function buildTargetedPackage({ pkg, sourceMaps }) { _log.log.debug(`[${pkg.name}] deleting old output`); - await (0, _promises.rm)(pkg.targetLocation, { force: true, recursive: true }); - _log.log.debug(`[${pkg.name}] generating type definitions`); - await (0, _child_process.spawn)('tsc', [...(sourceMaps ? ['--declarationMap', 'true'] : [])], { cwd: pkg.path - }); // Generate [A], [A and B], or [A, B, and C] labels + }); + // Generate [A], [A and B], or [A, B, and C] labels const targetsDisplayLabel = pkg.buildTargets.join(', ').replace(/, ([^,]+)$/, pkg.buildTargets.length > 2 ? ', and $1' : ' and $1'); - _log.log.debug(`[${pkg.name}] transpiling for ${targetsDisplayLabel}`); - await Promise.all([...pkg.buildTargets.map(target => (0, _child_process.spawn)('babel', ['src', '--no-babelrc', '--presets', BuildTargetPresets[target], '--out-dir', (0, _path.resolve)(pkg.targetLocation, target), '--extensions', '.ts,.js,.tsx', '--ignore', '**/*.test.ts,**/*.test.tsx', '--quiet', ...(sourceMaps ? ['--source-maps', 'inline'] : [])], { env: { BABEL_ENV: target @@ -28888,7 +28729,7 @@ async function buildTargetedPackage({ } /***/ }), -/* 329 */ +/* 330 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -28899,23 +28740,14 @@ Object.defineProperty(exports, "__esModule", { }); exports.copyWorkspacePackages = copyWorkspacePackages; exports.workspacePackagePaths = workspacePackagePaths; - var _glob = _interopRequireDefault(__webpack_require__(148)); - var _path = _interopRequireDefault(__webpack_require__(4)); - var _util = __webpack_require__(112); - -var _config = __webpack_require__(330); - +var _config = __webpack_require__(331); var _fs = __webpack_require__(131); - var _package_json = __webpack_require__(166); - var _projects = __webpack_require__(147); - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - /* * SPDX-License-Identifier: Apache-2.0 * @@ -28945,57 +28777,49 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de * specific language governing permissions and limitations * under the License. */ -const glob = (0, _util.promisify)(_glob.default); +const glob = (0, _util.promisify)(_glob.default); async function workspacePackagePaths(rootPath) { const rootPkgJson = await (0, _package_json.readPackageJson)(rootPath); - if (!rootPkgJson.workspaces) { return []; } - const workspacesPathsPatterns = rootPkgJson.workspaces.packages; let workspaceProjectsPaths = []; - for (const pattern of workspacesPathsPatterns) { workspaceProjectsPaths = workspaceProjectsPaths.concat(await packagesFromGlobPattern({ pattern, rootPath })); - } // Filter out exclude glob patterns - + } + // Filter out exclude glob patterns for (const pattern of workspacesPathsPatterns) { if (pattern.startsWith('!')) { const pathToRemove = _path.default.join(rootPath, pattern.slice(1), 'package.json'); - workspaceProjectsPaths = workspaceProjectsPaths.filter(p => p !== pathToRemove); } } - return workspaceProjectsPaths; } - async function copyWorkspacePackages(rootPath) { const projectPaths = (0, _config.getProjectPaths)({ rootPath }); const projects = await (0, _projects.getProjects)(rootPath, projectPaths); - for (const project of projects.values()) { const dest = _path.default.resolve(rootPath, 'node_modules', project.name); - if ((await (0, _fs.isSymlink)(dest)) === false) { continue; - } // Remove the symlink - + } - await (0, _fs.unlink)(dest); // Copy in the package + // Remove the symlink + await (0, _fs.unlink)(dest); + // Copy in the package await (0, _fs.copyDirectory)(project.path, dest); } } - function packagesFromGlobPattern({ pattern, rootPath @@ -29014,7 +28838,7 @@ function packagesFromGlobPattern({ } /***/ }), -/* 330 */ +/* 331 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -29024,9 +28848,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.getProjectPaths = getProjectPaths; - var _path = __webpack_require__(4); - /* * SPDX-License-Identifier: Apache-2.0 * @@ -29065,7 +28887,9 @@ function getProjectPaths({ ossOnly, skipOpenSearchDashboardsPlugins }) { - const projectPaths = [rootPath, (0, _path.resolve)(rootPath, 'packages/*')]; // This is needed in order to install the dependencies for the declared + const projectPaths = [rootPath, (0, _path.resolve)(rootPath, 'packages/*')]; + + // This is needed in order to install the dependencies for the declared // plugin functional used in the selenium functional tests. // As we are now using the webpack dll for the client vendors dependencies // when we run the plugin functional tests against the distributable @@ -29074,11 +28898,9 @@ function getProjectPaths({ // into the webpack dll reference plugin. // In anyway, have a plugin declaring their own dependencies is the // correct and the expect behavior. - projectPaths.push((0, _path.resolve)(rootPath, 'test/plugin_functional/plugins/*')); projectPaths.push((0, _path.resolve)(rootPath, 'test/interpreter_functional/plugins/*')); projectPaths.push((0, _path.resolve)(rootPath, 'examples/*')); - if (!skipOpenSearchDashboardsPlugins) { projectPaths.push((0, _path.resolve)(rootPath, '../opensearch-dashboards-extra/*')); projectPaths.push((0, _path.resolve)(rootPath, '../opensearch-dashboards-extra/*/packages/*')); @@ -29087,12 +28909,11 @@ function getProjectPaths({ projectPaths.push((0, _path.resolve)(rootPath, 'plugins/*/packages/*')); projectPaths.push((0, _path.resolve)(rootPath, 'plugins/*/plugins/*')); } - return projectPaths; } /***/ }), -/* 331 */ +/* 332 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -29102,17 +28923,11 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.getAllChecksums = getAllChecksums; - var _promises = __webpack_require__(143); - -var _crypto = _interopRequireDefault(__webpack_require__(332)); - -var _execa = _interopRequireDefault(__webpack_require__(284)); - -var _yarn_lock = __webpack_require__(333); - +var _crypto = _interopRequireDefault(__webpack_require__(333)); +var _execa = _interopRequireDefault(__webpack_require__(285)); +var _yarn_lock = __webpack_require__(334); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - /* * SPDX-License-Identifier: Apache-2.0 * @@ -29142,10 +28957,12 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de * specific language governing permissions and limitations * under the License. */ -const projectBySpecificitySorter = (a, b) => b.path.length - a.path.length; -/** Get the changed files for a set of projects */ +/** map of [repo relative path to changed file, type of change] */ + +const projectBySpecificitySorter = (a, b) => b.path.length - a.path.length; +/** Get the changed files for a set of projects */ async function getChangesForProjects(projects, osd, log) { log.verbose('getting changed files'); const { @@ -29155,12 +28972,10 @@ async function getChangesForProjects(projects, osd, log) { }); const output = stdout.trim(); const unassignedChanges = new Map(); - if (output) { for (const line of output.split('\n')) { const [tag, ...pathParts] = line.trim().split(' '); const path = pathParts.join(' '); - switch (tag) { case 'M': case 'C': @@ -29170,17 +28985,13 @@ async function getChangesForProjects(projects, osd, log) { if (unassignedChanges.get(path) !== 'deleted') { unassignedChanges.set(path, 'modified'); } - break; - case 'R': unassignedChanges.set(path, 'deleted'); break; - case '?': unassignedChanges.set(path, 'untracked'); break; - case 'H': case 'S': case 'K': @@ -29191,44 +29002,35 @@ async function getChangesForProjects(projects, osd, log) { } } } - const sortedRelevantProjects = Array.from(projects.values()).sort(projectBySpecificitySorter); const changesByProject = new Map(); - for (const project of sortedRelevantProjects) { if (osd.isOutsideRepo(project)) { changesByProject.set(project, undefined); continue; } - const ownChanges = new Map(); const prefix = osd.getRelative(project.path); - for (const [path, type] of unassignedChanges) { if (path.startsWith(prefix)) { ownChanges.set(path, type); unassignedChanges.delete(path); } } - log.verbose(`[${project.name}] found ${ownChanges.size} changes`); changesByProject.set(project, ownChanges); } - if (unassignedChanges.size) { throw new Error(`unable to assign all change paths to a project: ${JSON.stringify(Array.from(unassignedChanges.entries()))}`); } - return changesByProject; } -/** Get the latest commit sha for a project */ - +/** Get the latest commit sha for a project */ async function getLatestSha(project, osd) { if (osd.isOutsideRepo(project)) { return; } - const { stdout } = await (0, _execa.default)('git', ['log', '-n', '1', '--pretty=format:%H', '--', project.path], { @@ -29236,28 +29038,23 @@ async function getLatestSha(project, osd) { }); return stdout.trim() || undefined; } + /** * Get the checksum for a specific project in the workspace */ - - async function getChecksum(project, changes, yarnLock, osd, log) { const sha = await getLatestSha(project, osd); - if (sha) { log.verbose(`[${project.name}] local sha:`, sha); } - if (!changes || Array.from(changes.values()).includes('invalid')) { log.warning(`[${project.name}] unable to determine local changes, caching disabled`); return; } - const changesSummary = await Promise.all(Array.from(changes).sort((a, b) => a[0].localeCompare(b[0])).map(async ([path, type]) => { if (type === 'deleted') { return `${path}:deleted`; } - const stats = await (0, _promises.stat)(osd.getAbsolute(path)); log.verbose(`[${project.name}] modified time ${stats.mtimeMs} for ${path}`); return `${path}:${stats.mtimeMs}`; @@ -29270,11 +29067,9 @@ async function getChecksum(project, changes, yarnLock, osd, log) { includeDependentProject: false, productionDepsOnly: false }); - if (!depMap) { return; } - const deps = Array.from(depMap.values()).map(({ name, version @@ -29285,29 +29080,25 @@ async function getChecksum(project, changes, yarnLock, osd, log) { changes: changesSummary, deps }, null, 2); - if (process.env.BOOTSTRAP_CACHE_DEBUG_CHECKSUM) { return checksum; } - const hash = _crypto.default.createHash('sha1'); - hash.update(checksum); return hash.digest('hex'); } + /** * Calculate checksums for all projects in the workspace based on * - last git commit to project directory * - un-committed changes * - resolved dependencies from yarn.lock referenced by project package.json */ - - async function getAllChecksums(osd, log, yarnLock) { const projects = osd.getAllProjects(); const changesByProject = await getChangesForProjects(projects, osd, log); - /** map of [project.name, cacheKey] */ + /** map of [project.name, cacheKey] */ const cacheKeys = new Map(); await Promise.all(Array.from(projects.values()).map(async project => { cacheKeys.set(project.name, await getChecksum(project, changesByProject.get(project), yarnLock, osd, log)); @@ -29316,13 +29107,13 @@ async function getAllChecksums(osd, log, yarnLock) { } /***/ }), -/* 332 */ +/* 333 */ /***/ (function(module, exports) { module.exports = require("crypto"); /***/ }), -/* 333 */ +/* 334 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -29333,15 +29124,10 @@ Object.defineProperty(exports, "__esModule", { }); exports.readYarnLock = readYarnLock; exports.resolveDepsForProject = resolveDepsForProject; - -var _lockfile = __webpack_require__(334); - -var _crossPlatform = __webpack_require__(335); - +var _lockfile = __webpack_require__(335); +var _crossPlatform = __webpack_require__(336); var _path = __webpack_require__(4); - var _fs = __webpack_require__(131); - /* * SPDX-License-Identifier: Apache-2.0 * @@ -29371,58 +29157,53 @@ var _fs = __webpack_require__(131); * specific language governing permissions and limitations * under the License. */ + // @ts-expect-error published types are worthless + async function readYarnLock(osd) { try { const contents = await (0, _fs.readFile)(osd.getAbsolute('yarn.lock'), 'utf8'); const yarnLock = (0, _lockfile.parse)(contents); - if (yarnLock.type === 'success') { return fixFileLinks(yarnLock.object, osd.getAbsolute()); } - throw new Error('unable to read yarn.lock file, please run `yarn osd bootstrap`'); } catch (error) { if (error.code !== 'ENOENT') { throw error; } } - return {}; } + /** * Converts relative `file:` paths to absolute paths * Yarn parsing method converts all file URIs to relative paths and this * breaks the single-version requirement as dependencies to the same path * would differ in their URIs across OSD and packages. */ - - function fixFileLinks(yarnLock, projectRoot) { const fileLinkDelimiter = '@file:'; const linkedKeys = Object.keys(yarnLock).filter(key => key.includes(fileLinkDelimiter)); if (linkedKeys.length === 0) return yarnLock; - const updatedYarnLock = { ...yarnLock + const updatedYarnLock = { + ...yarnLock }; - for (const key of linkedKeys) { const [keyName, keyPath, ...rest] = key.split(fileLinkDelimiter); - if (!(0, _path.isAbsolute)(keyPath)) { const updatedKeyName = [keyName, (0, _crossPlatform.standardize)((0, _path.resolve)(projectRoot, keyPath)), ...rest].join(fileLinkDelimiter); updatedYarnLock[updatedKeyName] = updatedYarnLock[key]; } } - return updatedYarnLock; } + /** * Get a list of the absolute dependencies of this project, as resolved * in the yarn.lock file, does not include other projects in the workspace * or their dependencies */ - - function resolveDepsForProject({ project: rootProject, yarnLock, @@ -29436,59 +29217,47 @@ function resolveDepsForProject({ const seenProjects = new Set(); const projectQueue = [rootProject]; const depQueue = []; - while (projectQueue.length) { const project = projectQueue.shift(); - if (seenProjects.has(project)) { continue; } - seenProjects.add(project); const projectDeps = Object.entries(productionDepsOnly ? project.productionDependencies : project.allDependencies); - for (const [name, versionRange] of projectDeps) { depQueue.push([name, versionRange]); } - while (depQueue.length) { const [name, versionRange] = depQueue.shift(); const req = `${name}@${versionRange}`; - if (resolved.has(req)) { continue; } - if (includeDependentProject && osd.hasProject(name)) { projectQueue.push(osd.getProject(name)); } - if (!osd.hasProject(name)) { const pkg = yarnLock[req]; - if (!pkg) { log.warning('yarn.lock file is out of date, please run `yarn osd bootstrap` to re-enable caching'); return; } - resolved.set(req, { name, version: pkg.version }); const allDepsEntries = [...Object.entries(pkg.dependencies || {}), ...Object.entries(pkg.optionalDependencies || {})]; - for (const [childName, childVersionRange] of allDepsEntries) { depQueue.push([childName, childVersionRange]); } } } } - return resolved; } /***/ }), -/* 334 */ +/* 335 */ /***/ (function(module, exports, __webpack_require__) { module.exports = @@ -31047,7 +30816,7 @@ module.exports = invariant; /* 9 */ /***/ (function(module, exports) { -module.exports = __webpack_require__(332); +module.exports = __webpack_require__(333); /***/ }), /* 10 */, @@ -33371,7 +33140,7 @@ function onceStrict (fn) { /* 63 */ /***/ (function(module, exports) { -module.exports = __webpack_require__(313); +module.exports = __webpack_require__(314); /***/ }), /* 64 */, @@ -39766,50 +39535,42 @@ module.exports = process && support(supportLevel); /******/ ]); /***/ }), -/* 335 */ +/* 336 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; + /* * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 */ - Object.defineProperty(exports, "__esModule", { value: true }); - const tslib_1 = __webpack_require__(7); - -tslib_1.__exportStar(__webpack_require__(336), exports); - tslib_1.__exportStar(__webpack_require__(337), exports); - tslib_1.__exportStar(__webpack_require__(338), exports); +tslib_1.__exportStar(__webpack_require__(339), exports); /***/ }), -/* 336 */ +/* 337 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; + /* * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 */ - Object.defineProperty(exports, "__esModule", { value: true }); exports.realshortpathSync = exports.realShortPathSync = exports.realpathSync = exports.realPathSync = exports.resolveToShortNameSync = exports.resolveToShortPathSync = exports.resolveToFullNameSync = exports.resolveToFullPathSync = exports.shortNameSupportedSync = exports.shortNamesSupportedSync = exports.standardize = exports.NAMESPACE_PREFIX = void 0; - -const child_process_1 = __webpack_require__(285); - +const child_process_1 = __webpack_require__(286); const path_1 = __webpack_require__(4); - const fs_1 = __webpack_require__(134); - exports.NAMESPACE_PREFIX = process.platform === 'win32' ? '\\\\?\\' : ''; /** * Get a standardized reference to a path @@ -39818,38 +39579,33 @@ exports.NAMESPACE_PREFIX = process.platform === 'win32' ? '\\\\?\\' : ''; * @param {boolean} [escapedBackslashes=true] - on Windows, double-backslash the reference * @param {boolean} [returnUNC=false] - produce an extended reference */ - -exports.standardize = (path, usePosix = true, escapedBackslashes = true, returnUNC = false) => { +const standardize = (path, usePosix = true, escapedBackslashes = true, returnUNC = false) => { var _process; - // Force os-dependant separators - const normal = path_1.normalize(path); // Filter out in-browser executions as well as non-windows ones - + const normal = (0, path_1.normalize)(path); + // Filter out in-browser executions as well as non-windows ones if (((_process = process) === null || _process === void 0 ? void 0 : _process.platform) !== 'win32') return normal; if (usePosix) return normal.replace(/\\/g, '/');else if (escapedBackslashes) return normal.replace(/\\/g, '\\\\');else if (returnUNC) return '\\\\?\\' + normal; return normal; }; +exports.standardize = standardize; /** * Windows-only function that uses PowerShell to calculate the full path * @param {string} path * @private */ - - const getFullPathSync = path => { if (process.platform !== 'win32') return path; - try { - var _child_process_1$exec, _child_process_1$exec2; - - const fullName = (_child_process_1$exec = child_process_1.execSync(`powershell "(Get-Item -LiteralPath '${path}').FullName"`, { + var _ref, _ref$trim; + const fullName = (_ref = (0, child_process_1.execSync)(`powershell "(Get-Item -LiteralPath '${path}').FullName"`, { encoding: 'utf8' - })) === null || _child_process_1$exec === void 0 ? void 0 : (_child_process_1$exec2 = _child_process_1$exec.trim) === null || _child_process_1$exec2 === void 0 ? void 0 : _child_process_1$exec2.call(_child_process_1$exec); // Make sure we got something back - + })) === null || _ref === void 0 || (_ref$trim = _ref.trim) === null || _ref$trim === void 0 ? void 0 : _ref$trim.call(_ref); + // Make sure we got something back if ((fullName === null || fullName === void 0 ? void 0 : fullName.length) > 2) return fullName; - } catch (ex) {// Do nothing + } catch (ex) { + // Do nothing } - return path; }; /** @@ -39857,136 +39613,118 @@ const getFullPathSync = path => { * @param {string} path * @private */ - - const getShortPathSync = path => { if (process.platform !== 'win32') return path; - try { - var _child_process_1$exec3, _child_process_1$exec4; - - const shortPath = (_child_process_1$exec3 = child_process_1.execSync(`powershell "$FSO = New-Object -ComObject Scripting.FileSystemObject; $O = (Get-Item -LiteralPath '${path}'); if ($O.PSIsContainer) { $FSO.GetFolder($O.FullName).ShortPath } else { $FSO.GetFile($O.FullName).ShortPath }"`, { + var _ref2, _ref2$trim; + const shortPath = (_ref2 = (0, child_process_1.execSync)(`powershell "$FSO = New-Object -ComObject Scripting.FileSystemObject; $O = (Get-Item -LiteralPath '${path}'); if ($O.PSIsContainer) { $FSO.GetFolder($O.FullName).ShortPath } else { $FSO.GetFile($O.FullName).ShortPath }"`, { encoding: 'utf8' - })) === null || _child_process_1$exec3 === void 0 ? void 0 : (_child_process_1$exec4 = _child_process_1$exec3.trim) === null || _child_process_1$exec4 === void 0 ? void 0 : _child_process_1$exec4.call(_child_process_1$exec3); // Make sure we got something back - + })) === null || _ref2 === void 0 || (_ref2$trim = _ref2.trim) === null || _ref2$trim === void 0 ? void 0 : _ref2$trim.call(_ref2); + // Make sure we got something back if ((shortPath === null || shortPath === void 0 ? void 0 : shortPath.length) > 2) return shortPath; - } catch (ex) {// Do nothing + } catch (ex) { + // Do nothing } - return path; }; /** * Checks if Windows 8.3 short names are supported on the volume of the given path * @param {string} [path='.'] - the path to examine */ - - -exports.shortNamesSupportedSync = (path = '.') => { +const shortNamesSupportedSync = (path = '.') => { if (process.platform !== 'win32') return false; const testFileName = '.___osd-cross-platform-test.file'; - const file = path_1.resolve(path, testFileName); // Create a test file if it doesn't exist - - if (!fs_1.existsSync(file)) fs_1.closeSync(fs_1.openSync(file, 'w')); // If the returned value's basename is not the same as the requested file name, it must be a short name - - const foundShortName = path_1.basename(getShortPathSync(file)) !== testFileName; // Cleanup - - fs_1.unlinkSync(file); + const file = (0, path_1.resolve)(path, testFileName); + // Create a test file if it doesn't exist + if (!(0, fs_1.existsSync)(file)) (0, fs_1.closeSync)((0, fs_1.openSync)(file, 'w')); + // If the returned value's basename is not the same as the requested file name, it must be a short name + const foundShortName = (0, path_1.basename)(getShortPathSync(file)) !== testFileName; + // Cleanup + (0, fs_1.unlinkSync)(file); return foundShortName; }; +exports.shortNamesSupportedSync = shortNamesSupportedSync; /** * @borrows shortNamesSupportedSync */ - - exports.shortNameSupportedSync = exports.shortNamesSupportedSync; /** * Get the full pathname * @param {string} path - the path to resolve */ - -exports.resolveToFullPathSync = path => getFullPathSync(path_1.resolve(path)); +const resolveToFullPathSync = path => getFullPathSync((0, path_1.resolve)(path)); +exports.resolveToFullPathSync = resolveToFullPathSync; /** * @borrows resolveToFullPathSync */ - - exports.resolveToFullNameSync = exports.resolveToFullPathSync; /** * Get the short pathname * @param {string} path - the path to resolve */ - -exports.resolveToShortPathSync = path => getShortPathSync(path_1.resolve(path)); +const resolveToShortPathSync = path => getShortPathSync((0, path_1.resolve)(path)); +exports.resolveToShortPathSync = resolveToShortPathSync; /** * @borrows resolveToShortPathSync */ - - exports.resolveToShortNameSync = exports.resolveToShortPathSync; /** * Get the canonical pathname * @param {string} path - the path to resolve */ - -exports.realPathSync = path => getFullPathSync(fs_1.realpathSync(path, 'utf8')); +const realPathSync = path => getFullPathSync((0, fs_1.realpathSync)(path, 'utf8')); +exports.realPathSync = realPathSync; /** * @borrows realPathSync */ - - exports.realpathSync = exports.realPathSync; /** * Get the canonical pathname * @param {string} path - the path to resolve */ - -exports.realShortPathSync = path => getShortPathSync(fs_1.realpathSync(path, 'utf8')); +const realShortPathSync = path => getShortPathSync((0, fs_1.realpathSync)(path, 'utf8')); +exports.realShortPathSync = realShortPathSync; /** * @borrows realShortPathSync */ - - exports.realshortpathSync = exports.realShortPathSync; /***/ }), -/* 337 */ +/* 338 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; + /* * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 */ - Object.defineProperty(exports, "__esModule", { value: true }); exports.PROCESS_POSIX_WORKING_DIR = exports.PROCESS_WORKING_DIR = void 0; - -const path_1 = __webpack_require__(336); +const path_1 = __webpack_require__(337); /** * The full pathname of the working directory of the process * @constant * @type {string} */ - - -exports.PROCESS_WORKING_DIR = path_1.resolveToFullPathSync(process.cwd()); +exports.PROCESS_WORKING_DIR = (0, path_1.resolveToFullPathSync)(process.cwd()); /** * The full pathname of the working directory of the process, in POSIX format * @constant * @type {string} */ - -exports.PROCESS_POSIX_WORKING_DIR = path_1.standardize(exports.PROCESS_WORKING_DIR); +exports.PROCESS_POSIX_WORKING_DIR = (0, path_1.standardize)(exports.PROCESS_WORKING_DIR); /***/ }), -/* 338 */ +/* 339 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; + /* * SPDX-License-Identifier: Apache-2.0 * @@ -39997,12 +39735,10 @@ exports.PROCESS_POSIX_WORKING_DIR = path_1.standardize(exports.PROCESS_WORKING_D * Any modifications Copyright OpenSearch Contributors. See * GitHub history for details. */ - Object.defineProperty(exports, "__esModule", { value: true }); exports.relativeToRepoRoot = exports.getRepoRoot = exports.getMatchingRoot = exports.UPSTREAM_BRANCH = exports.REPO_ROOT_8_3 = exports.REPO_ROOT = void 0; - const tslib_1 = __webpack_require__(7); /* * Licensed to Elasticsearch B.V. under one or more contributor @@ -40022,89 +39758,74 @@ const tslib_1 = __webpack_require__(7); * specific language governing permissions and limitations * under the License. */ - - const path_1 = __webpack_require__(4); - -const load_json_file_1 = tslib_1.__importDefault(__webpack_require__(339)); - -const path_2 = __webpack_require__(336); - +const load_json_file_1 = tslib_1.__importDefault(__webpack_require__(340)); +const path_2 = __webpack_require__(337); const readOpenSearchDashboardsPkgJson = dir => { try { - const path = path_1.resolve(dir, 'package.json'); + const path = (0, path_1.resolve)(dir, 'package.json'); const json = load_json_file_1.default.sync(path); - if ((json === null || json === void 0 ? void 0 : json.name) === 'opensearch-dashboards') { return json; } } catch (error) { - if (error && error.code === 'ENOENT') { + if ((error === null || error === void 0 ? void 0 : error.code) === 'ENOENT') { return; } - throw error; } }; - const findOpenSearchDashboardsPackageJson = () => { // search for the opensearch-dashboards directory, since this file is moved around it might // not be where we think but should always be a relatively close parent // of this directory - const startDir = path_2.realPathSync(__dirname); + const startDir = (0, path_2.realPathSync)(__dirname); const { root: rootDir - } = path_1.parse(startDir); + } = (0, path_1.parse)(startDir); let cursor = startDir; - while (true) { const opensearchDashboardsPkgJson = readOpenSearchDashboardsPkgJson(cursor); - if (opensearchDashboardsPkgJson) { return { opensearchDashboardsDir: cursor, opensearchDashboardsPkgJson: opensearchDashboardsPkgJson }; } - - const parent = path_1.dirname(cursor); - + const parent = (0, path_1.dirname)(cursor); if (parent === rootDir) { throw new Error(`unable to find opensearch-dashboards directory from ${startDir}`); } - cursor = parent; } }; - const { opensearchDashboardsDir, opensearchDashboardsPkgJson } = findOpenSearchDashboardsPackageJson(); -exports.REPO_ROOT = path_2.resolveToFullPathSync(opensearchDashboardsDir); -exports.REPO_ROOT_8_3 = path_2.resolveToShortPathSync(opensearchDashboardsDir); +exports.REPO_ROOT = (0, path_2.resolveToFullPathSync)(opensearchDashboardsDir); +exports.REPO_ROOT_8_3 = (0, path_2.resolveToShortPathSync)(opensearchDashboardsDir); exports.UPSTREAM_BRANCH = opensearchDashboardsPkgJson.branch; - -exports.getMatchingRoot = (path, rootPaths) => { - const rootPathsArray = Array.isArray(rootPaths) ? rootPaths : [rootPaths]; // We can only find the appropriate root if an absolute path was given - - if (path && path_1.isAbsolute(path)) { +const getMatchingRoot = (path, rootPaths) => { + const rootPathsArray = Array.isArray(rootPaths) ? rootPaths : [rootPaths]; + // We can only find the appropriate root if an absolute path was given + if (path && (0, path_1.isAbsolute)(path)) { // Return the matching root if one is found or return `undefined` return rootPathsArray.find(root => path.startsWith(root)); } - return undefined; }; - -exports.getRepoRoot = path => exports.getMatchingRoot(path, [exports.REPO_ROOT, exports.REPO_ROOT_8_3]); - -exports.relativeToRepoRoot = path => { - const repoRoot = exports.getRepoRoot(path); - return repoRoot ? path_1.relative(repoRoot, path) : null; +exports.getMatchingRoot = getMatchingRoot; +const getRepoRoot = path => (0, exports.getMatchingRoot)(path, [exports.REPO_ROOT, exports.REPO_ROOT_8_3]); +exports.getRepoRoot = getRepoRoot; +const relativeToRepoRoot = path => { + const repoRoot = (0, exports.getRepoRoot)(path); + return repoRoot ? (0, path_1.relative)(repoRoot, path) : null; }; +exports.relativeToRepoRoot = relativeToRepoRoot; /***/ }), -/* 339 */ +/* 340 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -40112,7 +39833,7 @@ exports.relativeToRepoRoot = path => { const path = __webpack_require__(4); const {promisify} = __webpack_require__(112); const fs = __webpack_require__(133); -const stripBom = __webpack_require__(340); +const stripBom = __webpack_require__(341); const parseJson = __webpack_require__(168); const parse = (data, filePath, options = {}) => { @@ -40130,7 +39851,7 @@ module.exports.sync = (filePath, options) => parse(fs.readFileSync(filePath, 'ut /***/ }), -/* 340 */ +/* 341 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -40152,7 +39873,7 @@ module.exports = string => { /***/ }), -/* 341 */ +/* 342 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -40162,56 +39883,73 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.BootstrapCacheFile = void 0; - var _fs = _interopRequireDefault(__webpack_require__(134)); - var _path = _interopRequireDefault(__webpack_require__(4)); - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - +function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } +function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } +function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } /* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Any modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ /* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ class BootstrapCacheFile { constructor(osd, project, checksums) { _defineProperty(this, "path", void 0); - _defineProperty(this, "expectedValue", void 0); - this.path = _path.default.resolve(project.targetLocation, '.bootstrap-cache'); - if (!checksums) { return; } - - const projectAndDepCacheKeys = Array.from(osd.getProjectAndDeps(project.name).values()) // sort deps by name so that the key is stable - .sort((a, b) => a.name.localeCompare(b.name)) // get the cacheKey for each project, return undefined if the cache key couldn't be determined + const projectAndDepCacheKeys = Array.from(osd.getProjectAndDeps(project.name).values()) + // sort deps by name so that the key is stable + .sort((a, b) => a.name.localeCompare(b.name)) + // get the cacheKey for each project, return undefined if the cache key couldn't be determined .map(p => { const cacheKey = checksums.get(p.name); - if (cacheKey) { return `${p.name}:${cacheKey}`; } - }); // if any of the relevant cache keys are undefined then the projectCacheKey must be too + }); + // if any of the relevant cache keys are undefined then the projectCacheKey must be too this.expectedValue = projectAndDepCacheKeys.some(k => !k) ? undefined : [`# this is only human readable for debugging, please don't try to parse this`, ...projectAndDepCacheKeys].join('\n'); } - isValid() { if (!this.expectedValue) { return false; } - try { return _fs.default.readFileSync(this.path, 'utf8') === this.expectedValue; } catch (error) { if (error.code === 'ENOENT') { return false; } - throw error; } } - delete() { try { _fs.default.unlinkSync(this.path); @@ -40221,25 +39959,20 @@ class BootstrapCacheFile { } } } - write() { if (!this.expectedValue) { return; } - _fs.default.mkdirSync(_path.default.dirname(this.path), { recursive: true }); - _fs.default.writeFileSync(this.path, this.expectedValue); } - } - exports.BootstrapCacheFile = BootstrapCacheFile; /***/ }), -/* 342 */ +/* 343 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -40249,21 +39982,16 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.validateDependencies = validateDependencies; - -var _lockfile = __webpack_require__(334); - +var _lockfile = __webpack_require__(335); var _dedent = _interopRequireDefault(__webpack_require__(2)); - var _chalk = _interopRequireDefault(__webpack_require__(113)); - -var _fs = __webpack_require__(131); - +var _path = _interopRequireDefault(__webpack_require__(4)); +var _fs = __webpack_require__(134); +var _semver = __webpack_require__(191); +var _fs2 = __webpack_require__(131); var _log = __webpack_require__(145); - -var _projects_tree = __webpack_require__(343); - +var _projects_tree = __webpack_require__(344); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - /* * SPDX-License-Identifier: Apache-2.0 * @@ -40274,7 +40002,6 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de * Any modifications Copyright OpenSearch Contributors. See * GitHub history for details. */ - /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -40294,29 +40021,58 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de * under the License. */ // @ts-expect-error published types are useless -async function validateDependencies(osd, yarnLock) { - // look through all of the packages in the yarn.lock file to see if +var SingleVersionResolution = /*#__PURE__*/function (SingleVersionResolution) { + SingleVersionResolution["STRICT"] = "strict"; + SingleVersionResolution["LOOSE"] = "loose"; + SingleVersionResolution["FORCE"] = "force"; + SingleVersionResolution["BRUTE_FORCE"] = "brute-force"; + SingleVersionResolution["IGNORE"] = "ignore"; + return SingleVersionResolution; +}(SingleVersionResolution || {}); +async function validateDependencies(osd, yarnLock, +/* `singleVersionResolution` controls how violations of single-version-dependencies is applied. + * STRICT (default): throw an error and exit + * LOOSE: identify and install a single version that satisfies all ranges + * BRUTE_FORCE: identify and install the newest version + * IGNORE: show all errors without exiting + * + * `LOOSE`: + * Reconciles the various versions installed as a result of having multiple ranges for a dependency, by + * choosing one that satisfies all said ranges. Even though installing the chosen version updates the + * lock-files, no package.json changes would be needed. + * + * `BRUTE_FORCE`: + * With no care for reconciliation, the newest of the various versions installed is chosen, irrespective of + * whether it satisfies any of the ranges. Installing the chosen version updates the lock-files and a range + * in the form of `^` is applied to all `package.json` files that declared the dependency. + * + * `FORCE`: + * For each dependency, first LOOSE resolution is attempted but if that fails, BRUTE_FORCE is applied. + * + * `IGNORE`: + * Behaves just like `strict` by showing errors when different ranges of a package are marked as + * dependencies, but it does not terminate the script. + */ +singleVersionResolution = SingleVersionResolution.STRICT) { + // look through all the packages in the yarn.lock file to see if // we have accidentally installed multiple lodash v4 versions const lodash4Versions = new Set(); const lodash4Reqs = new Set(); - for (const [req, dep] of Object.entries(yarnLock)) { if (req.startsWith('lodash@') && dep.version.startsWith('4.')) { lodash4Reqs.add(req); lodash4Versions.add(dep.version); } - } // if we find more than one lodash v4 version installed then delete + } + + // if we find more than one lodash v4 version installed then delete // lodash v4 requests from the yarn.lock file and prompt the user to // retry bootstrap so that a single v4 version will be installed - - if (lodash4Versions.size > 1) { for (const req of lodash4Reqs) { delete yarnLock[req]; } - - await (0, _fs.writeFile)(osd.getAbsolute('yarn.lock'), (0, _lockfile.stringify)(yarnLock), 'utf8'); - + await (0, _fs2.writeFile)(osd.getAbsolute('yarn.lock'), (0, _lockfile.stringify)(yarnLock), 'utf8'); _log.log.error((0, _dedent.default)` Multiple version of lodash v4 were detected, so they have been removed @@ -40330,23 +40086,21 @@ async function validateDependencies(osd, yarnLock) { If you have questions about this please reach out to the operations team. `); - process.exit(1); - } // look through all the dependencies of production packages and production + } + + // look through all the dependencies of production packages and production // dependencies of those packages to determine if we're shipping any versions // of lodash v3 in the distributable - - const prodDependencies = osd.resolveAllProductionDependencies(yarnLock, _log.log); const lodash3Versions = new Set(); - for (const dep of prodDependencies.values()) { if (dep.name === 'lodash' && dep.version.startsWith('3.')) { lodash3Versions.add(dep.version); } - } // if any lodash v3 packages were found we abort and tell the user to fix things - + } + // if any lodash v3 packages were found we abort and tell the user to fix things if (lodash3Versions.size) { _log.log.error((0, _dedent.default)` @@ -40361,18 +40115,17 @@ async function validateDependencies(osd, yarnLock) { If you have questions about this please reack out to the operations team. `); - process.exit(1); - } // TODO: remove this once we move into a single package.json - // look through all the package.json files to find packages which have mismatched version ranges - + } + let hasIssues = false; + // look through all the package.json files to find packages which have mismatched version ranges const depRanges = new Map(); - for (const project of osd.getAllProjects().values()) { - for (const [dep, range] of Object.entries(project.allDependencies)) { + for (const [dep, range] of Object.entries( + // Don't be bothered with validating dev-deps when validating single-version loosely + singleVersionResolution === SingleVersionResolution.LOOSE ? project.productionDependencies : project.allDependencies)) { const existingDep = depRanges.get(dep); - if (!existingDep) { depRanges.set(dep, [{ range, @@ -40380,9 +40133,7 @@ async function validateDependencies(osd, yarnLock) { }]); continue; } - const existingRange = existingDep.find(existing => existing.range === range); - if (!existingRange) { existingDep.push({ range, @@ -40390,17 +40141,148 @@ async function validateDependencies(osd, yarnLock) { }); continue; } - existingRange.projects.push(project); } } + const cachedManifests = new Map(); + const violatingSingleVersionDepRanges = new Map(); + depRangesLoop: for (const [depName, ranges] of depRanges) { + // No violation if just a single range of a dependency is used + if (ranges.length === 1) continue; + const installedVersions = new Set(); + const installedDepVersionsCache = new Map(); + const desiredRanges = new Map(); + rangesLoop: for (const { + range, + projects + } of ranges) { + for (const project of projects) { + var _deps; + if (!cachedManifests.has(project.path)) cachedManifests.set(project.path, + // If there are errors reading or parsing the lockfiles, don't catch and let them fall through + (0, _lockfile.parse)((0, _fs.readFileSync)(_path.default.join(project.path, 'yarn.lock'), 'utf-8'))); + const { + object: deps + } = cachedManifests.get(project.path); + if (deps !== null && deps !== void 0 && (_deps = deps[`${depName}@${range}`]) !== null && _deps !== void 0 && _deps.version) { + installedVersions.add(deps[`${depName}@${range}`].version); + installedDepVersionsCache.set(`${project.name}#${depName}`, deps[`${depName}@${range}`].version); + } else { + _log.log.warning(`Failed to find the installed version for ${depName}@${range}`); + // If we cannot read any one of the installed versions of a depName, there is no point in continuing with it + installedVersions.clear(); + desiredRanges.clear(); + break rangesLoop; + } + } + desiredRanges.set(range, projects); + } + + // More than one range is used but couldn't get all the installed versions: call out violation + if (installedVersions.size === 0) { + violatingSingleVersionDepRanges.set(depName, ranges); + continue; // go to the next depRange + } + + if (singleVersionResolution === SingleVersionResolution.LOOSE || + // validating with force first acts like loose + singleVersionResolution === SingleVersionResolution.FORCE) { + if (installedVersions.size === 1) { + hasIssues = true; + + /* When validating single-version loosely, ignore multiple ranges when they result in the installation of + * a single version. + */ + _log.log.info(`Ignored single version requirement for ${depName} as all installations are using v${installedVersions.values().next().value}.`); + continue; // go to the next depRange + } + + const sortedInstalledVersion = Array.from(installedVersions).sort(_semver.rcompare); + const rangePatterns = Array.from(desiredRanges.keys()); + for (const installedVersion of sortedInstalledVersion) { + if (rangePatterns.every(range => (0, _semver.satisfies)(installedVersion, range))) { + // Install the version on all projects that have this dep; keep the original range. + for (const { + range, + projects + } of ranges) { + for (const project of projects) { + // Don't bother updating anything if the desired version is already installed + if (installedDepVersionsCache.get(`${project.name}#${depName}`) === installedVersion) continue; + await project.installDependencyVersion(depName, installedVersion, depName in project.devDependencies, + // When validating single-version loosely, when a version change is needed, the range shouldn't change + range); + } + } + hasIssues = true; + const conflictingRanges = ranges.map(({ + range, + projects + }) => `${range} => ${projects.map(p => p.name).join(', ')}`).join('\n '); + _log.log.warning((0, _dedent.default)` + + [single_version_dependencies] Multiple version ranges for package "${depName}" + were found across different package.json files. A suitable version, v${installedVersion}, was + identified and installed. + + The conflicting version ranges are: + ${conflictingRanges} + `); + + // A usable version was identified so no need to check the lower versions + continue depRangesLoop; // go to the next depRange + } + } + + /* Here because a suitable version was not found. When validating single-version loosely and here, give up. + * However, don't give up when validating with force and act like brute-force! + */ + if (singleVersionResolution === SingleVersionResolution.LOOSE) { + violatingSingleVersionDepRanges.set(depName, ranges); + continue; // go to the next depRange + } + } + + if (singleVersionResolution === SingleVersionResolution.BRUTE_FORCE || + // validating with force here means we failed to get results when acting loosely + singleVersionResolution === SingleVersionResolution.FORCE) { + const sortedInstalledVersion = Array.from(installedVersions).sort(_semver.rcompare); + hasIssues = true; + const suitableVersion = sortedInstalledVersion[0]; + const suitableRange = `^${suitableVersion}`; + + // Install the version on all projects that have this dep; use the suitable range. + for (const { + projects + } of ranges) { + for (const project of projects) { + await project.installDependencyVersion(depName, suitableVersion, depName in project.devDependencies, suitableRange); + } + } + const conflictingRanges = ranges.map(({ + range, + projects + }) => `${range} => ${projects.map(p => p.name).join(', ')}`).join('\n '); + _log.log.warning((0, _dedent.default)` + + [single_version_dependencies] Multiple version ranges for package "${depName}" + were found across different package.json files. A version, v${suitableVersion}, was identified as the most recent + already installed replacement. All package.json files have been updated to indicate a dependency on \`${depName}@${suitableRange}\`. - const duplicateRanges = Array.from(depRanges.entries()).filter(([, ranges]) => ranges.length > 1).reduce((acc, [dep, ranges]) => [...acc, dep, ...ranges.map(({ - range, - projects - }) => ` ${range} => ${projects.map(p => p.name).join(', ')}`)], []).join('\n '); + The conflicting version ranges are: + ${conflictingRanges} + `); + continue; // go to the next depRange + } - if (duplicateRanges) { + // Here because validation was not loose, forced, or brute-forced; just call out the vilation. + violatingSingleVersionDepRanges.set(depName, ranges); + } + if (violatingSingleVersionDepRanges.size > 0) { + const duplicateRanges = Array.from(violatingSingleVersionDepRanges.entries()).reduce((acc, [dep, ranges]) => [...acc, dep, ...ranges.map(({ + range, + projects + }) => ` ${range} => ${projects.map(p => p.name).join(', ')}`)], []).join('\n '); _log.log.error((0, _dedent.default)` [single_version_dependencies] Multiple version ranges for the same dependency @@ -40414,14 +40296,14 @@ async function validateDependencies(osd, yarnLock) { ${duplicateRanges} `); + if (singleVersionResolution !== SingleVersionResolution.IGNORE) { + process.exit(1); + } + } - process.exit(1); - } // look for packages that have the the `opensearchDashboards.devOnly` flag in their package.json + // look for packages that have the `opensearchDashboards.devOnly` flag in their package.json // and make sure they aren't included in the production dependencies of OpenSearch Dashboards - - const devOnlyProjectsInProduction = getDevOnlyProductionDepsTree(osd, 'opensearch-dashboards'); - if (devOnlyProjectsInProduction) { _log.log.error((0, _dedent.default)` Some of the packages in the production dependency chain for OpenSearch Dashboards are @@ -40432,22 +40314,17 @@ async function validateDependencies(osd, yarnLock) { ${(0, _projects_tree.treeToString)(devOnlyProjectsInProduction).split('\n').join('\n ')} `); - process.exit(1); } - - _log.log.success('yarn.lock analysis completed without any issues'); + _log.log.success(hasIssues ? 'yarn.lock analysis completed' : 'yarn.lock analysis completed without any issues'); } - function getDevOnlyProductionDepsTree(osd, projectName) { const project = osd.getProject(projectName); const childProjectNames = [...Object.keys(project.productionDependencies).filter(name => osd.hasProject(name))]; const children = childProjectNames.map(n => getDevOnlyProductionDepsTree(osd, n)).filter(t => !!t); - if (!children.length && !project.isFlaggedAsDevOnly()) { return; } - const tree = { name: project.isFlaggedAsDevOnly() ? _chalk.default.red.bold(projectName) : projectName, children @@ -40456,7 +40333,7 @@ function getDevOnlyProductionDepsTree(osd, projectName) { } /***/ }), -/* 343 */ +/* 344 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -40467,15 +40344,170 @@ Object.defineProperty(exports, "__esModule", { }); exports.renderProjectsTree = renderProjectsTree; exports.treeToString = treeToString; - var _chalk = _interopRequireDefault(__webpack_require__(113)); - var _path = _interopRequireDefault(__webpack_require__(4)); +var _crossPlatform = __webpack_require__(336); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Any modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +const projectKey = Symbol('__project'); +function renderProjectsTree(rootPath, projects) { + const projectsTree = buildProjectsTree(rootPath, projects); + return treeToString(createTreeStructure(projectsTree)); +} -var _crossPlatform = __webpack_require__(335); +// eslint-disable-next-line @typescript-eslint/no-empty-interface -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +// eslint-disable-next-line @typescript-eslint/no-empty-interface + +function treeToString(tree) { + return [tree.name].concat(childrenToStrings(tree.children, '')).join('\n'); +} +function childrenToStrings(tree, treePrefix) { + if (tree === undefined) { + return []; + } + let strings = []; + tree.forEach((node, index) => { + const isLastNode = tree.length - 1 === index; + const nodePrefix = isLastNode ? '└── ' : '├── '; + const childPrefix = isLastNode ? ' ' : '│ '; + const childrenPrefix = treePrefix + childPrefix; + strings.push(`${treePrefix}${nodePrefix}${node.name}`); + strings = strings.concat(childrenToStrings(node.children, childrenPrefix)); + }); + return strings; +} +function createTreeStructure(tree) { + let name; + const children = []; + for (const [dir, project] of tree.entries()) { + // This is a leaf node (aka a project) + if (typeof project === 'string') { + name = _chalk.default.green(project); + continue; + } + + // If there's only one project and the key indicates it's a leaf node, we + // know that we're at a package folder that contains a package.json, so we + // "inline it" so we don't get unnecessary levels, i.e. we'll just see + // `foo` instead of `foo -> foo`. + if (project.size === 1 && project.has(projectKey)) { + const projectName = project.get(projectKey); + children.push({ + children: [], + name: dirOrProjectName(dir, projectName) + }); + continue; + } + const subtree = createTreeStructure(project); + // If the name is specified, we know there's a package at the "root" of the + // subtree itself. + if (subtree.name !== undefined) { + const projectName = subtree.name; + children.push({ + children: subtree.children, + name: dirOrProjectName(dir, projectName) + }); + continue; + } + + // Special-case whenever we have one child, so we don't get unnecessary + // folders in the output. E.g. instead of `foo -> bar -> baz` we get + // `foo/bar/baz` instead. + if (subtree.children && subtree.children.length === 1) { + const child = subtree.children[0]; + const newName = _chalk.default.dim((0, _crossPlatform.standardize)(_path.default.join(dir.toString(), child.name), true)); + children.push({ + children: child.children, + name: newName + }); + continue; + } + children.push({ + children: subtree.children, + name: _chalk.default.dim(dir.toString()) + }); + } + return { + name, + children + }; +} +function dirOrProjectName(dir, projectName) { + return dir === projectName ? _chalk.default.green(dir) : (0, _chalk.default)`{dim ${dir.toString()} ({reset.green ${projectName}})}`; +} +function buildProjectsTree(rootPath, projects) { + const tree = new Map(); + for (const project of projects.values()) { + if (rootPath === project.path) { + tree.set(projectKey, project.name); + } else { + const relativeProjectPath = _path.default.relative(rootPath, project.path); + addProjectToTree(tree, relativeProjectPath.split(_path.default.sep), project); + } + } + return tree; +} +function addProjectToTree(tree, pathParts, project) { + if (pathParts.length === 0) { + tree.set(projectKey, project.name); + } else { + const [currentDir, ...rest] = pathParts; + if (!tree.has(currentDir)) { + tree.set(currentDir, new Map()); + } + const subtree = tree.get(currentDir); + addProjectToTree(subtree, rest, project); + } +} + +/***/ }), +/* 345 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.CleanCommand = void 0; +var _del = _interopRequireDefault(__webpack_require__(346)); +var _ora = _interopRequireDefault(__webpack_require__(435)); +var _path = __webpack_require__(4); +var _fs = __webpack_require__(131); +var _log = __webpack_require__(145); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /* * SPDX-License-Identifier: Apache-2.0 * @@ -40505,192 +40537,12 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de * specific language governing permissions and limitations * under the License. */ -const projectKey = Symbol('__project'); - -function renderProjectsTree(rootPath, projects) { - const projectsTree = buildProjectsTree(rootPath, projects); - return treeToString(createTreeStructure(projectsTree)); -} - -function treeToString(tree) { - return [tree.name].concat(childrenToStrings(tree.children, '')).join('\n'); -} - -function childrenToStrings(tree, treePrefix) { - if (tree === undefined) { - return []; - } - - let strings = []; - tree.forEach((node, index) => { - const isLastNode = tree.length - 1 === index; - const nodePrefix = isLastNode ? '└── ' : '├── '; - const childPrefix = isLastNode ? ' ' : '│ '; - const childrenPrefix = treePrefix + childPrefix; - strings.push(`${treePrefix}${nodePrefix}${node.name}`); - strings = strings.concat(childrenToStrings(node.children, childrenPrefix)); - }); - return strings; -} - -function createTreeStructure(tree) { - let name; - const children = []; - - for (const [dir, project] of tree.entries()) { - // This is a leaf node (aka a project) - if (typeof project === 'string') { - name = _chalk.default.green(project); - continue; - } // If there's only one project and the key indicates it's a leaf node, we - // know that we're at a package folder that contains a package.json, so we - // "inline it" so we don't get unnecessary levels, i.e. we'll just see - // `foo` instead of `foo -> foo`. - - - if (project.size === 1 && project.has(projectKey)) { - const projectName = project.get(projectKey); - children.push({ - children: [], - name: dirOrProjectName(dir, projectName) - }); - continue; - } - - const subtree = createTreeStructure(project); // If the name is specified, we know there's a package at the "root" of the - // subtree itself. - - if (subtree.name !== undefined) { - const projectName = subtree.name; - children.push({ - children: subtree.children, - name: dirOrProjectName(dir, projectName) - }); - continue; - } // Special-case whenever we have one child, so we don't get unnecessary - // folders in the output. E.g. instead of `foo -> bar -> baz` we get - // `foo/bar/baz` instead. - - - if (subtree.children && subtree.children.length === 1) { - const child = subtree.children[0]; - - const newName = _chalk.default.dim((0, _crossPlatform.standardize)(_path.default.join(dir.toString(), child.name), true)); - - children.push({ - children: child.children, - name: newName - }); - continue; - } - - children.push({ - children: subtree.children, - name: _chalk.default.dim(dir.toString()) - }); - } - - return { - name, - children - }; -} - -function dirOrProjectName(dir, projectName) { - return dir === projectName ? _chalk.default.green(dir) : (0, _chalk.default)`{dim ${dir.toString()} ({reset.green ${projectName}})}`; -} - -function buildProjectsTree(rootPath, projects) { - const tree = new Map(); - - for (const project of projects.values()) { - if (rootPath === project.path) { - tree.set(projectKey, project.name); - } else { - const relativeProjectPath = _path.default.relative(rootPath, project.path); - - addProjectToTree(tree, relativeProjectPath.split(_path.default.sep), project); - } - } - - return tree; -} - -function addProjectToTree(tree, pathParts, project) { - if (pathParts.length === 0) { - tree.set(projectKey, project.name); - } else { - const [currentDir, ...rest] = pathParts; - - if (!tree.has(currentDir)) { - tree.set(currentDir, new Map()); - } - - const subtree = tree.get(currentDir); - addProjectToTree(subtree, rest, project); - } -} - -/***/ }), -/* 344 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.CleanCommand = void 0; - -var _del = _interopRequireDefault(__webpack_require__(345)); - -var _ora = _interopRequireDefault(__webpack_require__(434)); - -var _path = __webpack_require__(4); - -var _fs = __webpack_require__(131); - -var _log = __webpack_require__(145); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -const CleanCommand = { +const CleanCommand = exports.CleanCommand = { description: 'Remove the node_modules and target directories from all projects.', name: 'clean', - async run(projects) { const toDelete = []; - for (const project of projects.values()) { if (await (0, _fs.isDirectory)(project.nodeModulesLocation)) { toDelete.push({ @@ -40698,18 +40550,15 @@ const CleanCommand = { pattern: (0, _path.relative)(project.path, project.nodeModulesLocation) }); } - if (await (0, _fs.isDirectory)(project.targetLocation)) { toDelete.push({ cwd: project.path, pattern: (0, _path.relative)(project.path, project.targetLocation) }); } - const { extraPatterns } = project.getCleanConfig(); - if (extraPatterns) { toDelete.push({ cwd: project.path, @@ -40717,7 +40566,6 @@ const CleanCommand = { }); } } - if (toDelete.length === 0) { _log.log.success('Nothing to delete'); } else { @@ -40731,7 +40579,6 @@ const CleanCommand = { * patterns and does not impact the cwd check. */ const originalCwd = process.cwd(); - try { for (const { pattern, @@ -40739,11 +40586,9 @@ const CleanCommand = { } of toDelete) { process.chdir(cwd); const promise = (0, _del.default)(pattern); - if (_log.log.wouldLogLevel('info')) { _ora.default.promise(promise, (0, _path.relative)(originalCwd, (0, _path.join)(cwd, String(pattern)))); } - await promise; } } finally { @@ -40751,26 +40596,24 @@ const CleanCommand = { } } } - }; -exports.CleanCommand = CleanCommand; /***/ }), -/* 345 */ +/* 346 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const {promisify} = __webpack_require__(112); const path = __webpack_require__(4); -const globby = __webpack_require__(346); -const isGlob = __webpack_require__(358); -const slash = __webpack_require__(425); +const globby = __webpack_require__(347); +const isGlob = __webpack_require__(359); +const slash = __webpack_require__(426); const gracefulFs = __webpack_require__(133); -const isPathCwd = __webpack_require__(427); -const isPathInside = __webpack_require__(428); -const rimraf = __webpack_require__(429); -const pMap = __webpack_require__(430); +const isPathCwd = __webpack_require__(428); +const isPathInside = __webpack_require__(429); +const rimraf = __webpack_require__(430); +const pMap = __webpack_require__(431); const rimrafP = promisify(rimraf); @@ -40902,18 +40745,18 @@ module.exports.sync = (patterns, {force, dryRun, cwd = process.cwd(), ...options /***/ }), -/* 346 */ +/* 347 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(134); -const arrayUnion = __webpack_require__(347); -const merge2 = __webpack_require__(348); -const fastGlob = __webpack_require__(349); -const dirGlob = __webpack_require__(421); -const gitignore = __webpack_require__(423); -const {FilterStream, UniqueStream} = __webpack_require__(426); +const arrayUnion = __webpack_require__(348); +const merge2 = __webpack_require__(349); +const fastGlob = __webpack_require__(350); +const dirGlob = __webpack_require__(422); +const gitignore = __webpack_require__(424); +const {FilterStream, UniqueStream} = __webpack_require__(427); const DEFAULT_FILTER = () => false; @@ -41090,7 +40933,7 @@ module.exports.gitignore = gitignore; /***/ }), -/* 347 */ +/* 348 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -41102,7 +40945,7 @@ module.exports = (...arguments_) => { /***/ }), -/* 348 */ +/* 349 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -41253,18 +41096,18 @@ function pauseStreams (streams, options) { /***/ }), -/* 349 */ +/* 350 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const taskManager = __webpack_require__(350); -const patternManager = __webpack_require__(379); -const async_1 = __webpack_require__(380); -const stream_1 = __webpack_require__(417); -const sync_1 = __webpack_require__(418); -const settings_1 = __webpack_require__(420); -const utils = __webpack_require__(351); +const taskManager = __webpack_require__(351); +const patternManager = __webpack_require__(380); +const async_1 = __webpack_require__(381); +const stream_1 = __webpack_require__(418); +const sync_1 = __webpack_require__(419); +const settings_1 = __webpack_require__(421); +const utils = __webpack_require__(352); async function FastGlob(source, options) { assertPatternsInput(source); const works = getWorks(source, async_1.default, options); @@ -41328,14 +41171,14 @@ module.exports = FastGlob; /***/ }), -/* 350 */ +/* 351 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.convertPatternGroupToTask = exports.convertPatternGroupsToTasks = exports.groupPatternsByBaseDirectory = exports.getNegativePatternsAsPositive = exports.getPositivePatterns = exports.convertPatternsToTasks = exports.generate = void 0; -const utils = __webpack_require__(351); +const utils = __webpack_require__(352); function generate(patterns, settings) { const positivePatterns = getPositivePatterns(patterns); const negativePatterns = getNegativePatternsAsPositive(patterns, settings.ignore); @@ -41415,31 +41258,31 @@ exports.convertPatternGroupToTask = convertPatternGroupToTask; /***/ }), -/* 351 */ +/* 352 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.string = exports.stream = exports.pattern = exports.path = exports.fs = exports.errno = exports.array = void 0; -const array = __webpack_require__(352); +const array = __webpack_require__(353); exports.array = array; -const errno = __webpack_require__(353); +const errno = __webpack_require__(354); exports.errno = errno; -const fs = __webpack_require__(354); +const fs = __webpack_require__(355); exports.fs = fs; -const path = __webpack_require__(355); +const path = __webpack_require__(356); exports.path = path; -const pattern = __webpack_require__(356); +const pattern = __webpack_require__(357); exports.pattern = pattern; -const stream = __webpack_require__(377); +const stream = __webpack_require__(378); exports.stream = stream; -const string = __webpack_require__(378); +const string = __webpack_require__(379); exports.string = string; /***/ }), -/* 352 */ +/* 353 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -41468,7 +41311,7 @@ exports.splitWhen = splitWhen; /***/ }), -/* 353 */ +/* 354 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -41482,7 +41325,7 @@ exports.isEnoentCodeError = isEnoentCodeError; /***/ }), -/* 354 */ +/* 355 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -41508,7 +41351,7 @@ exports.createDirentFromStats = createDirentFromStats; /***/ }), -/* 355 */ +/* 356 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -41548,7 +41391,7 @@ exports.removeLeadingDotSegment = removeLeadingDotSegment; /***/ }), -/* 356 */ +/* 357 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -41556,8 +41399,8 @@ exports.removeLeadingDotSegment = removeLeadingDotSegment; Object.defineProperty(exports, "__esModule", { value: true }); exports.matchAny = exports.convertPatternsToRe = exports.makeRe = exports.getPatternParts = exports.expandBraceExpansion = exports.expandPatternsWithBraceExpansion = exports.isAffectDepthOfReadingPattern = exports.endsWithSlashGlobStar = exports.hasGlobStar = exports.getBaseDirectory = exports.isPatternRelatedToParentDirectory = exports.getPatternsOutsideCurrentDirectory = exports.getPatternsInsideCurrentDirectory = exports.getPositivePatterns = exports.getNegativePatterns = exports.isPositivePattern = exports.isNegativePattern = exports.convertToNegativePattern = exports.convertToPositivePattern = exports.isDynamicPattern = exports.isStaticPattern = void 0; const path = __webpack_require__(4); -const globParent = __webpack_require__(357); -const micromatch = __webpack_require__(360); +const globParent = __webpack_require__(358); +const micromatch = __webpack_require__(361); const GLOBSTAR = '**'; const ESCAPE_SYMBOL = '\\'; const COMMON_GLOB_SYMBOLS_RE = /[*?]|^!/; @@ -41724,13 +41567,13 @@ exports.matchAny = matchAny; /***/ }), -/* 357 */ +/* 358 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isGlob = __webpack_require__(358); +var isGlob = __webpack_require__(359); var pathPosixDirname = __webpack_require__(4).posix.dirname; var isWin32 = __webpack_require__(121).platform() === 'win32'; @@ -41806,7 +41649,7 @@ function isGlobby(str) { /***/ }), -/* 358 */ +/* 359 */ /***/ (function(module, exports, __webpack_require__) { /*! @@ -41816,7 +41659,7 @@ function isGlobby(str) { * Released under the MIT License. */ -var isExtglob = __webpack_require__(359); +var isExtglob = __webpack_require__(360); var chars = { '{': '}', '(': ')', '[': ']'}; var strictCheck = function(str) { if (str[0] === '!') { @@ -41962,7 +41805,7 @@ module.exports = function isGlob(str, options) { /***/ }), -/* 359 */ +/* 360 */ /***/ (function(module, exports) { /*! @@ -41988,16 +41831,16 @@ module.exports = function isExtglob(str) { /***/ }), -/* 360 */ +/* 361 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const util = __webpack_require__(112); -const braces = __webpack_require__(361); -const picomatch = __webpack_require__(371); -const utils = __webpack_require__(374); +const braces = __webpack_require__(362); +const picomatch = __webpack_require__(372); +const utils = __webpack_require__(375); const isEmptyString = val => val === '' || val === './'; /** @@ -42462,16 +42305,16 @@ module.exports = micromatch; /***/ }), -/* 361 */ +/* 362 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const stringify = __webpack_require__(362); -const compile = __webpack_require__(364); -const expand = __webpack_require__(368); -const parse = __webpack_require__(369); +const stringify = __webpack_require__(363); +const compile = __webpack_require__(365); +const expand = __webpack_require__(369); +const parse = __webpack_require__(370); /** * Expand the given pattern or create a regex-compatible string. @@ -42639,13 +42482,13 @@ module.exports = braces; /***/ }), -/* 362 */ +/* 363 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const utils = __webpack_require__(363); +const utils = __webpack_require__(364); module.exports = (ast, options = {}) => { let stringify = (node, parent = {}) => { @@ -42678,7 +42521,7 @@ module.exports = (ast, options = {}) => { /***/ }), -/* 363 */ +/* 364 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -42797,14 +42640,14 @@ exports.flatten = (...args) => { /***/ }), -/* 364 */ +/* 365 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const fill = __webpack_require__(365); -const utils = __webpack_require__(363); +const fill = __webpack_require__(366); +const utils = __webpack_require__(364); const compile = (ast, options = {}) => { let walk = (node, parent = {}) => { @@ -42861,7 +42704,7 @@ module.exports = compile; /***/ }), -/* 365 */ +/* 366 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -42875,7 +42718,7 @@ module.exports = compile; const util = __webpack_require__(112); -const toRegexRange = __webpack_require__(366); +const toRegexRange = __webpack_require__(367); const isObject = val => val !== null && typeof val === 'object' && !Array.isArray(val); @@ -43117,7 +42960,7 @@ module.exports = fill; /***/ }), -/* 366 */ +/* 367 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -43130,7 +42973,7 @@ module.exports = fill; -const isNumber = __webpack_require__(367); +const isNumber = __webpack_require__(368); const toRegexRange = (min, max, options) => { if (isNumber(min) === false) { @@ -43412,7 +43255,7 @@ module.exports = toRegexRange; /***/ }), -/* 367 */ +/* 368 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -43437,15 +43280,15 @@ module.exports = function(num) { /***/ }), -/* 368 */ +/* 369 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const fill = __webpack_require__(365); -const stringify = __webpack_require__(362); -const utils = __webpack_require__(363); +const fill = __webpack_require__(366); +const stringify = __webpack_require__(363); +const utils = __webpack_require__(364); const append = (queue = '', stash = '', enclose = false) => { let result = []; @@ -43557,13 +43400,13 @@ module.exports = expand; /***/ }), -/* 369 */ +/* 370 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const stringify = __webpack_require__(362); +const stringify = __webpack_require__(363); /** * Constants @@ -43585,7 +43428,7 @@ const { CHAR_SINGLE_QUOTE, /* ' */ CHAR_NO_BREAK_SPACE, CHAR_ZERO_WIDTH_NOBREAK_SPACE -} = __webpack_require__(370); +} = __webpack_require__(371); /** * parse @@ -43897,7 +43740,7 @@ module.exports = parse; /***/ }), -/* 370 */ +/* 371 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -43961,27 +43804,27 @@ module.exports = { /***/ }), -/* 371 */ +/* 372 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -module.exports = __webpack_require__(372); +module.exports = __webpack_require__(373); /***/ }), -/* 372 */ +/* 373 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const scan = __webpack_require__(373); -const parse = __webpack_require__(376); -const utils = __webpack_require__(374); -const constants = __webpack_require__(375); +const scan = __webpack_require__(374); +const parse = __webpack_require__(377); +const utils = __webpack_require__(375); +const constants = __webpack_require__(376); const isObject = val => val && typeof val === 'object' && !Array.isArray(val); /** @@ -44320,13 +44163,13 @@ module.exports = picomatch; /***/ }), -/* 373 */ +/* 374 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const utils = __webpack_require__(374); +const utils = __webpack_require__(375); const { CHAR_ASTERISK, /* * */ CHAR_AT, /* @ */ @@ -44343,7 +44186,7 @@ const { CHAR_RIGHT_CURLY_BRACE, /* } */ CHAR_RIGHT_PARENTHESES, /* ) */ CHAR_RIGHT_SQUARE_BRACKET /* ] */ -} = __webpack_require__(375); +} = __webpack_require__(376); const isPathSeparator = code => { return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH; @@ -44718,7 +44561,7 @@ module.exports = scan; /***/ }), -/* 374 */ +/* 375 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -44731,7 +44574,7 @@ const { REGEX_REMOVE_BACKSLASH, REGEX_SPECIAL_CHARS, REGEX_SPECIAL_CHARS_GLOBAL -} = __webpack_require__(375); +} = __webpack_require__(376); exports.isObject = val => val !== null && typeof val === 'object' && !Array.isArray(val); exports.hasRegexChars = str => REGEX_SPECIAL_CHARS.test(str); @@ -44789,7 +44632,7 @@ exports.wrapOutput = (input, state = {}, options = {}) => { /***/ }), -/* 375 */ +/* 376 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -44975,14 +44818,14 @@ module.exports = { /***/ }), -/* 376 */ +/* 377 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const constants = __webpack_require__(375); -const utils = __webpack_require__(374); +const constants = __webpack_require__(376); +const utils = __webpack_require__(375); /** * Constants @@ -46073,14 +45916,14 @@ module.exports = parse; /***/ }), -/* 377 */ +/* 378 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.merge = void 0; -const merge2 = __webpack_require__(348); +const merge2 = __webpack_require__(349); function merge(streams) { const mergedStream = merge2(streams); streams.forEach((stream) => { @@ -46097,7 +45940,7 @@ function propagateCloseEventToSources(streams) { /***/ }), -/* 378 */ +/* 379 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -46115,7 +45958,7 @@ exports.isEmpty = isEmpty; /***/ }), -/* 379 */ +/* 380 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -46143,14 +45986,14 @@ exports.removeDuplicateSlashes = removeDuplicateSlashes; /***/ }), -/* 380 */ +/* 381 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const stream_1 = __webpack_require__(381); -const provider_1 = __webpack_require__(410); +const stream_1 = __webpack_require__(382); +const provider_1 = __webpack_require__(411); class ProviderAsync extends provider_1.default { constructor() { super(...arguments); @@ -46178,16 +46021,16 @@ exports.default = ProviderAsync; /***/ }), -/* 381 */ +/* 382 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const stream_1 = __webpack_require__(138); -const fsStat = __webpack_require__(382); -const fsWalk = __webpack_require__(387); -const reader_1 = __webpack_require__(409); +const fsStat = __webpack_require__(383); +const fsWalk = __webpack_require__(388); +const reader_1 = __webpack_require__(410); class ReaderStream extends reader_1.default { constructor() { super(...arguments); @@ -46240,16 +46083,16 @@ exports.default = ReaderStream; /***/ }), -/* 382 */ +/* 383 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.statSync = exports.stat = exports.Settings = void 0; -const async = __webpack_require__(383); -const sync = __webpack_require__(384); -const settings_1 = __webpack_require__(385); +const async = __webpack_require__(384); +const sync = __webpack_require__(385); +const settings_1 = __webpack_require__(386); exports.Settings = settings_1.default; function stat(path, optionsOrSettingsOrCallback, callback) { if (typeof optionsOrSettingsOrCallback === 'function') { @@ -46273,7 +46116,7 @@ function getSettings(settingsOrOptions = {}) { /***/ }), -/* 383 */ +/* 384 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -46316,7 +46159,7 @@ function callSuccessCallback(callback, result) { /***/ }), -/* 384 */ +/* 385 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -46346,13 +46189,13 @@ exports.read = read; /***/ }), -/* 385 */ +/* 386 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fs = __webpack_require__(386); +const fs = __webpack_require__(387); class Settings { constructor(_options = {}) { this._options = _options; @@ -46369,7 +46212,7 @@ exports.default = Settings; /***/ }), -/* 386 */ +/* 387 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -46393,17 +46236,17 @@ exports.createFileSystemAdapter = createFileSystemAdapter; /***/ }), -/* 387 */ +/* 388 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Settings = exports.walkStream = exports.walkSync = exports.walk = void 0; -const async_1 = __webpack_require__(388); -const stream_1 = __webpack_require__(405); -const sync_1 = __webpack_require__(406); -const settings_1 = __webpack_require__(408); +const async_1 = __webpack_require__(389); +const stream_1 = __webpack_require__(406); +const sync_1 = __webpack_require__(407); +const settings_1 = __webpack_require__(409); exports.Settings = settings_1.default; function walk(directory, optionsOrSettingsOrCallback, callback) { if (typeof optionsOrSettingsOrCallback === 'function') { @@ -46434,13 +46277,13 @@ function getSettings(settingsOrOptions = {}) { /***/ }), -/* 388 */ +/* 389 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const async_1 = __webpack_require__(389); +const async_1 = __webpack_require__(390); class AsyncProvider { constructor(_root, _settings) { this._root = _root; @@ -46471,17 +46314,17 @@ function callSuccessCallback(callback, entries) { /***/ }), -/* 389 */ +/* 390 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const events_1 = __webpack_require__(157); -const fsScandir = __webpack_require__(390); -const fastq = __webpack_require__(401); -const common = __webpack_require__(403); -const reader_1 = __webpack_require__(404); +const fsScandir = __webpack_require__(391); +const fastq = __webpack_require__(402); +const common = __webpack_require__(404); +const reader_1 = __webpack_require__(405); class AsyncReader extends reader_1.default { constructor(_root, _settings) { super(_root, _settings); @@ -46575,16 +46418,16 @@ exports.default = AsyncReader; /***/ }), -/* 390 */ +/* 391 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Settings = exports.scandirSync = exports.scandir = void 0; -const async = __webpack_require__(391); -const sync = __webpack_require__(398); -const settings_1 = __webpack_require__(399); +const async = __webpack_require__(392); +const sync = __webpack_require__(399); +const settings_1 = __webpack_require__(400); exports.Settings = settings_1.default; function scandir(path, optionsOrSettingsOrCallback, callback) { if (typeof optionsOrSettingsOrCallback === 'function') { @@ -46608,18 +46451,18 @@ function getSettings(settingsOrOptions = {}) { /***/ }), -/* 391 */ +/* 392 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.readdir = exports.readdirWithFileTypes = exports.read = void 0; -const fsStat = __webpack_require__(382); -const rpl = __webpack_require__(392); -const constants_1 = __webpack_require__(394); -const utils = __webpack_require__(395); -const common = __webpack_require__(397); +const fsStat = __webpack_require__(383); +const rpl = __webpack_require__(393); +const constants_1 = __webpack_require__(395); +const utils = __webpack_require__(396); +const common = __webpack_require__(398); function read(directory, settings, callback) { if (!settings.stats && constants_1.IS_SUPPORT_READDIR_WITH_FILE_TYPES) { readdirWithFileTypes(directory, settings, callback); @@ -46719,13 +46562,13 @@ function callSuccessCallback(callback, result) { /***/ }), -/* 392 */ +/* 393 */ /***/ (function(module, exports, __webpack_require__) { /*! run-parallel. MIT License. Feross Aboukhadijeh */ module.exports = runParallel -const queueMicrotask = __webpack_require__(393) +const queueMicrotask = __webpack_require__(394) function runParallel (tasks, cb) { let results, pending, keys @@ -46776,7 +46619,7 @@ function runParallel (tasks, cb) { /***/ }), -/* 393 */ +/* 394 */ /***/ (function(module, exports) { /*! queue-microtask. MIT License. Feross Aboukhadijeh */ @@ -46791,7 +46634,7 @@ module.exports = typeof queueMicrotask === 'function' /***/ }), -/* 394 */ +/* 395 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -46815,19 +46658,19 @@ exports.IS_SUPPORT_READDIR_WITH_FILE_TYPES = IS_MATCHED_BY_MAJOR || IS_MATCHED_B /***/ }), -/* 395 */ +/* 396 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.fs = void 0; -const fs = __webpack_require__(396); +const fs = __webpack_require__(397); exports.fs = fs; /***/ }), -/* 396 */ +/* 397 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -46853,7 +46696,7 @@ exports.createDirentFromStats = createDirentFromStats; /***/ }), -/* 397 */ +/* 398 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -46873,17 +46716,17 @@ exports.joinPathSegments = joinPathSegments; /***/ }), -/* 398 */ +/* 399 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.readdir = exports.readdirWithFileTypes = exports.read = void 0; -const fsStat = __webpack_require__(382); -const constants_1 = __webpack_require__(394); -const utils = __webpack_require__(395); -const common = __webpack_require__(397); +const fsStat = __webpack_require__(383); +const constants_1 = __webpack_require__(395); +const utils = __webpack_require__(396); +const common = __webpack_require__(398); function read(directory, settings) { if (!settings.stats && constants_1.IS_SUPPORT_READDIR_WITH_FILE_TYPES) { return readdirWithFileTypes(directory, settings); @@ -46934,15 +46777,15 @@ exports.readdir = readdir; /***/ }), -/* 399 */ +/* 400 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(4); -const fsStat = __webpack_require__(382); -const fs = __webpack_require__(400); +const fsStat = __webpack_require__(383); +const fs = __webpack_require__(401); class Settings { constructor(_options = {}) { this._options = _options; @@ -46965,7 +46808,7 @@ exports.default = Settings; /***/ }), -/* 400 */ +/* 401 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -46991,7 +46834,7 @@ exports.createFileSystemAdapter = createFileSystemAdapter; /***/ }), -/* 401 */ +/* 402 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -46999,7 +46842,7 @@ exports.createFileSystemAdapter = createFileSystemAdapter; /* eslint-disable no-var */ -var reusify = __webpack_require__(402) +var reusify = __webpack_require__(403) function fastqueue (context, worker, concurrency) { if (typeof context === 'function') { @@ -47281,7 +47124,7 @@ module.exports.promise = queueAsPromised /***/ }), -/* 402 */ +/* 403 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -47321,7 +47164,7 @@ module.exports = reusify /***/ }), -/* 403 */ +/* 404 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -47359,13 +47202,13 @@ exports.joinPathSegments = joinPathSegments; /***/ }), -/* 404 */ +/* 405 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const common = __webpack_require__(403); +const common = __webpack_require__(404); class Reader { constructor(_root, _settings) { this._root = _root; @@ -47377,14 +47220,14 @@ exports.default = Reader; /***/ }), -/* 405 */ +/* 406 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const stream_1 = __webpack_require__(138); -const async_1 = __webpack_require__(389); +const async_1 = __webpack_require__(390); class StreamProvider { constructor(_root, _settings) { this._root = _root; @@ -47418,13 +47261,13 @@ exports.default = StreamProvider; /***/ }), -/* 406 */ +/* 407 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const sync_1 = __webpack_require__(407); +const sync_1 = __webpack_require__(408); class SyncProvider { constructor(_root, _settings) { this._root = _root; @@ -47439,15 +47282,15 @@ exports.default = SyncProvider; /***/ }), -/* 407 */ +/* 408 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsScandir = __webpack_require__(390); -const common = __webpack_require__(403); -const reader_1 = __webpack_require__(404); +const fsScandir = __webpack_require__(391); +const common = __webpack_require__(404); +const reader_1 = __webpack_require__(405); class SyncReader extends reader_1.default { constructor() { super(...arguments); @@ -47505,14 +47348,14 @@ exports.default = SyncReader; /***/ }), -/* 408 */ +/* 409 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(4); -const fsScandir = __webpack_require__(390); +const fsScandir = __webpack_require__(391); class Settings { constructor(_options = {}) { this._options = _options; @@ -47538,15 +47381,15 @@ exports.default = Settings; /***/ }), -/* 409 */ +/* 410 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(4); -const fsStat = __webpack_require__(382); -const utils = __webpack_require__(351); +const fsStat = __webpack_require__(383); +const utils = __webpack_require__(352); class Reader { constructor(_settings) { this._settings = _settings; @@ -47578,17 +47421,17 @@ exports.default = Reader; /***/ }), -/* 410 */ +/* 411 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(4); -const deep_1 = __webpack_require__(411); -const entry_1 = __webpack_require__(414); -const error_1 = __webpack_require__(415); -const entry_2 = __webpack_require__(416); +const deep_1 = __webpack_require__(412); +const entry_1 = __webpack_require__(415); +const error_1 = __webpack_require__(416); +const entry_2 = __webpack_require__(417); class Provider { constructor(_settings) { this._settings = _settings; @@ -47633,14 +47476,14 @@ exports.default = Provider; /***/ }), -/* 411 */ +/* 412 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(351); -const partial_1 = __webpack_require__(412); +const utils = __webpack_require__(352); +const partial_1 = __webpack_require__(413); class DeepFilter { constructor(_settings, _micromatchOptions) { this._settings = _settings; @@ -47702,13 +47545,13 @@ exports.default = DeepFilter; /***/ }), -/* 412 */ +/* 413 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const matcher_1 = __webpack_require__(413); +const matcher_1 = __webpack_require__(414); class PartialMatcher extends matcher_1.default { match(filepath) { const parts = filepath.split('/'); @@ -47747,13 +47590,13 @@ exports.default = PartialMatcher; /***/ }), -/* 413 */ +/* 414 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(351); +const utils = __webpack_require__(352); class Matcher { constructor(_patterns, _settings, _micromatchOptions) { this._patterns = _patterns; @@ -47804,13 +47647,13 @@ exports.default = Matcher; /***/ }), -/* 414 */ +/* 415 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(351); +const utils = __webpack_require__(352); class EntryFilter { constructor(_settings, _micromatchOptions) { this._settings = _settings; @@ -47871,13 +47714,13 @@ exports.default = EntryFilter; /***/ }), -/* 415 */ +/* 416 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(351); +const utils = __webpack_require__(352); class ErrorFilter { constructor(_settings) { this._settings = _settings; @@ -47893,13 +47736,13 @@ exports.default = ErrorFilter; /***/ }), -/* 416 */ +/* 417 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(351); +const utils = __webpack_require__(352); class EntryTransformer { constructor(_settings) { this._settings = _settings; @@ -47926,15 +47769,15 @@ exports.default = EntryTransformer; /***/ }), -/* 417 */ +/* 418 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const stream_1 = __webpack_require__(138); -const stream_2 = __webpack_require__(381); -const provider_1 = __webpack_require__(410); +const stream_2 = __webpack_require__(382); +const provider_1 = __webpack_require__(411); class ProviderStream extends provider_1.default { constructor() { super(...arguments); @@ -47964,14 +47807,14 @@ exports.default = ProviderStream; /***/ }), -/* 418 */ +/* 419 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const sync_1 = __webpack_require__(419); -const provider_1 = __webpack_require__(410); +const sync_1 = __webpack_require__(420); +const provider_1 = __webpack_require__(411); class ProviderSync extends provider_1.default { constructor() { super(...arguments); @@ -47994,15 +47837,15 @@ exports.default = ProviderSync; /***/ }), -/* 419 */ +/* 420 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsStat = __webpack_require__(382); -const fsWalk = __webpack_require__(387); -const reader_1 = __webpack_require__(409); +const fsStat = __webpack_require__(383); +const fsWalk = __webpack_require__(388); +const reader_1 = __webpack_require__(410); class ReaderSync extends reader_1.default { constructor() { super(...arguments); @@ -48044,7 +47887,7 @@ exports.default = ReaderSync; /***/ }), -/* 420 */ +/* 421 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -48108,13 +47951,13 @@ exports.default = Settings; /***/ }), -/* 421 */ +/* 422 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const pathType = __webpack_require__(422); +const pathType = __webpack_require__(423); const getExtensions = extensions => extensions.length > 1 ? `{${extensions.join(',')}}` : extensions[0]; @@ -48190,7 +48033,7 @@ module.exports.sync = (input, options) => { /***/ }), -/* 422 */ +/* 423 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -48240,7 +48083,7 @@ exports.isSymlinkSync = isTypeSync.bind(null, 'lstatSync', 'isSymbolicLink'); /***/ }), -/* 423 */ +/* 424 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -48248,9 +48091,9 @@ exports.isSymlinkSync = isTypeSync.bind(null, 'lstatSync', 'isSymbolicLink'); const {promisify} = __webpack_require__(112); const fs = __webpack_require__(134); const path = __webpack_require__(4); -const fastGlob = __webpack_require__(349); -const gitIgnore = __webpack_require__(424); -const slash = __webpack_require__(425); +const fastGlob = __webpack_require__(350); +const gitIgnore = __webpack_require__(425); +const slash = __webpack_require__(426); const DEFAULT_IGNORE = [ '**/node_modules/**', @@ -48367,7 +48210,7 @@ module.exports.sync = options => { /***/ }), -/* 424 */ +/* 425 */ /***/ (function(module, exports) { // A simple implementation of make-array @@ -48976,7 +48819,7 @@ if ( /***/ }), -/* 425 */ +/* 426 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -48994,7 +48837,7 @@ module.exports = path => { /***/ }), -/* 426 */ +/* 427 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -49047,7 +48890,7 @@ module.exports = { /***/ }), -/* 427 */ +/* 428 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -49069,7 +48912,7 @@ module.exports = path_ => { /***/ }), -/* 428 */ +/* 429 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -49088,7 +48931,7 @@ module.exports = (childPath, parentPath) => { /***/ }), -/* 429 */ +/* 430 */ /***/ (function(module, exports, __webpack_require__) { const assert = __webpack_require__(140) @@ -49454,12 +49297,12 @@ rimraf.sync = rimrafSync /***/ }), -/* 430 */ +/* 431 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const AggregateError = __webpack_require__(431); +const AggregateError = __webpack_require__(432); module.exports = async ( iterable, @@ -49542,13 +49385,13 @@ module.exports = async ( /***/ }), -/* 431 */ +/* 432 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const indentString = __webpack_require__(432); -const cleanStack = __webpack_require__(433); +const indentString = __webpack_require__(433); +const cleanStack = __webpack_require__(434); const cleanInternalStack = stack => stack.replace(/\s+at .*aggregate-error\/index.js:\d+:\d+\)?/g, ''); @@ -49596,7 +49439,7 @@ module.exports = AggregateError; /***/ }), -/* 432 */ +/* 433 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -49638,7 +49481,7 @@ module.exports = (string, count = 1, options) => { /***/ }), -/* 433 */ +/* 434 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -49685,12 +49528,12 @@ module.exports = (stack, options) => { /***/ }), -/* 434 */ +/* 435 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const readline = __webpack_require__(435); +const readline = __webpack_require__(283); const chalk = __webpack_require__(436); const cliCursor = __webpack_require__(439); const cliSpinners = __webpack_require__(441); @@ -50050,12 +49893,6 @@ module.exports.promise = (action, options) => { }; -/***/ }), -/* 435 */ -/***/ (function(module, exports) { - -module.exports = require("readline"); - /***/ }), /* 436 */ /***/ (function(module, exports, __webpack_require__) { @@ -50531,7 +50368,7 @@ exports.toggle = (force, writableStream) => { "use strict"; -const onetime = __webpack_require__(301); +const onetime = __webpack_require__(302); const signalExit = __webpack_require__(274); module.exports = onetime(() => { @@ -51850,15 +51687,10 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.RunCommand = void 0; - var _errors = __webpack_require__(164); - var _log = __webpack_require__(145); - var _parallelize = __webpack_require__(146); - var _projects = __webpack_require__(147); - /* * SPDX-License-Identifier: Apache-2.0 * @@ -51888,36 +51720,30 @@ var _projects = __webpack_require__(147); * specific language governing permissions and limitations * under the License. */ -const RunCommand = { + +const RunCommand = exports.RunCommand = { description: 'Run script defined in package.json in each package that contains that script.', name: 'run', - async run(projects, projectGraph, { extraArgs }) { const batchedProjects = (0, _projects.topologicallyBatchProjects)(projects, projectGraph); - if (extraArgs.length === 0) { throw new _errors.CliError('No script specified'); } - const scriptName = extraArgs[0]; const scriptArgs = extraArgs.slice(1); await (0, _parallelize.parallelizeBatches)(batchedProjects, async project => { if (project.hasScript(scriptName)) { _log.log.info(`[${project.name}] running "${scriptName}" script`); - await project.runScriptStreaming(scriptName, { args: scriptArgs }); - _log.log.success(`[${project.name}] complete`); } }); } - }; -exports.RunCommand = RunCommand; /***/ }), /* 458 */ @@ -51930,17 +51756,11 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.WatchCommand = void 0; - var _errors = __webpack_require__(164); - var _log = __webpack_require__(145); - var _parallelize = __webpack_require__(146); - var _projects = __webpack_require__(147); - var _watch = __webpack_require__(459); - /* * SPDX-License-Identifier: Apache-2.0 * @@ -51975,11 +51795,12 @@ var _watch = __webpack_require__(459); * Name of the script in the package/project package.json file to run during `osd watch`. */ const watchScriptName = 'osd:watch'; + /** * Name of the OpenSearch Dashboards project. */ - const opensearchDashboardsProjectName = 'opensearch-dashboards'; + /** * Command that traverses through list of available projects/packages that have `osd:watch` script in their * package.json files, groups them into topology aware batches and then processes theses batches one by one @@ -51990,49 +51811,38 @@ const opensearchDashboardsProjectName = 'opensearch-dashboards'; * the `osd:watch` script and eventually for the entire batch. Currently we support completion "markers" for * `webpack` and `tsc` only, for the rest we rely on predefined timeouts. */ - -const WatchCommand = { +const WatchCommand = exports.WatchCommand = { description: 'Runs `osd:watch` script for every project.', name: 'watch', - async run(projects, projectGraph) { const projectsToWatch = new Map(); - for (const project of projects.values()) { // We can't watch project that doesn't have `osd:watch` script. if (project.hasScript(watchScriptName)) { projectsToWatch.set(project.name, project); } } - if (projectsToWatch.size === 0) { throw new _errors.CliError(`There are no projects to watch found. Make sure that projects define 'osd:watch' script in 'package.json'.`); } - const projectNames = Array.from(projectsToWatch.keys()); + _log.log.info(`Running ${watchScriptName} scripts for [${projectNames.join(', ')}].`); - _log.log.info(`Running ${watchScriptName} scripts for [${projectNames.join(', ')}].`); // OpenSearch Dashboards should always be run the last, so we don't rely on automatic + // OpenSearch Dashboards should always be run the last, so we don't rely on automatic // topological batching and push it to the last one-entry batch manually. - - const shouldWatchOpenSearchDashboardsProject = projectsToWatch.delete(opensearchDashboardsProjectName); const batchedProjects = (0, _projects.topologicallyBatchProjects)(projectsToWatch, projectGraph); - if (shouldWatchOpenSearchDashboardsProject) { batchedProjects.push([projects.get(opensearchDashboardsProjectName)]); } - await (0, _parallelize.parallelizeBatches)(batchedProjects, async pkg => { const completionHint = await (0, _watch.waitUntilWatchIsReady)(pkg.runScriptStreaming(watchScriptName, { debug: false }).stdout); - _log.log.success(`[${pkg.name}] Initial build completed (${completionHint}).`); }); } - }; -exports.WatchCommand = WatchCommand; /***/ }), /* 459 */ @@ -52045,15 +51855,10 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.waitUntilWatchIsReady = waitUntilWatchIsReady; - var Rx = _interopRequireWildcard(__webpack_require__(8)); - var _operators = __webpack_require__(460); - -function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } - -function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } - +function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } +function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } /* * SPDX-License-Identifier: Apache-2.0 * @@ -52088,12 +51893,13 @@ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && * Number of milliseconds we wait before we fall back to the default watch handler. */ const defaultHandlerDelay = 3000; + /** * If default watch handler is used, then it's the number of milliseconds we wait for * any build output before we consider watch task ready. */ - const defaultHandlerReadinessTimeout = 2000; + /** * Describes configurable watch options. */ @@ -52107,16 +51913,11 @@ function getWatchHandlers(buildOutput$, { const defaultHandler = Rx.of(undefined).pipe((0, _operators.delay)(handlerReadinessTimeout), (0, _operators.map)(() => buildOutput$.pipe((0, _operators.timeout)(handlerDelay), (0, _operators.catchError)(() => Rx.of('timeout'))))); return [typescriptHandler, webpackHandler, defaultHandler]; } - function waitUntilWatchIsReady(stream, opts = {}) { const buildOutput$ = new Rx.Subject(); - const onDataListener = data => buildOutput$.next(data.toString('utf-8')); - const onEndListener = () => buildOutput$.complete(); - const onErrorListener = e => buildOutput$.error(e); - stream.once('end', onEndListener); stream.once('error', onErrorListener); stream.on('data', onDataListener); @@ -58173,17 +57974,11 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.runCommand = runCommand; - var _errors = __webpack_require__(164); - var _log = __webpack_require__(145); - var _projects = __webpack_require__(147); - -var _projects_tree = __webpack_require__(343); - +var _projects_tree = __webpack_require__(344); var _opensearch_dashboards = __webpack_require__(559); - /* * SPDX-License-Identifier: Apache-2.0 * @@ -58213,10 +58008,10 @@ var _opensearch_dashboards = __webpack_require__(559); * specific language governing permissions and limitations * under the License. */ + async function runCommand(command, config) { try { _log.log.debug(`Running [${command.name}] command from [${config.rootPath}]`); - const osd = await _opensearch_dashboards.OpenSearchDashboards.loadFrom(config.rootPath); const projects = osd.getFilteredProjects({ skipOpenSearchDashboardsPlugins: Boolean(config.options['skip-opensearch-dashboards-plugins']), @@ -58224,52 +58019,38 @@ async function runCommand(command, config) { exclude: toArray(config.options.exclude), include: toArray(config.options.include) }); - if (projects.size === 0) { _log.log.error(`There are no projects found. Double check project name(s) in '-i/--include' and '-e/--exclude' filters.`); - return process.exit(1); } - const projectGraph = (0, _projects.buildProjectGraph)(projects); - _log.log.debug(`Found ${projects.size.toString()} projects`); - _log.log.debug((0, _projects_tree.renderProjectsTree)(config.rootPath, projects)); - - await command.run(projects, projectGraph, { ...config, + await command.run(projects, projectGraph, { + ...config, osd }); } catch (error) { _log.log.error(`[${command.name}] failed:`); - if (error instanceof _errors.CliError) { _log.log.error(error.message); - const metaOutput = Object.entries(error.meta).map(([key, value]) => `${key}: ${value}`).join('\n'); - if (metaOutput) { _log.log.info('Additional debugging info:\n'); - _log.log.indent(2); - _log.log.info(metaOutput); - _log.log.indent(-2); } } else { _log.log.error(error); } - process.exit(1); } } - function toArray(value) { if (value == null) { return []; } - return Array.isArray(value) ? value : [value]; } @@ -58284,23 +58065,42 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.OpenSearchDashboards = void 0; - var _path = _interopRequireDefault(__webpack_require__(4)); - var _multimatch = _interopRequireDefault(__webpack_require__(560)); - -var _isPathInside = _interopRequireDefault(__webpack_require__(428)); - -var _yarn_lock = __webpack_require__(333); - +var _isPathInside = _interopRequireDefault(__webpack_require__(429)); +var _yarn_lock = __webpack_require__(334); var _projects = __webpack_require__(147); - -var _config = __webpack_require__(330); - +var _config = __webpack_require__(331); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - +function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } +function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } +function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } /* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Any modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ /* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ /** * Helper class for dealing with a set of projects as children of * the OpenSearch Dashboards project. The osd/pm is currently implemented to be @@ -58318,96 +58118,77 @@ class OpenSearchDashboards { rootPath }))); } - constructor(allWorkspaceProjects) { this.allWorkspaceProjects = allWorkspaceProjects; - _defineProperty(this, "opensearchDashboardsProject", void 0); - const opensearchDashboardsProject = allWorkspaceProjects.get('opensearch-dashboards'); - if (!opensearchDashboardsProject) { throw new TypeError('Unable to create OpenSearch Dashboards object without all projects, including the OpenSearch Dashboards project.'); } - this.opensearchDashboardsProject = opensearchDashboardsProject; } - /** make an absolute path by resolving subPath relative to the opensearch-dashboards repo */ - + /** make an absolute path by resolving subPath relative to the opensearch-dashboards repo */ getAbsolute(...subPath) { return _path.default.resolve(this.opensearchDashboardsProject.path, ...subPath); } - /** convert an absolute path to a relative path, relative to the opensearch-dashboards repo */ - + /** convert an absolute path to a relative path, relative to the opensearch-dashboards repo */ getRelative(absolute) { return _path.default.relative(this.opensearchDashboardsProject.path, absolute); } - /** get a copy of the map of all projects in the opensearch-dashboards workspace */ - + /** get a copy of the map of all projects in the opensearch-dashboards workspace */ getAllProjects() { return new Map(this.allWorkspaceProjects); } - /** determine if a project with the given name exists */ - + /** determine if a project with the given name exists */ hasProject(name) { return this.allWorkspaceProjects.has(name); } - /** get a specific project, throws if the name is not known (use hasProject() first) */ - + /** get a specific project, throws if the name is not known (use hasProject() first) */ getProject(name) { const project = this.allWorkspaceProjects.get(name); - if (!project) { throw new Error(`No package with name "${name}" in the workspace`); } - return project; } - /** get a project and all of the projects it depends on in a ProjectMap */ - + /** get a project and all of the projects it depends on in a ProjectMap */ getProjectAndDeps(name) { const project = this.getProject(name); return (0, _projects.includeTransitiveProjects)([project], this.allWorkspaceProjects); } - /** filter the projects to just those matching certain paths/include/exclude tags */ - + /** filter the projects to just those matching certain paths/include/exclude tags */ getFilteredProjects(options) { const allProjects = this.getAllProjects(); const filteredProjects = new Map(); const pkgJsonPaths = Array.from(allProjects.values()).map(p => p.packageJsonLocation); - const filteredPkgJsonGlobs = (0, _config.getProjectPaths)({ ...options, + const filteredPkgJsonGlobs = (0, _config.getProjectPaths)({ + ...options, rootPath: this.opensearchDashboardsProject.path }).map(g => _path.default.resolve(g, 'package.json')); const matchingPkgJsonPaths = (0, _multimatch.default)(pkgJsonPaths, filteredPkgJsonGlobs); - for (const project of allProjects.values()) { const pathMatches = matchingPkgJsonPaths.includes(project.packageJsonLocation); const notExcluded = !options.exclude.includes(project.name); const isIncluded = !options.include.length || options.include.includes(project.name); - if (pathMatches && notExcluded && isIncluded) { filteredProjects.set(project.name, project); } } - return filteredProjects; } - isPartOfRepo(project) { return project.path === this.opensearchDashboardsProject.path || (0, _isPathInside.default)(project.path, this.opensearchDashboardsProject.path); } - isOutsideRepo(project) { return !this.isPartOfRepo(project); } - resolveAllProductionDependencies(yarnLock, log) { const opensearchDashboardsDeps = (0, _yarn_lock.resolveDepsForProject)({ project: this.opensearchDashboardsProject, @@ -58419,9 +58200,7 @@ class OpenSearchDashboards { }); return new Map([...opensearchDashboardsDeps.entries()]); } - } - exports.OpenSearchDashboards = OpenSearchDashboards; /***/ }), @@ -58431,7 +58210,7 @@ exports.OpenSearchDashboards = OpenSearchDashboards; "use strict"; const minimatch = __webpack_require__(151); -const arrayUnion = __webpack_require__(347); +const arrayUnion = __webpack_require__(348); const arrayDiffer = __webpack_require__(561); const arrify = __webpack_require__(562); @@ -58517,7 +58296,6 @@ Object.defineProperty(exports, "buildProductionProjects", { return _build_production_projects.buildProductionProjects; } }); - var _build_production_projects = __webpack_require__(564); /***/ }), @@ -58531,25 +58309,15 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.buildProductionProjects = buildProductionProjects; - var _cpy = _interopRequireDefault(__webpack_require__(565)); - -var _del = _interopRequireDefault(__webpack_require__(345)); - +var _del = _interopRequireDefault(__webpack_require__(346)); var _path = __webpack_require__(4); - -var _config = __webpack_require__(330); - +var _config = __webpack_require__(331); var _fs = __webpack_require__(131); - var _log = __webpack_require__(145); - var _package_json = __webpack_require__(166); - var _projects = __webpack_require__(147); - function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - /* * SPDX-License-Identifier: Apache-2.0 * @@ -58579,6 +58347,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de * specific language governing permissions and limitations * under the License. */ + async function buildProductionProjects({ opensearchDashboardsRoot, buildRoot @@ -58587,9 +58356,7 @@ async function buildProductionProjects({ const projectGraph = (0, _projects.buildProjectGraph)(projects); const batchedProjects = (0, _projects.topologicallyBatchProjects)(projects, projectGraph); const projectNames = [...projects.values()].map(project => project.name); - _log.log.info(`Preparing production build for [${projectNames.join(', ')}]`); - for (const batch of batchedProjects) { for (const project of batch) { await deleteTarget(project); @@ -58598,6 +58365,7 @@ async function buildProductionProjects({ } } } + /** * Returns the subset of projects that should be built into the production * bundle. As we copy these into OpenSearch Dashboards 's `node_modules` during the build step, @@ -58605,8 +58373,6 @@ async function buildProductionProjects({ * we only include OpenSearch Dashboards 's transitive _production_ dependencies. If onlyOSS * is supplied, we omit projects with build.oss in their package.json set to false. */ - - async function getProductionProjects(rootPath) { const projectPaths = (0, _config.getProjectPaths)({ rootPath @@ -58615,8 +58381,9 @@ async function getProductionProjects(rootPath) { const projectsSubset = [projects.get('opensearch-dashboards')]; const productionProjects = (0, _projects.includeTransitiveProjects)(projectsSubset, projects, { onlyProductionDependencies: true - }); // We remove OpenSearch Dashboards , as we're already building OpenSearch Dashboards + }); + // We remove OpenSearch Dashboards , as we're already building OpenSearch Dashboards productionProjects.delete('opensearch-dashboards'); productionProjects.forEach(project => { if (project.getBuildConfig().oss === false) { @@ -58625,17 +58392,14 @@ async function getProductionProjects(rootPath) { }); return productionProjects; } - async function deleteTarget(project) { const targetDir = project.targetLocation; - if (await (0, _fs.isDirectory)(targetDir)) { await (0, _del.default)(targetDir, { force: true }); } } - async function buildProject(project) { // Explicitly defined targets override any bootstrap scripts if (project.hasBuildTargets()) { @@ -58644,6 +58408,7 @@ async function buildProject(project) { await project.runScript('build'); } } + /** * Copy all the project's files from its "intermediate build directory" and * into the build. The intermediate directory can either be the root of the @@ -58655,8 +58420,6 @@ async function buildProject(project) { * manage dependencies is that it will "dedupe" them, so we don't include * unnecessary copies of dependencies. */ - - async function copyToBuild(project, opensearchDashboardsRoot, buildRoot) { // We want the package to have the same relative location within the build const relativeProjectPath = (0, _path.relative)(opensearchDashboardsRoot, project.path); @@ -58666,14 +58429,15 @@ async function copyToBuild(project, opensearchDashboardsRoot, buildRoot) { dot: true, nodir: true, parents: true - }); // If a project is using an intermediate build directory, we special-case our + }); + + // If a project is using an intermediate build directory, we special-case our // handling of `package.json`, as the project build process might have copied // (a potentially modified) `package.json` into the intermediate build // directory already. If so, we want to use that `package.json` as the basis // for creating the production-ready `package.json`. If it's not present in // the intermediate build, we fall back to using the project's already defined // `package.json`. - const packageJson = (await (0, _fs.isFile)((0, _path.join)(buildProjectPath, 'package.json'))) ? await (0, _package_json.readPackageJson)(buildProjectPath) : project.json; await (0, _package_json.writePackageJson)(buildProjectPath, packageJson); } @@ -58850,7 +58614,7 @@ module.exports = (source, destination, { "use strict"; -const AggregateError = __webpack_require__(431); +const AggregateError = __webpack_require__(432); module.exports = async ( iterable, @@ -59406,8 +59170,8 @@ exports.convertPatternGroupToTask = convertPatternGroupToTask; Object.defineProperty(exports, "__esModule", { value: true }); var path = __webpack_require__(4); -var globParent = __webpack_require__(357); -var isGlob = __webpack_require__(358); +var globParent = __webpack_require__(358); +var isGlob = __webpack_require__(359); var micromatch = __webpack_require__(575); var GLOBSTAR = '**'; /** @@ -81901,7 +81665,7 @@ exports.flatten = flatten; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var merge2 = __webpack_require__(348); +var merge2 = __webpack_require__(349); /** * Merge multiple streams and propagate their errors into one stream in parallel. */ @@ -82766,7 +82530,7 @@ module.exports = function hasGlob(val) { * Licensed under the MIT License. */ -var isExtglob = __webpack_require__(359); +var isExtglob = __webpack_require__(360); module.exports = function isGlob(str) { if (typeof str !== 'string' || str === '') { diff --git a/packages/osd-pm/package.json b/packages/osd-pm/package.json index 90b7b9a29634..f6c25309e992 100644 --- a/packages/osd-pm/package.json +++ b/packages/osd-pm/package.json @@ -13,11 +13,9 @@ "prettier": "prettier --write './src/**/*.ts'" }, "devDependencies": { - "@babel/core": "^7.16.5", - "@babel/plugin-proposal-class-properties": "^7.16.5", - "@babel/plugin-proposal-object-rest-spread": "^7.16.5", - "@babel/preset-env": "^7.16.5", - "@babel/preset-typescript": "^7.16.5", + "@babel/core": "^7.22.9", + "@babel/preset-env": "^7.22.9", + "@babel/preset-typescript": "^7.22.9", "@node-rs/xxhash": "^1.3.0", "@osd/babel-preset": "1.0.0", "@osd/dev-utils": "1.0.0", @@ -58,6 +56,7 @@ "prettier": "^2.1.1", "read-pkg": "^5.2.0", "rxjs": "^6.5.5", + "semver": "^7.5.3", "strip-ansi": "^6.0.0", "strong-log-transformer": "^2.1.0", "tempy": "^0.3.0", diff --git a/packages/osd-pm/src/cli.ts b/packages/osd-pm/src/cli.ts index 420298aefb0d..63e9a1a274e7 100644 --- a/packages/osd-pm/src/cli.ts +++ b/packages/osd-pm/src/cli.ts @@ -42,7 +42,7 @@ function help() { dedent` usage: osd [] - By default commands are run for OpenSearch Dashboards itself, all packages in the 'packages/' + By default, commands are run for OpenSearch Dashboards itself, all packages in the 'packages/' folder and for all plugins in './plugins' and '../opensearch-dashboards-extra'. Available commands: @@ -57,6 +57,7 @@ function help() { -i, --include Include only specified projects. If left unspecified, it defaults to including all projects. --skip-opensearch-dashboards-plugins Filter all plugins in ./plugins and ../opensearch-dashboards-extra when running command. --no-cache Disable the bootstrap cache + --single-version Set single version validation method: 'strict', 'loose', 'ignore', or 'brute-force' --verbose Set log level to verbose --debug Set log level to debug --quiet Set log level to error @@ -92,6 +93,7 @@ export async function run(argv: string[]) { cache: true, }, boolean: ['prefer-offline', 'frozen-lockfile', 'cache'], + string: ['single-version'], }); const args = options._; diff --git a/packages/osd-pm/src/commands/bootstrap.ts b/packages/osd-pm/src/commands/bootstrap.ts index 54d48daa5f54..b3a0551f6111 100644 --- a/packages/osd-pm/src/commands/bootstrap.ts +++ b/packages/osd-pm/src/commands/bootstrap.ts @@ -69,7 +69,7 @@ export const BootstrapCommand: ICommand = { const yarnLock = await readYarnLock(osd); - await validateDependencies(osd, yarnLock); + await validateDependencies(osd, yarnLock, options['single-version']?.toLowerCase?.()); await linkProjectExecutables(projects, projectGraph); diff --git a/packages/osd-pm/src/utils/project.ts b/packages/osd-pm/src/utils/project.ts index 3eddcfd472bb..b6ced1517bf9 100644 --- a/packages/osd-pm/src/utils/project.ts +++ b/packages/osd-pm/src/utils/project.ts @@ -28,8 +28,8 @@ * under the License. */ -import fs from 'fs'; -import Path from 'path'; +import { existsSync, unlinkSync } from 'fs'; +import { resolve, relative } from 'path'; import { inspect } from 'util'; import { CliError } from './errors'; @@ -43,6 +43,7 @@ import { } from './package_json'; import { installInDir, + patchFile, runScriptInPackage, runScriptInPackageStreaming, yarnWorkspacesInfo, @@ -97,9 +98,9 @@ export class Project { this.json = Object.freeze(packageJson); this.path = projectPath; - this.packageJsonLocation = Path.resolve(this.path, 'package.json'); - this.nodeModulesLocation = Path.resolve(this.path, 'node_modules'); - this.targetLocation = Path.resolve(this.path, 'target'); + this.packageJsonLocation = resolve(this.path, 'package.json'); + this.nodeModulesLocation = resolve(this.path, 'node_modules'); + this.targetLocation = resolve(this.path, 'target'); this.version = this.json.version; this.productionDependencies = this.json.dependencies || {}; @@ -130,7 +131,7 @@ export class Project { if (dependentProjectIsInWorkspace) { expectedVersionInPackageJson = project.json.version; } else { - const relativePathToProject = normalizePath(Path.relative(this.path, project.path)); + const relativePathToProject = normalizePath(relative(this.path, project.path)); expectedVersionInPackageJson = `link:${relativePathToProject}`; } @@ -168,7 +169,7 @@ export class Project { * instead of everything located in the project directory. */ public getIntermediateBuildDirectory() { - return Path.resolve(this.path, this.getBuildConfig().intermediateBuildDirectory || '.'); + return resolve(this.path, this.getBuildConfig().intermediateBuildDirectory || '.'); } public getCleanConfig(): CleanConfig { @@ -196,14 +197,14 @@ export class Project { if (typeof raw === 'string') { return { - [this.name]: Path.resolve(this.path, raw), + [this.name]: resolve(this.path, raw), }; } if (typeof raw === 'object') { const binsConfig: { [k: string]: string } = {}; for (const binName of Object.keys(raw)) { - binsConfig[binName] = Path.resolve(this.path, raw[binName]); + binsConfig[binName] = resolve(this.path, raw[binName]); } return binsConfig; } @@ -261,6 +262,51 @@ export class Project { await this.removeExtraneousNodeModules(); } + /** + * Install a specific version of a dependency and update the package.json. + * When a range is not specified, ^ is used. The range is then + * placed in the package.json with intentionally no validation. + */ + public async installDependencyVersion( + depName: string, + version: string, + dev: boolean = false, + range?: string + ) { + log.info(`[${this.name}] running yarn to install ${depName}@${version}`); + + log.write(''); + + const rangeToUse = range || `^${version}`; + + const extraArgs = [`${depName}@${version}`]; + if (dev) extraArgs.push('--dev'); + + if (this.isWorkspaceProject) { + await installInDir(this.path); + } else { + await installInDir(this.path, extraArgs, true); + } + + log.info(`[${this.name}] updating manifests with ${depName}@${rangeToUse}`); + + await patchFile( + this.packageJsonLocation, + `"${depName}": "${version}"`, + `"${depName}": "${rangeToUse}"` + ); + // The lock-file of workspace packages are symlinked to the root project's and editing the one in the project suffices + await patchFile( + resolve(this.path, 'yarn.lock'), + `${depName}@${version}`, + `${depName}@${rangeToUse}` + ); + + log.write(''); + + await this.removeExtraneousNodeModules(); + } + /** * Yarn workspaces symlinks workspace projects to the root node_modules, even * when there is no depenency on the project. This results in unnecicary, and @@ -283,13 +329,13 @@ export class Project { unusedWorkspaces.forEach((name) => { const { dependencies, devDependencies } = this.json; - const nodeModulesPath = Path.resolve(this.nodeModulesLocation, name); + const nodeModulesPath = resolve(this.nodeModulesLocation, name); const isDependency = dependencies && dependencies.hasOwnProperty(name); const isDevDependency = devDependencies && devDependencies.hasOwnProperty(name); - if (!isDependency && !isDevDependency && fs.existsSync(nodeModulesPath)) { + if (!isDependency && !isDevDependency && existsSync(nodeModulesPath)) { log.debug(`No dependency on ${name}, removing link in node_modules`); - fs.unlinkSync(nodeModulesPath); + unlinkSync(nodeModulesPath); } }); } diff --git a/packages/osd-pm/src/utils/scripts.ts b/packages/osd-pm/src/utils/scripts.ts index cbb49875c021..7011c09fb585 100644 --- a/packages/osd-pm/src/utils/scripts.ts +++ b/packages/osd-pm/src/utils/scripts.ts @@ -28,6 +28,8 @@ * under the License. */ +import { createReadStream, createWriteStream, unlinkSync, renameSync } from 'fs'; +import { createInterface } from 'readline'; import { spawn, spawnStreaming } from './child_process'; import { Project } from './project'; @@ -45,8 +47,8 @@ interface WorkspacesInfo { /** * Install all dependencies in the given directory */ -export async function installInDir(directory: string, extraArgs: string[] = []) { - const options = ['install', '--non-interactive', ...extraArgs]; +export async function installInDir(directory: string, extraArgs: string[] = [], useAdd = false) { + const options = [useAdd ? 'add' : 'install', '--non-interactive', ...extraArgs]; // We pass the mutex flag to ensure only one instance of yarn runs at any // given time (e.g. to avoid conflicts). @@ -55,6 +57,40 @@ export async function installInDir(directory: string, extraArgs: string[] = []) }); } +/** + * Patch a file by replacing a given string + */ +export function patchFile( + filePath: string, + searchValue: string, + replacement: string +): Promise { + return new Promise(async (resolve, reject) => { + const patchWriter = createWriteStream(`${filePath}.patched`, { + flags: 'w', + }); + const fileReader = createInterface({ + input: createReadStream(filePath), + crlfDelay: Infinity, + }); + for await (const line of fileReader) { + if (line.includes(searchValue)) { + patchWriter.write(line.replace(searchValue, replacement) + '\n', 'utf8'); + } else { + patchWriter.write(line + '\n', 'utf8'); + } + } + + patchWriter.on('finish', () => resolve()); + patchWriter.on('error', reject); + + fileReader.close(); + patchWriter.end(); + unlinkSync(filePath); + renameSync(`${filePath}.patched`, filePath); + }); +} + /** * Run script in the given directory */ diff --git a/packages/osd-pm/src/utils/validate_dependencies.ts b/packages/osd-pm/src/utils/validate_dependencies.ts index 2264681b986c..930f48ef5947 100644 --- a/packages/osd-pm/src/utils/validate_dependencies.ts +++ b/packages/osd-pm/src/utils/validate_dependencies.ts @@ -29,9 +29,12 @@ */ // @ts-expect-error published types are useless -import { stringify as stringifyLockfile } from '@yarnpkg/lockfile'; +import { stringify as stringifyLockfile, parse as parseLockFile } from '@yarnpkg/lockfile'; import dedent from 'dedent'; import chalk from 'chalk'; +import path from 'path'; +import { readFileSync } from 'fs'; +import { satisfies, rcompare } from 'semver'; import { writeFile } from './fs'; import { OpenSearchDashboards } from '../utils/opensearch_dashboards'; @@ -40,8 +43,43 @@ import { log } from './log'; import { Project } from './project'; import { ITree, treeToString } from './projects_tree'; -export async function validateDependencies(osd: OpenSearchDashboards, yarnLock: YarnLock) { - // look through all of the packages in the yarn.lock file to see if +enum SingleVersionResolution { + STRICT = 'strict', + LOOSE = 'loose', + FORCE = 'force', + BRUTE_FORCE = 'brute-force', + IGNORE = 'ignore', +} + +export async function validateDependencies( + osd: OpenSearchDashboards, + yarnLock: YarnLock, + /* `singleVersionResolution` controls how violations of single-version-dependencies is applied. + * STRICT (default): throw an error and exit + * LOOSE: identify and install a single version that satisfies all ranges + * BRUTE_FORCE: identify and install the newest version + * IGNORE: show all errors without exiting + * + * `LOOSE`: + * Reconciles the various versions installed as a result of having multiple ranges for a dependency, by + * choosing one that satisfies all said ranges. Even though installing the chosen version updates the + * lock-files, no package.json changes would be needed. + * + * `BRUTE_FORCE`: + * With no care for reconciliation, the newest of the various versions installed is chosen, irrespective of + * whether it satisfies any of the ranges. Installing the chosen version updates the lock-files and a range + * in the form of `^` is applied to all `package.json` files that declared the dependency. + * + * `FORCE`: + * For each dependency, first LOOSE resolution is attempted but if that fails, BRUTE_FORCE is applied. + * + * `IGNORE`: + * Behaves just like `strict` by showing errors when different ranges of a package are marked as + * dependencies, but it does not terminate the script. + */ + singleVersionResolution: SingleVersionResolution = SingleVersionResolution.STRICT +) { + // look through all the packages in the yarn.lock file to see if // we have accidentally installed multiple lodash v4 versions const lodash4Versions = new Set(); const lodash4Reqs = new Set(); @@ -109,11 +147,17 @@ export async function validateDependencies(osd: OpenSearchDashboards, yarnLock: process.exit(1); } - // TODO: remove this once we move into a single package.json + let hasIssues = false; + // look through all the package.json files to find packages which have mismatched version ranges const depRanges = new Map>(); for (const project of osd.getAllProjects().values()) { - for (const [dep, range] of Object.entries(project.allDependencies)) { + for (const [dep, range] of Object.entries( + // Don't be bothered with validating dev-deps when validating single-version loosely + singleVersionResolution === SingleVersionResolution.LOOSE + ? project.productionDependencies + : project.allDependencies + )) { const existingDep = depRanges.get(dep); if (!existingDep) { depRanges.set(dep, [ @@ -138,21 +182,181 @@ export async function validateDependencies(osd: OpenSearchDashboards, yarnLock: } } - const duplicateRanges = Array.from(depRanges.entries()) - .filter(([, ranges]) => ranges.length > 1) - .reduce( - (acc: string[], [dep, ranges]) => [ - ...acc, - dep, - ...ranges.map( - ({ range, projects }) => ` ${range} => ${projects.map((p) => p.name).join(', ')}` - ), - ], - [] - ) - .join('\n '); - - if (duplicateRanges) { + const cachedManifests = new Map(); + const violatingSingleVersionDepRanges = new Map< + string, + Array<{ range: string; projects: Project[] }> + >(); + depRangesLoop: for (const [depName, ranges] of depRanges) { + // No violation if just a single range of a dependency is used + if (ranges.length === 1) continue; + + const installedVersions = new Set(); + const installedDepVersionsCache = new Map(); + const desiredRanges = new Map(); + + rangesLoop: for (const { range, projects } of ranges) { + for (const project of projects) { + if (!cachedManifests.has(project.path)) + cachedManifests.set( + project.path, + // If there are errors reading or parsing the lockfiles, don't catch and let them fall through + parseLockFile(readFileSync(path.join(project.path, 'yarn.lock'), 'utf-8')) + ); + const { object: deps } = cachedManifests.get(project.path); + if (deps?.[`${depName}@${range}`]?.version) { + installedVersions.add(deps[`${depName}@${range}`].version); + installedDepVersionsCache.set( + `${project.name}#${depName}`, + deps[`${depName}@${range}`].version + ); + } else { + log.warning(`Failed to find the installed version for ${depName}@${range}`); + // If we cannot read any one of the installed versions of a depName, there is no point in continuing with it + installedVersions.clear(); + desiredRanges.clear(); + break rangesLoop; + } + } + + desiredRanges.set(range, projects); + } + + // More than one range is used but couldn't get all the installed versions: call out violation + if (installedVersions.size === 0) { + violatingSingleVersionDepRanges.set(depName, ranges); + continue; // go to the next depRange + } + + if ( + singleVersionResolution === SingleVersionResolution.LOOSE || + // validating with force first acts like loose + singleVersionResolution === SingleVersionResolution.FORCE + ) { + if (installedVersions.size === 1) { + hasIssues = true; + + /* When validating single-version loosely, ignore multiple ranges when they result in the installation of + * a single version. + */ + log.info( + `Ignored single version requirement for ${depName} as all installations are using v${ + installedVersions.values().next().value + }.` + ); + + continue; // go to the next depRange + } + + const sortedInstalledVersion = Array.from(installedVersions).sort(rcompare); + const rangePatterns = Array.from(desiredRanges.keys()); + + for (const installedVersion of sortedInstalledVersion) { + if (rangePatterns.every((range) => satisfies(installedVersion, range))) { + // Install the version on all projects that have this dep; keep the original range. + for (const { range, projects } of ranges) { + for (const project of projects) { + // Don't bother updating anything if the desired version is already installed + if (installedDepVersionsCache.get(`${project.name}#${depName}`) === installedVersion) + continue; + + await project.installDependencyVersion( + depName, + installedVersion, + depName in project.devDependencies, + // When validating single-version loosely, when a version change is needed, the range shouldn't change + range + ); + } + } + + hasIssues = true; + + const conflictingRanges = ranges + .map(({ range, projects }) => `${range} => ${projects.map((p) => p.name).join(', ')}`) + .join('\n '); + log.warning(dedent` + + [single_version_dependencies] Multiple version ranges for package "${depName}" + were found across different package.json files. A suitable version, v${installedVersion}, was + identified and installed. + + The conflicting version ranges are: + ${conflictingRanges} + `); + + // A usable version was identified so no need to check the lower versions + continue depRangesLoop; // go to the next depRange + } + } + + /* Here because a suitable version was not found. When validating single-version loosely and here, give up. + * However, don't give up when validating with force and act like brute-force! + */ + if (singleVersionResolution === SingleVersionResolution.LOOSE) { + violatingSingleVersionDepRanges.set(depName, ranges); + continue; // go to the next depRange + } + } + + if ( + singleVersionResolution === SingleVersionResolution.BRUTE_FORCE || + // validating with force here means we failed to get results when acting loosely + singleVersionResolution === SingleVersionResolution.FORCE + ) { + const sortedInstalledVersion = Array.from(installedVersions).sort(rcompare); + + hasIssues = true; + + const suitableVersion = sortedInstalledVersion[0]; + const suitableRange = `^${suitableVersion}`; + + // Install the version on all projects that have this dep; use the suitable range. + for (const { projects } of ranges) { + for (const project of projects) { + await project.installDependencyVersion( + depName, + suitableVersion, + depName in project.devDependencies, + suitableRange + ); + } + } + + const conflictingRanges = ranges + .map(({ range, projects }) => `${range} => ${projects.map((p) => p.name).join(', ')}`) + .join('\n '); + log.warning(dedent` + + [single_version_dependencies] Multiple version ranges for package "${depName}" + were found across different package.json files. A version, v${suitableVersion}, was identified as the most recent + already installed replacement. All package.json files have been updated to indicate a dependency on \`${depName}@${suitableRange}\`. + + The conflicting version ranges are: + ${conflictingRanges} + `); + + continue; // go to the next depRange + } + + // Here because validation was not loose, forced, or brute-forced; just call out the vilation. + violatingSingleVersionDepRanges.set(depName, ranges); + } + + if (violatingSingleVersionDepRanges.size > 0) { + const duplicateRanges = Array.from(violatingSingleVersionDepRanges.entries()) + .reduce( + (acc: string[], [dep, ranges]) => [ + ...acc, + dep, + ...ranges.map( + ({ range, projects }) => ` ${range} => ${projects.map((p) => p.name).join(', ')}` + ), + ], + [] + ) + .join('\n '); + log.error(dedent` [single_version_dependencies] Multiple version ranges for the same dependency @@ -167,10 +371,12 @@ export async function validateDependencies(osd: OpenSearchDashboards, yarnLock: ${duplicateRanges} `); - process.exit(1); + if (singleVersionResolution !== SingleVersionResolution.IGNORE) { + process.exit(1); + } } - // look for packages that have the the `opensearchDashboards.devOnly` flag in their package.json + // look for packages that have the `opensearchDashboards.devOnly` flag in their package.json // and make sure they aren't included in the production dependencies of OpenSearch Dashboards const devOnlyProjectsInProduction = getDevOnlyProductionDepsTree(osd, 'opensearch-dashboards'); if (devOnlyProjectsInProduction) { @@ -187,7 +393,9 @@ export async function validateDependencies(osd: OpenSearchDashboards, yarnLock: process.exit(1); } - log.success('yarn.lock analysis completed without any issues'); + log.success( + hasIssues ? 'yarn.lock analysis completed' : 'yarn.lock analysis completed without any issues' + ); } function getDevOnlyProductionDepsTree(osd: OpenSearchDashboards, projectName: string) { diff --git a/packages/osd-std/src/json.ts b/packages/osd-std/src/json.ts index 7c619dcd1656..4761eadea2dd 100644 --- a/packages/osd-std/src/json.ts +++ b/packages/osd-std/src/json.ts @@ -285,6 +285,7 @@ export const parse = ( if ( numeralsAreNumbers && typeof val === 'number' && + isFinite(val) && (val < Number.MAX_SAFE_INTEGER || val > Number.MAX_SAFE_INTEGER) ) { numeralsAreNumbers = false; diff --git a/packages/osd-stylelint-config/config/global_selectors.json b/packages/osd-stylelint-config/config/global_selectors.json index 760717c8dab5..285f11f40a2b 100644 --- a/packages/osd-stylelint-config/config/global_selectors.json +++ b/packages/osd-stylelint-config/config/global_selectors.json @@ -23,9 +23,9 @@ "src/plugins/vis_builder/public/application/components/searchable_dropdown.scss", "src/plugins/vis_builder/public/application/components/side_nav.scss", "packages/osd-ui-framework/src/components/button/button_group/_button_group.scss", - "src/plugins/discover_legacy/public/application/components/sidebar/discover_sidebar.scss", - "src/plugins/discover_legacy/public/application/angular/doc_table/components/table_row/_open.scss", - "src/plugins/discover/public/application/components/data_grid/data_grid_table_cell_value.scss" + "src/plugins/discover/public/application/components/data_grid/data_grid_table_cell_value.scss", + "src/plugins/discover/public/application/view_components/canvas/discover_canvas.scss", + "src/plugins/discover/public/application/components/sidebar/discover_sidebar.scss" ] } -} +} \ No newline at end of file diff --git a/packages/osd-stylelint-config/config/restricted_properties.json b/packages/osd-stylelint-config/config/restricted_properties.json index d229764c8d88..5b1262328200 100644 --- a/packages/osd-stylelint-config/config/restricted_properties.json +++ b/packages/osd-stylelint-config/config/restricted_properties.json @@ -2,7 +2,6 @@ "font-family": { "explanation": "All \"font-family\" styles should be inherited from OUI themes and components. Remove the rule.", "approved": [ - "src/plugins/discover_legacy/public/application/_discover.scss", "src/plugins/maps_legacy/public/map/_leaflet_overrides.scss", "src/plugins/maps_legacy/public/map/_legend.scss", "src/plugins/opensearch_dashboards_legacy/public/font_awesome/font_awesome.scss", @@ -12,8 +11,7 @@ "src/plugins/data/public/ui/typeahead/_suggestion.scss", "src/plugins/vis_type_timeseries/public/application/components/_error.scss", "packages/osd-ui-framework/src/components/form/check_box/_check_box.scss", - "src/plugins/discover/public/application/components/doc_viewer/doc_viewer.scss", - "src/plugins/discover_legacy/public/application/components/doc_viewer/doc_viewer.scss" + "src/plugins/discover/public/application/components/doc_viewer/doc_viewer.scss" ] } } \ No newline at end of file diff --git a/packages/osd-stylelint-plugin-stylelint/src/utils/README.md b/packages/osd-stylelint-plugin-stylelint/src/utils/README.md new file mode 100644 index 000000000000..cbbe66f9288b --- /dev/null +++ b/packages/osd-stylelint-plugin-stylelint/src/utils/README.md @@ -0,0 +1,60 @@ +# OpenSearch Dashboards-related util files + +Below is an overview of each file: + +## `compliance_engine.ts` +- **Purpose:** + - Implements a compliance engine for tracking and validating rules. + - Provides functions to check if a node is tracked, retrieve compliance rules, and more. +- **Exported Functions:** + - `isTracked(rules: ValueBasedConfig, nodeInfo: { selector: string; prop: string }): boolean` + - `getComplianceRule(rules: ValueBasedConfig, nodeInfo: { selector: string; prop: string; value: string }): ComplianceRule | undefined` + + +## `extract_regex.ts` +- **Purpose:** + - Defines a function to extract a regular expression from a string value. +- **Exported Function:** + - `extractRegex(value: string): RegExp | undefined` + + +## `get_message.ts` +- **Purpose:** + - Contains functions to generate messages related to compliance tracking. +- **Exported Functions:** + - `getUntrackedMessage(nodeInfo: { selector: string; prop: string; value: string }): string` + - `getTrackedMessage(nodeInfo: { selector: string; prop: string; value: string }): string` + - `getNotCompliantMessage(message: string, explanation?: string): string` + + +## `get_rules_from_config.ts` +- **Purpose:** + - Provides functions to retrieve rules from a configuration file. +- **Exported Functions:** + - `getRulesFromConfig(configPath: string): ValueBasedConfig` + - `getRuleFromConfig(rules: FileBasedConfig, value: string): { approved?: string[]; explanation?: string } | undefined` + + +## `is_color_property.ts` +- **Purpose:** + - Checks if a given CSS property potentially modifies a color. + - Provides a function to get the parent rule of a declaration if the declaration is a color property. +- **Exported Functions:** + - `isColorProperty(prop: string): boolean` + - `getColorPropertyParent(decl: Declaration): Rule | undefined` + + +## `is_valid_options.ts` +- **Purpose:** + - Validates options for a Stylelint rule. +- **Exported Function:** + - `isValidOptions(postcssResult: any, ruleName: string, primaryOption: Record): boolean` + + +## `matches.ts` +- **Purpose:** + - Checks if a given value matches a specified pattern. +- **Exported Function:** + - `matches(matcher: string, value: string): boolean` + +This project follows the Apache-2.0 license. Contributions must be made under this license or a compatible open-source license. diff --git a/packages/osd-stylelint-plugin-stylelint/src/utils/compliance_engine.ts b/packages/osd-stylelint-plugin-stylelint/src/utils/compliance_engine.ts index 7b95e94e536c..edee4cd63288 100644 --- a/packages/osd-stylelint-plugin-stylelint/src/utils/compliance_engine.ts +++ b/packages/osd-stylelint-plugin-stylelint/src/utils/compliance_engine.ts @@ -18,6 +18,12 @@ export interface ComplianceRule { message: string; } +/** + * Retrieves a specific rule from the provided rules based on the node information. + * @param rules - The ValueBasedConfig containing rules. + * @param nodeInfo - The information about the node (selector and prop). + * @returns The specific rule if found, otherwise undefined. + */ const getRule = (rules: ValueBasedConfig, nodeInfo: { selector: string; prop: string }) => { const rule = rules[nodeInfo.prop]; if (!rule) { @@ -40,10 +46,22 @@ const getRule = (rules: ValueBasedConfig, nodeInfo: { selector: string; prop: st return rule[ruleKey]; }; +/** + * Checks if the specified node is tracked based on the provided rules. + * @param rules - The ValueBasedConfig containing rules. + * @param nodeInfo - The information about the node (selector and prop). + * @returns True if the node is tracked, otherwise false. + */ const isTracked = (rules: ValueBasedConfig, nodeInfo: { selector: string; prop: string }) => { return getRule(rules, nodeInfo) !== undefined; }; +/** + * Retrieves the compliance rule for the specified node and value. + * @param rules - The ValueBasedConfig containing rules. + * @param nodeInfo - The information about the node (selector, prop, and value). + * @returns The ComplianceRule object if a rule is found, otherwise undefined. + */ const getComplianceRule = ( rules: ValueBasedConfig, nodeInfo: { selector: string; prop: string; value: string } diff --git a/packages/osd-stylelint-plugin-stylelint/src/utils/extract_regex.ts b/packages/osd-stylelint-plugin-stylelint/src/utils/extract_regex.ts index 937b486ad940..049b541879df 100644 --- a/packages/osd-stylelint-plugin-stylelint/src/utils/extract_regex.ts +++ b/packages/osd-stylelint-plugin-stylelint/src/utils/extract_regex.ts @@ -3,6 +3,11 @@ * SPDX-License-Identifier: Apache-2.0 */ +/** + * Extracts a regular expression from the provided string value. + * @param value - The string value to extract the regular expression from. + * @returns The extracted regular expression if the value is a valid regex, otherwise undefined. + */ export const extractRegex = (value: string) => { if (!value.startsWith('/')) { return undefined; diff --git a/packages/osd-stylelint-plugin-stylelint/src/utils/get_message.ts b/packages/osd-stylelint-plugin-stylelint/src/utils/get_message.ts index b966f775e4d6..4a34793806df 100644 --- a/packages/osd-stylelint-plugin-stylelint/src/utils/get_message.ts +++ b/packages/osd-stylelint-plugin-stylelint/src/utils/get_message.ts @@ -9,12 +9,28 @@ * GitHub history for details. */ +/** + * Generates a message for untracked node information. + * @param nodeInfo - Information about the node (selector, prop, value). + * @returns The untracked message. + */ export const getUntrackedMessage = (nodeInfo: { selector: string; prop: string; value: string }) => `Untracked: "${nodeInfo.selector}.${nodeInfo.prop}: ${nodeInfo.value}"`; +/** + * Generates a message for tracked node information missing approval. + * @param nodeInfo - Information about the node (selector, prop, value). + * @returns The tracked but missing approval message. + */ export const getTrackedMessage = (nodeInfo: { selector: string; prop: string; value: string }) => `Tracked but missing approval: "${nodeInfo.selector}.${nodeInfo.prop}: ${nodeInfo.value}"`; +/** + * Generates a not compliant message with an optional explanation. + * @param message - The base not compliant message. + * @param explanation - Optional explanation for the not compliant message. + * @returns The not compliant message with or without an explanation. + */ export const getNotCompliantMessage = (message: string, explanation?: string) => { if (explanation) { return `${message} ${explanation}`; diff --git a/packages/osd-stylelint-plugin-stylelint/src/utils/get_rules_from_config.ts b/packages/osd-stylelint-plugin-stylelint/src/utils/get_rules_from_config.ts index 5467915cbabb..9b51ed310d9b 100644 --- a/packages/osd-stylelint-plugin-stylelint/src/utils/get_rules_from_config.ts +++ b/packages/osd-stylelint-plugin-stylelint/src/utils/get_rules_from_config.ts @@ -13,17 +13,35 @@ import path from 'path'; import { readFileSync } from 'fs'; import { matches } from './matches'; +/** + * configuration based on files. + */ export type FileBasedConfig = Record; + +/** + * configuration based on files. + */ export type ValueBasedConfig = Record< string, Record> >; +/** + * Retrieves rules from a configuration file. + * @param configPath - The path to the configuration file. + * @returns Parsed rules from the configuration file. + */ export const getRulesFromConfig = (configPath: string) => { const filePath = path.resolve(__dirname, configPath); return JSON.parse(readFileSync(filePath, 'utf-8')); }; +/** + * Retrieves a rule from a file-based configuration. + * @param rules - The file-based configuration rules. + * @param value - The value to match against the configuration rules. + * @returns The rule corresponding to the matched value, or undefined if no match is found. + */ export const getRuleFromConfig = (rules: FileBasedConfig, value: string) => { for (const key of Object.keys(rules)) { if (matches(key, value)) { diff --git a/packages/osd-stylelint-plugin-stylelint/src/utils/is_color_property.ts b/packages/osd-stylelint-plugin-stylelint/src/utils/is_color_property.ts index ef7ad9118f81..7b760ea977d7 100644 --- a/packages/osd-stylelint-plugin-stylelint/src/utils/is_color_property.ts +++ b/packages/osd-stylelint-plugin-stylelint/src/utils/is_color_property.ts @@ -57,10 +57,21 @@ const COLOR_PROPERTIES = [ * each one, therefore this is to optimize the linter to * skip any property that does not impact colors. */ + +/** + * Checks if a given CSS property potentially modifies a color. + * @param prop - The CSS property to check. + * @returns A boolean indicating whether the property can impact colors. + */ export const isColorProperty = (prop: string) => { return COLOR_PROPERTIES.includes(prop); }; +/** + * Gets the parent rule of a declaration if the declaration is a color property. + * @param decl - The CSS declaration. + * @returns The parent rule if the declaration is a color property, otherwise undefined. + */ export const getColorPropertyParent = (decl: Declaration) => { if (!isColorProperty(decl.prop)) { return undefined; diff --git a/packages/osd-stylelint-plugin-stylelint/src/utils/is_valid_options.ts b/packages/osd-stylelint-plugin-stylelint/src/utils/is_valid_options.ts index 15fae8862eaa..dd555667c273 100644 --- a/packages/osd-stylelint-plugin-stylelint/src/utils/is_valid_options.ts +++ b/packages/osd-stylelint-plugin-stylelint/src/utils/is_valid_options.ts @@ -13,6 +13,14 @@ import stylelint from 'stylelint'; const { validateOptions } = stylelint.utils; +/** + * Validates options for a Stylelint rule. + * + * @param postcssResult - The PostCSS result object. + * @param ruleName - The name of the Stylelint rule. + * @param primaryOption - The primary option for the rule. + * @returns {boolean} - A boolean indicating whether the options are valid. + */ export const isValidOptions = ( postcssResult: any, ruleName: string, diff --git a/packages/osd-stylelint-plugin-stylelint/src/utils/matches.ts b/packages/osd-stylelint-plugin-stylelint/src/utils/matches.ts index 02cc327615bd..fcf6f661bb5d 100644 --- a/packages/osd-stylelint-plugin-stylelint/src/utils/matches.ts +++ b/packages/osd-stylelint-plugin-stylelint/src/utils/matches.ts @@ -5,6 +5,13 @@ import { extractRegex } from './extract_regex'; +/** + * Checks if a given value matches a specified pattern. + * + * @param matcher - The pattern to match against (can be a string or regex). + * @param value - The value to check for a match. + * @returns {boolean} - A boolean indicating whether the value matches the pattern. + */ export const matches = (matcher: string, value: string) => { const regex = extractRegex(matcher); if (!regex) { diff --git a/packages/osd-telemetry-tools/src/tools/__snapshots__/extract_collectors.test.ts.snap b/packages/osd-telemetry-tools/src/tools/__snapshots__/extract_collectors.test.ts.snap index 4725be77533a..cf9cf12a75a5 100644 --- a/packages/osd-telemetry-tools/src/tools/__snapshots__/extract_collectors.test.ts.snap +++ b/packages/osd-telemetry-tools/src/tools/__snapshots__/extract_collectors.test.ts.snap @@ -9,7 +9,7 @@ Array [ "fetch": Object { "typeDescriptor": Object { "locale": Object { - "kind": 146, + "kind": 149, "type": "StringKeyword", }, }, @@ -31,7 +31,7 @@ Array [ "fetch": Object { "typeDescriptor": Object { "locale": Object { - "kind": 146, + "kind": 149, "type": "StringKeyword", }, }, @@ -53,7 +53,7 @@ Array [ "fetch": Object { "typeDescriptor": Object { "locale": Object { - "kind": 146, + "kind": 149, "type": "StringKeyword", }, }, @@ -75,7 +75,7 @@ Array [ "fetch": Object { "typeDescriptor": Object { "locale": Object { - "kind": 146, + "kind": 149, "type": "StringKeyword", }, }, @@ -98,11 +98,11 @@ Array [ "typeDescriptor": Object { "@@INDEX@@": Object { "count_1": Object { - "kind": 143, + "kind": 146, "type": "NumberKeyword", }, "count_2": Object { - "kind": 143, + "kind": 146, "type": "NumberKeyword", }, }, @@ -127,7 +127,7 @@ Array [ "fetch": Object { "typeDescriptor": Object { "locale": Object { - "kind": 146, + "kind": 149, "type": "StringKeyword", }, }, @@ -149,21 +149,21 @@ Array [ "fetch": Object { "typeDescriptor": Object { "flat": Object { - "kind": 146, + "kind": 149, "type": "StringKeyword", }, "my_objects": Object { "total": Object { - "kind": 143, + "kind": 146, "type": "NumberKeyword", }, "type": Object { - "kind": 131, + "kind": 133, "type": "BooleanKeyword", }, }, "my_str": Object { - "kind": 146, + "kind": 149, "type": "StringKeyword", }, }, @@ -196,44 +196,44 @@ Array [ "fetch": Object { "typeDescriptor": Object { "flat": Object { - "kind": 146, + "kind": 149, "type": "StringKeyword", }, "my_array": Object { "items": Object { "total": Object { - "kind": 143, + "kind": 146, "type": "NumberKeyword", }, "type": Object { - "kind": 131, + "kind": 133, "type": "BooleanKeyword", }, }, }, "my_index_signature_prop": Object { "@@INDEX@@": Object { - "kind": 143, + "kind": 146, "type": "NumberKeyword", }, }, "my_objects": Object { "total": Object { - "kind": 143, + "kind": 146, "type": "NumberKeyword", }, "type": Object { - "kind": 131, + "kind": 133, "type": "BooleanKeyword", }, }, "my_str": Object { - "kind": 146, + "kind": 149, "type": "StringKeyword", }, "my_str_array": Object { "items": Object { - "kind": 146, + "kind": 149, "type": "StringKeyword", }, }, diff --git a/packages/osd-test/package.json b/packages/osd-test/package.json index c1ee4f1687cd..5512203ca3c9 100644 --- a/packages/osd-test/package.json +++ b/packages/osd-test/package.json @@ -13,7 +13,7 @@ "devOnly": true }, "devDependencies": { - "@babel/cli": "^7.16.0", + "@babel/cli": "^7.22.9", "@osd/babel-preset": "1.0.0", "@osd/dev-utils": "1.0.0", "@osd/utils": "1.0.0", diff --git a/packages/osd-test/src/failed_tests_reporter/github_api.ts b/packages/osd-test/src/failed_tests_reporter/github_api.ts index c9df43472585..d6be81e0cfe4 100644 --- a/packages/osd-test/src/failed_tests_reporter/github_api.ts +++ b/packages/osd-test/src/failed_tests_reporter/github_api.ts @@ -28,7 +28,7 @@ * under the License. */ -import Axios, { AxiosRequestConfig, AxiosInstance } from 'axios'; +import Axios, { AxiosRequestConfig, AxiosInstance, AxiosHeaderValue } from 'axios'; import parseLinkHeader from 'parse-link-header'; import { ToolingLog, isAxiosResponseError, isAxiosRequestError } from '@osd/dev-utils'; @@ -206,7 +206,7 @@ export class GithubApi { ): Promise<{ status: number; statusText: string; - headers: Record; + headers: Record; data: T; }> { const executeRequest = !this.dryRun || options.safeForDryRun; @@ -231,7 +231,7 @@ export class GithubApi { const githubApiFailed = isAxiosResponseError(error) && error.response.status >= 500; const errorResponseLog = isAxiosResponseError(error) && - `[${error.config.method} ${error.config.url}] ${error.response.status} ${error.response.statusText} Error`; + `[${error.config?.method} ${error.config?.url}] ${error.response.status} ${error.response.statusText} Error`; if ((unableToReachGithub || githubApiFailed) && attempt < maxAttempts) { const waitMs = 1000 * attempt; diff --git a/packages/osd-test/src/functional_test_runner/lib/lifecycle_phase.ts b/packages/osd-test/src/functional_test_runner/lib/lifecycle_phase.ts index 02106a4b1dd1..f39f5ee6420b 100644 --- a/packages/osd-test/src/functional_test_runner/lib/lifecycle_phase.ts +++ b/packages/osd-test/src/functional_test_runner/lib/lifecycle_phase.ts @@ -44,16 +44,17 @@ export class LifecyclePhase { private readonly beforeSubj = new Rx.Subject(); public readonly before$ = this.beforeSubj.asObservable(); - private readonly afterSubj = this.options.singular - ? new Rx.ReplaySubject(1) - : new Rx.Subject(); - public readonly after$ = this.afterSubj.asObservable(); + private readonly afterSubj: Rx.Subject; + public readonly after$: Rx.Observable; constructor( private readonly options: { singular?: boolean; } = {} - ) {} + ) { + this.afterSubj = this.options.singular ? new Rx.ReplaySubject(1) : new Rx.Subject(); + this.after$ = this.afterSubj.asObservable(); + } public add(fn: (...args: Args) => Promise | void) { this.handlers.push(fn); diff --git a/packages/osd-ui-framework/Gruntfile.js b/packages/osd-ui-framework/Gruntfile.js index 10295493609c..a0d8bd0abee8 100644 --- a/packages/osd-ui-framework/Gruntfile.js +++ b/packages/osd-ui-framework/Gruntfile.js @@ -29,7 +29,7 @@ */ const { strip } = require('comment-stripper'); -const sass = require('node-sass'); +const sass = require('sass-embedded'); const postcss = require('postcss'); const postcssConfig = require('@osd/optimizer/postcss.config.js'); @@ -70,6 +70,26 @@ module.exports = function (grunt) { grunt.registerTask('prodBuild', ['clean:target', 'copy:makeProdBuild', 'babel:prodBuild']); + const uiFrameworkCompile = async (src, dest) => { + try { + const { css: compiledCSS } = await sass.compileAsync(src); + const result = await postcss([postcssConfig]).process( + strip(compiledCSS, { language: 'css' }), + { + from: src, + to: dest, + } + ); + + grunt.file.write(dest, result.css); + if (result.map) { + grunt.file.write(`${dest}.map`, result.map); + } + } catch (ex) { + grunt.log.error(ex); + } + }; + grunt.registerTask('compileCss', function () { const done = this.async(); Promise.all([ @@ -79,34 +99,4 @@ module.exports = function (grunt) { uiFrameworkCompile('src/kui_next_dark.scss', 'dist/kui_next_dark.css'), ]).then(done); }); - - function uiFrameworkCompile(src, dest) { - return new Promise((resolve) => { - sass.render( - { - file: src, - }, - function (error, result) { - if (error) { - grunt.log.error(error); - } - - postcss([postcssConfig]) - .process(strip(result.css.toString('utf8'), { language: 'css' }), { - from: src, - to: dest, - }) - .then((result) => { - grunt.file.write(dest, result.css); - - if (result.map) { - grunt.file.write(`${dest}.map`, result.map); - } - - resolve(); - }); - } - ); - }); - } }; diff --git a/packages/osd-ui-framework/package.json b/packages/osd-ui-framework/package.json index 156ccbbdfcd6..68b2950369dd 100644 --- a/packages/osd-ui-framework/package.json +++ b/packages/osd-ui-framework/package.json @@ -23,7 +23,7 @@ "enzyme-adapter-react-16": "^1.9.1" }, "devDependencies": { - "@elastic/eui": "npm:@opensearch-project/oui@1.3.0", + "@elastic/eui": "npm:@opensearch-project/oui@1.5.1", "@osd/babel-preset": "1.0.0", "@osd/optimizer": "1.0.0", "comment-stripper": "^0.0.4", @@ -31,8 +31,8 @@ "grunt-babel": "^8.0.0", "grunt-contrib-clean": "^2.0.0", "grunt-contrib-copy": "^1.0.0", - "node-sass": "npm:@amoo-miki/node-sass@9.0.0-libsass-3.6.5", - "sass-loader": "npm:@amoo-miki/sass-loader@10.4.1-node-sass-9.0.0-libsass-3.6.5", + "sass-embedded": "1.66.1", + "sass-loader": "npm:@amoo-miki/sass-loader@10.4.1-node-sass-9.0.0-libsass-3.6.5-with-sass-embedded.rc1", "postcss": "^8.4.5", "sinon": "^7.4.2" } diff --git a/packages/osd-ui-framework/src/components/local_nav/_local_date_picker.scss b/packages/osd-ui-framework/src/components/local_nav/_local_date_picker.scss index 0a38a115c4a0..ec60ffb4a918 100644 --- a/packages/osd-ui-framework/src/components/local_nav/_local_date_picker.scss +++ b/packages/osd-ui-framework/src/components/local_nav/_local_date_picker.scss @@ -54,19 +54,6 @@ .kuiDatePickerRowCell { padding: 0; text-align: center; - - /** - * This state class exists to support weird angular-bootstrap datepicker functionality, - * in which you can't select a day on the "From" calendar if it falls after the selected day in - * the "To" calendar (and vice versa, you can't select a "To" day if it is before the "From" day). - */ - &.kuiDatePickerRowCell-isBlocked { - cursor: not-allowed; - - .kuiDatePickerRowCellContent { - pointer-events: none; - } - } } /** diff --git a/packages/osd-ui-framework/src/components/local_nav/_local_search.scss b/packages/osd-ui-framework/src/components/local_nav/_local_search.scss index c1355a5f57c4..097d96738325 100644 --- a/packages/osd-ui-framework/src/components/local_nav/_local_search.scss +++ b/packages/osd-ui-framework/src/components/local_nav/_local_search.scss @@ -43,7 +43,7 @@ */ .kuiLocalSearchAssistedInput__assistance { position: absolute; - right: $kuiFormControlHorizontalPadding / 2; + right: calc($kuiFormControlHorizontalPadding / 2); top: 50%; /* 1 */ z-index: 2; transform: translateY(-50%); /* 1 */ diff --git a/packages/osd-ui-shared-deps/entry.js b/packages/osd-ui-shared-deps/entry.js index 813fe512a554..8afb2b08da6c 100644 --- a/packages/osd-ui-shared-deps/entry.js +++ b/packages/osd-ui-shared-deps/entry.js @@ -30,16 +30,13 @@ require('./polyfills'); -// must load before angular export const Jquery = require('jquery'); window.$ = window.jQuery = Jquery; require('./flot_charts'); // stateful deps export const OsdI18n = require('@osd/i18n'); -export const OsdI18nAngular = require('@osd/i18n/angular'); export const OsdI18nReact = require('@osd/i18n/react'); -export const Angular = require('angular'); export const Moment = require('moment'); export const MomentTimezone = require('moment-timezone/moment-timezone'); export const OsdMonaco = require('@osd/monaco'); diff --git a/packages/osd-ui-shared-deps/index.js b/packages/osd-ui-shared-deps/index.js index 1ebd54a55a97..36218a28d4eb 100644 --- a/packages/osd-ui-shared-deps/index.js +++ b/packages/osd-ui-shared-deps/index.js @@ -40,9 +40,7 @@ exports.darkCssDistFilename = 'osd-ui-shared-deps.v7.dark.css'; exports.darkV8CssDistFilename = 'osd-ui-shared-deps.v8.dark.css'; exports.externals = { // stateful deps - angular: '__osdSharedDeps__.Angular', '@osd/i18n': '__osdSharedDeps__.OsdI18n', - '@osd/i18n/angular': '__osdSharedDeps__.OsdI18nAngular', '@osd/i18n/react': '__osdSharedDeps__.OsdI18nReact', jquery: '__osdSharedDeps__.Jquery', moment: '__osdSharedDeps__.Moment', diff --git a/packages/osd-ui-shared-deps/package.json b/packages/osd-ui-shared-deps/package.json index fca9abd7c537..1c0a69be93ee 100644 --- a/packages/osd-ui-shared-deps/package.json +++ b/packages/osd-ui-shared-deps/package.json @@ -10,14 +10,13 @@ }, "dependencies": { "@elastic/charts": "31.1.0", - "@elastic/eui": "npm:@opensearch-project/oui@1.3.0", - "@elastic/numeral": "^2.5.0", + "@elastic/eui": "npm:@opensearch-project/oui@1.5.1", + "@elastic/numeral": "npm:@amoo-miki/numeral@2.6.0", "@opensearch/datemath": "5.0.3", "@osd/i18n": "1.0.0", "@osd/monaco": "1.0.0", "abortcontroller-polyfill": "^1.4.0", - "angular": "^1.8.2", - "axios": "^0.27.2", + "axios": "^1.6.1", "compression-webpack-plugin": "npm:@amoo-miki/compression-webpack-plugin@4.0.1-rc.1", "core-js": "^3.6.5", "custom-event-polyfill": "^0.3.0", @@ -47,9 +46,9 @@ "css-loader": "^5.2.7", "del": "^6.1.1", "loader-utils": "^2.0.4", - "node-sass": "npm:@amoo-miki/node-sass@9.0.0-libsass-3.6.5", - "sass-loader": "npm:@amoo-miki/sass-loader@10.4.1-node-sass-9.0.0-libsass-3.6.5", + "sass-embedded": "1.66.1", + "sass-loader": "npm:@amoo-miki/sass-loader@10.4.1-node-sass-9.0.0-libsass-3.6.5-with-sass-embedded.rc1", "val-loader": "^2.1.2", "webpack": "npm:@amoo-miki/webpack@4.46.0-rc.2" } -} +} \ No newline at end of file diff --git a/packages/osd-ui-shared-deps/webpack.config.js b/packages/osd-ui-shared-deps/webpack.config.js index d9bfd81af523..80e7aeef9c44 100644 --- a/packages/osd-ui-shared-deps/webpack.config.js +++ b/packages/osd-ui-shared-deps/webpack.config.js @@ -131,6 +131,17 @@ exports.getWebpackConfig = ({ dev = false } = {}) => ({ }, ], }, + { + test: /worker_proxy_service\.js$/, + exclude: /node_modules/, + use: { + loader: 'babel-loader', + options: { + babelrc: false, + presets: [require.resolve('@osd/babel-preset/webpack_preset')], + }, + }, + }, ], }, diff --git a/release-notes/opensearch-dashboards.release-notes-1.3.13.md b/release-notes/opensearch-dashboards.release-notes-1.3.13.md new file mode 100644 index 000000000000..8ad93c19ba22 --- /dev/null +++ b/release-notes/opensearch-dashboards.release-notes-1.3.13.md @@ -0,0 +1,24 @@ +# Version 1.3.13 Release Notes + +### 🛡 Security + +- [CVE-2019-11358] Bump version of `tinygradient` from `0.4.3` to `1.1.5` ([#4571](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4751)) +- [CVE-2023-26136] Bump `word-wrap` from `1.2.3` to `1.2.4` ([#5002](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5002)) +- [CVE-2022-21670] Bump `markdown-it` from `10.0.0` to `12.3.2` ([#5016](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5016)) +- [CVE-2022-33987] Partially fix security issues for `got` by bumping `@elastic/makelogs` from `6.0.0` to `6.1.1` and updating yarn.lock ([#5006](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5006)) +- Bump `yo` from `2.0.6` to `3.1.1` ([#5005]( https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5005)) +- [CVE-2023-0842] Bump `xml2js` from `0.4.22` to `0.6.2` ([#5024](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5024)) + +### 📈 Features/Enhancements + +### 🐛 Bug Fixes + +### 🚞 Infrastructure + +### 📝 Documentation + +### 🛠 Maintenance + +- [Version] Increment version to 1.3.13 ([#4721](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4721)) +- [Chore] Add company.net to exclusion list in linkchecker ([#4704](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4704)) +- [Chore] Exclude checking dead link in linkchecker ([#4868](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4868)) diff --git a/release-notes/opensearch-dashboards.release-notes-1.3.14.md b/release-notes/opensearch-dashboards.release-notes-1.3.14.md new file mode 100644 index 000000000000..441b7a602257 --- /dev/null +++ b/release-notes/opensearch-dashboards.release-notes-1.3.14.md @@ -0,0 +1,19 @@ +# Version 1.3.14 Release Notes + +### 🛡 Security + +- [CVE-2023-46234] Bump `eslint-import-resolver-webpack` from `0.11.1` to `0.13.8` and `browserify-sign` from `4.2.1` to `4.2.2` ([#5414](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5414/)) +- [CVE-2023-45133] Add package resolution for `@babel/traverse` to `7.23.2` to fix vulnerability ([#5309](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5309)) +- [CVE-2017-16137] Bump `debug` versions via yarn updates and resolutions ([#5573](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5573)) + +### 📈 Features/Enhancements + +### 🐛 Bug Fixes + +### 🚞 Infrastructure + +### 📝 Documentation + +### 🛠 Maintenance + +- [Version] Increment version to 1.3.14 ([#5531](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5531)) diff --git a/release-notes/opensearch-dashboards.release-notes-2.11.0.md b/release-notes/opensearch-dashboards.release-notes-2.11.0.md new file mode 100644 index 000000000000..b1f605edfccb --- /dev/null +++ b/release-notes/opensearch-dashboards.release-notes-2.11.0.md @@ -0,0 +1,39 @@ +## Version 2.11.0 Release Notes + +### 🛡 Security + +- [CVE-2022-25869] Remove AngularJS `1.8` ([#5086](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5086)) + +### 📈 Features/Enhancements + +- [Console] Add support for JSON with long numerals ([#4562](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4562)) +- [Data] Add `DataSource` service and `DataSourceSelector` for multiple datasource support ([#5167](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5167)) + +### 🐛 Bug Fixes + +- Bump `agentkeepalive` to `4.5.0` to solve a problem preventing the use `https://ip` in `opensearch.hosts` ([#4949](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4949)) +- [Data Explorer][Discover] Add `onQuerySubmit` to top nav and allow force update to embeddable ([#5160](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5160)) +- [Data Explorer][Discover] Automatically load default index pattern ([#5171](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5171)) +- [Data Explorer][Discover] Fix total hits issue for no time based data ([#5087](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5087)) +- [Data Explorer][Discover] Allow data grid to auto adjust size based on fetched data count ([#5191](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5191)) +- [Data Explorer][Discover] Allow filter and query persist when refresh page or paste url to a new tab ([#5206](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5206)) +- [Data Explorer][Discover] Fix misc navigation issues ([#5168](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5168)) +- [Data Explorer][Discover] Fix mobile view ([#5168](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5168)) +- [Table Visualization] Fix width of multiple tables when rendered in column view ([#4638](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4638)) +- [Table Visualization] Fix filter actions on data table vis cells ([#4837](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4837)) +- [Vis Augmenter] Fix errors in conditions for activating `vizAugmenter` ([#5213](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5213)) +- [Vis Augmenter] Fix `visAugmenter` forming empty key-value pairs in its calls to the `SavedObject` API ([#5190](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5190)) + +### 🚞 Infrastructure + +- [CI] Add `NODE_OPTIONS` and disable disk allocation threshold ([#5172](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5172)) + +### 🛠 Maintenance + +- [Version] Version increment from 2.10 to 2.11 ([#4975](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4975)) + +### 🔩 Tests + +- [Functional][Doc Views] Remove angular code from `plugin_functional` and update tests ([#5221](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5221)) +- [Unit][Data Explorer][Discover] Fix wrong test due to time conversion ([#5174](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5174)) +- [Unit][Data Explorer][Discover]Fix `buildPointSeriesData` unit test fails due to local timezone ([#4992](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4992)) diff --git a/release-notes/opensearch-dashboards.release-notes-2.11.1.md b/release-notes/opensearch-dashboards.release-notes-2.11.1.md new file mode 100644 index 000000000000..dd92226bfdc9 --- /dev/null +++ b/release-notes/opensearch-dashboards.release-notes-2.11.1.md @@ -0,0 +1,25 @@ +## Version 2.11.1 Release Notes + +### 🛡 Security + +- [CVE-2023-45133] Add package resolution for `@babel/traverse` to `7.23.2` to fix vulnerability ([#5309](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5309)) +- [CVE-2023-46234] Bump `eslint-import-resolver-webpack` from `0.11.1` to `0.13.8` and `browserify-sign` from `4.2.1` to `4.2.2` ([#5414](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5414/)) + +### 📈 Features/Enhancements + +### 🐛 Bug Fixes + +- Fix navigation issue across dashboards ([#5435](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5435)) +- [Discover] Fix table panel auto-sizing ([#5441](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5441)) + +### 🚞 Infrastructure + +- [CI][Test] Add plugin functional tests on GitHub Actions ([#5383](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5383)) + +### 📝 Documentation + +- Add Release Notes and update CHANGELOG.md for 2.11.1 ([#5486](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5486)) + +### 🔩 Tests + +### 🛠 Maintenance diff --git a/release-notes/opensearch-dashboards.release-notes-2.12.0.md b/release-notes/opensearch-dashboards.release-notes-2.12.0.md new file mode 100644 index 000000000000..15c2f4068c58 --- /dev/null +++ b/release-notes/opensearch-dashboards.release-notes-2.12.0.md @@ -0,0 +1,74 @@ +## Version 2.12.0 Release Notes + +### 🛡 Security + +- Add support for TLS v1.3 ([#5133](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5133)) +- [CVE-2020-8203] Bump `cheerio` from `0.22.0` to `1.0.0-rc.1` to fix vulnerable `lodash` dependency ([#5797](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5797)) +- [CVE-2023-52079] Bump `msgpackr` from `1.9.7` to `1.10.1` ([#5803](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5803)) + +### 📈 Features/Enhancements + +- Add support for read-only mode through tenants ([#4498](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4498)) +- [Decouple] Add new cross compatibility check core service which export functionality for plugins to verify if their OpenSearch plugin counterpart is installed on the cluster or has incompatible version to configure the plugin behavior([#4710](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/4710)) +- [Workspace] Add core workspace service module to enable the implementation of workspace features within OSD plugins ([#5092](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5092)) +- [Custom Branding] Allow relative URL for logos ([#5572](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5572)) +- [Discover] Display inner properties in the left navigation bar [#5429](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5429) +- Replace OuiSelect component with OuiSuperSelect in data-source plugin ([#5315](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5315)) +- [Discover] Enhance the data source selector with added sorting functionality ([#5609](https://github.com/opensearch-project/OpenSearch-Dashboards/issues/5609)) +- [Multiple Datasource] Add datasource picker component and use it in devtools and tutorial page when multiple datasource is enabled ([#5756](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5756)) +- [Discover] Add customizable pagination options based on Discover UI settings [#5610](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5610) +- [PM] Enhance single version requirements imposed during bootstrapping ([#5675](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5675)) +- Change SavedObjects' Import API to allow selecting a data source when uploading files ([#5777](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5777)) +- [Multiple Datasource] Add datasource picker to import saved object flyout when multiple data source is enabled ([#5781](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5781)) +- [Discover] Revert to legacy discover table and add toggle to new discover table ([#5789](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5789)) +- [Discover] Add collapsible and resizeable sidebar ([#5789](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5789)) +- [Discover] Update fonts of the datagrid ([#5841](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5841)) + +### 🐛 Bug Fixes + +- [BUG][data] Support custom filters with heterogeneous data fields ([5577](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5577)) +- [BUG] Fix filtering issue in data source selector ([5484](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5484)) +- Modify data source text for dev tool ([#5475](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5475)) +- Fix missing border for header navigation control on right ([#5450](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5450)) +- [Fix] Fix duplicate icon rendering in Markdown Visualization Editor ([#5511](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5511)) +- [Discover] Fix inactive state on 'Discover' tab in side navigation menu ([#5432](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5432)) +- [BUG][data] Fix empty suggestion history when querying in search bar [#5349](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5349) +- [Console] Fix dev tool console autocomplete not loading issue for aliases ([#5568](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5568)) +- [BUG][discover] Fix displayed text in `selected fields` when removing columns from canvas [#5537](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5537) +- [BUG] Remove duplicate sample data as id 90943e30-9a47-11e8-b64d-95841ca0b247 ([5668](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5668)) +- [BUG][multiple datasource] Fix datasource testing connection unexpectedly passed with wrong endpoint [#5663](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5663) +- [BUG][discover] Show 0 filters when there are no active filters ([#5508](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5508)) +- [BUG][discover] Fix advanced setting `discover:modifyColumnsOnSwitch` ([#5508](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5508)) +- [Discover] Fix missing index pattern field from breaking Discover [#5626](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5626) +- [Table Visualization] Fix filter action buttons for split table aggregations ([#5619](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5619)) +- [BUG][discover] Fix Discover table panel not adjusting its size automatically when the time range changes ([#5789](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5789)) +- [BUG] Fix incorrect number of rows when changing from a search with few results to more results ([#5789](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5789)) +- [BUG] Fix copying data from columns in Discover including extra data ([#5789](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5789)) +- [BUG] Fix no line wrapping when displaying fields in Discover datagrid ([#5789](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5789)) +- [BUG] Fix 'truncate:maxHeight' not working in Discover since 2.10.0 ([#5789](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5789)) +- [BUG] Fix UI glitch when mouseover Discover datagrid element ([#5789](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5789)) +- Fix a broken link by updating a typo ([#5517](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5517)) + +### 🚞 Infrastructure + +- [Chore] Add `--security` for `opensearch snapshot` and `opensearch_dashboards` to configure local setup with the security plugin ([#5451](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5451)) +- [Chore] Update default dev environment security credentials ([#5736](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5736)) + +### 🛠 Maintenance + +- Add @SuZhou-Joe as a maintainer. ([#5594](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5594)) +- [Data Explorer] Add Readme for Data Explorer ([#5273](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5273)) +- Replace `node-sass` with `sass-embedded` ([#5338](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5338)) +- Add documentation on files in util in addition to a README.md with an overview of each file and its exported functions ([#5540](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5540)) +- Bump `OUI` to `1.5.1` ([#5862](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5862)) +- Remove `ui-select` dev dependency ([#5660](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5660)) +- Move @seanneumann to emeritus maintainer ([#5634](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5634)) +- Rename `withLongNumerals` to `withLongNumeralsSupport` in `HttpFetchOptions` [#5592](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5592) + +### 🔩 Tests + +- [Home] Add more unit tests for other complications of overview ([#5418](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5418)) + +### 🪛 Refactoring + +- [UiSharedDeps] Standardize theme JSON imports to be light/dark-mode aware ([#5662](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5662)) diff --git a/scripts/bwctest_osd.sh b/scripts/bwctest_osd.sh index 6185c3af6265..b635465adea8 100755 --- a/scripts/bwctest_osd.sh +++ b/scripts/bwctest_osd.sh @@ -5,9 +5,9 @@ set -e -. scripts/bwc/utils.sh -. scripts/bwc/opensearch_service.sh -. scripts/bwc/opensearch_dashboards_service.sh +. scripts/common/utils.sh +. scripts/common/opensearch_service.sh +. scripts/common/opensearch_dashboards_service.sh . scripts/bwc/generate_test_data.sh # For every release, add sample data and new version below: diff --git a/scripts/bwc/opensearch_dashboards_service.sh b/scripts/common/opensearch_dashboards_service.sh similarity index 100% rename from scripts/bwc/opensearch_dashboards_service.sh rename to scripts/common/opensearch_dashboards_service.sh diff --git a/scripts/bwc/opensearch_service.sh b/scripts/common/opensearch_service.sh similarity index 100% rename from scripts/bwc/opensearch_service.sh rename to scripts/common/opensearch_service.sh diff --git a/scripts/bwc/utils.sh b/scripts/common/utils.sh similarity index 100% rename from scripts/bwc/utils.sh rename to scripts/common/utils.sh diff --git a/scripts/cypress_tests.sh b/scripts/cypress_tests.sh new file mode 100644 index 000000000000..e4c2cbfc9352 --- /dev/null +++ b/scripts/cypress_tests.sh @@ -0,0 +1,95 @@ +#!/bin/bash + +# Copyright OpenSearch Contributors +# SPDX-License-Identifier: Apache-2.0 + +set -e + +. $OSD_PATH/scripts/common/utils.sh +. $OSD_PATH/scripts/common/opensearch_service.sh +. $OSD_PATH/scripts/common/opensearch_dashboards_service.sh + +CWD=$(pwd) +CREDENTIAL="admin:myStrongPassword123!" + +if [ $SECURITY_ENABLED == "false" ]; +then + OPENSEARCH_MSG="\"status\":\"green\"" + OPENSEARCH_URL="http://localhost:9200/_cluster/health" + OPENSEARCH_ARGS="" +else + OPENSEARCH_MSG="\"status\":\"yellow\"" + OPENSEARCH_URL="https://localhost:9200/_cluster/health" + OPENSEARCH_ARGS="-u $CREDENTIAL --insecure" +fi +DASHBOARDS_URL="http://localhost:5601/api/status" + +# Starts OpenSearch, if verifying a distribution it will install the certs then start. +function run_opensearch() { + echo "[ Set up OpenSearch for cypress tests... ]" + setup_opensearch >> /dev/null 2>&1 & + sleep 100 + cd "$OPENSEARCH_DIR" + # Check if opensearch-tar-install.sh exists + if [ -f "./opensearch-tar-install.sh" ]; then + spawn_process_and_save_PID "./opensearch-tar-install.sh &" + else + spawn_process_and_save_PID "./bin/opensearch &" + fi + check_status $OPENSEARCH_URL $OPENSEARCH_MSG +} + +# Checks the running status of OpenSearch and Dashboards +# if success, the while loop in the check_status will end and it prints out successful message +function check_status() { + STATUS_URL=$1 + STATUS_MSG=$2 + echo "Checking the status of URL: $STATUS_URL" + # Calculate end time as 180s from now + check_status_end_time=$(expr 180 + "$(date '+%s')") + while true; do + # Break if the current time exceeds the end time + if [ $check_status_end_time -lt $(date '+%s') ]; then + echo "Error: Status check has timed out" + exit 1 + fi + # Use curl to fetch data and check for the specified message + if curl -s $STATUS_URL | grep -q "$STATUS_MSG"; then + echo "Status check successful" + break + fi + + # Pause for a short interval before the next check + sleep 1 + done + echo "$STATUS_URL is up!" +} + +# Starts OpenSearch Dashboards and run tests in the cypress folder +function run_dashboards_cypress_tests() { + echo "[ OpenSearch Dashboards setup before it starts... ]" + setup_dashboards >> /dev/null 2>&1 & + sleep 100 + cd "$DASHBOARDS_DIR" + if [ -x "./bin/opensearch-dashboards" ]; then + spawn_process_and_save_PID "./bin/opensearch-dashboards &" + else + echo "Error: opensearch-dashboards executable not found in $DASHBOARDS_DIR" + exit 1 + fi + check_status $DASHBOARDS_URL $DASHBOARDS_MSG + # Run cypress tests + run_cypress +} + +function run_cypress() { + cd "$CWD"/osd + echo "SPEC found: $SPEC" + if [ $SECURITY_ENABLED = "true" ]; then + echo "run security enabled tests" + yarn cypress:run-with-security --browser $CYPRESS_BROWSER --spec $SPEC + else + echo "run security disabled tests" + yarn cypress:run-without-security --browser $CYPRESS_BROWSER --spec $SPEC + fi +} diff --git a/scripts/postinstall.js b/scripts/postinstall.js index e84ee6b38ac8..7865473ee494 100644 --- a/scripts/postinstall.js +++ b/scripts/postinstall.js @@ -30,8 +30,61 @@ const removeUnwantedFolders = async (root, unwantedNames) => { return promises; }; + +const patchFile = async (file, patch) => { + console.log(`Patching ${file}`); + const patches = Array.isArray(patch) ? patch : [patch]; + let fileContent = await fs.readFile(file, 'utf8'); + for (const { from, to } of patches) { + // The splitting by `to` is to make sure we don't patch already patched ones + fileContent = fileContent + .split(to) + .map((token) => token.split(from)) + .flat() + .join(to); + } + await fs.writeFile(file, fileContent); +}; + const run = async () => { const promises = await removeUnwantedFolders('node_modules', ['demo', 'example', 'examples']); + + promises.push( + patchFile('node_modules/font-awesome/scss/_variables.scss', { + from: '(30em / 14)', + to: 'calc(30em / 14)', + }) + ); + promises.push( + patchFile('node_modules/@elastic/charts/dist/theme.scss', [ + { + from: '$legendItemVerticalPadding / 2', + to: 'calc($legendItemVerticalPadding / 2)', + }, + { + from: '$echLegendRowGap / 2', + to: 'calc($echLegendRowGap / 2)', + }, + { + from: '$euiBorderRadius / 2', + to: 'calc($euiBorderRadius / 2)', + }, + ]) + ); + promises.push( + patchFile('node_modules/rison-node/js/rison.js', [ + { + from: 'return Number(s)', + to: + 'return isFinite(s) && (s > Number.MAX_SAFE_INTEGER || s < Number.MIN_SAFE_INTEGER) ? BigInt(s) : Number(s)', + }, + { + from: 's = {', + to: 's = {\n bigint: x => x.toString(),', + }, + ]) + ); + await Promise.all(promises); }; diff --git a/src/cli/serve/serve.js b/src/cli/serve/serve.js index 6f89911ed089..aed5d74a2c01 100644 --- a/src/cli/serve/serve.js +++ b/src/cli/serve/serve.js @@ -80,11 +80,11 @@ function applyConfigOverrides(rawConfig, opts, extraCliOptions) { set('env', 'development'); if (!has('opensearch.username')) { - set('opensearch.username', 'opensearch_dashboards_system'); + set('opensearch.username', 'kibanaserver'); } if (!has('opensearch.password')) { - set('opensearch.password', 'changeme'); + set('opensearch.password', 'kibanaserver'); } if (opts.ssl) { @@ -125,6 +125,59 @@ function applyConfigOverrides(rawConfig, opts, extraCliOptions) { set('opensearch.hosts', opensearchHosts); set('opensearch.ssl.certificateAuthorities', CA_CERT_PATH); } + + if (opts.security) { + const customOpenSearchHosts = opts.opensearch + ? opts.opensearch.split(',') + : [].concat(get('opensearch.hosts') || []); + + const opensearchHosts = ( + (customOpenSearchHosts.length > 0 && customOpenSearchHosts) || ['https://localhost:9200'] + ).map((hostUrl) => { + const parsedUrl = new URL('', hostUrl); + return `https://localhost:${parsedUrl.port}`; + }); + + if (!get('opensearch.hosts')) { + set('opensearch.hosts', opensearchHosts); + } + + if (!get('opensearch.ssl.verificationMode')) { + set('opensearch.ssl.verificationMode', 'none'); + } + + if (process.env.OPENSEARCH_USERNAME) { + set('opensearch.username', process.env.OPENSEARCH_USERNAME); + } + if (process.env.OPENSEARCH_PASSWORD) { + set('opensearch.password', process.env.OPENSEARCH_PASSWORD); + } + + if (!get('opensearch.requestHeadersWhitelist')) { + set('opensearch.requestHeadersWhitelist', ['authorization', 'securitytenant']); + } + + if (!get('opensearch_security.multitenancy.enabled')) { + set('opensearch_security.multitenancy.enabled', true); + } + + if (!get('opensearch_security.multitenancy.tenants.preferred')) { + set('opensearch_security.multitenancy.tenants.preferred', ['Private', 'Global']); + } + + if ( + !get('opensearch_security.readonly_mode.roles') && + process.env.OPENSEARCH_SECURITY_READONLY_ROLE + ) { + set('opensearch_security.readonly_mode.roles', [ + process.env.OPENSEARCH_SECURITY_READONLY_ROLE, + ]); + } + + if (!get('opensearch_security.cookie.secure')) { + set('opensearch_security.cookie.secure', false); + } + } } if (opts.opensearch) set('opensearch.hosts', opts.opensearch.split(',')); @@ -195,6 +248,7 @@ export default function (program) { command .option('--dev', 'Run the server with development mode defaults') .option('--ssl', 'Run the dev server using HTTPS') + .option('--security', 'Run the dev server using security defaults') .option('--dist', 'Use production assets from osd/optimizer') .option( '--no-base-path', diff --git a/src/core/public/application/application_service.test.ts b/src/core/public/application/application_service.test.ts index c03afbba2767..691ba64cf00a 100644 --- a/src/core/public/application/application_service.test.ts +++ b/src/core/public/application/application_service.test.ts @@ -708,7 +708,7 @@ describe('#start()', () => { // Create an app and a promise that allows us to control when the app completes mounting const createWaitingApp = (props: Partial): [App, () => void] => { let finishMount: () => void; - const mountPromise = new Promise((resolve) => (finishMount = resolve)); + const mountPromise = new Promise((resolve) => (finishMount = resolve)); const app = { id: 'some-id', title: 'some-title', diff --git a/src/core/public/application/application_service.tsx b/src/core/public/application/application_service.tsx index f6efbfac422e..62c13694e245 100644 --- a/src/core/public/application/application_service.tsx +++ b/src/core/public/application/application_service.tsx @@ -32,6 +32,7 @@ import React from 'react'; import { BehaviorSubject, Observable, Subject, Subscription } from 'rxjs'; import { map, shareReplay, takeUntil, distinctUntilChanged, filter } from 'rxjs/operators'; import { createBrowserHistory, History } from 'history'; +import { RecursiveReadonly } from '@osd/utility-types'; import { MountPoint } from '../types'; import { HttpSetup, HttpStart } from '../http'; @@ -73,7 +74,7 @@ interface StartDeps { // Mount functions with two arguments are assumed to expect deprecated `context` object. const isAppMountDeprecated = (mount: (...args: any[]) => any): mount is AppMountDeprecated => mount.length === 2; -function filterAvailable(m: Map, capabilities: Capabilities) { +function filterAvailable(m: Map, capabilities: RecursiveReadonly) { return new Map( [...m].filter( ([id]) => capabilities.navLinks[id] === undefined || capabilities.navLinks[id] === true diff --git a/src/core/public/application/integration_tests/application_service.test.tsx b/src/core/public/application/integration_tests/application_service.test.tsx index 1b659c0dec5f..9d53d99c9d8c 100644 --- a/src/core/public/application/integration_tests/application_service.test.tsx +++ b/src/core/public/application/integration_tests/application_service.test.tsx @@ -77,7 +77,7 @@ describe('ApplicationService', () => { const { register } = service.setup(setupDeps); let resolveMount: () => void; - const promise = new Promise((resolve) => { + const promise = new Promise((resolve) => { resolveMount = resolve; }); @@ -111,7 +111,7 @@ describe('ApplicationService', () => { const { register } = service.setup(setupDeps); let resolveMount: () => void; - const promise = new Promise((resolve) => { + const promise = new Promise((resolve) => { resolveMount = resolve; }); @@ -453,7 +453,7 @@ describe('ApplicationService', () => { const { register } = service.setup(setupDeps); let resolveMount: () => void; - const promise = new Promise((resolve) => { + const promise = new Promise((resolve) => { resolveMount = resolve; }); @@ -491,7 +491,7 @@ describe('ApplicationService', () => { const { register } = service.setup(setupDeps); let resolveMount: () => void; - const promise = new Promise((resolve) => { + const promise = new Promise((resolve) => { resolveMount = resolve; }); diff --git a/src/core/public/application/ui/app_container.test.tsx b/src/core/public/application/ui/app_container.test.tsx index e9e2caed02e0..3e658fa25665 100644 --- a/src/core/public/application/ui/app_container.test.tsx +++ b/src/core/public/application/ui/app_container.test.tsx @@ -50,7 +50,7 @@ describe('AppContainer', () => { }); const flushPromises = async () => { - await new Promise(async (resolve) => { + await new Promise(async (resolve) => { setImmediate(() => resolve()); }); }; diff --git a/src/core/public/chrome/chrome_service.test.ts b/src/core/public/chrome/chrome_service.test.ts index 7d60c333638f..193b77107ffd 100644 --- a/src/core/public/chrome/chrome_service.test.ts +++ b/src/core/public/chrome/chrome_service.test.ts @@ -44,9 +44,12 @@ import { getAppInfo } from '../application/utils'; import { workspacesServiceMock } from '../workspace/workspaces_service.mock'; class FakeApp implements App { - public title = `${this.id} App`; + public title: string; public mount = () => () => {}; - constructor(public id: string, public chromeless?: boolean) {} + + constructor(public id: string, public chromeless?: boolean) { + this.title = `${this.id} App`; + } } const store = new Map(); const originalLocalStorage = window.localStorage; @@ -107,6 +110,44 @@ afterAll(() => { (window as any).localStorage = originalLocalStorage; }); +describe('setup', () => { + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('register custom Nav Header render', async () => { + const customHeaderMock = React.createElement('TestCustomNavHeader'); + const renderMock = jest.fn().mockReturnValue(customHeaderMock); + const chrome = new ChromeService({ browserSupportsCsp: true }); + + const chromeSetup = chrome.setup(); + chromeSetup.registerCollapsibleNavHeader(renderMock); + + const chromeStart = await chrome.start(defaultStartDeps()); + const wrapper = shallow(React.createElement(() => chromeStart.getHeaderComponent())); + expect(wrapper.prop('collapsibleNavHeaderRender')).toBeDefined(); + expect(wrapper.prop('collapsibleNavHeaderRender')()).toEqual(customHeaderMock); + }); + + it('should output warning message if calling `registerCollapsibleNavHeader` more than once', () => { + const warnMock = jest.fn(); + jest.spyOn(console, 'warn').mockImplementation(warnMock); + const customHeaderMock = React.createElement('TestCustomNavHeader'); + const renderMock = jest.fn().mockReturnValue(customHeaderMock); + const chrome = new ChromeService({ browserSupportsCsp: true }); + + const chromeSetup = chrome.setup(); + // call 1st time + chromeSetup.registerCollapsibleNavHeader(renderMock); + // call 2nd time + chromeSetup.registerCollapsibleNavHeader(renderMock); + expect(warnMock).toHaveBeenCalledTimes(1); + expect(warnMock).toHaveBeenCalledWith( + '[ChromeService] An existing custom collapsible navigation bar header render has been overridden.' + ); + }); +}); + describe('start', () => { it('adds legacy browser warning if browserSupportsCsp is disabled and warnLegacyBrowsers is enabled', async () => { const { startDeps } = await start({ options: { browserSupportsCsp: false } }); diff --git a/src/core/public/chrome/chrome_service.tsx b/src/core/public/chrome/chrome_service.tsx index 928bfebf78f3..60516d6a1537 100644 --- a/src/core/public/chrome/chrome_service.tsx +++ b/src/core/public/chrome/chrome_service.tsx @@ -89,7 +89,7 @@ interface ConstructorParams { browserSupportsCsp: boolean; } -interface StartDeps { +export interface StartDeps { application: InternalApplicationStart; docLinks: DocLinksStart; http: HttpStart; @@ -154,6 +154,12 @@ export class ChromeService { public setup() { return { registerCollapsibleNavHeader: (render: CollapsibleNavHeaderRender) => { + if (this.collapsibleNavHeaderRender) { + // eslint-disable-next-line no-console + console.warn( + '[ChromeService] An existing custom collapsible navigation bar header render has been overridden.' + ); + } this.collapsibleNavHeaderRender = render; }, }; @@ -290,9 +296,7 @@ export class ChromeService { branding={injectedMetadata.getBranding()} logos={logos} survey={injectedMetadata.getSurvey()} - collapsibleNavHeaderRender={ - this.collapsibleNavHeaderRender ? collapsibleNavHeaderRender : undefined - } + collapsibleNavHeaderRender={collapsibleNavHeaderRender} /> ), diff --git a/src/core/public/chrome/index.ts b/src/core/public/chrome/index.ts index 2f230203c949..4004c2c323f9 100644 --- a/src/core/public/chrome/index.ts +++ b/src/core/public/chrome/index.ts @@ -32,8 +32,8 @@ export { ChromeBadge, ChromeBreadcrumb, ChromeService, - ChromeStart, ChromeSetup, + ChromeStart, InternalChromeStart, ChromeHelpExtension, } from './chrome_service'; diff --git a/src/core/public/chrome/recently_accessed/recently_accessed_service.test.ts b/src/core/public/chrome/recently_accessed/recently_accessed_service.test.ts index 7046d5efc236..90e72af35652 100644 --- a/src/core/public/chrome/recently_accessed/recently_accessed_service.test.ts +++ b/src/core/public/chrome/recently_accessed/recently_accessed_service.test.ts @@ -69,11 +69,9 @@ describe('RecentlyAccessed#start()', () => { // @ts-expect-error to allow redeclaring a readonly prop delete window.localStorage; - // @ts-expect-error window.localStorage = new LocalStorageMock(); }); beforeEach(() => localStorage.clear()); - // @ts-expect-error afterAll(() => (window.localStorage = originalLocalStorage)); const getStart = async () => { diff --git a/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap b/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap index 3900138c7a4b..b02ef3710ed4 100644 --- a/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap +++ b/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap @@ -4168,6 +4168,7 @@ exports[`Header handles visibility and lock changes 1`] = ` "thrownError": null, } } + side="right" />
@@ -9713,6 +9714,7 @@ exports[`Header renders condensed header 1`] = ` "thrownError": null, } } + side="right" />
diff --git a/src/core/public/chrome/ui/header/header.tsx b/src/core/public/chrome/ui/header/header.tsx index 698d6a1a1047..cca0ca9e685f 100644 --- a/src/core/public/chrome/ui/header/header.tsx +++ b/src/core/public/chrome/ui/header/header.tsx @@ -230,7 +230,7 @@ export function Header({ - + diff --git a/src/core/public/chrome/ui/header/nav_link.test.tsx b/src/core/public/chrome/ui/header/nav_link.test.tsx new file mode 100644 index 000000000000..a89bdc01d5bb --- /dev/null +++ b/src/core/public/chrome/ui/header/nav_link.test.tsx @@ -0,0 +1,63 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { isActiveNavLink, createEuiListItem } from './nav_link'; +import { ChromeNavLink } from '../../..'; +import { httpServiceMock } from '../../../http/http_service.mock'; + +describe('isActiveNavLink', () => { + it('should return true if the appId is "discover" and linkId is "discover"', () => { + expect(isActiveNavLink('discover', 'discover')).toBe(true); + }); + + it('should return true if the appId is "data-explorer" and linkId is "data-explorer"', () => { + expect(isActiveNavLink('data-explorer', 'data-explorer')).toBe(true); + }); + + it('should return true if the appId is "data-explorer" and linkId is "discover"', () => { + expect(isActiveNavLink('data-explorer', 'discover')).toBe(true); + }); + + it('should return false if the appId and linkId do not match', () => { + expect(isActiveNavLink('dashboard', 'discover')).toBe(false); + }); +}); + +const mockBasePath = httpServiceMock.createSetupContract({ basePath: '/test' }).basePath; + +describe('createEuiListItem', () => { + const mockLink: Partial = { + href: 'test', + id: 'test', + title: 'Test App', + disabled: false, + euiIconType: 'inputOutput', + icon: 'testIcon', + tooltip: 'Test App Tooltip', + }; + + const mockProps = { + link: mockLink as ChromeNavLink, + appId: 'test', + basePath: mockBasePath, + dataTestSubj: 'test-subj', + onClick: jest.fn(), + navigateToApp: jest.fn(), + externalLink: false, + }; + + it('creates a list item with the correct properties', () => { + const listItem = createEuiListItem(mockProps); + expect(listItem).toHaveProperty('label', mockProps.link.tooltip); + expect(listItem).toHaveProperty('href', mockProps.link.href); + expect(listItem).toHaveProperty('data-test-subj', mockProps.dataTestSubj); + expect(listItem).toHaveProperty('onClick'); + expect(listItem).toHaveProperty( + 'isActive', + isActiveNavLink(mockProps.appId, mockProps.link.id) + ); + expect(listItem).toHaveProperty('isDisabled', mockProps.link.disabled); + }); +}); diff --git a/src/core/public/chrome/ui/header/nav_link.tsx b/src/core/public/chrome/ui/header/nav_link.tsx index 5665e0ecb25f..65dcc07c8b9b 100644 --- a/src/core/public/chrome/ui/header/nav_link.tsx +++ b/src/core/public/chrome/ui/header/nav_link.tsx @@ -39,6 +39,14 @@ export const isModifiedOrPrevented = (event: React.MouseEvent + !!(appId === linkId || aliasedApps[linkId]?.includes(appId || '')); + interface Props { link: CollapsibleNavLink; appId?: string; @@ -80,7 +88,7 @@ export function createEuiListItem({ navigateToApp(id); } }, - isActive: appId === id, + isActive: isActiveNavLink(appId, id), isDisabled: disabled, 'data-test-subj': dataTestSubj, ...(basePath && { diff --git a/src/core/public/context/context_service.ts b/src/core/public/context/context_service.ts index 5071288a1405..433e96c48d8b 100644 --- a/src/core/public/context/context_service.ts +++ b/src/core/public/context/context_service.ts @@ -32,7 +32,7 @@ import { PluginOpaqueId } from '../../server'; import { IContextContainer, ContextContainer, HandlerFunction } from '../../utils/context'; import { CoreContext } from '../core_system'; -interface StartDeps { +export interface StartDeps { pluginDependencies: ReadonlyMap; } diff --git a/src/core/public/core_app/core_app.ts b/src/core/public/core_app/core_app.ts index fcbcc5de5655..e1e91b775374 100644 --- a/src/core/public/core_app/core_app.ts +++ b/src/core/public/core_app/core_app.ts @@ -43,14 +43,14 @@ import type { InjectedMetadataSetup } from '../injected_metadata'; import { renderApp as renderErrorApp, setupUrlOverflowDetection } from './errors'; import { renderApp as renderStatusApp } from './status'; -interface SetupDeps { +export interface SetupDeps { application: InternalApplicationSetup; http: HttpSetup; injectedMetadata: InjectedMetadataSetup; notifications: NotificationsSetup; } -interface StartDeps { +export interface StartDeps { application: InternalApplicationStart; http: HttpStart; notifications: NotificationsStart; diff --git a/src/core/public/core_app/styles/_mixins.scss b/src/core/public/core_app/styles/_mixins.scss index 42c1feb5e914..45c1a8b9c6d4 100644 --- a/src/core/public/core_app/styles/_mixins.scss +++ b/src/core/public/core_app/styles/_mixins.scss @@ -18,7 +18,7 @@ text-align: center; background-color: $euiColorEmptyShade; border-radius: 100%; - padding: $euiSize / 2; + padding: calc($euiSize / 2); .euiIcon { vertical-align: baseline; diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts index 7dd8c6561ade..d73a663a64b3 100644 --- a/src/core/public/doc_links/doc_links_service.ts +++ b/src/core/public/doc_links/doc_links_service.ts @@ -32,7 +32,7 @@ import { deepFreeze } from '@osd/std'; import { parse } from 'semver'; import { InjectedMetadataSetup } from '../injected_metadata'; -interface StartDeps { +export interface StartDeps { injectedMetadata: InjectedMetadataSetup; } @@ -388,6 +388,8 @@ export class DocLinksService { ganttCharts: `${OPENSEARCH_DASHBOARDS_VERSIONED_DOCS}gantt`, // https://opensearch.org/docs/latest/dashboards/reporting/ reporting: `${OPENSEARCH_DASHBOARDS_VERSIONED_DOCS}reporting`, + // https://opensearch.org/docs/latest/dashboards/dev-tools/index-dev/ + devTools: `${OPENSEARCH_DASHBOARDS_VERSIONED_DOCS}dev-tools/index-dev/`, notebooks: { // https://opensearch.org/docs/latest/dashboards/notebooks/ base: `${OPENSEARCH_DASHBOARDS_VERSIONED_DOCS}notebooks`, diff --git a/src/core/public/fatal_errors/fatal_errors_service.tsx b/src/core/public/fatal_errors/fatal_errors_service.tsx index 59a23171ed17..73159ff20e4d 100644 --- a/src/core/public/fatal_errors/fatal_errors_service.tsx +++ b/src/core/public/fatal_errors/fatal_errors_service.tsx @@ -38,7 +38,7 @@ import { InjectedMetadataSetup } from '../injected_metadata'; import { FatalErrorsScreen } from './fatal_errors_screen'; import { FatalErrorInfo, getErrorInfo } from './get_error_info'; -interface Deps { +export interface Deps { i18n: I18nStart; injectedMetadata: InjectedMetadataSetup; } diff --git a/src/core/public/http/fetch.test.ts b/src/core/public/http/fetch.test.ts index 20f070dbba80..b97a281b8663 100644 --- a/src/core/public/http/fetch.test.ts +++ b/src/core/public/http/fetch.test.ts @@ -839,7 +839,9 @@ describe('Fetch', () => { }, }); - await expect(fetchInstance.fetch('/my/path', { withLongNumerals: true })).resolves.toEqual({ + await expect( + fetchInstance.fetch('/my/path', { withLongNumeralsSupport: true }) + ).resolves.toEqual({ 'long-max': longPositive, 'long-min': longNegative, }); @@ -854,7 +856,9 @@ describe('Fetch', () => { }, }); - await expect(fetchInstance.fetch('/my/path', { withLongNumerals: true })).resolves.toEqual({ + await expect( + fetchInstance.fetch('/my/path', { withLongNumeralsSupport: true }) + ).resolves.toEqual({ 'long-max': longPositive, 'long-min': longNegative, }); diff --git a/src/core/public/http/fetch.ts b/src/core/public/http/fetch.ts index 767d58643003..03b01fc35741 100644 --- a/src/core/public/http/fetch.ts +++ b/src/core/public/http/fetch.ts @@ -190,12 +190,20 @@ export class Fetch { if (NDJSON_CONTENT.test(contentType)) { body = await response.blob(); } else if (JSON_CONTENT.test(contentType)) { - body = fetchOptions.withLongNumerals ? parse(await response.text()) : await response.json(); + // ToDo: [3.x] Remove withLongNumerals + body = + fetchOptions.withLongNumeralsSupport || fetchOptions.withLongNumerals + ? parse(await response.text()) + : await response.json(); } else { const text = await response.text(); try { - body = fetchOptions.withLongNumerals ? parse(text) : JSON.parse(text); + // ToDo: [3.x] Remove withLongNumerals + body = + fetchOptions.withLongNumeralsSupport || fetchOptions.withLongNumerals + ? parse(text) + : JSON.parse(text); } catch (err) { body = text; } @@ -212,6 +220,8 @@ export class Fetch { } private shorthand(method: string): HttpHandler { + // ToDo: find why 'TResponseBody' of HttpHandler is not assignable to type 'HttpResponse' + // @ts-expect-error return (pathOrOptions: string | HttpFetchOptionsWithPath, options?: HttpFetchOptions) => { const optionsWithPath = validateFetchArguments(pathOrOptions, options); return this.fetch({ ...optionsWithPath, method }); diff --git a/src/core/public/http/types.ts b/src/core/public/http/types.ts index 98809d4c885b..709494963162 100644 --- a/src/core/public/http/types.ts +++ b/src/core/public/http/types.ts @@ -277,6 +277,9 @@ export interface HttpFetchOptions extends HttpRequestInit { * When `true`, if the response has a JSON mime type, the {@link HttpResponse} will use an alternate JSON parser * that converts long numerals to BigInts. Defaults to `false`. */ + withLongNumeralsSupport?: boolean; + + /** @deprecated use {@link withLongNumeralsSupport} instead */ withLongNumerals?: boolean; } diff --git a/src/core/public/index.ts b/src/core/public/index.ts index 484ff309ef4d..1c55911d0ab3 100644 --- a/src/core/public/index.ts +++ b/src/core/public/index.ts @@ -63,8 +63,8 @@ import { ChromeNavLinks, ChromeNavLinkUpdateableFields, ChromeDocTitle, - ChromeStart, ChromeSetup, + ChromeStart, ChromeRecentlyAccessed, ChromeRecentlyAccessedHistoryItem, NavType, @@ -105,6 +105,7 @@ export { StringValidationRegex, StringValidationRegexString, WorkspaceObject, + WorkspaceAttribute, } from '../types'; export { @@ -222,6 +223,7 @@ export interface CoreSetup((resolve, reject) => { + return new Promise((resolve, reject) => { const prev = this.pendingChanges || NOOP_CHANGES; this.pendingChanges = { diff --git a/src/core/public/ui_settings/ui_settings_client.test.ts b/src/core/public/ui_settings/ui_settings_client.test.ts index b297167439cc..9060b0d6db4e 100644 --- a/src/core/public/ui_settings/ui_settings_client.test.ts +++ b/src/core/public/ui_settings/ui_settings_client.test.ts @@ -36,7 +36,13 @@ import { UiSettingsClient } from './ui_settings_client'; let done$: Subject; function setup(options: { defaults?: any; initialSettings?: any } = {}) { - const { defaults = { dateFormat: { value: 'Browser' } }, initialSettings = {} } = options; + const { + defaults = { + dateFormat: { value: 'Browser' }, + aLongNumeral: { value: `${BigInt(Number.MAX_SAFE_INTEGER) + 11n}`, type: 'number' }, + }, + initialSettings = {}, + } = options; const batchSet = jest.fn(() => ({ settings: {}, @@ -62,6 +68,7 @@ describe('#get', () => { it('gives access to uiSettings values', () => { const { client } = setup(); expect(client.get('dateFormat')).toMatchSnapshot(); + expect(client.get('aLongNumeral')).toBe(BigInt(Number.MAX_SAFE_INTEGER) + 11n); }); it('supports the default value overload', () => { @@ -82,13 +89,19 @@ describe('#get', () => { const { client } = setup(); // if you are hitting this error, then a test is setting this client value globally and not unsetting it! expect(client.isDefault('dateFormat')).toBe(true); + expect(client.isDefault('aLongNumeral')).toBe(true); const defaultDateFormat = client.get('dateFormat'); + const defaultLongNumeral = client.get('aLongNumeral'); expect(client.get('dateFormat', 'xyz')).toBe('xyz'); + expect(client.get('aLongNumeral', 1n)).toBe(1n); + // shouldn't change other usages expect(client.get('dateFormat')).toBe(defaultDateFormat); expect(client.get('dataFormat', defaultDateFormat)).toBe(defaultDateFormat); + expect(client.get('aLongNumeral')).toBe(defaultLongNumeral); + expect(client.get('aLongNumeral', defaultLongNumeral)).toBe(defaultLongNumeral); }); it("throws on unknown properties that don't have a value yet.", () => { diff --git a/src/core/public/ui_settings/ui_settings_client.ts b/src/core/public/ui_settings/ui_settings_client.ts index d2015468befa..4aaa4dcd504c 100644 --- a/src/core/public/ui_settings/ui_settings_client.ts +++ b/src/core/public/ui_settings/ui_settings_client.ts @@ -97,11 +97,11 @@ You can use \`IUiSettingsClient.get("${key}", defaultValue)\`, which will just r return JSON.parse(value); } - if (type === 'number') { - return parseFloat(value); - } - - return value; + return type === 'number' && typeof value !== 'bigint' + ? isFinite(value) && (value > Number.MAX_SAFE_INTEGER || value < Number.MIN_SAFE_INTEGER) + ? BigInt(value) + : parseFloat(value) + : value; } get$(key: string, defaultOverride?: T) { @@ -198,7 +198,7 @@ You can use \`IUiSettingsClient.get("${key}", defaultValue)\`, which will just r this.setLocally(key, newVal); try { - const { settings } = await this.api.batchSet(key, newVal); + const { settings } = (await this.api.batchSet(key, newVal)) || {}; this.cache = defaultsDeep({}, defaults, settings); this.saved$.next({ key, newValue: newVal, oldValue: initialVal }); return true; diff --git a/src/core/public/ui_settings/ui_settings_service.ts b/src/core/public/ui_settings/ui_settings_service.ts index 9c677ff1c990..10c6b9ed784a 100644 --- a/src/core/public/ui_settings/ui_settings_service.ts +++ b/src/core/public/ui_settings/ui_settings_service.ts @@ -37,7 +37,7 @@ import { UiSettingsApi } from './ui_settings_api'; import { UiSettingsClient } from './ui_settings_client'; import { IUiSettingsClient } from './types'; -interface UiSettingsServiceDeps { +export interface UiSettingsServiceDeps { http: HttpSetup; injectedMetadata: InjectedMetadataSetup; } diff --git a/src/core/public/workspace/workspaces_service.ts b/src/core/public/workspace/workspaces_service.ts index d9d6664582b7..b743701cb900 100644 --- a/src/core/public/workspace/workspaces_service.ts +++ b/src/core/public/workspace/workspaces_service.ts @@ -7,7 +7,7 @@ import { BehaviorSubject, combineLatest } from 'rxjs'; import { isEqual } from 'lodash'; import { CoreService, WorkspaceObject } from '../../types'; -interface WorkspaceObservables { +export interface WorkspaceObservables { /** * Indicates the current activated workspace id, the value should be changed every time * when switching to a different workspace @@ -24,7 +24,8 @@ interface WorkspaceObservables { /** * The list of available workspaces. This workspace list should be set by whoever - * the workspace functionalities + * implemented the workspace functionalities, core workspace module should not be + * aware of how to populate the workspace list. */ workspaceList$: BehaviorSubject; @@ -35,8 +36,8 @@ interface WorkspaceObservables { initialized$: BehaviorSubject; } -enum WORKSPACE_ERROR_REASON_MAP { - WORKSPACE_STALED = 'WORKSPACE_STALED', +enum WORKSPACE_ERROR { + WORKSPACE_IS_STALE = 'WORKSPACE_IS_STALE', } export type WorkspacesSetup = WorkspaceObservables; @@ -63,13 +64,13 @@ export class WorkspacesService implements CoreService; } diff --git a/src/core/server/core_app/assets/legacy_dark_theme.css b/src/core/server/core_app/assets/legacy_dark_theme.css index 4ef4c726e414..b56eeed717a1 100644 --- a/src/core/server/core_app/assets/legacy_dark_theme.css +++ b/src/core/server/core_app/assets/legacy_dark_theme.css @@ -802,7 +802,7 @@ width: 100%; max-width: 100%; margin-bottom: 20px; - font-size: 14px; + font-size: 12px; } .table thead { font-size: 12px; diff --git a/src/core/server/core_app/assets/legacy_light_theme.css b/src/core/server/core_app/assets/legacy_light_theme.css index 9f9a0dc118d1..d4f6d10e7022 100644 --- a/src/core/server/core_app/assets/legacy_light_theme.css +++ b/src/core/server/core_app/assets/legacy_light_theme.css @@ -802,7 +802,7 @@ width: 100%; max-width: 100%; margin-bottom: 20px; - font-size: 14px; + font-size: 12px; } .table thead { font-size: 12px; diff --git a/src/core/server/core_usage_data/core_usage_data_service.mock.ts b/src/core/server/core_usage_data/core_usage_data_service.mock.ts index 5e1bcbe7867b..ae6326a8c2e9 100644 --- a/src/core/server/core_usage_data/core_usage_data_service.mock.ts +++ b/src/core/server/core_usage_data/core_usage_data_service.mock.ts @@ -105,7 +105,7 @@ const createStartContractMock = () => { keyConfigured: false, keystoreConfigured: false, redirectHttpFromPortConfigured: false, - supportedProtocols: ['TLSv1.1', 'TLSv1.2'], + supportedProtocols: ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'], truststoreConfigured: false, }, xsrf: { diff --git a/src/core/server/core_usage_data/core_usage_data_service.test.ts b/src/core/server/core_usage_data/core_usage_data_service.test.ts index ff3b0f1a1134..7e28a74b98de 100644 --- a/src/core/server/core_usage_data/core_usage_data_service.test.ts +++ b/src/core/server/core_usage_data/core_usage_data_service.test.ts @@ -163,6 +163,7 @@ describe('CoreUsageDataService', () => { "supportedProtocols": Array [ "TLSv1.1", "TLSv1.2", + "TLSv1.3", ], "truststoreConfigured": false, }, diff --git a/src/core/server/cross_compatibility/README.md b/src/core/server/cross_compatibility/README.md new file mode 100644 index 000000000000..88b4aaefe424 --- /dev/null +++ b/src/core/server/cross_compatibility/README.md @@ -0,0 +1,78 @@ +## Cross Compatibility Service + +The cross compatibility service provides a way for OpenSearch Dashboards plugins to check if they are compatible with the installed OpenSearch plugins. This allows plugins to gracefully degrade their functionality or disable themselves if they are not compatible with the current OpenSearch plugin version. + +### Overview + +OpenSearch Dashboards plugins depend on specific versions of OpenSearch plugins. When a plugin is installed, OpenSearch Dashboards checks to make sure that the required OpenSearch plugins are installed and compatible. If a required plugin is not installed or is not compatible, OpenSearch Dashboards will log a warning but will still allow the plugin to start. + +The cross compatibility service provides a way for plugins to check for compatibility with their OpenSearch counterparts. This allows plugins to make informed decisions about how to behave when they are not compatible. For example, a plugin could disable itself, limit its functionality, or notify the user that they are using an incompatible plugin. + +### Usage + +To use the Cross Compatibility service, plugins can call the `verifyOpenSearchPluginsState()` API. This API checks the compatibility of the plugin with the installed OpenSearch plugins. The API returns a list of `CrossCompatibilityResult` objects, which contain information about the compatibility of each plugin. + +The `CrossCompatibilityResult` object has the following properties: + +`pluginName`: The OpenSearch Plugin name. +`isCompatible`: A boolean indicating whether the plugin is compatible. +`incompatibilityReason`: The reason the OpenSearch Plugin version is not compatible with the plugin. +`installedVersions`: The version of the plugin that is installed. + +Plugins can use the information in the `CrossCompatibilityResult` object to decide how to behave. For example, a plugin could disable itself if the `isCompatible` property is false. + +The `verifyOpenSearchPluginsState()` API should be called from the `start()` lifecycle method. This allows plugins to check for compatibility before they start. + +### Example usage inside DashboardsSample Plugin + +``` +export class DashboardsSamplePlugin implements Plugin { + + public setup(core: CoreSetup) { + this.logger.debug('Dashboard sample plugin setup'); + this.capabilitiesService = core.capabilities; + return {}; + } + public start(core: CoreStart) { + this.logger.debug('Dashboard sample plugin: Started'); + exampleCompatibilityCheck(core); + return {}; + } + ...... + + // Example capability provider + export const capabilitiesProvider = () => ({ + exampleDashboardsPlugin: { + show: true, + createShortUrl: true, + }, + }); + + function exampleCompatibilityCheck(core: CoreStart) { + const pluginName = 'exampleDashboardsPlugin'; + const result = await core.versionCompatibility.verifyOpenSearchPluginsState(pluginName); + result.forEach((mustHavePlugin) => { + if (!mustHavePlugin.isCompatible) { + // use capabilities provider API to register plugin's capability to enable/disbale plugin + this.capabilitiesService.registerProvider(capabilitiesProvider); + } + else { // feature to enable when plugin has compatible version installed } + }); + ...... + } + ..... +} + +``` +The `exampleCompatibilityCheck()` function uses the `verifyOpenSearchPluginsState()` API to check for compatibility with the `DashboardsSample` plugin. If the plugin is compatible, the function enables the plugin's features. If the plugin is not compatible, the function gracefully degrades the plugin's functionality. + +### Use cases: + +The cross compatibility service can be used by plugins to: + +* Disable themselves if they are not compatible with the installed OpenSearch plugins. +* Limit their functionality if they are not fully compatible with the installed OpenSearch plugins. +* Notify users if they are using incompatible plugins. +* Provide information to users about how to upgrade their plugins. + +The cross compatibility service is a valuable tool for developers who are building plugins for OpenSearch Dashboards. It allows plugins to be more resilient to changes in the OpenSearch ecosystem. \ No newline at end of file diff --git a/src/core/server/cross_compatibility/cross_compatibility.mock.ts b/src/core/server/cross_compatibility/cross_compatibility.mock.ts new file mode 100644 index 000000000000..ffd0f6dcf8e6 --- /dev/null +++ b/src/core/server/cross_compatibility/cross_compatibility.mock.ts @@ -0,0 +1,17 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { CrossCompatibilityServiceStart } from './types'; + +const createStartContractMock = () => { + const startContract: jest.Mocked = { + verifyOpenSearchPluginsState: jest.fn().mockReturnValue(Promise.resolve({})), + }; + return startContract; +}; + +export const crossCompatibilityServiceMock = { + createStartContract: createStartContractMock, +}; diff --git a/src/core/server/cross_compatibility/cross_compatibility_service.test.ts b/src/core/server/cross_compatibility/cross_compatibility_service.test.ts new file mode 100644 index 000000000000..076af55c21ca --- /dev/null +++ b/src/core/server/cross_compatibility/cross_compatibility_service.test.ts @@ -0,0 +1,81 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { CrossCompatibilityService } from './cross_compatibility_service'; +import { CompatibleEnginePluginVersions } from '../plugins/types'; +import { mockCoreContext } from '../core_context.mock'; +import { opensearchServiceMock } from '../opensearch/opensearch_service.mock'; + +describe('CrossCompatibilityService', () => { + let service: CrossCompatibilityService; + let opensearch: any; + const plugins = new Map(); + + beforeEach(() => { + opensearch = opensearchServiceMock.createStart(); + opensearch.client.asInternalUser.cat.plugins.mockResolvedValue({ + body: [ + { + name: 'node1', + component: 'os-plugin', + version: '1.1.0.0', + }, + ], + } as any); + + plugins?.set('foo', { 'os-plugin': '1.0.0 - 2.0.0' }); + plugins?.set('incompatiblePlugin', { 'os-plugin': '^3.0.0' }); + plugins?.set('test', {}); + service = new CrossCompatibilityService(mockCoreContext.create()); + }); + + it('should start the cross compatibility service', async () => { + const startDeps = { opensearch, plugins }; + const startResult = await service.start(startDeps); + expect(startResult).toEqual({ + verifyOpenSearchPluginsState: expect.any(Function), + }); + }); + + it('should return an array of CrossCompatibilityResult objects if plugin dependencies are specified', async () => { + const pluginName = 'foo'; + const startDeps = { opensearch, plugins }; + const startResult = await service.start(startDeps); + const results = await startResult.verifyOpenSearchPluginsState(pluginName); + expect(results).not.toBeUndefined(); + expect(results.length).toEqual(1); + expect(results[0].pluginName).toEqual('os-plugin'); + expect(results[0].isCompatible).toEqual(true); + expect(results[0].incompatibilityReason).toEqual(''); + expect(results[0].installedVersions).toEqual(['1.1.0.0']); + expect(opensearch.client.asInternalUser.cat.plugins).toHaveBeenCalledTimes(1); + }); + + it('should return an empty array if no plugin dependencies are specified', async () => { + const pluginName = 'test'; + const startDeps = { opensearch, plugins }; + const startResult = await service.start(startDeps); + const results = await startResult.verifyOpenSearchPluginsState(pluginName); + expect(results).not.toBeUndefined(); + expect(results.length).toEqual(0); + expect(opensearch.client.asInternalUser.cat.plugins).toHaveBeenCalledTimes(1); + }); + + it('should return an array of CrossCompatibilityResult objects with the incompatible reason if the plugin is not installed', async () => { + const pluginName = 'incompatiblePlugin'; + const startDeps = { opensearch, plugins }; + const startResult = await service.start(startDeps); + const results = await startResult.verifyOpenSearchPluginsState(pluginName); + expect(results).not.toBeUndefined(); + expect(results.length).toEqual(1); + expect(results[0].pluginName).toEqual('os-plugin'); + expect(results[0].isCompatible).toEqual(false); + expect(results[0].incompatibilityReason).toEqual( + 'OpenSearch plugin "os-plugin" in the version range "^3.0.0" is not installed on the OpenSearch for the OpenSearch Dashboards plugin to function as expected.' + ); + expect(results[0].installedVersions).toEqual(['1.1.0.0']); + expect(opensearch.client.asInternalUser.cat.plugins).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/core/server/cross_compatibility/cross_compatibility_service.ts b/src/core/server/cross_compatibility/cross_compatibility_service.ts new file mode 100644 index 000000000000..cb088e7feac7 --- /dev/null +++ b/src/core/server/cross_compatibility/cross_compatibility_service.ts @@ -0,0 +1,115 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { CatPluginsResponse } from '@opensearch-project/opensearch/api/types'; +import semver from 'semver'; +import { CrossCompatibilityResult, CrossCompatibilityServiceStart } from './types'; +import { CoreContext } from '../core_context'; +import { Logger } from '../logging'; +import { OpenSearchServiceStart } from '../opensearch'; +import { CompatibleEnginePluginVersions, PluginName } from '../plugins/types'; + +export interface StartDeps { + opensearch: OpenSearchServiceStart; + plugins: Map; +} + +export class CrossCompatibilityService { + private readonly log: Logger; + + constructor(coreContext: CoreContext) { + this.log = coreContext.logger.get('cross-compatibility-service'); + } + + start({ opensearch, plugins }: StartDeps): CrossCompatibilityServiceStart { + this.log.warn('Starting cross compatibility service'); + return { + verifyOpenSearchPluginsState: (pluginName: string) => { + const pluginOpenSearchDeps = plugins.get(pluginName) || {}; + return this.verifyOpenSearchPluginsState(opensearch, pluginOpenSearchDeps, pluginName); + }, + }; + } + + public async getOpenSearchPlugins(opensearch: OpenSearchServiceStart) { + // Makes cat.plugin api call to fetch list of OpenSearch plugins installed on the cluster + try { + const { body } = await opensearch.client.asInternalUser.cat.plugins({ + format: 'JSON', + }); + return body; + } catch (error) { + this.log.warn( + `Cat API call to OpenSearch to get list of plugins installed on the cluster has failed: ${error}` + ); + return []; + } + } + + public checkPluginVersionCompatibility( + pluginOpenSearchDeps: CompatibleEnginePluginVersions, + opensearchInstalledPlugins: CatPluginsResponse, + dashboardsPluginName: string + ) { + const results: CrossCompatibilityResult[] = []; + for (const [pluginName, versionRange] of Object.entries(pluginOpenSearchDeps)) { + // add check to see if the Dashboards plugin version is compatible with installed OpenSearch plugin + const { isCompatible, installedPluginVersions } = this.isVersionCompatibleOSPluginInstalled( + opensearchInstalledPlugins, + pluginName, + versionRange + ); + results.push({ + pluginName, + isCompatible: !isCompatible ? false : true, + incompatibilityReason: !isCompatible + ? `OpenSearch plugin "${pluginName}" in the version range "${versionRange}" is not installed on the OpenSearch for the OpenSearch Dashboards plugin to function as expected.` + : '', + installedVersions: installedPluginVersions, + }); + + if (!isCompatible) { + this.log.warn( + `OpenSearch plugin "${pluginName}" is not installed on the cluster for the OpenSearch Dashboards plugin "${dashboardsPluginName}" to function as expected.` + ); + } + } + return results; + } + + private async verifyOpenSearchPluginsState( + opensearch: OpenSearchServiceStart, + pluginOpenSearchDeps: CompatibleEnginePluginVersions, + pluginName: string + ): Promise { + this.log.info('Checking OpenSearch Plugin version compatibility'); + // make _cat/plugins?format=json call to the OpenSearch instance + const opensearchInstalledPlugins = await this.getOpenSearchPlugins(opensearch); + const results = this.checkPluginVersionCompatibility( + pluginOpenSearchDeps, + opensearchInstalledPlugins, + pluginName + ); + return results; + } + + private isVersionCompatibleOSPluginInstalled( + opensearchInstalledPlugins: CatPluginsResponse, + depPluginName: string, + depPluginVersionRange: string + ) { + let isCompatible = false; + const installedPluginVersions = new Set(); + opensearchInstalledPlugins.forEach((obj) => { + if (obj.component === depPluginName && obj.version) { + installedPluginVersions.add(obj.version); + if (semver.satisfies(semver.coerce(obj.version)!.version, depPluginVersionRange)) { + isCompatible = true; + } + } + }); + return { isCompatible, installedPluginVersions: [...installedPluginVersions] }; + } +} diff --git a/src/core/server/cross_compatibility/index.ts b/src/core/server/cross_compatibility/index.ts new file mode 100644 index 000000000000..02db8b6d2cfc --- /dev/null +++ b/src/core/server/cross_compatibility/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export { CrossCompatibilityService } from './cross_compatibility_service'; +export { CrossCompatibilityResult, CrossCompatibilityServiceStart } from './types'; diff --git a/src/core/server/cross_compatibility/types.ts b/src/core/server/cross_compatibility/types.ts new file mode 100644 index 000000000000..30f32aa6d5fd --- /dev/null +++ b/src/core/server/cross_compatibility/types.ts @@ -0,0 +1,22 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { CrossCompatibilityResult } from '../../types/cross_compatibility'; + +/** + * API to check if the OpenSearch Dashboards plugin version is compatible with the installed OpenSearch plugin. + * + * @public + */ +export interface CrossCompatibilityServiceStart { + /** + * Checks if the OpenSearch Dashboards plugin version is compatible with the installed OpenSearch plugin. + * + * @returns {Promise} + */ + verifyOpenSearchPluginsState: (pluginName: string) => Promise; +} + +export { CrossCompatibilityResult }; diff --git a/src/core/server/http/__snapshots__/http_config.test.ts.snap b/src/core/server/http/__snapshots__/http_config.test.ts.snap index 70c8abf4ed7a..120299b6a349 100644 --- a/src/core/server/http/__snapshots__/http_config.test.ts.snap +++ b/src/core/server/http/__snapshots__/http_config.test.ts.snap @@ -78,6 +78,7 @@ Object { "supportedProtocols": Array [ "TLSv1.1", "TLSv1.2", + "TLSv1.3", ], "truststore": Object {}, }, diff --git a/src/core/server/http/http_service.ts b/src/core/server/http/http_service.ts index 8627557c7332..ed1da8754721 100644 --- a/src/core/server/http/http_service.ts +++ b/src/core/server/http/http_service.ts @@ -56,7 +56,7 @@ import { import { RequestHandlerContext } from '../../server'; import { registerCoreHandlers } from './lifecycle_handlers'; -interface SetupDeps { +export interface SetupDeps { context: ContextSetup; } diff --git a/src/core/server/http/router/response.ts b/src/core/server/http/router/response.ts index c38c58f3ba1c..ddf1c4f9481e 100644 --- a/src/core/server/http/router/response.ts +++ b/src/core/server/http/router/response.ts @@ -87,6 +87,8 @@ export interface HttpResponseOptions { body?: HttpResponsePayload; /** HTTP Headers with additional information about response */ headers?: ResponseHeaders; + /** Indicates if alternate serialization should be employed */ + withLongNumeralsSupport?: boolean; } /** diff --git a/src/core/server/http/router/response_adapter.ts b/src/core/server/http/router/response_adapter.ts index 08dcaf1e9c60..1597a2d7ab53 100644 --- a/src/core/server/http/router/response_adapter.ts +++ b/src/core/server/http/router/response_adapter.ts @@ -35,6 +35,7 @@ import { import typeDetect from 'type-detect'; import Boom from '@hapi/boom'; import * as stream from 'stream'; +import { stringify } from '@osd/std'; import { HttpResponsePayload, @@ -108,16 +109,27 @@ export class HapiResponseAdapter { opensearchDashboardsResponse: OpenSearchDashboardsResponse ) { const response = this.responseToolkit - .response(opensearchDashboardsResponse.payload) + .response( + opensearchDashboardsResponse.options.withLongNumeralsSupport + ? stringify(opensearchDashboardsResponse.payload) + : opensearchDashboardsResponse.payload + ) .code(opensearchDashboardsResponse.status); setHeaders(response, opensearchDashboardsResponse.options.headers); + if (opensearchDashboardsResponse.options.withLongNumeralsSupport) { + setHeaders(response, { + 'content-type': 'application/json', + }); + } return response; } private toRedirect( opensearchDashboardsResponse: OpenSearchDashboardsResponse ) { - const { headers } = opensearchDashboardsResponse.options; + const { + headers, + }: { headers?: Record } = opensearchDashboardsResponse.options; if (!headers || typeof headers.location !== 'string') { throw new Error("expected 'location' header to be set"); } diff --git a/src/core/server/http/ssl_config.test.ts b/src/core/server/http/ssl_config.test.ts index db83e44e282b..e1331f74e6ba 100644 --- a/src/core/server/http/ssl_config.test.ts +++ b/src/core/server/http/ssl_config.test.ts @@ -277,14 +277,19 @@ describe('#sslSchema', () => { certificate: '/path/to/certificate', enabled: true, key: '/path/to/key', - supportedProtocols: ['TLSv1', 'TLSv1.1', 'TLSv1.2'], + supportedProtocols: ['TLSv1', 'TLSv1.1', 'TLSv1.2', 'TLSv1.3'], }; const singleKnownProtocolConfig = sslSchema.validate(singleKnownProtocol); expect(singleKnownProtocolConfig.supportedProtocols).toEqual(['TLSv1']); const allKnownProtocolsConfig = sslSchema.validate(allKnownProtocols); - expect(allKnownProtocolsConfig.supportedProtocols).toEqual(['TLSv1', 'TLSv1.1', 'TLSv1.2']); + expect(allKnownProtocolsConfig.supportedProtocols).toEqual([ + 'TLSv1', + 'TLSv1.1', + 'TLSv1.2', + 'TLSv1.3', + ]); }); test('rejects unknown protocols`', () => { @@ -299,21 +304,23 @@ describe('#sslSchema', () => { certificate: '/path/to/certificate', enabled: true, key: '/path/to/key', - supportedProtocols: ['TLSv1', 'TLSv1.1', 'TLSv1.2', 'SOMEv100500'], + supportedProtocols: ['TLSv1', 'TLSv1.1', 'TLSv1.2', 'TLSv1.3', 'SOMEv100500'], }; expect(() => sslSchema.validate(singleUnknownProtocol)).toThrowErrorMatchingInlineSnapshot(` "[supportedProtocols.0]: types that failed validation: - [supportedProtocols.0.0]: expected value to equal [TLSv1] - [supportedProtocols.0.1]: expected value to equal [TLSv1.1] -- [supportedProtocols.0.2]: expected value to equal [TLSv1.2]" +- [supportedProtocols.0.2]: expected value to equal [TLSv1.2] +- [supportedProtocols.0.3]: expected value to equal [TLSv1.3]" `); expect(() => sslSchema.validate(allKnownWithOneUnknownProtocols)) .toThrowErrorMatchingInlineSnapshot(` -"[supportedProtocols.3]: types that failed validation: -- [supportedProtocols.3.0]: expected value to equal [TLSv1] -- [supportedProtocols.3.1]: expected value to equal [TLSv1.1] -- [supportedProtocols.3.2]: expected value to equal [TLSv1.2]" +"[supportedProtocols.4]: types that failed validation: +- [supportedProtocols.4.0]: expected value to equal [TLSv1] +- [supportedProtocols.4.1]: expected value to equal [TLSv1.1] +- [supportedProtocols.4.2]: expected value to equal [TLSv1.2] +- [supportedProtocols.4.3]: expected value to equal [TLSv1.3]" `); }); }); diff --git a/src/core/server/http/ssl_config.ts b/src/core/server/http/ssl_config.ts index 8887c14a13e4..8fc725ca937e 100644 --- a/src/core/server/http/ssl_config.ts +++ b/src/core/server/http/ssl_config.ts @@ -41,6 +41,7 @@ const protocolMap = new Map([ ['TLSv1', cryptoConstants.SSL_OP_NO_TLSv1], ['TLSv1.1', cryptoConstants.SSL_OP_NO_TLSv1_1], ['TLSv1.2', cryptoConstants.SSL_OP_NO_TLSv1_2], + ['TLSv1.3', cryptoConstants.SSL_OP_NO_TLSv1_3], ]); export const sslSchema = schema.object( @@ -67,8 +68,13 @@ export const sslSchema = schema.object( }), redirectHttpFromPort: schema.maybe(schema.number()), supportedProtocols: schema.arrayOf( - schema.oneOf([schema.literal('TLSv1'), schema.literal('TLSv1.1'), schema.literal('TLSv1.2')]), - { defaultValue: ['TLSv1.1', 'TLSv1.2'], minSize: 1 } + schema.oneOf([ + schema.literal('TLSv1'), + schema.literal('TLSv1.1'), + schema.literal('TLSv1.2'), + schema.literal('TLSv1.3'), + ]), + { defaultValue: ['TLSv1.1', 'TLSv1.2', 'TLSv1.3'], minSize: 1 } ), clientAuthentication: schema.oneOf( [schema.literal('none'), schema.literal('optional'), schema.literal('required')], diff --git a/src/core/server/index.ts b/src/core/server/index.ts index 01b24777b0bd..502433aaf83a 100644 --- a/src/core/server/index.ts +++ b/src/core/server/index.ts @@ -76,6 +76,8 @@ import { StatusServiceSetup } from './status'; import { Auditor, AuditTrailSetup, AuditTrailStart } from './audit_trail'; import { AppenderConfigType, appendersSchema, LoggingServiceSetup } from './logging'; import { CoreUsageDataStart } from './core_usage_data'; +import { SecurityServiceSetup } from './security/types'; +import { CrossCompatibilityServiceStart } from './cross_compatibility/types'; // Because of #79265 we need to explicity import, then export these types for // scripts/telemetry_check.js to work as expected @@ -456,6 +458,8 @@ export interface CoreSetup { platformLogger.warn('warn'); platformLogger.error('error'); - expect(mockConsoleLog).toHaveBeenCalledTimes(3); + /* ToDo: change to `toHaveBeenCalledTimes(3) and clean the snapshot when the dependency + * on `elasticsearch` is removed. There should only be 3 calls but we get an extra + * one from `agentkeepalive:deprecated`. + */ + expect(mockConsoleLog).toHaveBeenCalledTimes(4); expect(getPlatformLogsFromMock(mockConsoleLog)).toMatchInlineSnapshot(` Array [ + "[agentkeepalive:deprecated] %s", "[xxxx-xx-xxTxx:xx:xx.xxxZ][INFO ][test-file] info", "[xxxx-xx-xxTxx:xx:xx.xxxZ][WARN ][test-file] warn", "[xxxx-xx-xxTxx:xx:xx.xxxZ][ERROR][test-file] error", @@ -180,10 +185,15 @@ describe('logging service', () => { platformLogger.warn('warn'); platformLogger.error('error'); - expect(mockConsoleLog).toHaveBeenCalledTimes(3); + /* ToDo: change to `toHaveBeenCalledTimes(3) and clean the snapshot when the dependency + * on `elasticsearch` is removed. There should only be 3 calls but we get an extra + * one from `agentkeepalive:deprecated`. + */ + expect(mockConsoleLog).toHaveBeenCalledTimes(4); expect(getPlatformLogsFromMock(mockConsoleLog)).toMatchInlineSnapshot(` Array [ + "[agentkeepalive:deprecated] %s", "[xxxx-xx-xxTxx:xx:xx.xxxZ][INFO ][test-file] info", "[xxxx-xx-xxTxx:xx:xx.xxxZ][WARN ][test-file] warn", "[xxxx-xx-xxTxx:xx:xx.xxxZ][ERROR][test-file] error", @@ -217,10 +227,15 @@ describe('logging service', () => { platformLogger.warn('warn'); platformLogger.error('error'); - expect(mockConsoleLog).toHaveBeenCalledTimes(3); + /* ToDo: change to `toHaveBeenCalledTimes(3) and clean the snapshot when the dependency + * on `elasticsearch` is removed. There should only be 3 calls but we get an extra + * one from `agentkeepalive:deprecated`. + */ + expect(mockConsoleLog).toHaveBeenCalledTimes(4); expect(getPlatformLogsFromMock(mockConsoleLog)).toMatchInlineSnapshot(` Array [ + "[agentkeepalive:deprecated] %s", "[xxxx-xx-xxTxx:xx:xx.xxxZ][INFO ][test-file] info", "[xxxx-xx-xxTxx:xx:xx.xxxZ][WARN ][test-file] warn", "[xxxx-xx-xxTxx:xx:xx.xxxZ][ERROR][test-file] error", diff --git a/src/core/server/legacy/legacy_service.test.ts b/src/core/server/legacy/legacy_service.test.ts index 8ad4c738df44..3dd4ce6589bd 100644 --- a/src/core/server/legacy/legacy_service.test.ts +++ b/src/core/server/legacy/legacy_service.test.ts @@ -60,6 +60,7 @@ import { statusServiceMock } from '../status/status_service.mock'; import { auditTrailServiceMock } from '../audit_trail/audit_trail_service.mock'; import { loggingServiceMock } from '../logging/logging_service.mock'; import { metricsServiceMock } from '../metrics/metrics_service.mock'; +import { securityServiceMock } from '../security/security_service.mock'; const MockOsdServer: jest.Mock = OsdServer as any; @@ -108,6 +109,7 @@ beforeEach(() => { auditTrail: auditTrailServiceMock.createSetupContract(), logging: loggingServiceMock.createInternalSetupContract(), metrics: metricsServiceMock.createInternalSetupContract(), + security: securityServiceMock.createSetupContract(), }, plugins: { 'plugin-id': 'plugin-value' }, uiPlugins: { diff --git a/src/core/server/legacy/legacy_service.ts b/src/core/server/legacy/legacy_service.ts index 165a67aa1f83..4dec545fd186 100644 --- a/src/core/server/legacy/legacy_service.ts +++ b/src/core/server/legacy/legacy_service.ts @@ -233,6 +233,7 @@ export class LegacyService implements CoreService { throw new Error('core.start.coreUsageData.getCoreUsageData is unsupported in legacy'); }, }, + crossCompatibility: startDeps.core.crossCompatibility, }; const router = setupDeps.core.http.createRouter('', this.legacyId); @@ -301,6 +302,7 @@ export class LegacyService implements CoreService { }, auditTrail: setupDeps.core.auditTrail, getStartServices: () => Promise.resolve([coreStart, startDeps.plugins, {}]), + security: setupDeps.core.security, }; // eslint-disable-next-line @typescript-eslint/no-var-requires diff --git a/src/core/server/logging/appenders/file/file_appender.ts b/src/core/server/logging/appenders/file/file_appender.ts index 87959641e9fb..9d00d26fe654 100644 --- a/src/core/server/logging/appenders/file/file_appender.ts +++ b/src/core/server/logging/appenders/file/file_appender.ts @@ -82,7 +82,7 @@ export class FileAppender implements DisposableAppender { * Disposes `FileAppender`. Waits for the underlying file stream to be completely flushed and closed. */ public async dispose() { - await new Promise((resolve) => { + await new Promise((resolve) => { if (this.outputStream === undefined) { return resolve(); } diff --git a/src/core/server/logging/logging_service.ts b/src/core/server/logging/logging_service.ts index 7459d4b1790c..80a67f1265f8 100644 --- a/src/core/server/logging/logging_service.ts +++ b/src/core/server/logging/logging_service.ts @@ -68,7 +68,7 @@ export interface InternalLoggingServiceSetup { configure(contextParts: string[], config$: Observable): void; } -interface SetupDeps { +export interface SetupDeps { loggingSystem: ILoggingSystem; } diff --git a/src/core/server/metrics/metrics_service.ts b/src/core/server/metrics/metrics_service.ts index 62e1c9706309..4181d40e4bce 100644 --- a/src/core/server/metrics/metrics_service.ts +++ b/src/core/server/metrics/metrics_service.ts @@ -38,7 +38,7 @@ import { InternalMetricsServiceSetup, InternalMetricsServiceStart, OpsMetrics } import { OpsMetricsCollector } from './ops_metrics_collector'; import { opsConfig, OpsConfigType } from './ops_config'; -interface MetricsServiceSetupDeps { +export interface MetricsServiceSetupDeps { http: InternalHttpServiceSetup; } diff --git a/src/core/server/mocks.ts b/src/core/server/mocks.ts index 3dd289669a01..2a6114013b22 100644 --- a/src/core/server/mocks.ts +++ b/src/core/server/mocks.ts @@ -50,6 +50,8 @@ import { environmentServiceMock } from './environment/environment_service.mock'; import { statusServiceMock } from './status/status_service.mock'; import { auditTrailServiceMock } from './audit_trail/audit_trail_service.mock'; import { coreUsageDataServiceMock } from './core_usage_data/core_usage_data_service.mock'; +import { securityServiceMock } from './security/security_service.mock'; +import { crossCompatibilityServiceMock } from './cross_compatibility/cross_compatibility.mock'; export { configServiceMock } from './config/mocks'; export { httpServerMock } from './http/http_server.mocks'; @@ -69,6 +71,7 @@ export { statusServiceMock } from './status/status_service.mock'; export { contextServiceMock } from './context/context_service.mock'; export { capabilitiesServiceMock } from './capabilities/capabilities_service.mock'; export { coreUsageDataServiceMock } from './core_usage_data/core_usage_data_service.mock'; +export { crossCompatibilityServiceMock } from './cross_compatibility/cross_compatibility.mock'; export function pluginInitializerContextConfigMock(config: T) { const globalConfig: SharedGlobalConfig = { @@ -157,6 +160,7 @@ function createCoreSetupMock({ getStartServices: jest .fn, object, any]>, []>() .mockResolvedValue([createCoreStartMock(), pluginStartDeps, pluginStartContract]), + security: securityServiceMock.createSetupContract(), }; return mock; @@ -172,6 +176,7 @@ function createCoreStartMock() { savedObjects: savedObjectsServiceMock.createStartContract(), uiSettings: uiSettingsServiceMock.createStartContract(), coreUsageData: coreUsageDataServiceMock.createStartContract(), + crossCompatibility: crossCompatibilityServiceMock.createStartContract(), }; return mock; @@ -192,6 +197,7 @@ function createInternalCoreSetupMock() { auditTrail: auditTrailServiceMock.createSetupContract(), logging: loggingServiceMock.createInternalSetupContract(), metrics: metricsServiceMock.createInternalSetupContract(), + security: securityServiceMock.createSetupContract(), }; return setupDeps; } @@ -206,6 +212,7 @@ function createInternalCoreStartMock() { uiSettings: uiSettingsServiceMock.createStartContract(), auditTrail: auditTrailServiceMock.createStartContract(), coreUsageData: coreUsageDataServiceMock.createStartContract(), + crossCompatibility: crossCompatibilityServiceMock.createStartContract(), }; return startDeps; } diff --git a/src/core/server/opensearch/client/cluster_client.test.ts b/src/core/server/opensearch/client/cluster_client.test.ts index 81f55b987805..c188426e8517 100644 --- a/src/core/server/opensearch/client/cluster_client.test.ts +++ b/src/core/server/opensearch/client/cluster_client.test.ts @@ -55,19 +55,32 @@ describe('ClusterClient', () => { let getAuthHeaders: jest.MockedFunction; let internalClient: ReturnType; let scopedClient: ReturnType; + let internalClientWithLongNumeralsSupport: ReturnType; + let scopedClientWithLongNumeralsSupport: ReturnType; beforeEach(() => { logger = loggingSystemMock.createLogger(); internalClient = opensearchClientMock.createInternalClient(); scopedClient = opensearchClientMock.createInternalClient(); + internalClientWithLongNumeralsSupport = opensearchClientMock.createInternalClient(true); + scopedClientWithLongNumeralsSupport = opensearchClientMock.createInternalClient(true); getAuthHeaders = jest.fn().mockImplementation(() => ({ authorization: 'auth', foo: 'bar', })); - configureClientMock.mockImplementation((config, { scoped = false }) => { - return scoped ? scopedClient : internalClient; - }); + configureClientMock.mockImplementation( + (config, { scoped = false, withLongNumeralsSupport = false }) => { + // prettier-ignore + return withLongNumeralsSupport + ? scoped + ? scopedClientWithLongNumeralsSupport + : internalClientWithLongNumeralsSupport + : scoped + ? scopedClient + : internalClient; + } + ); }); afterEach(() => { @@ -79,7 +92,7 @@ describe('ClusterClient', () => { new ClusterClient(config, logger, getAuthHeaders); - expect(configureClientMock).toHaveBeenCalledTimes(2); + expect(configureClientMock).toHaveBeenCalledTimes(4); expect(configureClientMock).toHaveBeenCalledWith(config, { logger }); expect(configureClientMock).toHaveBeenCalledWith(config, { logger, scoped: true }); }); @@ -100,18 +113,22 @@ describe('ClusterClient', () => { const scopedClusterClient = clusterClient.asScoped(request); const expected = { headers: expect.any(Object) }; - expect(scopedClient.child).toHaveBeenCalledTimes(2); - expect(scopedClient.child).toHaveBeenNthCalledWith(1, expect.objectContaining(expected)); - expect(scopedClient.child).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ - ...expected, - enableLongNumeralSupport: true, - }) - ); + + expect(scopedClient.child).toHaveBeenCalledTimes(1); + expect(scopedClient.child).toHaveBeenCalledWith(expected); expect(scopedClusterClient.asInternalUser).toBe(clusterClient.asInternalUser); expect(scopedClusterClient.asCurrentUser).toBe(scopedClient.child.mock.results[0].value); + + expect(scopedClientWithLongNumeralsSupport.child).toHaveBeenCalledTimes(1); + expect(scopedClientWithLongNumeralsSupport.child).toHaveBeenCalledWith(expected); + + expect(scopedClusterClient.asInternalUserWithLongNumeralsSupport).toBe( + clusterClient.asInternalUserWithLongNumeralsSupport + ); + expect(scopedClusterClient.asCurrentUserWithLongNumeralsSupport).toBe( + scopedClientWithLongNumeralsSupport.child.mock.results[0].value + ); }); it('returns a distinct scoped cluster client on each call', () => { @@ -121,10 +138,14 @@ describe('ClusterClient', () => { const scopedClusterClient1 = clusterClient.asScoped(request); const scopedClusterClient2 = clusterClient.asScoped(request); - expect(scopedClient.child).toHaveBeenCalledTimes(2 * 2); + expect(scopedClient.child).toHaveBeenCalledTimes(2); + expect(scopedClientWithLongNumeralsSupport.child).toHaveBeenCalledTimes(2); expect(scopedClusterClient1).not.toBe(scopedClusterClient2); expect(scopedClusterClient1.asInternalUser).toBe(scopedClusterClient2.asInternalUser); + expect(scopedClusterClient1.asInternalUserWithLongNumeralsSupport).toBe( + scopedClusterClient2.asInternalUserWithLongNumeralsSupport + ); }); it('creates a scoped client with filtered request headers', () => { @@ -146,15 +167,12 @@ describe('ClusterClient', () => { const expected = { headers: { ...DEFAULT_HEADERS, foo: 'bar', 'x-opaque-id': expect.any(String) }, }; - expect(scopedClient.child).toHaveBeenCalledTimes(2); - expect(scopedClient.child).toHaveBeenNthCalledWith(1, expect.objectContaining(expected)); - expect(scopedClient.child).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ - ...expected, - enableLongNumeralSupport: true, - }) - ); + + expect(scopedClient.child).toHaveBeenCalledTimes(1); + expect(scopedClient.child).toHaveBeenCalledWith(expected); + + expect(scopedClientWithLongNumeralsSupport.child).toHaveBeenCalledTimes(1); + expect(scopedClientWithLongNumeralsSupport.child).toHaveBeenCalledWith(expected); }); it('creates a scoped facade with filtered auth headers', () => { @@ -174,15 +192,12 @@ describe('ClusterClient', () => { const expected = { headers: { ...DEFAULT_HEADERS, authorization: 'auth', 'x-opaque-id': expect.any(String) }, }; - expect(scopedClient.child).toHaveBeenCalledTimes(2); - expect(scopedClient.child).toHaveBeenNthCalledWith(1, expect.objectContaining(expected)); - expect(scopedClient.child).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ - ...expected, - enableLongNumeralSupport: true, - }) - ); + + expect(scopedClient.child).toHaveBeenCalledTimes(1); + expect(scopedClient.child).toHaveBeenCalledWith(expected); + + expect(scopedClientWithLongNumeralsSupport.child).toHaveBeenCalledTimes(1); + expect(scopedClientWithLongNumeralsSupport.child).toHaveBeenCalledWith(expected); }); it('respects auth headers precedence', () => { @@ -206,15 +221,12 @@ describe('ClusterClient', () => { const expected = { headers: { ...DEFAULT_HEADERS, authorization: 'auth', 'x-opaque-id': expect.any(String) }, }; - expect(scopedClient.child).toHaveBeenCalledTimes(2); - expect(scopedClient.child).toHaveBeenNthCalledWith(1, expect.objectContaining(expected)); - expect(scopedClient.child).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ - ...expected, - enableLongNumeralSupport: true, - }) - ); + + expect(scopedClient.child).toHaveBeenCalledTimes(1); + expect(scopedClient.child).toHaveBeenCalledWith(expected); + + expect(scopedClientWithLongNumeralsSupport.child).toHaveBeenCalledTimes(1); + expect(scopedClientWithLongNumeralsSupport.child).toHaveBeenCalledWith(expected); }); it('includes the `customHeaders` from the config without filtering them', () => { @@ -240,15 +252,12 @@ describe('ClusterClient', () => { 'x-opaque-id': expect.any(String), }, }; - expect(scopedClient.child).toHaveBeenCalledTimes(2); - expect(scopedClient.child).toHaveBeenNthCalledWith(1, expect.objectContaining(expected)); - expect(scopedClient.child).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ - ...expected, - enableLongNumeralSupport: true, - }) - ); + + expect(scopedClient.child).toHaveBeenCalledTimes(1); + expect(scopedClient.child).toHaveBeenCalledWith(expected); + + expect(scopedClientWithLongNumeralsSupport.child).toHaveBeenCalledTimes(1); + expect(scopedClientWithLongNumeralsSupport.child).toHaveBeenCalledWith(expected); }); it('adds the x-opaque-id header based on the request id', () => { @@ -271,15 +280,12 @@ describe('ClusterClient', () => { 'x-opaque-id': 'my-fake-id', }, }; - expect(scopedClient.child).toHaveBeenCalledTimes(2); - expect(scopedClient.child).toHaveBeenNthCalledWith(1, expect.objectContaining(expected)); - expect(scopedClient.child).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ - ...expected, - enableLongNumeralSupport: true, - }) - ); + + expect(scopedClient.child).toHaveBeenCalledTimes(1); + expect(scopedClient.child).toHaveBeenCalledWith(expected); + + expect(scopedClientWithLongNumeralsSupport.child).toHaveBeenCalledTimes(1); + expect(scopedClientWithLongNumeralsSupport.child).toHaveBeenCalledWith(expected); }); it('respect the precedence of auth headers over config headers', () => { @@ -307,15 +313,12 @@ describe('ClusterClient', () => { 'x-opaque-id': expect.any(String), }, }; - expect(scopedClient.child).toHaveBeenCalledTimes(2); - expect(scopedClient.child).toHaveBeenNthCalledWith(1, expect.objectContaining(expected)); - expect(scopedClient.child).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ - ...expected, - enableLongNumeralSupport: true, - }) - ); + + expect(scopedClient.child).toHaveBeenCalledTimes(1); + expect(scopedClient.child).toHaveBeenCalledWith(expected); + + expect(scopedClientWithLongNumeralsSupport.child).toHaveBeenCalledTimes(1); + expect(scopedClientWithLongNumeralsSupport.child).toHaveBeenCalledWith(expected); }); it('respect the precedence of request headers over config headers', () => { @@ -343,15 +346,12 @@ describe('ClusterClient', () => { 'x-opaque-id': expect.any(String), }, }; - expect(scopedClient.child).toHaveBeenCalledTimes(2); - expect(scopedClient.child).toHaveBeenNthCalledWith(1, expect.objectContaining(expected)); - expect(scopedClient.child).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ - ...expected, - enableLongNumeralSupport: true, - }) - ); + + expect(scopedClient.child).toHaveBeenCalledTimes(1); + expect(scopedClient.child).toHaveBeenCalledWith(expected); + + expect(scopedClientWithLongNumeralsSupport.child).toHaveBeenCalledTimes(1); + expect(scopedClientWithLongNumeralsSupport.child).toHaveBeenCalledWith(expected); }); it('respect the precedence of config headers over default headers', () => { @@ -374,15 +374,12 @@ describe('ClusterClient', () => { 'x-opaque-id': expect.any(String), }, }; - expect(scopedClient.child).toHaveBeenCalledTimes(2); - expect(scopedClient.child).toHaveBeenNthCalledWith(1, expect.objectContaining(expected)); - expect(scopedClient.child).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ - ...expected, - enableLongNumeralSupport: true, - }) - ); + + expect(scopedClient.child).toHaveBeenCalledTimes(1); + expect(scopedClient.child).toHaveBeenCalledWith(expected); + + expect(scopedClientWithLongNumeralsSupport.child).toHaveBeenCalledTimes(1); + expect(scopedClientWithLongNumeralsSupport.child).toHaveBeenCalledWith(expected); }); it('respect the precedence of request headers over default headers', () => { @@ -405,15 +402,12 @@ describe('ClusterClient', () => { 'x-opaque-id': expect.any(String), }, }; - expect(scopedClient.child).toHaveBeenCalledTimes(2); - expect(scopedClient.child).toHaveBeenNthCalledWith(1, expect.objectContaining(expected)); - expect(scopedClient.child).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ - ...expected, - enableLongNumeralSupport: true, - }) - ); + + expect(scopedClient.child).toHaveBeenCalledTimes(1); + expect(scopedClient.child).toHaveBeenCalledWith(expected); + + expect(scopedClientWithLongNumeralsSupport.child).toHaveBeenCalledTimes(1); + expect(scopedClientWithLongNumeralsSupport.child).toHaveBeenCalledWith(expected); }); it('respect the precedence of x-opaque-id header over config headers', () => { @@ -441,15 +435,12 @@ describe('ClusterClient', () => { 'x-opaque-id': 'from request', }, }; - expect(scopedClient.child).toHaveBeenCalledTimes(2); - expect(scopedClient.child).toHaveBeenNthCalledWith(1, expect.objectContaining(expected)); - expect(scopedClient.child).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ - ...expected, - enableLongNumeralSupport: true, - }) - ); + + expect(scopedClient.child).toHaveBeenCalledTimes(1); + expect(scopedClient.child).toHaveBeenCalledWith(expected); + + expect(scopedClientWithLongNumeralsSupport.child).toHaveBeenCalledTimes(1); + expect(scopedClientWithLongNumeralsSupport.child).toHaveBeenCalledWith(expected); }); it('filter headers when called with a `FakeRequest`', () => { @@ -471,15 +462,12 @@ describe('ClusterClient', () => { const expected = { headers: { ...DEFAULT_HEADERS, authorization: 'auth' }, }; - expect(scopedClient.child).toHaveBeenCalledTimes(2); - expect(scopedClient.child).toHaveBeenNthCalledWith(1, expect.objectContaining(expected)); - expect(scopedClient.child).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ - ...expected, - enableLongNumeralSupport: true, - }) - ); + + expect(scopedClient.child).toHaveBeenCalledTimes(1); + expect(scopedClient.child).toHaveBeenCalledWith(expected); + + expect(scopedClientWithLongNumeralsSupport.child).toHaveBeenCalledTimes(1); + expect(scopedClientWithLongNumeralsSupport.child).toHaveBeenCalledWith(expected); }); it('does not add auth headers when called with a `FakeRequest`', () => { @@ -503,42 +491,46 @@ describe('ClusterClient', () => { const expected = { headers: { ...DEFAULT_HEADERS, foo: 'bar' }, }; - expect(scopedClient.child).toHaveBeenCalledTimes(2); - expect(scopedClient.child).toHaveBeenNthCalledWith(1, expect.objectContaining(expected)); - expect(scopedClient.child).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ - ...expected, - enableLongNumeralSupport: true, - }) - ); + + expect(scopedClient.child).toHaveBeenCalledTimes(1); + expect(scopedClient.child).toHaveBeenCalledWith(expected); + + expect(scopedClientWithLongNumeralsSupport.child).toHaveBeenCalledTimes(1); + expect(scopedClientWithLongNumeralsSupport.child).toHaveBeenCalledWith(expected); }); }); describe('#close', () => { - it('closes both underlying clients', async () => { + it('closes all underlying clients', async () => { const clusterClient = new ClusterClient(createConfig(), logger, getAuthHeaders); await clusterClient.close(); expect(internalClient.close).toHaveBeenCalledTimes(1); expect(scopedClient.close).toHaveBeenCalledTimes(1); + + expect(internalClientWithLongNumeralsSupport.close).toHaveBeenCalledTimes(1); + expect(scopedClientWithLongNumeralsSupport.close).toHaveBeenCalledTimes(1); }); - it('waits for both clients to close', (done) => { - expect.assertions(4); + it('waits for all clients to close', (done) => { + expect.assertions(8); const clusterClient = new ClusterClient(createConfig(), logger, getAuthHeaders); let internalClientClosed = false; let scopedClientClosed = false; + let internalClientWithLongNumeralsSupportClosed = false; + let scopedClientWithLongNumeralsSupportClosed = false; let clusterClientClosed = false; let closeInternalClient: () => void; let closeScopedClient: () => void; + let closeInternalClientWithLongNumeralsSupport: () => void; + let closeScopedClientWithLongNumeralsSupport: () => void; internalClient.close.mockReturnValue( - new Promise((resolve) => { + new Promise((resolve) => { closeInternalClient = resolve; }).then(() => { expect(clusterClientClosed).toBe(false); @@ -546,23 +538,43 @@ describe('ClusterClient', () => { }) ); scopedClient.close.mockReturnValue( - new Promise((resolve) => { + new Promise((resolve) => { closeScopedClient = resolve; }).then(() => { expect(clusterClientClosed).toBe(false); scopedClientClosed = true; }) ); + internalClientWithLongNumeralsSupport.close.mockReturnValue( + new Promise((resolve) => { + closeInternalClientWithLongNumeralsSupport = resolve; + }).then(() => { + expect(clusterClientClosed).toBe(false); + internalClientWithLongNumeralsSupportClosed = true; + }) + ); + scopedClientWithLongNumeralsSupport.close.mockReturnValue( + new Promise((resolve) => { + closeScopedClientWithLongNumeralsSupport = resolve; + }).then(() => { + expect(clusterClientClosed).toBe(false); + scopedClientWithLongNumeralsSupportClosed = true; + }) + ); clusterClient.close().then(() => { clusterClientClosed = true; expect(internalClientClosed).toBe(true); expect(scopedClientClosed).toBe(true); + expect(internalClientWithLongNumeralsSupportClosed).toBe(true); + expect(scopedClientWithLongNumeralsSupportClosed).toBe(true); done(); }); closeInternalClient!(); closeScopedClient!(); + closeInternalClientWithLongNumeralsSupport!(); + closeScopedClientWithLongNumeralsSupport!(); }); it('return a rejected promise is any client rejects', async () => { @@ -583,11 +595,17 @@ describe('ClusterClient', () => { expect(internalClient.close).toHaveBeenCalledTimes(1); expect(scopedClient.close).toHaveBeenCalledTimes(1); + expect(internalClientWithLongNumeralsSupport.close).toHaveBeenCalledTimes(1); + expect(scopedClientWithLongNumeralsSupport.close).toHaveBeenCalledTimes(1); + await clusterClient.close(); await clusterClient.close(); expect(internalClient.close).toHaveBeenCalledTimes(1); expect(scopedClient.close).toHaveBeenCalledTimes(1); + + expect(internalClientWithLongNumeralsSupport.close).toHaveBeenCalledTimes(1); + expect(scopedClientWithLongNumeralsSupport.close).toHaveBeenCalledTimes(1); }); }); }); diff --git a/src/core/server/opensearch/client/cluster_client.ts b/src/core/server/opensearch/client/cluster_client.ts index 8ea87bb910f7..58de097a3c2e 100644 --- a/src/core/server/opensearch/client/cluster_client.ts +++ b/src/core/server/opensearch/client/cluster_client.ts @@ -76,6 +76,8 @@ export interface ICustomClusterClient extends IClusterClient { export class ClusterClient implements ICustomClusterClient { public readonly asInternalUser: Client; private readonly rootScopedClient: Client; + public readonly asInternalUserWithLongNumeralsSupport: Client; + private readonly rootScopedClientWithLongNumeralsSupport: Client; private isClosed = false; @@ -86,6 +88,16 @@ export class ClusterClient implements ICustomClusterClient { ) { this.asInternalUser = configureClient(config, { logger }); this.rootScopedClient = configureClient(config, { logger, scoped: true }); + + this.asInternalUserWithLongNumeralsSupport = configureClient(config, { + logger, + withLongNumeralsSupport: true, + }); + this.rootScopedClientWithLongNumeralsSupport = configureClient(config, { + logger, + scoped: true, + withLongNumeralsSupport: true, + }); } asScoped(request: ScopeableRequest) { @@ -95,20 +107,14 @@ export class ClusterClient implements ICustomClusterClient { headers: scopedHeaders, }); - const asInternalUserWithLongNumeralsSupport = this.asInternalUser.child({ - // @ts-expect-error - Remove ignoring after https://github.com/opensearch-project/opensearch-js/pull/598 is included in a release - enableLongNumeralSupport: true, - }); - - const scopedClientWithLongNumeralsSupport = this.rootScopedClient.child({ + const scopedClientWithLongNumeralsSupport = this.rootScopedClientWithLongNumeralsSupport.child({ headers: scopedHeaders, - // @ts-expect-error - Remove ignoring after https://github.com/opensearch-project/opensearch-js/pull/598 is included in a release - enableLongNumeralSupport: true, }); + return new ScopedClusterClient( this.asInternalUser, scopedClient, - asInternalUserWithLongNumeralsSupport, + this.asInternalUserWithLongNumeralsSupport, scopedClientWithLongNumeralsSupport ); } @@ -118,7 +124,12 @@ export class ClusterClient implements ICustomClusterClient { return; } this.isClosed = true; - await Promise.all([this.asInternalUser.close(noop), this.rootScopedClient.close(noop)]); + await Promise.all([ + this.asInternalUser.close(noop), + this.rootScopedClient.close(noop), + this.asInternalUserWithLongNumeralsSupport.close(noop), + this.rootScopedClientWithLongNumeralsSupport.close(noop), + ]); } private getScopedHeaders(request: ScopeableRequest): Headers { diff --git a/src/core/server/opensearch/client/configure_client.ts b/src/core/server/opensearch/client/configure_client.ts index 3a13120966b7..339f6a6ca8e7 100644 --- a/src/core/server/opensearch/client/configure_client.ts +++ b/src/core/server/opensearch/client/configure_client.ts @@ -38,9 +38,15 @@ import { parseClientOptions, OpenSearchClientConfig } from './client_config'; export const configureClient = ( config: OpenSearchClientConfig, - { logger, scoped = false }: { logger: Logger; scoped?: boolean } + { + logger, + scoped = false, + withLongNumeralsSupport = false, + }: { logger: Logger; scoped?: boolean; withLongNumeralsSupport?: boolean } ): Client => { const clientOptions = parseClientOptions(config, scoped); + // @ts-expect-error - ToDo: Remove ignoring after https://github.com/opensearch-project/opensearch-js/pull/598 is included in a release + if (withLongNumeralsSupport) clientOptions.enableLongNumeralSupport = true; const client = new Client(clientOptions); addLogging(client, logger, config.logQueries); diff --git a/src/core/server/opensearch/client/mocks.ts b/src/core/server/opensearch/client/mocks.ts index 40b731f0f3bf..5cf76ab0ae6d 100644 --- a/src/core/server/opensearch/client/mocks.ts +++ b/src/core/server/opensearch/client/mocks.ts @@ -33,10 +33,12 @@ import { TransportRequestPromise } from '@opensearch-project/opensearch/lib/Tran import { OpenSearchClient } from './types'; import { ICustomClusterClient } from './cluster_client'; -const createInternalClientMock = (): DeeplyMockedKeys => { +const createInternalClientMock = (withLongNumeralsSupport = false): DeeplyMockedKeys => { // we mimic 'reflection' on a concrete instance of the client to generate the mocked functions. const client = new Client({ node: 'http://localhost', + // @ts-expect-error - ToDo: Remove ignoring after https://github.com/opensearch-project/opensearch-js/pull/598 is included in a release + enableLongNumeralSupport: withLongNumeralsSupport, }) as any; const omittedProps = [ @@ -80,7 +82,9 @@ const createInternalClientMock = (): DeeplyMockedKeys => { mockify(client, omittedProps); client.close = jest.fn().mockReturnValue(Promise.resolve()); - client.child = jest.fn().mockImplementation(() => createInternalClientMock()); + client.child = jest + .fn() + .mockImplementation(() => createInternalClientMock(withLongNumeralsSupport)); const mockGetter = (obj: Record, propertyName: string) => { Object.defineProperty(obj, propertyName, { @@ -106,8 +110,8 @@ const createInternalClientMock = (): DeeplyMockedKeys => { export type OpenSearchClientMock = DeeplyMockedKeys; -const createClientMock = (): OpenSearchClientMock => - (createInternalClientMock() as unknown) as OpenSearchClientMock; +const createClientMock = (withLongNumeralsSupport = false): OpenSearchClientMock => + (createInternalClientMock(withLongNumeralsSupport) as unknown) as OpenSearchClientMock; export interface ScopedClusterClientMock { asInternalUser: OpenSearchClientMock; @@ -120,8 +124,8 @@ const createScopedClusterClientMock = () => { const mock: ScopedClusterClientMock = { asInternalUser: createClientMock(), asCurrentUser: createClientMock(), - asInternalUserWithLongNumeralsSupport: createClientMock(), - asCurrentUserWithLongNumeralsSupport: createClientMock(), + asInternalUserWithLongNumeralsSupport: createClientMock(true), + asCurrentUserWithLongNumeralsSupport: createClientMock(true), }; return mock; @@ -129,17 +133,15 @@ const createScopedClusterClientMock = () => { export interface ClusterClientMock { asInternalUser: OpenSearchClientMock; - asScoped: jest.MockedFunction<() => ScopedClusterClientMock>; asInternalUserWithLongNumeralsSupport: OpenSearchClientMock; - asCurrentUserWithLongNumeralsSupport: jest.MockedFunction<() => ScopedClusterClientMock>; + asScoped: jest.MockedFunction<() => ScopedClusterClientMock>; } const createClusterClientMock = () => { const mock: ClusterClientMock = { asInternalUser: createClientMock(), + asInternalUserWithLongNumeralsSupport: createClientMock(true), asScoped: jest.fn(), - asInternalUserWithLongNumeralsSupport: createClientMock(), - asCurrentUserWithLongNumeralsSupport: jest.fn(), }; mock.asScoped.mockReturnValue(createScopedClusterClientMock()); @@ -152,9 +154,8 @@ export type CustomClusterClientMock = jest.Mocked & Cluste const createCustomClusterClientMock = () => { const mock: CustomClusterClientMock = { asInternalUser: createClientMock(), + asInternalUserWithLongNumeralsSupport: createClientMock(true), asScoped: jest.fn(), - asInternalUserWithLongNumeralsSupport: createClientMock(), - asCurrentUserWithLongNumeralsSupport: jest.fn(), close: jest.fn(), }; diff --git a/src/core/server/opensearch/client/scoped_cluster_client.test.ts b/src/core/server/opensearch/client/scoped_cluster_client.test.ts index 5e3d222c3be2..4d22ca762dc2 100644 --- a/src/core/server/opensearch/client/scoped_cluster_client.test.ts +++ b/src/core/server/opensearch/client/scoped_cluster_client.test.ts @@ -36,27 +36,39 @@ describe('ScopedClusterClient', () => { const internalClient = opensearchClientMock.createOpenSearchClient(); const scopedClient = opensearchClientMock.createOpenSearchClient(); + const internalClientWithLongNumeralsSupport = opensearchClientMock.createOpenSearchClient(true); + const scopedClientWithLongNumeralsSupport = opensearchClientMock.createOpenSearchClient(true); + const scopedClusterClient = new ScopedClusterClient( internalClient, scopedClient, - internalClient, - scopedClient + internalClientWithLongNumeralsSupport, + scopedClientWithLongNumeralsSupport ); expect(scopedClusterClient.asInternalUser).toBe(internalClient); + expect(scopedClusterClient.asInternalUserWithLongNumeralsSupport).toBe( + internalClientWithLongNumeralsSupport + ); }); it('uses the scoped client passed in the constructor', () => { const internalClient = opensearchClientMock.createOpenSearchClient(); const scopedClient = opensearchClientMock.createOpenSearchClient(); + const internalClientWithLongNumeralsSupport = opensearchClientMock.createOpenSearchClient(true); + const scopedClientWithLongNumeralsSupport = opensearchClientMock.createOpenSearchClient(true); + const scopedClusterClient = new ScopedClusterClient( internalClient, scopedClient, - internalClient, - scopedClient + internalClientWithLongNumeralsSupport, + scopedClientWithLongNumeralsSupport ); expect(scopedClusterClient.asCurrentUser).toBe(scopedClient); + expect(scopedClusterClient.asCurrentUserWithLongNumeralsSupport).toBe( + scopedClientWithLongNumeralsSupport + ); }); }); diff --git a/src/core/server/opensearch/opensearch_service.ts b/src/core/server/opensearch/opensearch_service.ts index bab3e7ede9f3..6881ce06a0d5 100644 --- a/src/core/server/opensearch/opensearch_service.ts +++ b/src/core/server/opensearch/opensearch_service.ts @@ -48,11 +48,11 @@ import { InternalOpenSearchServiceSetup, InternalOpenSearchServiceStart } from ' import { pollOpenSearchNodesVersion } from './version_check/ensure_opensearch_version'; import { calculateStatus$ } from './status'; -interface SetupDeps { +export interface SetupDeps { http: InternalHttpServiceSetup; } -interface StartDeps { +export interface StartDeps { auditTrail: AuditTrailStart; } diff --git a/src/core/server/plugins/plugin_context.ts b/src/core/server/plugins/plugin_context.ts index ab028e169a71..c0eb5b29bb63 100644 --- a/src/core/server/plugins/plugin_context.ts +++ b/src/core/server/plugins/plugin_context.ts @@ -220,6 +220,7 @@ export function createPluginSetupContext( }, getStartServices: () => plugin.startDependencies, auditTrail: deps.auditTrail, + security: deps.security, }; } @@ -270,5 +271,6 @@ export function createPluginStartContext( }, auditTrail: deps.auditTrail, coreUsageData: deps.coreUsageData, + crossCompatibility: deps.crossCompatibility, }; } diff --git a/src/core/server/plugins/plugins_service.test.ts b/src/core/server/plugins/plugins_service.test.ts index 36c594908845..06b12643a64a 100644 --- a/src/core/server/plugins/plugins_service.test.ts +++ b/src/core/server/plugins/plugins_service.test.ts @@ -491,6 +491,7 @@ describe('PluginsService', () => { requiredPlugins: [], requiredBundles: [], optionalPlugins: [], + requiredEnginePlugins: {}, }, ]; diff --git a/src/core/server/plugins/plugins_service.ts b/src/core/server/plugins/plugins_service.ts index a85c649e65ee..2f4a3dfbc07c 100644 --- a/src/core/server/plugins/plugins_service.ts +++ b/src/core/server/plugins/plugins_service.ts @@ -38,7 +38,13 @@ import { CoreContext } from '../core_context'; import { Logger } from '../logging'; import { discover, PluginDiscoveryError, PluginDiscoveryErrorType } from './discovery'; import { PluginWrapper } from './plugin'; -import { DiscoveredPlugin, PluginConfigDescriptor, PluginName, InternalPluginInfo } from './types'; +import { + DiscoveredPlugin, + PluginConfigDescriptor, + PluginName, + InternalPluginInfo, + CompatibleEnginePluginVersions, +} from './types'; import { PluginsConfig, PluginsConfigType } from './plugins_config'; import { PluginsSystem } from './plugins_system'; import { InternalCoreSetup, InternalCoreStart } from '../internal_types'; @@ -97,6 +103,7 @@ export class PluginsService implements CoreService; private readonly pluginConfigDescriptors = new Map(); private readonly uiPluginInternalInfo = new Map(); + private readonly openSearchPluginInfo = new Map(); constructor(private readonly coreContext: CoreContext) { this.log = coreContext.logger.get('plugins-service'); @@ -128,6 +135,7 @@ export class PluginsService implements CoreService { expect(log.info).toHaveBeenCalledWith(`Starting [2] plugins: [order-1,order-0]`); }); - it('validates opensearch plugin installation when dependency is fulfilled', async () => { + it('validates plugin start when opensearch dependency is fulfilled', async () => { [ createPlugin('dependency-fulfilled-plugin', { requiredOSPlugin: { @@ -569,12 +569,9 @@ describe('start', () => { await pluginsSystem.setupPlugins(setupDeps); const pluginsStart = await pluginsSystem.startPlugins(startDeps); expect(pluginsStart).toBeInstanceOf(Map); - expect(opensearch.client.asInternalUser.cat.plugins).toHaveBeenCalledTimes(1); - const log = logger.get.mock.results[0].value as jest.Mocked; - expect(log.warn).toHaveBeenCalledTimes(0); }); - it('validates opensearch plugin installation and does not error out when plugin is not installed', async () => { + it('validates plugin start when opensearch plugin dependency is not installed', async () => { [ createPlugin('dependency-missing-plugin', { requiredOSPlugin: { @@ -591,52 +588,5 @@ describe('start', () => { await pluginsSystem.setupPlugins(setupDeps); const pluginsStart = await pluginsSystem.startPlugins(startDeps); expect(pluginsStart).toBeInstanceOf(Map); - expect(opensearch.client.asInternalUser.cat.plugins).toHaveBeenCalledTimes(1); - const log = logger.get.mock.results[0].value as jest.Mocked; - expect(log.warn).toHaveBeenCalledTimes(1); - expect(log.warn).toHaveBeenCalledWith( - `OpenSearch plugin "missing-opensearch-dep" is not installed on the engine for the OpenSearch Dashboards plugin to function as expected.` - ); - }); - - it('validates opensearch plugin installation and log warning when plugin exist but version is incompatible', async () => { - [ - createPlugin('version-mismatch-plugin', { - requiredOSPlugin: { - 'test-plugin-version-mismatch': '^1.0.0', - }, - }), - createPlugin('no-dependency-plugin'), - ].forEach((plugin, index) => { - jest.spyOn(plugin, 'setup').mockResolvedValue(`setup-as-${index}`); - jest.spyOn(plugin, 'start').mockResolvedValue(`started-as-${index}`); - pluginsSystem.addPlugin(plugin); - }); - - await pluginsSystem.setupPlugins(setupDeps); - const pluginsStart = await pluginsSystem.startPlugins(startDeps); - expect(pluginsStart).toBeInstanceOf(Map); - expect(opensearch.client.asInternalUser.cat.plugins).toHaveBeenCalledTimes(1); - const log = logger.get.mock.results[0].value as jest.Mocked; - expect(log.warn).toHaveBeenCalledTimes(1); - expect(log.warn).toHaveBeenCalledWith( - `OpenSearch plugin "test-plugin-version-mismatch" is not installed on the engine for the OpenSearch Dashboards plugin to function as expected.` - ); - }); - - it('validates opensearch plugin installation and does not warn when there is no dependency', async () => { - [createPlugin('no-dependency-plugin-1'), createPlugin('no-dependency-plugin-2')].forEach( - (plugin, index) => { - jest.spyOn(plugin, 'setup').mockResolvedValue(`setup-as-${index}`); - jest.spyOn(plugin, 'start').mockResolvedValue(`started-as-${index}`); - pluginsSystem.addPlugin(plugin); - } - ); - await pluginsSystem.setupPlugins(setupDeps); - const pluginsStart = await pluginsSystem.startPlugins(startDeps); - expect(pluginsStart).toBeInstanceOf(Map); - expect(opensearch.client.asInternalUser.cat.plugins).toHaveBeenCalledTimes(1); - const log = logger.get.mock.results[0].value as jest.Mocked; - expect(log.warn).toHaveBeenCalledTimes(0); }); }); diff --git a/src/core/server/plugins/plugins_system.ts b/src/core/server/plugins/plugins_system.ts index 07aa88e9db31..dc69dec97b45 100644 --- a/src/core/server/plugins/plugins_system.ts +++ b/src/core/server/plugins/plugins_system.ts @@ -29,8 +29,6 @@ */ import { withTimeout } from '@osd/std'; -import semver from 'semver'; -import { CatPluginsResponse } from '@opensearch-project/opensearch/api/types'; import { CoreContext } from '../core_context'; import { Logger } from '../logging'; import { PluginWrapper } from './plugin'; @@ -38,17 +36,20 @@ import { DiscoveredPlugin, PluginName } from './types'; import { createPluginSetupContext, createPluginStartContext } from './plugin_context'; import { PluginsServiceSetupDeps, PluginsServiceStartDeps } from './plugins_service'; import { PluginDependencies } from '.'; +import { CrossCompatibilityService } from '../cross_compatibility'; const Sec = 1000; /** @internal */ export class PluginsSystem { private readonly plugins = new Map(); private readonly log: Logger; + private readonly crossCompatibilityService: CrossCompatibilityService; // `satup`, the past-tense version of the noun `setup`. private readonly satupPlugins: PluginName[] = []; constructor(private readonly coreContext: CoreContext) { this.log = coreContext.logger.get('plugins-system'); + this.crossCompatibilityService = new CrossCompatibilityService(coreContext); } public addPlugin(plugin: PluginWrapper) { @@ -167,54 +168,19 @@ export class PluginsSystem { return contracts; } - private async healthCheckOpenSearchPlugins(deps: PluginsServiceStartDeps) { + public async healthCheckOpenSearchPlugins(deps: PluginsServiceStartDeps) { // make _cat/plugins?format=json call to the OpenSearch instance - const opensearchInstalledPlugins = await this.getOpenSearchPlugins(deps); + const opensearchInstalledPlugins = await this.crossCompatibilityService.getOpenSearchPlugins( + deps.opensearch + ); for (const pluginName of this.satupPlugins) { this.log.debug(`For plugin "${pluginName}"...`); const plugin = this.plugins.get(pluginName)!; - const pluginOpenSearchDeps = Object.entries(plugin.requiredEnginePlugins); - for (const [enginePluginName, versionRange] of pluginOpenSearchDeps) { - // add check to see if the installing Dashboards plugin version is compatible with installed OpenSearch plugin - if ( - !this.isVersionCompatibleOSPluginInstalled( - opensearchInstalledPlugins, - enginePluginName, - versionRange - ) - ) { - this.log.warn( - `OpenSearch plugin "${enginePluginName}" is not installed on the engine for the OpenSearch Dashboards plugin to function as expected.` - ); - } - } - } - } - - private isVersionCompatibleOSPluginInstalled( - opensearchInstalledPlugins: CatPluginsResponse, - depPlugin: string, - versionRange: string - ) { - return opensearchInstalledPlugins.find( - (obj) => - obj.component === depPlugin && - semver.satisfies(semver.coerce(obj.version)!.version, versionRange) - ); - } - - private async getOpenSearchPlugins(deps: PluginsServiceStartDeps) { - // Makes cat.plugin api call to fetch list of OpenSearch plugins installed on the cluster - try { - const { body } = await deps.opensearch.client.asInternalUser.cat.plugins({ - format: 'JSON', - }); - return body; - } catch (error) { - this.log.warn( - `Cat API call to OpenSearch to get list of plugins installed on the cluster has failed: ${error}` + this.crossCompatibilityService.checkPluginVersionCompatibility( + plugin.requiredEnginePlugins, + opensearchInstalledPlugins, + pluginName ); - return []; } } @@ -256,6 +222,7 @@ export class PluginsSystem { uiPluginNames.includes(p) ), requiredBundles: plugin.manifest.requiredBundles, + requiredEnginePlugins: plugin.manifest.requiredEnginePlugins, }, ]; }) diff --git a/src/core/server/plugins/types.ts b/src/core/server/plugins/types.ts index 66c8115efc4a..b7667b5bd2d2 100644 --- a/src/core/server/plugins/types.ts +++ b/src/core/server/plugins/types.ts @@ -231,6 +231,12 @@ export interface DiscoveredPlugin { */ readonly optionalPlugins: readonly PluginName[]; + /** + * An optional list of the OpenSearch plugins that **must be** installed on the cluster + * for this plugin to function properly. + */ + readonly requiredEnginePlugins: CompatibleEnginePluginVersions; + /** * List of plugin ids that this plugin's UI code imports modules from that are * not in `requiredPlugins`. diff --git a/src/core/server/rendering/rendering_service.test.ts b/src/core/server/rendering/rendering_service.test.ts index 56a39915e73c..5fa7d010989e 100644 --- a/src/core/server/rendering/rendering_service.test.ts +++ b/src/core/server/rendering/rendering_service.test.ts @@ -195,6 +195,11 @@ describe('RenderingService', () => { const result = await service.isUrlValid('/', 'config'); expect(result).toEqual(false); }); + + it('checks relative URL returns true', async () => { + const result = await service.isUrlValid('/demo/opensearch_mark_default.png', 'config'); + expect(result).toEqual(true); + }); }); describe('isTitleValid()', () => { diff --git a/src/core/server/rendering/rendering_service.tsx b/src/core/server/rendering/rendering_service.tsx index d0a62555d4b2..437d8e1e3d46 100644 --- a/src/core/server/rendering/rendering_service.tsx +++ b/src/core/server/rendering/rendering_service.tsx @@ -35,8 +35,7 @@ import { i18n } from '@osd/i18n'; import { Agent as HttpsAgent } from 'https'; import Axios from 'axios'; -// @ts-expect-error untyped internal module used to prevent axios from using xhr adapter in tests -import AxiosHttpAdapter from 'axios/lib/adapters/http'; + import { UiPlugins } from '../plugins'; import { CoreContext } from '../core_context'; import { Template } from './views'; @@ -372,9 +371,12 @@ export class RenderingService { this.logger.get('branding').error(`${configName} config is invalid. Using default branding.`); return false; } + if (url.startsWith('/')) { + return true; + } return await Axios.get(url, { httpsAgent: this.httpsAgent, - adapter: AxiosHttpAdapter, + adapter: 'http', maxRedirects: 0, }) .then(() => { diff --git a/src/core/server/saved_objects/import/check_conflict_for_data_source.test.ts b/src/core/server/saved_objects/import/check_conflict_for_data_source.test.ts new file mode 100644 index 000000000000..2b50ba8e9b35 --- /dev/null +++ b/src/core/server/saved_objects/import/check_conflict_for_data_source.test.ts @@ -0,0 +1,143 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { mockUuidv4 } from './__mocks__'; +import { SavedObjectReference, SavedObjectsImportRetry } from 'opensearch-dashboards/public'; +import { SavedObject } from '../types'; +import { SavedObjectsErrorHelpers } from '..'; +import { + checkConflictsForDataSource, + ConflictsForDataSourceParams, +} from './check_conflict_for_data_source'; + +type SavedObjectType = SavedObject<{ title?: string }>; + +/** + * Function to create a realistic-looking import object given a type and ID + */ +const createObject = (type: string, id: string): SavedObjectType => ({ + type, + id, + attributes: { title: 'some-title' }, + references: (Symbol() as unknown) as SavedObjectReference[], +}); + +const getResultMock = { + conflict: (type: string, id: string) => { + const error = SavedObjectsErrorHelpers.createConflictError(type, id).output.payload; + return { type, id, error }; + }, + unresolvableConflict: (type: string, id: string) => { + const conflictMock = getResultMock.conflict(type, id); + const metadata = { isNotOverwritable: true }; + return { ...conflictMock, error: { ...conflictMock.error, metadata } }; + }, + invalidType: (type: string, id: string) => { + const error = SavedObjectsErrorHelpers.createUnsupportedTypeError(type).output.payload; + return { type, id, error }; + }, +}; + +/** + * Create a variety of different objects to exercise different import / result scenarios + */ +const dataSourceObj = createObject('data-source', 'data-source-id-1'); // -> data-source type, no need to add in the filteredObjects +const dataSourceObj1 = createObject('type-1', 'ds_id-1'); // -> object with data source id +const dataSourceObj2 = createObject('type-2', 'ds_id-2'); // -> object with data source id +const objectsWithDataSource = [dataSourceObj, dataSourceObj1, dataSourceObj2]; +const dataSourceObj1Error = getResultMock.conflict(dataSourceObj1.type, dataSourceObj1.id); + +describe('#checkConflictsForDataSource', () => { + const setupParams = (partial: { + objects: SavedObjectType[]; + ignoreRegularConflicts?: boolean; + retries?: SavedObjectsImportRetry[]; + createNewCopies?: boolean; + dataSourceId?: string; + }): ConflictsForDataSourceParams => { + return { ...partial }; + }; + + beforeEach(() => { + mockUuidv4.mockReset(); + mockUuidv4.mockReturnValueOnce(`new-object-id`); + }); + + it('exits early if there are no objects to check', async () => { + const params = setupParams({ objects: [] }); + const checkConflictsForDataSourceResult = await checkConflictsForDataSource(params); + expect(checkConflictsForDataSourceResult).toEqual({ + filteredObjects: [], + errors: [], + importIdMap: new Map(), + }); + }); + + it('return obj if it is not data source obj and there is no conflict of the data source id', async () => { + const params = setupParams({ objects: objectsWithDataSource, dataSourceId: 'ds' }); + const checkConflictsForDataSourceResult = await checkConflictsForDataSource(params); + expect(checkConflictsForDataSourceResult).toEqual({ + filteredObjects: [dataSourceObj1, dataSourceObj2], + errors: [], + importIdMap: new Map(), + }); + }); + + it('can resolve the data source id conflict when the ds it not match when ignoreRegularConflicts=true', async () => { + const params = setupParams({ + objects: objectsWithDataSource, + ignoreRegularConflicts: true, + dataSourceId: 'currentDsId', + }); + const checkConflictsForDataSourceResult = await checkConflictsForDataSource(params); + + expect(checkConflictsForDataSourceResult).toEqual( + expect.objectContaining({ + filteredObjects: [ + { + ...dataSourceObj1, + id: 'currentDsId_id-1', + }, + { + ...dataSourceObj2, + id: 'currentDsId_id-2', + }, + ], + errors: [], + importIdMap: new Map([ + [ + `${dataSourceObj1.type}:${dataSourceObj1.id}`, + { id: 'currentDsId_id-1', omitOriginId: true }, + ], + [ + `${dataSourceObj2.type}:${dataSourceObj2.id}`, + { id: 'currentDsId_id-2', omitOriginId: true }, + ], + ]), + }) + ); + }); + + it('can push error when do not override with data source conflict', async () => { + const params = setupParams({ + objects: [dataSourceObj1], + ignoreRegularConflicts: false, + dataSourceId: 'currentDs', + }); + const checkConflictsForDataSourceResult = await checkConflictsForDataSource(params); + expect(checkConflictsForDataSourceResult).toEqual({ + filteredObjects: [], + errors: [ + { + ...dataSourceObj1Error, + title: dataSourceObj1.attributes.title, + meta: { title: dataSourceObj1.attributes.title }, + error: { type: 'conflict' }, + }, + ], + importIdMap: new Map(), + }); + }); +}); diff --git a/src/core/server/saved_objects/import/check_conflict_for_data_source.ts b/src/core/server/saved_objects/import/check_conflict_for_data_source.ts new file mode 100644 index 000000000000..6611b01dfb2a --- /dev/null +++ b/src/core/server/saved_objects/import/check_conflict_for_data_source.ts @@ -0,0 +1,85 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { SavedObject, SavedObjectsImportError, SavedObjectsImportRetry } from '../types'; + +export interface ConflictsForDataSourceParams { + objects: Array>; + ignoreRegularConflicts?: boolean; + retries?: SavedObjectsImportRetry[]; + dataSourceId?: string; +} + +interface ImportIdMapEntry { + id?: string; + omitOriginId?: boolean; +} + +/** + * function to only check the data souerce conflict when multiple data sources are enabled. + * the purpose of this function is to check the conflict of the imported saved objects and data source + * @param objects, this the array of saved objects to be verified whether contains the data source conflict + * @param ignoreRegularConflicts whether to override + * @param retries import operations list + * @param dataSourceId the id to identify the data source + * @returns {filteredObjects, errors, importIdMap } + */ +export async function checkConflictsForDataSource({ + objects, + ignoreRegularConflicts, + retries = [], + dataSourceId, +}: ConflictsForDataSourceParams) { + const filteredObjects: Array> = []; + const errors: SavedObjectsImportError[] = []; + const importIdMap = new Map(); + // exit early if there are no objects to check + if (objects.length === 0) { + return { filteredObjects, errors, importIdMap }; + } + const retryMap = retries.reduce( + (acc, cur) => acc.set(`${cur.type}:${cur.id}`, cur), + new Map() + ); + objects.forEach((object) => { + const { + type, + id, + attributes: { title }, + } = object; + const { destinationId } = retryMap.get(`${type}:${id}`) || {}; + + if (object.type !== 'data-source') { + const parts = id.split('_'); // this is the array to host the split results of the id + const previoudDataSourceId = parts.length > 1 ? parts[0] : undefined; + const rawId = previoudDataSourceId ? parts[1] : parts[0]; + + /** + * for import saved object from osd exported + * when the imported saved objects with the different dataSourceId comparing to the current dataSourceId + */ + + if ( + previoudDataSourceId && + previoudDataSourceId !== dataSourceId && + !ignoreRegularConflicts + ) { + const error = { type: 'conflict' as 'conflict', ...(destinationId && { destinationId }) }; + errors.push({ type, id, title, meta: { title }, error }); + } else if (previoudDataSourceId && previoudDataSourceId === dataSourceId) { + filteredObjects.push(object); + } else { + /** + * Only update importIdMap and filtered objects + */ + const omitOriginId = ignoreRegularConflicts; + importIdMap.set(`${type}:${id}`, { id: `${dataSourceId}_${rawId}`, omitOriginId }); + filteredObjects.push({ ...object, id: `${dataSourceId}_${rawId}` }); + } + } + }); + + return { filteredObjects, errors, importIdMap }; +} diff --git a/src/core/server/saved_objects/import/check_origin_conflicts.ts b/src/core/server/saved_objects/import/check_origin_conflicts.ts index 7ef98982e149..5010c04f337a 100644 --- a/src/core/server/saved_objects/import/check_origin_conflicts.ts +++ b/src/core/server/saved_objects/import/check_origin_conflicts.ts @@ -45,6 +45,7 @@ interface CheckOriginConflictsParams { namespace?: string; ignoreRegularConflicts?: boolean; importIdMap: Map; + dataSourceId?: string; } type CheckOriginConflictParams = Omit & { diff --git a/src/core/server/saved_objects/import/collect_saved_objects.ts b/src/core/server/saved_objects/import/collect_saved_objects.ts index 8fd015c44991..747c384862e0 100644 --- a/src/core/server/saved_objects/import/collect_saved_objects.ts +++ b/src/core/server/saved_objects/import/collect_saved_objects.ts @@ -46,6 +46,7 @@ interface CollectSavedObjectsOptions { objectLimit: number; filter?: (obj: SavedObject) => boolean; supportedTypes: string[]; + dataSourceId?: string; } export async function collectSavedObjects({ @@ -53,6 +54,7 @@ export async function collectSavedObjects({ objectLimit, filter, supportedTypes, + dataSourceId, }: CollectSavedObjectsOptions) { const errors: SavedObjectsImportError[] = []; const entries: Array<{ type: string; id: string }> = []; diff --git a/src/core/server/saved_objects/import/create_saved_objects.test.ts b/src/core/server/saved_objects/import/create_saved_objects.test.ts index 9c40124c34cd..f1118842c967 100644 --- a/src/core/server/saved_objects/import/create_saved_objects.test.ts +++ b/src/core/server/saved_objects/import/create_saved_objects.test.ts @@ -53,6 +53,8 @@ const createObject = (type: string, id: string, originId?: string): SavedObject const MULTI_NS_TYPE = 'multi'; const OTHER_TYPE = 'other'; +const DATA_SOURCE = 'data-source'; + /** * Create a variety of different objects to exercise different import / result scenarios */ @@ -69,11 +71,56 @@ const obj10 = createObject(OTHER_TYPE, 'id-10', 'originId-f'); // -> success const obj11 = createObject(OTHER_TYPE, 'id-11', 'originId-g'); // -> conflict const obj12 = createObject(OTHER_TYPE, 'id-12'); // -> success const obj13 = createObject(OTHER_TYPE, 'id-13'); // -> conflict +// data source object +const dataSourceObj1 = createObject(DATA_SOURCE, 'ds-id1'); // -> success +const dataSourceObj2 = createObject(DATA_SOURCE, 'ds-id2'); // -> conflict +const dashboardObjWithDataSource = createObject('dashboard', 'ds_dashboard-id1'); // -> success +const visualizationObjWithDataSource = createObject('visualization', 'ds_visualization-id1'); // -> success +const searchObjWithDataSource = createObject('search', 'ds_search-id1'); // -> success + +// objs without data source id, used to test can get saved object with data source id +const searchObj = { + id: '6aea5700-ac94-11e8-a651-614b2788174a', + type: 'search', + attributes: { + title: 'some-title', + }, + references: [], + source: { + title: 'mysavedsearch', + kibanaSavedObjectMeta: { + searchSourceJSON: + '{"index":"4c3f3c30-ac94-11e8-a651-614b2788174a","highlightAll":true,"version":true,"query":{"query":"","language":"lucene"},"filter":[]}', + }, + }, +}; // -> success + +const visualizationObj = { + id: '8411daa0-ac94-11e8-a651-614b2788174a', + type: 'visualization', + attributes: { + title: 'visualization-title', + }, + references: [], + source: { + title: 'mysavedviz', + visState: + '{"title":"mysavedviz","type":"pie","params":{"type":"pie","addTooltip":true,"addLegend":true,"legendPosition":"right","isDonut":true,"labels":{"show":false,"values":true,"last_level":true,"truncate":100}},"aggs":[{"id":"1","enabled":true,"type":"count","schema":"metric","params":{}}]}', + uiStateJSON: '{}', + description: '', + savedSearchId: '6aea5700-ac94-11e8-a651-614b2788174a', + version: 1, + kibanaSavedObjectMeta: { + searchSourceJSON: '{"query":{"query":"","language":"lucene"},"filter":[]}', + }, + }, +}; // non-multi-namespace types shouldn't have origin IDs, but we include test cases to ensure it's handled gracefully // non-multi-namespace types by definition cannot result in an unresolvable conflict, so we don't include test cases for those const importId3 = 'id-foo'; const importId4 = 'id-bar'; const importId8 = 'id-baz'; + const importIdMap = new Map([ [`${obj3.type}:${obj3.id}`, { id: importId3, omitOriginId: true }], [`${obj4.type}:${obj4.id}`, { id: importId4 }], @@ -93,6 +140,8 @@ describe('#createSavedObjects', () => { accumulatedErrors?: SavedObjectsImportError[]; namespace?: string; overwrite?: boolean; + dataSourceId?: string; + dataSourceTitle?: string; }): CreateSavedObjectsParams => { savedObjectsClient = savedObjectsClientMock.create(); bulkCreate = savedObjectsClient.bulkCreate; @@ -177,6 +226,15 @@ describe('#createSavedObjects', () => { expect(createSavedObjectsResult).toEqual({ createdObjects: [], errors: [] }); }); + test('filters out objects that have errors present with data source', async () => { + const error = { type: dataSourceObj1.type, id: dataSourceObj1.id } as SavedObjectsImportError; + const options = setupParams({ objects: [dataSourceObj1], accumulatedErrors: [error] }); + + const createSavedObjectsResult = await createSavedObjects(options); + expect(bulkCreate).not.toHaveBeenCalled(); + expect(createSavedObjectsResult).toEqual({ createdObjects: [], errors: [] }); + }); + test('exits early if there are no objects to create', async () => { const options = setupParams({ objects: [] }); @@ -186,6 +244,13 @@ describe('#createSavedObjects', () => { }); const objs = [obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8, obj9, obj10, obj11, obj12, obj13]; + const dataSourceObjs = [ + dataSourceObj1, + dataSourceObj2, + dashboardObjWithDataSource, + visualizationObjWithDataSource, + searchObjWithDataSource, + ]; const setupMockResults = (options: CreateSavedObjectsParams) => { bulkCreate.mockResolvedValue({ @@ -207,6 +272,26 @@ describe('#createSavedObjects', () => { }); }; + const setupMockResultsWithDataSource = (options: CreateSavedObjectsParams) => { + bulkCreate.mockResolvedValue({ + saved_objects: [ + getResultMock.conflict(dataSourceObj1.type, dataSourceObj1.id), + getResultMock.success(dataSourceObj2, options), + getResultMock.success(dashboardObjWithDataSource, options), + getResultMock.success(visualizationObjWithDataSource, options), + getResultMock.success(searchObjWithDataSource, options), + ], + }); + }; + const setupMockResultsToConstructDataSource = (options: CreateSavedObjectsParams) => { + bulkCreate.mockResolvedValue({ + saved_objects: [ + getResultMock.success(searchObj, options), + getResultMock.success(visualizationObj, options), + ], + }); + }; + describe('handles accumulated errors as expected', () => { const resolvableErrors: SavedObjectsImportError[] = [ { type: 'foo', id: 'foo-id', error: { type: 'conflict' } } as SavedObjectsImportError, @@ -234,6 +319,14 @@ describe('#createSavedObjects', () => { } }); + test('does not call bulkCreate when resolvable errors are present with data source objects', async () => { + for (const error of resolvableErrors) { + const options = setupParams({ objects: dataSourceObjs, accumulatedErrors: [error] }); + await createSavedObjects(options); + expect(bulkCreate).not.toHaveBeenCalled(); + } + }); + test('calls bulkCreate when unresolvable errors or no errors are present', async () => { for (const error of unresolvableErrors) { const options = setupParams({ objects: objs, accumulatedErrors: [error] }); @@ -247,6 +340,20 @@ describe('#createSavedObjects', () => { await createSavedObjects(options); expect(bulkCreate).toHaveBeenCalledTimes(1); }); + + test('calls bulkCreate when unresolvable errors or no errors are present with data source', async () => { + for (const error of unresolvableErrors) { + const options = setupParams({ objects: dataSourceObjs, accumulatedErrors: [error] }); + setupMockResultsWithDataSource(options); + await createSavedObjects(options); + expect(bulkCreate).toHaveBeenCalledTimes(1); + bulkCreate.mockClear(); + } + const options = setupParams({ objects: dataSourceObjs }); + setupMockResultsWithDataSource(options); + await createSavedObjects(options); + expect(bulkCreate).toHaveBeenCalledTimes(1); + }); }); it('filters out version from objects before create', async () => { @@ -270,6 +377,52 @@ describe('#createSavedObjects', () => { const argObjs = [obj1, obj2, x3, x4, obj5, obj6, obj7, x8, obj9, obj10, obj11, obj12, obj13]; expectBulkCreateArgs.objects(1, argObjs); }; + + const testBulkCreateObjectsWithDataSource = async ( + namespace?: string, + dataSourceId?: string, + dataSourceTitle?: string + ) => { + const options = setupParams({ + objects: dataSourceObjs, + namespace, + dataSourceId, + dataSourceTitle, + }); + setupMockResultsWithDataSource(options); + + await createSavedObjects(options); + expect(bulkCreate).toHaveBeenCalledTimes(1); + const argObjs = [ + dataSourceObj1, + dataSourceObj2, + dashboardObjWithDataSource, + visualizationObjWithDataSource, + searchObjWithDataSource, + ]; + expectBulkCreateArgs.objects(1, argObjs); + }; + + // testBulkCreateObjectsToAddDataSourceTitle + const testBulkCreateObjectsToAddDataSourceTitle = async ( + namespace?: string, + dataSourceId?: string, + dataSourceTitle?: string + ) => { + const options = setupParams({ + objects: [searchObj, visualizationObj], + namespace, + dataSourceId, + dataSourceTitle, + }); + setupMockResultsToConstructDataSource(options); + const result = (await createSavedObjects(options)).createdObjects; + expect(bulkCreate).toHaveBeenCalledTimes(1); + result.map((resultObj) => + expect(JSON.stringify(resultObj.attributes)).toContain('some-data-source-title') + ); + }; + const testBulkCreateOptions = async (namespace?: string) => { const overwrite = (Symbol() as unknown) as boolean; const options = setupParams({ objects: objs, namespace, overwrite }); @@ -279,6 +432,26 @@ describe('#createSavedObjects', () => { expect(bulkCreate).toHaveBeenCalledTimes(1); expectBulkCreateArgs.options(1, options); }; + + const testBulkCreateOptionsWithDataSource = async ( + namespace?: string, + dataSourceId?: string, + dataSourceTitle?: string + ) => { + const overwrite = (Symbol() as unknown) as boolean; + const options = setupParams({ + objects: dataSourceObjs, + namespace, + overwrite, + dataSourceId, + dataSourceTitle, + }); + setupMockResultsWithDataSource(options); + + await createSavedObjects(options); + expect(bulkCreate).toHaveBeenCalledTimes(1); + expectBulkCreateArgs.options(1, options); + }; const testReturnValue = async (namespace?: string) => { const options = setupParams({ objects: objs, namespace }); setupMockResults(options); @@ -293,6 +466,30 @@ describe('#createSavedObjects', () => { expect(results).toEqual(expectedResults); }; + const testReturnValueWithDataSource = async ( + namespace?: string, + dataSourceId?: string, + dataSourceTitle?: string + ) => { + const options = setupParams({ + objects: dataSourceObjs, + namespace, + dataSourceId, + dataSourceTitle, + }); + setupMockResultsWithDataSource(options); + + const results = await createSavedObjects(options); + const resultSavedObjectsWithDataSource = (await bulkCreate.mock.results[0].value).saved_objects; + const [dsr1, dsr2, dsr3, dsr4, dsr5] = resultSavedObjectsWithDataSource; + const transformedResultsWithDataSource = [dsr1, dsr2, dsr3, dsr4, dsr5]; + const expectedResultsWithDataSource = getExpectedResults( + transformedResultsWithDataSource, + dataSourceObjs + ); + expect(results).toEqual(expectedResultsWithDataSource); + }; + describe('with an undefined namespace', () => { test('calls bulkCreate once with input objects', async () => { await testBulkCreateObjects(); @@ -317,4 +514,36 @@ describe('#createSavedObjects', () => { await testReturnValue(namespace); }); }); + + describe('with a data source', () => { + test('calls bulkCreate once with input objects with data source id', async () => { + await testBulkCreateObjectsWithDataSource( + 'some-namespace', + 'some-datasource-id', + 'some-data-source-title' + ); + }); + test('calls bulkCreate once with input options with data source id', async () => { + await testBulkCreateOptionsWithDataSource( + 'some-namespace', + 'some-datasource-id', + 'some-data-source-title' + ); + }); + test('returns bulkCreate results that are remapped to IDs of imported objects with data source id', async () => { + await testReturnValueWithDataSource( + 'some-namespace', + 'some-datasource-id', + 'some-data-source-title' + ); + }); + + test('can correct attach datasource id to a search object', async () => { + await testBulkCreateObjectsToAddDataSourceTitle( + 'some-namespace', + 'some-datasource-id', + 'some-data-source-title' + ); + }); + }); }); diff --git a/src/core/server/saved_objects/import/create_saved_objects.ts b/src/core/server/saved_objects/import/create_saved_objects.ts index b67cffce1e96..67bc8dfc6227 100644 --- a/src/core/server/saved_objects/import/create_saved_objects.ts +++ b/src/core/server/saved_objects/import/create_saved_objects.ts @@ -40,6 +40,8 @@ interface CreateSavedObjectsParams { namespace?: string; overwrite?: boolean; workspaces?: string[]; + dataSourceId?: string; + dataSourceTitle?: string; } interface CreateSavedObjectsResult { createdObjects: Array>; @@ -58,6 +60,8 @@ export const createSavedObjects = async ({ namespace, overwrite, workspaces, + dataSourceId, + dataSourceTitle, }: CreateSavedObjectsParams): Promise> => { // filter out any objects that resulted in errors const errorSet = accumulatedErrors.reduce( @@ -78,7 +82,70 @@ export const createSavedObjects = async ({ ); // filter out the 'version' field of each object, if it exists + const objectsToCreate = filteredObjects.map(({ version, ...object }) => { + if (dataSourceId) { + // @ts-expect-error + if (dataSourceTitle && object.attributes.title) { + if ( + object.type === 'dashboard' || + object.type === 'visualization' || + object.type === 'search' + ) { + // @ts-expect-error + object.attributes.title = object.attributes.title + `_${dataSourceTitle}`; + } + } + + if (object.type === 'index-pattern') { + object.references = [ + { + id: `${dataSourceId}`, + type: 'data-source', + name: 'dataSource', + }, + ]; + } + + if (object.type === 'visualization' || object.type === 'search') { + // @ts-expect-error + const searchSourceString = object.attributes?.kibanaSavedObjectMeta?.searchSourceJSON; + // @ts-expect-error + const visStateString = object.attributes?.visState; + + if (searchSourceString) { + const searchSource = JSON.parse(searchSourceString); + if (searchSource.index) { + const searchSourceIndex = searchSource.index.includes('_') + ? searchSource.index.split('_')[searchSource.index.split('_').length - 1] + : searchSource.index; + searchSource.index = `${dataSourceId}_` + searchSourceIndex; + + // @ts-expect-error + object.attributes.kibanaSavedObjectMeta.searchSourceJSON = JSON.stringify(searchSource); + } + } + + if (visStateString) { + const visState = JSON.parse(visStateString); + const controlList = visState.params?.controls; + if (controlList) { + // @ts-expect-error + controlList.map((control) => { + if (control.indexPattern) { + const controlIndexPattern = control.indexPattern.includes('_') + ? control.indexPattern.split('_')[control.indexPattern.split('_').length - 1] + : control.indexPattern; + control.indexPattern = `${dataSourceId}_` + controlIndexPattern; + } + }); + } + // @ts-expect-error + object.attributes.visState = JSON.stringify(visState); + } + } + } + // use the import ID map to ensure that each reference is being created with the correct ID const references = object.references?.map((reference) => { const { type, id } = reference; @@ -98,7 +165,6 @@ export const createSavedObjects = async ({ } return { ...object, ...(references && { references }) }; }); - const resolvableErrors = ['conflict', 'ambiguous_conflict', 'missing_references']; let expectedResults = objectsToCreate; if (!accumulatedErrors.some(({ error: { type } }) => resolvableErrors.includes(type))) { diff --git a/src/core/server/saved_objects/import/import_saved_objects.test.ts b/src/core/server/saved_objects/import/import_saved_objects.test.ts index 4bf1717e895c..dcb8d685d42c 100644 --- a/src/core/server/saved_objects/import/import_saved_objects.test.ts +++ b/src/core/server/saved_objects/import/import_saved_objects.test.ts @@ -47,6 +47,7 @@ import { validateReferences } from './validate_references'; import { checkConflicts } from './check_conflicts'; import { checkOriginConflicts } from './check_origin_conflicts'; import { createSavedObjects } from './create_saved_objects'; +import { checkConflictsForDataSource } from './check_conflict_for_data_source'; jest.mock('./collect_saved_objects'); jest.mock('./regenerate_ids'); @@ -54,6 +55,7 @@ jest.mock('./validate_references'); jest.mock('./check_conflicts'); jest.mock('./check_origin_conflicts'); jest.mock('./create_saved_objects'); +jest.mock('./check_conflict_for_data_source'); const getMockFn = any, U>(fn: (...args: Parameters) => U) => fn as jest.MockedFunction<(...args: Parameters) => U>; @@ -81,6 +83,11 @@ describe('#importSavedObjectsFromStream', () => { importIdMap: new Map(), pendingOverwrites: new Set(), }); + getMockFn(checkConflictsForDataSource).mockResolvedValue({ + errors: [], + filteredObjects: [], + importIdMap: new Map(), + }); getMockFn(createSavedObjects).mockResolvedValue({ errors: [], createdObjects: [] }); }); @@ -90,8 +97,12 @@ describe('#importSavedObjectsFromStream', () => { let savedObjectsClient: jest.Mocked; let typeRegistry: jest.Mocked; const namespace = 'some-namespace'; + const testDataSourceId = 'some-datasource'; - const setupOptions = (createNewCopies: boolean = false): SavedObjectsImportOptions => { + const setupOptions = ( + createNewCopies: boolean = false, + dataSourceId: string | undefined = undefined + ): SavedObjectsImportOptions => { readStream = new Readable(); savedObjectsClient = savedObjectsClientMock.create(); typeRegistry = typeRegistryMock.create(); @@ -110,14 +121,17 @@ describe('#importSavedObjectsFromStream', () => { typeRegistry, namespace, createNewCopies, + dataSourceId, }; }; - const createObject = (): SavedObject<{ + const createObject = ( + dataSourceId: string | undefined = undefined + ): SavedObject<{ title: string; }> => { return { type: 'foo-type', - id: uuidv4(), + id: dataSourceId ? `${dataSourceId}_${uuidv4()}` : uuidv4(), references: [], attributes: { title: 'some-title' }, }; @@ -226,6 +240,30 @@ describe('#importSavedObjectsFromStream', () => { expect(checkOriginConflicts).toHaveBeenCalledWith(checkOriginConflictsParams); }); + test('checks data source conflicts', async () => { + const options = setupOptions(false, testDataSourceId); + const collectedObjects = [createObject()]; + getMockFn(collectSavedObjects).mockResolvedValue({ + errors: [], + collectedObjects, + importIdMap: new Map(), + }); + getMockFn(checkConflicts).mockResolvedValue({ + errors: [], + filteredObjects: collectedObjects, + importIdMap: new Map([['bar', { id: 'newId1' }]]), + pendingOverwrites: new Set(), + }); + + await importSavedObjectsFromStream(options); + const checkConflictsForDataSourceParams = { + objects: collectedObjects, + ignoreRegularConflicts: overwrite, + dataSourceId: testDataSourceId, + }; + expect(checkConflictsForDataSource).toHaveBeenCalledWith(checkConflictsForDataSourceParams); + }); + test('creates saved objects', async () => { const options = setupOptions(); const collectedObjects = [createObject()]; @@ -291,16 +329,18 @@ describe('#importSavedObjectsFromStream', () => { }); await importSavedObjectsFromStream(options); - expect(regenerateIds).toHaveBeenCalledWith(collectedObjects); + expect(regenerateIds).toHaveBeenCalledWith(collectedObjects, undefined); }); - test('does not check conflicts or check origin conflicts', async () => { + test('does not check conflicts or check origin conflicts or check data source conflict', async () => { const options = setupOptions(true); + getMockFn(validateReferences).mockResolvedValue([]); await importSavedObjectsFromStream(options); expect(checkConflicts).not.toHaveBeenCalled(); expect(checkOriginConflicts).not.toHaveBeenCalled(); + expect(checkConflictsForDataSource).not.toHaveBeenCalled(); }); test('creates saved objects', async () => { @@ -358,10 +398,20 @@ describe('#importSavedObjectsFromStream', () => { const obj1 = createObject(); const tmp = createObject(); const obj2 = { ...tmp, destinationId: 'some-destinationId', originId: tmp.id }; - const obj3 = { ...createObject(), destinationId: 'another-destinationId' }; // empty originId + const obj3 = { ...createObject(undefined), destinationId: 'another-destinationId' }; // empty originId const createdObjects = [obj1, obj2, obj3]; const error1 = createError(); const error2 = createError(); + // add some objects with data source id + const dataSourceObj1 = createObject(testDataSourceId); + const tmp2 = createObject(testDataSourceId); + const dataSourceObj2 = { ...tmp2, destinationId: 'some-destinationId', originId: tmp.id }; + const dataSourceObj3 = { + ...createObject(testDataSourceId), + destinationId: 'another-destinationId', + }; + const createdDsObjects = [dataSourceObj1, dataSourceObj2, dataSourceObj3]; + // results const success1 = { type: obj1.type, @@ -382,6 +432,26 @@ describe('#importSavedObjectsFromStream', () => { }; const errors = [error1, error2]; + const dsSuccess1 = { + type: dataSourceObj1.type, + id: dataSourceObj1.id, + meta: { title: dataSourceObj1.attributes.title, icon: `${dataSourceObj1.type}-icon` }, + }; + + const dsSuccess2 = { + type: dataSourceObj2.type, + id: dataSourceObj2.id, + meta: { title: dataSourceObj2.attributes.title, icon: `${dataSourceObj2.type}-icon` }, + destinationId: dataSourceObj2.destinationId, + }; + + const dsSuccess3 = { + type: dataSourceObj3.type, + id: dataSourceObj3.id, + meta: { title: dataSourceObj3.attributes.title, icon: `${dataSourceObj3.type}-icon` }, + destinationId: dataSourceObj3.destinationId, + }; + test('with createNewCopies disabled', async () => { const options = setupOptions(); getMockFn(checkConflicts).mockResolvedValue({ @@ -420,7 +490,7 @@ describe('#importSavedObjectsFromStream', () => { test('with createNewCopies enabled', async () => { // however, we include it here for posterity - const options = setupOptions(true); + const options = setupOptions(true, undefined); getMockFn(createSavedObjects).mockResolvedValue({ errors, createdObjects }); const result = await importSavedObjectsFromStream(options); @@ -438,6 +508,68 @@ describe('#importSavedObjectsFromStream', () => { errors: errorResults, }); }); + + test('with createNewCopies disabled and with data source id', async () => { + const options = setupOptions(false, testDataSourceId); + getMockFn(checkConflictsForDataSource).mockResolvedValue({ + errors: [], + filteredObjects: [], + importIdMap: new Map(), + }); + getMockFn(checkConflicts).mockResolvedValue({ + errors: [], + filteredObjects: [], + importIdMap: new Map(), + pendingOverwrites: new Set([ + `${dsSuccess2.type}:${dsSuccess2.id}`, // the dsSuccess2 object was overwritten + `${error2.type}:${error2.id}`, // an attempt was made to overwrite the error2 object + ]), + }); + getMockFn(createSavedObjects).mockResolvedValue({ + errors, + createdObjects: createdDsObjects, + }); + + const result = await importSavedObjectsFromStream(options); + // successResults only includes the imported object's type, id, and destinationId (if a new one was generated) + const successResults = [ + dsSuccess1, + { ...dsSuccess2, overwrite: true }, + { ...dsSuccess3, createNewCopy: true }, + ]; + const errorResults = [ + { ...error1, meta: { ...error1.meta, icon: `${error1.type}-icon` } }, + { ...error2, meta: { ...error2.meta, icon: `${error2.type}-icon` }, overwrite: true }, + ]; + expect(result).toEqual({ + success: false, + successCount: 3, + successResults, + errors: errorResults, + }); + }); + + test('with createNewCopies enabled and with data source id', async () => { + const options = setupOptions(true, testDataSourceId); + getMockFn(createSavedObjects).mockResolvedValue({ + errors, + createdObjects: createdDsObjects, + }); + + const result = await importSavedObjectsFromStream(options); + const successDsResults = [dsSuccess1, dsSuccess2, dsSuccess3]; + + const errorResults = [ + { ...error1, meta: { ...error1.meta, icon: `${error1.type}-icon` } }, + { ...error2, meta: { ...error2.meta, icon: `${error2.type}-icon` } }, + ]; + expect(result).toEqual({ + success: false, + successCount: 3, + successResults: successDsResults, + errors: errorResults, + }); + }); }); test('accumulates multiple errors', async () => { diff --git a/src/core/server/saved_objects/import/import_saved_objects.ts b/src/core/server/saved_objects/import/import_saved_objects.ts index fce50bab7abc..51a790aca5d2 100644 --- a/src/core/server/saved_objects/import/import_saved_objects.ts +++ b/src/core/server/saved_objects/import/import_saved_objects.ts @@ -39,6 +39,7 @@ import { checkOriginConflicts } from './check_origin_conflicts'; import { createSavedObjects } from './create_saved_objects'; import { checkConflicts } from './check_conflicts'; import { regenerateIds, regenerateIdsWithReference } from './regenerate_ids'; +import { checkConflictsForDataSource } from './check_conflict_for_data_source'; /** * Import saved objects from given stream. See the {@link SavedObjectsImportOptions | options} for more @@ -55,6 +56,8 @@ export async function importSavedObjectsFromStream({ typeRegistry, namespace, workspaces, + dataSourceId, + dataSourceTitle, }: SavedObjectsImportOptions): Promise { let errorAccumulator: SavedObjectsImportError[] = []; const supportedTypes = typeRegistry.getImportableAndExportableTypes().map((type) => type.name); @@ -64,6 +67,7 @@ export async function importSavedObjectsFromStream({ readStream, objectLimit, supportedTypes, + dataSourceId, }); errorAccumulator = [...errorAccumulator, ...collectSavedObjectsResult.errors]; /** Map of all IDs for objects that we are attempting to import; each value is empty by default */ @@ -79,7 +83,8 @@ export async function importSavedObjectsFromStream({ errorAccumulator = [...errorAccumulator, ...validateReferencesResult]; if (createNewCopies) { - importIdMap = regenerateIds(collectSavedObjectsResult.collectedObjects); + // randomly generated id + importIdMap = regenerateIds(collectSavedObjectsResult.collectedObjects, dataSourceId); } else { importIdMap = await regenerateIdsWithReference({ savedObjects: collectSavedObjectsResult.collectedObjects, @@ -88,6 +93,7 @@ export async function importSavedObjectsFromStream({ objectLimit, importIdMap, }); + // in check conclict and override mode // Check single-namespace objects for conflicts in this namespace, and check multi-namespace objects for conflicts across all namespaces const checkConflictsParams = { objects: collectSavedObjectsResult.collectedObjects, @@ -96,10 +102,11 @@ export async function importSavedObjectsFromStream({ ignoreRegularConflicts: overwrite, workspaces, }; + const checkConflictsResult = await checkConflicts(checkConflictsParams); errorAccumulator = [...errorAccumulator, ...checkConflictsResult.errors]; importIdMap = new Map([...importIdMap, ...checkConflictsResult.importIdMap]); - pendingOverwrites = checkConflictsResult.pendingOverwrites; + pendingOverwrites = new Set([...pendingOverwrites, ...checkConflictsResult.pendingOverwrites]); // Check multi-namespace object types for origin conflicts in this namespace const checkOriginConflictsParams = { @@ -110,6 +117,19 @@ export async function importSavedObjectsFromStream({ ignoreRegularConflicts: overwrite, importIdMap, }; + + /** + * If dataSourceId exist, + */ + if (dataSourceId) { + const checkConflictsForDataSourceResult = await checkConflictsForDataSource({ + objects: checkConflictsResult.filteredObjects, + ignoreRegularConflicts: overwrite, + dataSourceId, + }); + checkOriginConflictsParams.objects = checkConflictsForDataSourceResult.filteredObjects; + } + const checkOriginConflictsResult = await checkOriginConflicts(checkOriginConflictsParams); errorAccumulator = [...errorAccumulator, ...checkOriginConflictsResult.errors]; importIdMap = new Map([...importIdMap, ...checkOriginConflictsResult.importIdMap]); @@ -121,13 +141,17 @@ export async function importSavedObjectsFromStream({ // Create objects in bulk const createSavedObjectsParams = { - objects: collectSavedObjectsResult.collectedObjects, + objects: dataSourceId + ? collectSavedObjectsResult.collectedObjects.filter((object) => object.type !== 'data-source') + : collectSavedObjectsResult.collectedObjects, accumulatedErrors: errorAccumulator, savedObjectsClient, importIdMap, overwrite, namespace, ...(workspaces ? { workspaces } : {}), + dataSourceId, + dataSourceTitle, }; const createSavedObjectsResult = await createSavedObjects(createSavedObjectsParams); errorAccumulator = [...errorAccumulator, ...createSavedObjectsResult.errors]; @@ -146,6 +170,7 @@ export async function importSavedObjectsFromStream({ }; } ); + const errorResults = errorAccumulator.map((error) => { const icon = typeRegistry.getType(error.type)?.management?.icon; const attemptedOverwrite = pendingOverwrites.has(`${error.type}:${error.id}`); diff --git a/src/core/server/saved_objects/import/regenerate_ids.test.ts b/src/core/server/saved_objects/import/regenerate_ids.test.ts index 11556c8a21c1..c7dbfb8b50bc 100644 --- a/src/core/server/saved_objects/import/regenerate_ids.test.ts +++ b/src/core/server/saved_objects/import/regenerate_ids.test.ts @@ -39,12 +39,18 @@ describe('#regenerateIds', () => { { type: 'baz', id: '3' }, ] as any) as SavedObject[]; + const dataSourceObjects = ([{ type: 'data-source', id: '1' }] as any) as SavedObject[]; + + test('can filter out data source object', () => { + expect(regenerateIds(dataSourceObjects, '').size).toBe(0); + }); + test('returns expected values', () => { mockUuidv4 .mockReturnValueOnce('uuidv4 #1') .mockReturnValueOnce('uuidv4 #2') .mockReturnValueOnce('uuidv4 #3'); - expect(regenerateIds(objects)).toMatchInlineSnapshot(` + expect(regenerateIds(objects, '')).toMatchInlineSnapshot(` Map { "foo:1" => Object { "id": "uuidv4 #1", diff --git a/src/core/server/saved_objects/import/regenerate_ids.ts b/src/core/server/saved_objects/import/regenerate_ids.ts index 27b10e575f44..b412bb80bc6b 100644 --- a/src/core/server/saved_objects/import/regenerate_ids.ts +++ b/src/core/server/saved_objects/import/regenerate_ids.ts @@ -37,9 +37,14 @@ import { SavedObjectsUtils } from '../service'; * * @param objects The saved objects to generate new IDs for. */ -export const regenerateIds = (objects: SavedObject[]) => { +export const regenerateIds = (objects: SavedObject[], dataSourceId: string | undefined) => { const importIdMap = objects.reduce((acc, object) => { - return acc.set(`${object.type}:${object.id}`, { id: uuidv4(), omitOriginId: true }); + return object.type === 'data-source' + ? acc + : acc.set(`${object.type}:${object.id}`, { + id: dataSourceId ? `${dataSourceId}_${uuidv4()}` : uuidv4(), + omitOriginId: true, + }); }, new Map()); return importIdMap; }; diff --git a/src/core/server/saved_objects/import/resolve_import_errors.test.ts b/src/core/server/saved_objects/import/resolve_import_errors.test.ts index bd0ba1afdc9d..ef22155f046b 100644 --- a/src/core/server/saved_objects/import/resolve_import_errors.test.ts +++ b/src/core/server/saved_objects/import/resolve_import_errors.test.ts @@ -369,7 +369,7 @@ describe('#importSavedObjectsFromStream', () => { }); await resolveSavedObjectsImportErrors(options); - expect(regenerateIds).toHaveBeenCalledWith(collectedObjects); + expect(regenerateIds).toHaveBeenCalledWith(collectedObjects, undefined); }); test('creates saved objects', async () => { diff --git a/src/core/server/saved_objects/import/resolve_import_errors.ts b/src/core/server/saved_objects/import/resolve_import_errors.ts index 5bad2e4907b8..a3d10c6f1ace 100644 --- a/src/core/server/saved_objects/import/resolve_import_errors.ts +++ b/src/core/server/saved_objects/import/resolve_import_errors.ts @@ -60,6 +60,8 @@ export async function resolveSavedObjectsImportErrors({ namespace, createNewCopies, workspaces, + dataSourceId, + dataSourceTitle, }: SavedObjectsResolveImportErrorsOptions): Promise { // throw a BadRequest error if we see invalid retries validateRetries(retries); @@ -77,6 +79,7 @@ export async function resolveSavedObjectsImportErrors({ objectLimit, filter, supportedTypes, + dataSourceId, } ); errorAccumulator = [...errorAccumulator, ...collectorErrors]; @@ -117,7 +120,7 @@ export async function resolveSavedObjectsImportErrors({ if (createNewCopies) { // In case any missing reference errors were resolved, ensure that we regenerate those object IDs as well // This is because a retry to resolve a missing reference error may not necessarily specify a destinationId - importIdMap = regenerateIds(objectsToResolve); + importIdMap = regenerateIds(objectsToResolve, dataSourceId); } // Check single-namespace objects for conflicts in this namespace, and check multi-namespace objects for conflicts across all namespaces @@ -128,6 +131,7 @@ export async function resolveSavedObjectsImportErrors({ retries, createNewCopies, workspaces, + dataSourceId, }; const checkConflictsResult = await checkConflicts(checkConflictsParams); errorAccumulator = [...errorAccumulator, ...checkConflictsResult.errors]; @@ -160,6 +164,8 @@ export async function resolveSavedObjectsImportErrors({ namespace, overwrite, workspaces, + dataSourceId, + dataSourceTitle, }; const { createdObjects, errors: bulkCreateErrors } = await createSavedObjects( createSavedObjectsParams diff --git a/src/core/server/saved_objects/import/types.ts b/src/core/server/saved_objects/import/types.ts index 924d1b18895a..a02d42e5c363 100644 --- a/src/core/server/saved_objects/import/types.ts +++ b/src/core/server/saved_objects/import/types.ts @@ -189,6 +189,8 @@ export interface SavedObjectsImportOptions { createNewCopies: boolean; /** if specified, will import in given workspaces, else will import as global object */ workspaces?: string[]; + dataSourceId?: string; + dataSourceTitle?: string; } /** @@ -212,6 +214,8 @@ export interface SavedObjectsResolveImportErrorsOptions { createNewCopies: boolean; /** if specified, will import in given workspaces, else will import as global object */ workspaces?: string[]; + dataSourceId?: string; + dataSourceTitle?: string; } export type CreatedObject = SavedObject & { destinationId?: string }; diff --git a/src/core/server/saved_objects/routes/import.ts b/src/core/server/saved_objects/routes/import.ts index 2d221d2d47bc..6c7cc77d9b84 100644 --- a/src/core/server/saved_objects/routes/import.ts +++ b/src/core/server/saved_objects/routes/import.ts @@ -63,6 +63,7 @@ export const registerImportRoute = (router: IRouter, config: SavedObjectConfig) workspaces: schema.maybe( schema.oneOf([schema.string(), schema.arrayOf(schema.string())]) ), + dataSourceId: schema.maybe(schema.string({ defaultValue: '' })), }, { validate: (object) => { @@ -78,13 +79,29 @@ export const registerImportRoute = (router: IRouter, config: SavedObjectConfig) }, }, router.handleLegacyErrors(async (context, req, res) => { - const { overwrite, createNewCopies } = req.query; + const { overwrite, createNewCopies, dataSourceId } = req.query; const file = req.body.file as FileStream; const fileExtension = extname(file.hapi.filename).toLowerCase(); if (fileExtension !== '.ndjson') { return res.badRequest({ body: `Invalid file extension ${fileExtension}` }); } + // get datasource from saved object service + // dataSource is '' when there is no dataSource pass in the url + const dataSource = dataSourceId + ? await context.core.savedObjects.client + .get('data-source', dataSourceId) + .then((response) => { + const attributes: any = response?.attributes || {}; + return { + id: response.id, + title: attributes.title, + }; + }) + : ''; + + const dataSourceTitle = dataSource ? dataSource.title : ''; + let readStream: Readable; try { readStream = await createSavedObjectsStreamFromNdJson(file); @@ -107,6 +124,8 @@ export const registerImportRoute = (router: IRouter, config: SavedObjectConfig) overwrite, createNewCopies, workspaces, + dataSourceId, + dataSourceTitle, }); return res.ok({ body: result }); diff --git a/src/core/server/saved_objects/routes/resolve_import_errors.ts b/src/core/server/saved_objects/routes/resolve_import_errors.ts index 32d67b5ae8ab..dedcc960a675 100644 --- a/src/core/server/saved_objects/routes/resolve_import_errors.ts +++ b/src/core/server/saved_objects/routes/resolve_import_errors.ts @@ -61,6 +61,7 @@ export const registerResolveImportErrorsRoute = (router: IRouter, config: SavedO workspaces: schema.maybe( schema.oneOf([schema.string(), schema.arrayOf(schema.string())]) ), + dataSourceId: schema.maybe(schema.string({ defaultValue: '' })), }), body: schema.object({ file: schema.stream(), @@ -92,6 +93,24 @@ export const registerResolveImportErrorsRoute = (router: IRouter, config: SavedO return res.badRequest({ body: `Invalid file extension ${fileExtension}` }); } + const dataSourceId = req.query.dataSourceId; + + // get datasource from saved object service + + const dataSource = dataSourceId + ? await context.core.savedObjects.client + .get('data-source', dataSourceId) + .then((response) => { + const attributes: any = response?.attributes || {}; + return { + id: response.id, + title: attributes.title, + }; + }) + : ''; + + const dataSourceTitle = dataSource ? dataSource.title : ''; + let readStream: Readable; try { readStream = await createSavedObjectsStreamFromNdJson(file); @@ -114,6 +133,8 @@ export const registerResolveImportErrorsRoute = (router: IRouter, config: SavedO objectLimit: maxImportExportSize, createNewCopies: req.query.createNewCopies, workspaces, + dataSourceId, + dataSourceTitle, }); return res.ok({ body: result }); diff --git a/src/core/server/security/readonly_service.test.ts b/src/core/server/security/readonly_service.test.ts new file mode 100644 index 000000000000..739d9e3daac3 --- /dev/null +++ b/src/core/server/security/readonly_service.test.ts @@ -0,0 +1,34 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { OpenSearchDashboardsRequest } from '../index'; +import { ReadonlyService } from './readonly_service'; +import { httpServerMock } from '../http/http_server.mocks'; + +describe('ReadonlyService', () => { + let readonlyService: ReadonlyService; + let request: OpenSearchDashboardsRequest; + + beforeEach(() => { + readonlyService = new ReadonlyService(); + request = httpServerMock.createOpenSearchDashboardsRequest(); + }); + + it('isReadonly returns false by default', () => { + expect(readonlyService.isReadonly(request)).resolves.toBeFalsy(); + }); + + it('hideForReadonly merges capabilites to hide', () => { + readonlyService.isReadonly = jest.fn(() => new Promise(() => true)); + const result = readonlyService.hideForReadonly( + request, + { foo: { show: true } }, + { foo: { show: false } } + ); + + expect(readonlyService.isReadonly).toBeCalledTimes(1); + expect(result).resolves.toEqual({ foo: { show: false } }); + }); +}); diff --git a/src/core/server/security/readonly_service.ts b/src/core/server/security/readonly_service.ts new file mode 100644 index 000000000000..a41dc0fde3b1 --- /dev/null +++ b/src/core/server/security/readonly_service.ts @@ -0,0 +1,22 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { merge } from 'lodash'; +import { OpenSearchDashboardsRequest, Capabilities } from '../index'; +import { IReadOnlyService } from './types'; + +export class ReadonlyService implements IReadOnlyService { + async isReadonly(request: OpenSearchDashboardsRequest): Promise { + return false; + } + + async hideForReadonly( + request: OpenSearchDashboardsRequest, + capabilites: Partial, + hideCapabilities: Partial + ): Promise> { + return (await this.isReadonly(request)) ? merge(capabilites, hideCapabilities) : capabilites; + } +} diff --git a/src/core/server/security/security_service.mock.ts b/src/core/server/security/security_service.mock.ts new file mode 100644 index 000000000000..687509e42b5e --- /dev/null +++ b/src/core/server/security/security_service.mock.ts @@ -0,0 +1,18 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { SecurityServiceSetup } from './types'; + +const createSetupContractMock = () => { + const setupContract: jest.Mocked = { + readonlyService: jest.fn(), + registerReadonlyService: jest.fn(), + }; + return setupContract; +}; + +export const securityServiceMock = { + createSetupContract: createSetupContractMock, +}; diff --git a/src/core/server/security/security_service.test.ts b/src/core/server/security/security_service.test.ts new file mode 100644 index 000000000000..cf09b64ae36a --- /dev/null +++ b/src/core/server/security/security_service.test.ts @@ -0,0 +1,45 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { OpenSearchDashboardsRequest } from '../index'; +import { mockCoreContext } from '../core_context.mock'; +import { SecurityService } from './security_service'; +import { httpServerMock } from '../http/http_server.mocks'; +import { IReadOnlyService } from './types'; + +describe('SecurityService', () => { + let securityService: SecurityService; + let request: OpenSearchDashboardsRequest; + + beforeEach(() => { + const coreContext = mockCoreContext.create(); + securityService = new SecurityService(coreContext); + request = httpServerMock.createOpenSearchDashboardsRequest(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('#readonlyService', () => { + it("uses core's readonly service by default", () => { + const setupContext = securityService.setup(); + expect(setupContext.readonlyService().isReadonly(request)).resolves.toBeFalsy(); + }); + + it('registers custom readonly service and it uses it', () => { + const setupContext = securityService.setup(); + const readonlyServiceMock: jest.Mocked = { + isReadonly: jest.fn(), + hideForReadonly: jest.fn(), + }; + + setupContext.registerReadonlyService(readonlyServiceMock); + setupContext.readonlyService().isReadonly(request); + + expect(readonlyServiceMock.isReadonly).toBeCalledTimes(1); + }); + }); +}); diff --git a/src/core/server/security/security_service.ts b/src/core/server/security/security_service.ts new file mode 100644 index 000000000000..1916afc165dd --- /dev/null +++ b/src/core/server/security/security_service.ts @@ -0,0 +1,43 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { CoreService } from '../../types'; +import { IReadOnlyService, InternalSecurityServiceSetup } from './types'; +import { CoreContext } from '../core_context'; +import { Logger } from '../logging'; +import { ReadonlyService } from './readonly_service'; + +export class SecurityService implements CoreService { + private logger: Logger; + private readonlyService: IReadOnlyService; + + constructor(coreContext: CoreContext) { + this.logger = coreContext.logger.get('security-service'); + this.readonlyService = new ReadonlyService(); + } + + public setup() { + this.logger.debug('Setting up Security service'); + + const securityService = this; + + return { + registerReadonlyService(service: IReadOnlyService) { + securityService.readonlyService = service; + }, + readonlyService() { + return securityService.readonlyService; + }, + }; + } + + public start() { + this.logger.debug('Starting plugin'); + } + + public stop() { + this.logger.debug('Stopping plugin'); + } +} diff --git a/src/core/server/security/types.ts b/src/core/server/security/types.ts new file mode 100644 index 000000000000..43a599d99625 --- /dev/null +++ b/src/core/server/security/types.ts @@ -0,0 +1,22 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Capabilities, OpenSearchDashboardsRequest } from '../index'; + +export interface SecurityServiceSetup { + registerReadonlyService(service: IReadOnlyService): void; + readonlyService(): IReadOnlyService; +} + +export type InternalSecurityServiceSetup = SecurityServiceSetup; + +export interface IReadOnlyService { + isReadonly(request: OpenSearchDashboardsRequest): Promise; + hideForReadonly( + request: OpenSearchDashboardsRequest, + capabilites: Capabilities, + hideCapabilities: Partial + ): Promise>; +} diff --git a/src/core/server/server.test.ts b/src/core/server/server.test.ts index c80795e02eb5..71789d76c337 100644 --- a/src/core/server/server.test.ts +++ b/src/core/server/server.test.ts @@ -62,6 +62,7 @@ beforeEach(() => { mockPluginsService.discover.mockResolvedValue({ pluginTree: { asOpaqueIds: new Map(), asNames: new Map() }, uiPlugins: { internal: new Map(), public: new Map(), browserConfigs: new Map() }, + requiredEnginePlugins: new Map(), }); }); @@ -111,6 +112,7 @@ test('injects legacy dependency to context#setup()', async () => { mockPluginsService.discover.mockResolvedValue({ pluginTree: { asOpaqueIds: pluginDependencies, asNames: new Map() }, uiPlugins: { internal: new Map(), public: new Map(), browserConfigs: new Map() }, + requiredEnginePlugins: new Map(), }); await server.setup(); diff --git a/src/core/server/server.ts b/src/core/server/server.ts index d4c041725ac7..b76ec61cebd0 100644 --- a/src/core/server/server.ts +++ b/src/core/server/server.ts @@ -41,12 +41,19 @@ import { RenderingService } from './rendering'; import { LegacyService, ensureValidConfiguration } from './legacy'; import { Logger, LoggerFactory, LoggingService, ILoggingSystem } from './logging'; import { UiSettingsService } from './ui_settings'; -import { PluginsService, config as pluginsConfig } from './plugins'; +import { + CompatibleEnginePluginVersions, + PluginName, + PluginsService, + config as pluginsConfig, +} from './plugins'; import { SavedObjectsService } from '../server/saved_objects'; import { MetricsService, opsConfig } from './metrics'; import { CapabilitiesService } from './capabilities'; import { EnvironmentService, config as pidConfig } from './environment'; import { StatusService } from './status/status_service'; +import { SecurityService } from './security/security_service'; +import { CrossCompatibilityService } from './cross_compatibility'; import { config as cspConfig } from './csp'; import { config as opensearchConfig } from './opensearch'; @@ -86,11 +93,15 @@ export class Server { private readonly coreApp: CoreApp; private readonly auditTrail: AuditTrailService; private readonly coreUsageData: CoreUsageDataService; + private readonly security: SecurityService; + private readonly crossCompatibility: CrossCompatibilityService; #pluginsInitialized?: boolean; private coreStart?: InternalCoreStart; private readonly logger: LoggerFactory; + private openSearchPluginDeps: Map = new Map(); + constructor( rawConfigProvider: RawConfigurationProvider, public readonly env: Env, @@ -118,6 +129,8 @@ export class Server { this.auditTrail = new AuditTrailService(core); this.logging = new LoggingService(core); this.coreUsageData = new CoreUsageDataService(core); + this.security = new SecurityService(core); + this.crossCompatibility = new CrossCompatibilityService(core); } public async setup() { @@ -127,9 +140,10 @@ export class Server { const environmentSetup = await this.environment.setup(); // Discover any plugins before continuing. This allows other systems to utilize the plugin dependency graph. - const { pluginTree, uiPlugins } = await this.plugins.discover({ + const { pluginTree, uiPlugins, requiredEnginePlugins } = await this.plugins.discover({ environment: environmentSetup, }); + this.openSearchPluginDeps = requiredEnginePlugins; const legacyConfigSetup = await this.legacy.setupLegacyConfig(); // Immediately terminate in case of invalid configuration @@ -196,6 +210,8 @@ export class Server { loggingSystem: this.loggingSystem, }); + const securitySetup = this.security.setup(); + this.coreUsageData.setup({ metrics: metricsSetup }); const coreSetup: InternalCoreSetup = { @@ -212,6 +228,7 @@ export class Server { auditTrail: auditTrailSetup, logging: loggingSetup, metrics: metricsSetup, + security: securitySetup, }; const pluginsSetup = await this.plugins.setup(coreSetup); @@ -254,6 +271,11 @@ export class Server { savedObjects: savedObjectsStart, }); + const crossCompatibilityServiceStart = this.crossCompatibility.start({ + opensearch: opensearchStart, + plugins: this.openSearchPluginDeps, + }); + this.coreStart = { capabilities: capabilitiesStart, opensearch: opensearchStart, @@ -263,6 +285,7 @@ export class Server { uiSettings: uiSettingsStart, auditTrail: auditTrailStart, coreUsageData: coreUsageDataStart, + crossCompatibility: crossCompatibilityServiceStart, }; const pluginsStart = await this.plugins.start(this.coreStart); @@ -277,6 +300,8 @@ export class Server { await this.http.start(); + await this.security.start(); + startTransaction?.end(); return this.coreStart; } @@ -295,6 +320,7 @@ export class Server { await this.status.stop(); await this.logging.stop(); await this.auditTrail.stop(); + await this.security.stop(); } private registerCoreContext(coreSetup: InternalCoreSetup) { diff --git a/src/core/server/status/status_service.ts b/src/core/server/status/status_service.ts index 10547e510fa6..d243aa4f5045 100644 --- a/src/core/server/status/status_service.ts +++ b/src/core/server/status/status_service.ts @@ -48,7 +48,7 @@ import { ServiceStatus, CoreStatus, InternalStatusServiceSetup } from './types'; import { getSummaryStatus } from './get_summary_status'; import { PluginsStatusService } from './plugins_status'; -interface SetupDeps { +export interface SetupDeps { opensearch: Pick; environment: InternalEnvironmentServiceSetup; pluginDependencies: ReadonlyMap; diff --git a/src/core/types/cross_compatibility.ts b/src/core/types/cross_compatibility.ts new file mode 100644 index 000000000000..38cd061a5dce --- /dev/null +++ b/src/core/types/cross_compatibility.ts @@ -0,0 +1,25 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +/** @public */ +export interface CrossCompatibilityResult { + /** The OpenSearch Plugin name. */ + pluginName: string; + + /** Whether the current OpenSearch Plugin version is compatible with the dashboards plugin. */ + isCompatible: boolean; + + /** + * The reason the OpenSearch Plugin version is not compatible with the plugin. + * This will be `undefined` if the OpenSearch Plugin version is compatible. + */ + incompatibilityReason?: string; + + /** + * The array of versions of dependency OpenSearch Plugin if any present on the cluster. + * This will be empty if the OpenSearch Plugin is not present. + */ + installedVersions: string[]; +} diff --git a/src/core/types/index.ts b/src/core/types/index.ts index 4afe9c537f75..f65d683f6bdc 100644 --- a/src/core/types/index.ts +++ b/src/core/types/index.ts @@ -40,3 +40,4 @@ export * from './saved_objects'; export * from './serializable'; export * from './custom_branding'; export * from './workspace'; +export * from './cross_compatibility'; diff --git a/src/dev/build/args.test.ts b/src/dev/build/args.test.ts index 2304c560a687..4a3489044eb7 100644 --- a/src/dev/build/args.test.ts +++ b/src/dev/build/args.test.ts @@ -60,6 +60,7 @@ it('build dist for current platform, without packages, by default', () => { "targetAllPlatforms": false, "targetPlatforms": Object { "darwin": false, + "darwinArm": false, "linux": false, "linuxArm": false, "windows": false, @@ -89,6 +90,7 @@ it('build dist for linux x64 platform, without packages, if --linux is passed', "targetAllPlatforms": false, "targetPlatforms": Object { "darwin": false, + "darwinArm": false, "linux": true, "linuxArm": false, "windows": false, @@ -118,6 +120,7 @@ it('build dist for linux arm64 platform, without packages, if --linux-arm is pas "targetAllPlatforms": false, "targetPlatforms": Object { "darwin": false, + "darwinArm": false, "linux": false, "linuxArm": true, "windows": false, @@ -147,6 +150,7 @@ it('build dist for darwin x64 platform, without packages, if --darwin is passed' "targetAllPlatforms": false, "targetPlatforms": Object { "darwin": true, + "darwinArm": false, "linux": false, "linuxArm": false, "windows": false, @@ -176,6 +180,7 @@ it('build dist for windows x64 platform, without packages, if --windows is passe "targetAllPlatforms": false, "targetPlatforms": Object { "darwin": false, + "darwinArm": false, "linux": false, "linuxArm": false, "windows": true, @@ -205,6 +210,7 @@ it('builds packages if --all-platforms is passed', () => { "targetAllPlatforms": true, "targetPlatforms": Object { "darwin": false, + "darwinArm": false, "linux": false, "linuxArm": false, "windows": false, @@ -234,6 +240,7 @@ it('limits packages if --rpm passed with --all-platforms', () => { "targetAllPlatforms": true, "targetPlatforms": Object { "darwin": false, + "darwinArm": false, "linux": false, "linuxArm": false, "windows": false, @@ -263,6 +270,7 @@ it('limits packages if --deb passed with --all-platforms', () => { "targetAllPlatforms": true, "targetPlatforms": Object { "darwin": false, + "darwinArm": false, "linux": false, "linuxArm": false, "windows": false, @@ -293,6 +301,7 @@ it('limits packages if --docker passed with --all-platforms', () => { "targetAllPlatforms": true, "targetPlatforms": Object { "darwin": false, + "darwinArm": false, "linux": false, "linuxArm": false, "windows": false, @@ -323,6 +332,7 @@ it('limits packages if --docker passed with --skip-docker-ubi and --all-platform "targetAllPlatforms": true, "targetPlatforms": Object { "darwin": false, + "darwinArm": false, "linux": false, "linuxArm": false, "windows": false, diff --git a/src/dev/build/args.ts b/src/dev/build/args.ts index 7e131174e330..c7b6c9aaf32f 100644 --- a/src/dev/build/args.ts +++ b/src/dev/build/args.ts @@ -52,6 +52,7 @@ export function readCliArgs(argv: string[]) { 'all-platforms', 'windows', 'darwin', + 'darwin-arm', 'linux', 'linux-arm', 'verbose', @@ -133,6 +134,7 @@ export function readCliArgs(argv: string[]) { targetPlatforms: { windows: Boolean(flags.windows), darwin: Boolean(flags.darwin), + darwinArm: Boolean(flags['darwin-arm']), linux: Boolean(flags.linux), linuxArm: Boolean(flags['linux-arm']), }, diff --git a/src/dev/build/cli.ts b/src/dev/build/cli.ts index b075a6047acd..2287696c87ca 100644 --- a/src/dev/build/cli.ts +++ b/src/dev/build/cli.ts @@ -61,6 +61,7 @@ if (showHelp) { --linux {dim Produce archives only for linux x64 platform} --linux-arm {dim Produce archives only for linux arm64 platform} --darwin {dim Produce archives only for darwin x64 platform} + --darwin-arm {dim Produce archives only for darwin arm64 platform} --windows {dim Produce archives only for windows x64 platform} --rpm {dim Only build the rpm package} --deb {dim Only build the deb package} diff --git a/src/dev/build/lib/config.test.ts b/src/dev/build/lib/config.test.ts index 145954c1fb40..51c7f66be893 100644 --- a/src/dev/build/lib/config.test.ts +++ b/src/dev/build/lib/config.test.ts @@ -52,6 +52,7 @@ const setup = async ({ targetAllPlatforms = true, targetPlatforms = { darwin: false, + darwinArm: false, linux: false, linuxArm: false, windows: false, @@ -60,6 +61,7 @@ const setup = async ({ targetAllPlatforms?: boolean; targetPlatforms?: { darwin: boolean; + darwinArm: boolean; linux: boolean; linuxArm: boolean; windows: boolean; @@ -89,9 +91,7 @@ describe('#getNodeRange()', () => { describe('#getRepoRelativePath()', () => { it('converts an absolute path to relative path, from the root of the repo', async () => { const config = await setup(); - expect(config.getRepoRelativePath(__dirname)).toMatchInlineSnapshot( - `"${standardize('src/dev/build/lib', false, true)}"` - ); + expect(config.getRepoRelativePath(__dirname)).toMatchInlineSnapshot(`"src/dev/build/lib"`); }); }); @@ -117,6 +117,7 @@ describe('#hasSpecifiedPlatform', () => { targetAllPlatforms: false, targetPlatforms: { darwin: true, + darwinArm: false, linux: false, linuxArm: false, windows: false, @@ -130,6 +131,7 @@ describe('#hasSpecifiedPlatform', () => { targetAllPlatforms: false, targetPlatforms: { darwin: false, + darwinArm: false, linux: false, linuxArm: true, windows: false, @@ -143,6 +145,7 @@ describe('#hasSpecifiedPlatform', () => { targetAllPlatforms: false, targetPlatforms: { darwin: false, + darwinArm: false, linux: true, linuxArm: false, windows: false, @@ -197,6 +200,7 @@ describe('#getTargetPlatforms()', () => { .sort() ).toMatchInlineSnapshot(` Array [ + "darwin-arm64", "darwin-x64", "linux-arm64", "linux-x64", @@ -210,6 +214,7 @@ describe('#getTargetPlatforms()', () => { targetAllPlatforms: false, targetPlatforms: { darwin: true, + darwinArm: false, linux: false, linuxArm: false, windows: false, @@ -233,6 +238,7 @@ describe('#getTargetPlatforms()', () => { targetAllPlatforms: false, targetPlatforms: { darwin: false, + darwinArm: false, linux: true, linuxArm: false, windows: false, @@ -256,6 +262,7 @@ describe('#getTargetPlatforms()', () => { targetAllPlatforms: false, targetPlatforms: { darwin: false, + darwinArm: false, linux: false, linuxArm: true, windows: false, @@ -279,6 +286,7 @@ describe('#getTargetPlatforms()', () => { targetAllPlatforms: false, targetPlatforms: { darwin: true, + darwinArm: false, linux: false, linuxArm: true, windows: false, @@ -315,7 +323,7 @@ describe('#getNodePlatforms()', () => { .getTargetPlatforms() .map((p) => p.getNodeArch()) .sort() - ).toEqual(['darwin-x64', 'linux-arm64', 'linux-x64', 'win32-x64']); + ).toEqual(['darwin-arm64', 'darwin-x64', 'linux-arm64', 'linux-x64', 'win32-x64']); }); it('returns this platform and linux, when targetAllPlatforms = false', async () => { diff --git a/src/dev/build/lib/config.ts b/src/dev/build/lib/config.ts index 6af5b8e6901a..c8750e9234b3 100644 --- a/src/dev/build/lib/config.ts +++ b/src/dev/build/lib/config.ts @@ -155,9 +155,10 @@ export class Config { const platforms: Platform[] = []; if (this.targetPlatforms.darwin) platforms.push(this.getPlatform('darwin', 'x64')); + if (this.targetPlatforms.darwinArm) platforms.push(this.getPlatform('darwin', 'arm64')); if (this.targetPlatforms.linux) platforms.push(this.getPlatform('linux', 'x64')); - if (this.targetPlatforms.windows) platforms.push(this.getPlatform('win32', 'x64')); if (this.targetPlatforms.linuxArm) platforms.push(this.getPlatform('linux', 'arm64')); + if (this.targetPlatforms.windows) platforms.push(this.getPlatform('win32', 'x64')); if (platforms.length > 0) return platforms; diff --git a/src/dev/build/lib/download.ts b/src/dev/build/lib/download.ts index cf5c0f675f82..65fd54583c0f 100644 --- a/src/dev/build/lib/download.ts +++ b/src/dev/build/lib/download.ts @@ -36,10 +36,6 @@ import { createHash } from 'crypto'; import Axios from 'axios'; import { ToolingLog } from '@osd/dev-utils'; -// https://github.com/axios/axios/tree/ffea03453f77a8176c51554d5f6c3c6829294649/lib/adapters -// @ts-expect-error untyped internal module used to prevent axios from using xhr adapter in tests -import AxiosHttpAdapter from 'axios/lib/adapters/http'; - import { mkdirp } from './fs'; function tryUnlink(path: string) { @@ -77,7 +73,7 @@ export async function download(options: DownloadOptions): Promise { const response = await Axios.request({ url, responseType: 'stream', - adapter: AxiosHttpAdapter, + adapter: 'http', }); if (response.status !== 200) { diff --git a/src/dev/build/lib/fs.ts b/src/dev/build/lib/fs.ts index 53fc241f7722..772db6689d7d 100644 --- a/src/dev/build/lib/fs.ts +++ b/src/dev/build/lib/fs.ts @@ -29,6 +29,7 @@ */ import fs from 'fs'; +import { rm } from 'fs/promises'; import { createHash } from 'crypto'; import { pipeline, Writable } from 'stream'; import { resolve, dirname, isAbsolute, sep } from 'path'; @@ -113,13 +114,17 @@ export async function deleteAll(patterns: string[], log: ToolingLog) { assertAbsolute(pattern.startsWith('!') ? pattern.slice(1) : pattern); } - const files = await del(patterns, { + // Doing a dry run to get a list but `rm` will do the actual deleting + const filesToDelete = await del(patterns, { concurrency: 4, + dryRun: true, }); + await Promise.all(filesToDelete.map((folder) => rm(folder, { force: true, recursive: true }))); + if (log) { - log.debug('Deleted %d files/directories', files.length); - log.verbose('Deleted:', longInspect(files)); + log.debug('Deleted %d files/directories', filesToDelete.length); + log.verbose('Deleted:', longInspect(filesToDelete)); } } @@ -138,22 +143,22 @@ export async function deleteEmptyFolders( ); assertAbsolute(rootFolderPath.startsWith('!') ? rootFolderPath.slice(1) : rootFolderPath); - // Delete empty is used to gather all the empty folders and - // then we use del to actually delete them + // `deleteEmpty` is used to gather all the empty folders then `rm` is used to actually delete them const emptyFoldersList = await deleteEmpty(rootFolderPath, { // @ts-expect-error DT package has incorrect types https://github.com/jonschlinkert/delete-empty/blob/6ae34547663e6845c3c98b184c606fa90ef79c0a/index.js#L160 dryRun: true, }); - const foldersToDelete = emptyFoldersList.filter((folderToDelete) => { - return !foldersToKeep.some((folderToKeep) => folderToDelete.includes(folderToKeep)); - }); - const deletedEmptyFolders = await del(foldersToDelete, { - concurrency: 4, - }); + const foldersToDelete = Array.isArray(emptyFoldersList) + ? emptyFoldersList.filter((folderToDelete: string[]) => { + return !foldersToKeep.some((folderToKeep) => folderToDelete.includes(folderToKeep)); + }) + : []; + + await Promise.all(foldersToDelete.map((folder) => rm(folder, { force: true, recursive: true }))); - log.debug('Deleted %d empty folders', deletedEmptyFolders.length); - log.verbose('Deleted:', longInspect(deletedEmptyFolders)); + log.debug('Deleted %d empty folders', foldersToDelete.length); + log.verbose('Deleted:', longInspect(foldersToDelete)); } interface CopyOptions { diff --git a/src/dev/build/lib/platform.ts b/src/dev/build/lib/platform.ts index 673356ec6205..f83107f73734 100644 --- a/src/dev/build/lib/platform.ts +++ b/src/dev/build/lib/platform.ts @@ -33,6 +33,7 @@ export type PlatformArchitecture = 'x64' | 'arm64'; export interface TargetPlatforms { darwin: boolean; + darwinArm: boolean; linuxArm: boolean; linux: boolean; windows: boolean; @@ -78,5 +79,6 @@ export const ALL_PLATFORMS = [ new Platform('linux', 'x64', 'linux-x64'), new Platform('linux', 'arm64', 'linux-arm64'), new Platform('darwin', 'x64', 'darwin-x64'), + new Platform('darwin', 'arm64', 'darwin-arm64'), new Platform('win32', 'x64', 'windows-x64'), ]; diff --git a/src/dev/build/tasks/create_archives_sources_task.ts b/src/dev/build/tasks/create_archives_sources_task.ts index 55d9b5313f12..5ba01ad12922 100644 --- a/src/dev/build/tasks/create_archives_sources_task.ts +++ b/src/dev/build/tasks/create_archives_sources_task.ts @@ -56,17 +56,21 @@ export const CreateArchivesSources: Task = { // ToDo [NODE14]: Remove this Node.js 14 fallback download // Copy the Node.js 14 binaries into node/fallback to be used by `use_node` - await scanCopy({ - source: ( - await getNodeVersionDownloadInfo( - NODE14_FALLBACK_VERSION, - platform.getNodeArch(), - platform.isWindows(), - config.resolveFromRepo() - ) - ).extractDir, - destination: build.resolvePathForPlatform(platform, 'node', 'fallback'), - }); + if (platform.getBuildName() === 'darwin-arm64') { + log.warning(`There are no fallback Node.js versions released for darwin-arm64.`); + } else { + await scanCopy({ + source: ( + await getNodeVersionDownloadInfo( + NODE14_FALLBACK_VERSION, + platform.getNodeArch(), + platform.isWindows(), + config.resolveFromRepo() + ) + ).extractDir, + destination: build.resolvePathForPlatform(platform, 'node', 'fallback'), + }); + } log.debug('Node.js copied into', platform.getNodeArch(), 'specific build directory'); }) diff --git a/src/dev/build/tasks/nodejs/__snapshots__/download_node_builds_task.test.ts.snap b/src/dev/build/tasks/nodejs/__snapshots__/download_node_builds_task.test.ts.snap new file mode 100644 index 000000000000..c12788fea48f --- /dev/null +++ b/src/dev/build/tasks/nodejs/__snapshots__/download_node_builds_task.test.ts.snap @@ -0,0 +1,13 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`downloads node builds for each platform 2`] = ` +Array [ + warn There are no fallback Node.js versions released for darwin-arm64., +] +`; + +exports[`rejects if any download fails 2`] = ` +Array [ + warn There are no fallback Node.js versions released for darwin-arm64., +] +`; diff --git a/src/dev/build/tasks/nodejs/download_node_builds_task.test.ts b/src/dev/build/tasks/nodejs/download_node_builds_task.test.ts index f5905534e121..b65e384a7e27 100644 --- a/src/dev/build/tasks/nodejs/download_node_builds_task.test.ts +++ b/src/dev/build/tasks/nodejs/download_node_builds_task.test.ts @@ -36,6 +36,7 @@ import { import { Config, Platform } from '../../lib'; import { DownloadNodeBuilds } from './download_node_builds_task'; +import { stripAnsiSnapshotSerializer } from '../../../../core/test_helpers/strip_ansi_snapshot_serializer'; jest.mock('./node_shasums'); jest.mock('./node_download_info'); @@ -43,6 +44,7 @@ jest.mock('../../lib/download'); jest.mock('../../lib/get_build_number'); expect.addSnapshotSerializer(createAnyInstanceSerializer(ToolingLog)); +expect.addSnapshotSerializer(stripAnsiSnapshotSerializer); const { getNodeDownloadInfo, getNodeVersionDownloadInfo } = jest.requireMock( './node_download_info' @@ -67,6 +69,8 @@ async function setup({ failOnUrl }: { failOnUrl?: string } = {}) { linux: false, linuxArm: false, darwin: false, + darwinArm: false, + windows: false, }, }); @@ -78,19 +82,23 @@ async function setup({ failOnUrl }: { failOnUrl?: string } = {}) { }; }); - getNodeVersionDownloadInfo.mockImplementation((version, architecture, isWindows, repoRoot) => { - return { - url: `https://mirrors.nodejs.org/dist/v${version}/node-v${version}-${architecture}.tar.gz`, - downloadName: `node-v${version}-${architecture}.tar.gz`, - downloadPath: `/mocked/path/.node_binaries/${version}/node-v${version}-${architecture}.tar.gz`, - extractDir: `/mocked/path/.node_binaries/${version}/${architecture}`, - version, - }; - }); + getNodeVersionDownloadInfo.mockImplementation( + (version: string, architecture: string, isWindows: boolean, repoRoot: string) => { + return { + url: `https://mirrors.nodejs.org/dist/v${version}/node-v${version}-${architecture}.tar.gz`, + downloadName: `node-v${version}-${architecture}.tar.gz`, + downloadPath: `/mocked/path/.node_binaries/${version}/node-v${version}-${architecture}.tar.gz`, + extractDir: `/mocked/path/.node_binaries/${version}/${architecture}`, + version, + }; + } + ); getNodeShasums.mockReturnValue({ 'linux:downloadName': 'linux:sha256', + 'linux-arm64:downloadName': 'linux-arm64:sha256', 'darwin:downloadName': 'darwin:sha256', + 'darwin-arm64:downloadName': 'darwin-arm64:sha256', 'win32:downloadName': 'win32:sha256', }); @@ -137,6 +145,15 @@ it('downloads node builds for each platform', async () => { "url": "darwin:url", }, ], + Array [ + Object { + "destination": "darwin:downloadPath", + "log": , + "retries": 3, + "sha256": "darwin:sha256", + "url": "darwin:url", + }, + ], Array [ Object { "destination": "win32:downloadPath", @@ -184,7 +201,10 @@ it('downloads node builds for each platform', async () => { ], ] `); - expect(testWriter.messages).toMatchInlineSnapshot(`Array []`); + /* ToDo [NODE14]: Replace when Node.js 14 support is removed + * expect(testWriter.messages).toMatchInlineSnapshot(`Array []`); + */ + expect(testWriter.messages).toMatchSnapshot(); }); it('rejects if any download fails', async () => { @@ -193,5 +213,8 @@ it('rejects if any download fails', async () => { await expect(DownloadNodeBuilds.run(config, log, [])).rejects.toMatchInlineSnapshot( `[Error: Download failed for reasons]` ); - expect(testWriter.messages).toMatchInlineSnapshot(`Array []`); + /* ToDo [NODE14]: Replace when Node.js 14 support is removed + * expect(testWriter.messages).toMatchInlineSnapshot(`Array []`); + */ + expect(testWriter.messages).toMatchSnapshot(); }); diff --git a/src/dev/build/tasks/nodejs/download_node_builds_task.ts b/src/dev/build/tasks/nodejs/download_node_builds_task.ts index 393a02176e17..4463f76621de 100644 --- a/src/dev/build/tasks/nodejs/download_node_builds_task.ts +++ b/src/dev/build/tasks/nodejs/download_node_builds_task.ts @@ -60,6 +60,10 @@ export const DownloadNodeBuilds: GlobalTask = { }), // ToDo [NODE14]: Remove this Node.js 14 fallback download ...config.getTargetPlatforms().map(async (platform) => { + if (platform.getBuildName() === 'darwin-arm64') { + log.warning(`There are no fallback Node.js versions released for darwin-arm64.`); + return; + } const { url, downloadPath, downloadName } = await getNodeVersionDownloadInfo( NODE14_FALLBACK_VERSION, platform.getNodeArch(), diff --git a/src/dev/build/tasks/nodejs/extract_node_builds_task.test.ts b/src/dev/build/tasks/nodejs/extract_node_builds_task.test.ts index e68539310903..497ec6e16de5 100644 --- a/src/dev/build/tasks/nodejs/extract_node_builds_task.test.ts +++ b/src/dev/build/tasks/nodejs/extract_node_builds_task.test.ts @@ -58,6 +58,7 @@ async function setup() { linux: false, linuxArm: false, darwin: false, + darwinArm: false, windows: false, }, }); @@ -123,6 +124,13 @@ it('runs expected fs operations', async () => { "strip": 1, }, ], + Array [ + /.node_binaries//node-v-darwin-arm64.tar.gz, + /.node_binaries//darwin-arm64, + Object { + "strip": 1, + }, + ], Array [ /.node_binaries/14.21.3/node-v14.21.3-linux-x64.tar.gz, /.node_binaries/14.21.3/linux-x64, diff --git a/src/dev/build/tasks/nodejs/extract_node_builds_task.ts b/src/dev/build/tasks/nodejs/extract_node_builds_task.ts index 7934718c0bce..f44f3d45834a 100644 --- a/src/dev/build/tasks/nodejs/extract_node_builds_task.ts +++ b/src/dev/build/tasks/nodejs/extract_node_builds_task.ts @@ -38,7 +38,7 @@ import { export const ExtractNodeBuilds: GlobalTask = { global: true, description: 'Extracting node.js builds for all platforms', - async run(config) { + async run(config, log) { await Promise.all([ ...config.getTargetPlatforms().map(async (platform) => { const { downloadPath, extractDir } = await getNodeDownloadInfo(config, platform); @@ -50,6 +50,10 @@ export const ExtractNodeBuilds: GlobalTask = { }), // ToDo [NODE14]: Remove this Node.js 14 fallback download ...config.getTargetPlatforms().map(async (platform) => { + if (platform.getBuildName() === 'darwin-arm64') { + log.warning(`There are no fallback Node.js versions released for darwin-arm64.`); + return; + } const { downloadPath, extractDir } = await getNodeVersionDownloadInfo( NODE14_FALLBACK_VERSION, platform.getNodeArch(), diff --git a/src/dev/build/tasks/nodejs/node_shasums.test.ts b/src/dev/build/tasks/nodejs/node_shasums.test.ts index b42262849c7d..4a8e52f9b6c6 100644 --- a/src/dev/build/tasks/nodejs/node_shasums.test.ts +++ b/src/dev/build/tasks/nodejs/node_shasums.test.ts @@ -28,50 +28,44 @@ * under the License. */ -const mockResponse = `155ae63f0bb47050e0c31b4f8c17dadc79dcfa8e8f4ec9e3974fd7592afa9a4f node-v8.9.4-aix-ppc64.tar.gz -ca50f7d2035eb805306e303b644bb1cde170ce2615e0a2c6e95fb80881c48c24 node-v8.9.4-darwin-x64.tar.gz -cb79e2da37d2b646a06adaddcda67ff6ba0f77f9ca733b041dabf3dad79c7468 node-v8.9.4-darwin-x64.tar.xz -ef7248e81706daeeec946c19808a50b60ac250e648365d78fda6e40f1f9b23a5 node-v8.9.4-headers.tar.gz -11ed407a4bc3d8c3e73305ac54e91e64c9a9f6a2ae5476791d6fcc14ac159bfc node-v8.9.4-headers.tar.xz -2b133c7d23033fbc2419e66fc08bba35c427a97aba83ed6848b6b4678c0cac65 node-v8.9.4-linux-arm64.tar.gz -7c0369a5dbc98d0989c208ca3ee1b6db4cba576343014fdbf7d36fd2659f7089 node-v8.9.4-linux-arm64.tar.xz -81f138e935323246bd5da518eb0ea8ad00008f3c8a8d606e17589a545a9c73d1 node-v8.9.4-linux-armv6l.tar.gz -501bcae62ea1769924facc9628f407d37753e7a024cf3b12a18ea9dab1b380c9 node-v8.9.4-linux-armv6l.tar.xz -a0dd9009cb8d4be89c8a31131df16ad5ea1580d10ae426c5142aa34b0ad4ea76 node-v8.9.4-linux-armv7l.tar.gz -fe19f195df3d4f362d0cf0eef43c1a6a0b6006a1be2a89ee1808091c2ef4d722 node-v8.9.4-linux-armv7l.tar.xz -c5df73b8571edf97f83b484d6139332fad3b710d51be4aeb8d846059862d4675 node-v8.9.4-linux-ppc64le.tar.gz -21178be5e4c1dbdd99610d24aa934234a368c542ebabb3d98c31d393cf4adf06 node-v8.9.4-linux-ppc64le.tar.xz -d6e53ab2f8364528d4c6800adc1e7fccec607fd07a97b83985732c749a7fc846 node-v8.9.4-linux-s390x.tar.gz -90c6c284db9482a478dd5110e2171435156d56a013aeda2f636b6240eba156bd node-v8.9.4-linux-s390x.tar.xz -21fb4690e349f82d708ae766def01d7fec1b085ce1f5ab30d9bda8ee126ca8fc node-v8.9.4-linux-x64.tar.gz -68b94aac38cd5d87ab79c5b38306e34a20575f31a3ea788d117c20fffcca3370 node-v8.9.4-linux-x64.tar.xz -cc2f7a300353422ede336f5e72b71f0d6eac46732a31b7640648378830dd7513 node-v8.9.4-linux-x86.tar.gz -79f241f31eab5dfe2976fb0633c598dababd207ab0b8a163004f296cd7794a65 node-v8.9.4-linux-x86.tar.xz -b93767f7e186b1ae7204fedafa4110534f577d18d4204f422b626afdd5061e28 node-v8.9.4.pkg -e4a5d945091043c937125cd0d515258785cd4ea806fe3b77000d888de23d2ba0 node-v8.9.4-sunos-x64.tar.gz -b33e8f1495b88fcc0ab1e2579f2f7cf4d39886d577430dcb920a024829d4cf28 node-v8.9.4-sunos-x64.tar.xz -551729411793e427f5760fe8e46f45612e1e8e7c63e55ad34243ebf8ea9a4a7a node-v8.9.4-sunos-x86.tar.gz -6b439bb7204362c0af7a654bce24fcf8059e1772b2f0a9e4e1f8a0b8caa85d26 node-v8.9.4-sunos-x86.tar.xz -729b44b32b2f82ecd5befac4f7518de0c4e3add34e8fe878f745740a66cbbc01 node-v8.9.4.tar.gz -6cdcde9c9c1ca9f450a0b24eafa229ca759e576daa0fae892ce74d541ecdc86f node-v8.9.4.tar.xz -15a847a28358f9ae40bae42f49b033b0180bc10661632c63a9c8487ae980a8ba node-v8.9.4-win-x64.7z -48946e99ac4484e071df25741d2300f3a656f476c5ff3f8116a4746c07ebe3b7 node-v8.9.4-win-x64.zip -50ad674fb4c89edf35d3fee2136da86631cb7c0504589eb71ce8a3bb176493ed node-v8.9.4-win-x86.7z -02e3c65000ac055e05c604aec4cf318212efbd4b60a945ed319072d58314ca32 node-v8.9.4-win-x86.zip -547689da69bacadfee619d208702b73698d14297bd5fef5d80656897989e91b6 node-v8.9.4-x64.msi -f9442188c2f66d167a0ac610dee6d16e226ba28ca93f9569e0276268eb8f85dc node-v8.9.4-x86.msi -b73841f25d6e75d635770fd1a32e4d74d6ab2feed0fd7708bb40b967ae06f33e win-x64/node.exe -5439dc6f0d632ecdeb7342986743a03fe0818e34f0a67e38de74fa9c94886a39 win-x64/node.lib -6ab35445dd564978019cf4f3cfe11dd342b8450015fc054df99aa6f35f21736a win-x64/node_pdb.7z -c064abba981c2373e7e1a8c53b4e4ed1d4927bd9c0f7c065b24dd13b731598bd win-x64/node_pdb.zip -c8430b20cd067d8784d5faae04f9447987a472b22b6d0a2403ea4362ecd3d0bc win-x86/node.exe -c4edece2c0aa68e816c4e067f397eb12e9d0c81bb37b3d349dbaf47cf246b0b7 win-x86/node.lib -6a2ee7a0b0074ece27d171418d82ce25a60b87750ec30c5c9fbeaaca8c206fa5 win-x86/node_pdb.7z -1b44176d888c1bc6a6b05fcc6234031b3b8a58da9de8b99661088f998ac5e269 win-x86/node_pdb.zip`; +const mockResponse = `a65bd3fe91ffeb31d12a208e811943e3ebba4706553a4845a03d857beaeec51e node-v99.99.99-aix-ppc64.tar.gz +82c7bb4869419ce7338669e6739a786dfc7e72f276ffbed663f85ffc905dcdb4 node-v99.99.99-darwin-arm64.tar.gz +b23cdf4fa0e9f77273720ab18eabdd7691edbb69e08ec3b65afd69bef23fe209 node-v99.99.99-darwin-arm64.tar.xz +cd520da6e2e89fab881c66a3e9aff02cb0d61d68104b1d6a571dd71bef920870 node-v99.99.99-darwin-x64.tar.gz +2c8aa0333111c2411564bfb85be44186aeb581392f73c4be5912cbb125d99043 node-v99.99.99-darwin-x64.tar.xz +effeb73616e5297922ed89a1b94d2664390040a83184504c1cc1305b0c0c853f node-v99.99.99-headers.tar.gz +0eb9823c2cc72792c2d4413f57b5a36232e173d7edefb1909c37e364a823f9c7 node-v99.99.99-headers.tar.xz +dc3dfaee899ed21682e47eaf15525f85aff29013c392490e9b25219cd95b1c35 node-v99.99.99-linux-arm64.tar.gz +c81dfa0bada232cb4583c44d171ea207934f7356f85f9184b32d0dde69e2e0ea node-v99.99.99-linux-arm64.tar.xz +a3968db44e5ae17243d126ff79b1756016b198f7cc94c6fad8522aac481b4ff3 node-v99.99.99-linux-armv7l.tar.gz +57ba6b71eb039fa896c329e68669b21f6717622c560c6f61a0c97d18ca866b2d node-v99.99.99-linux-armv7l.tar.xz +b4e66dcda5ba4a3697be3fded122dabb6a677deee3d7f4d3c7c13ebb5a13844c node-v99.99.99-linux-ppc64le.tar.gz +c43142fb9ef30658620ed095f8203beca92f469c1121eeb724df9a48bf0e59a5 node-v99.99.99-linux-ppc64le.tar.xz +a8b607c3c06f585c4fe9ba45be6dc76ce9459238c91b3f43533aa30344caed87 node-v99.99.99-linux-s390x.tar.gz +39b15c16347000b0be97133437bde0317dd2307d3fdfce15ddd8680b07a963ef node-v99.99.99-linux-s390x.tar.xz +fc83046a93d2189d919005a348db3b2372b598a145d84eb9781a3a4b0f032e95 node-v99.99.99-linux-x64.tar.gz +44d93d9b4627fe5ae343012d855491d62c7381b236c347f7666a7ad070f26548 node-v99.99.99-linux-x64.tar.xz +156aa5b9580288fb0b3c6134eb8fac64e50745d78d33eebe9e29eb7ff87b8e1e node-v99.99.99.pkg +6a4f5c5d76e5c50cef673099e56f19bc3266ae363f56ca0ab77dd2f3c5088c6d node-v99.99.99.tar.gz +33d81a233e235a509adda4a4f2209008d04591979de6b3f0f67c1c906093f118 node-v99.99.99.tar.xz +007848640ba414f32d968d303e75d9841ecd2cd95d6fdd81f80bc3dcbd74ae44 node-v99.99.99-win-x64.7z +4b3bd4cb5570cc217490639e93a7e1b7a7a341981366661e514ce61941824a85 node-v99.99.99-win-x64.zip +681be28e0acd057b4798f357d21eec5f49e21bc803bbbefeb1072bb4f166025a node-v99.99.99-win-x86.7z +2a7e0fb22e1a36144ee8183c80ef2705cd9754c1d894f94bb6c94a681de47924 node-v99.99.99-win-x86.zip +5bfb6f3ab89e198539408f7e0e8ec0b0bd5efe8898573ec05b381228efb45a5d node-v99.99.99-x64.msi +09534d1949c795c3e49d257fb72a9fd865ee28955673b87d569d4aec541333e7 node-v99.99.99-x86.msi +b548a55c2b5ef5de34f4636610bab27077fb9313d34f52280b9ec11dd25e9dd1 win-x64/node.exe +72b7fab9381af8f4958c8212f3d4cdfff8c7c5b1e33eaad0e7d5888293568cd5 win-x64/node.lib +3b9474e18a1bbb38b05b1876b4b37056063c2af82212d356a8a5cf91c1a3acf3 win-x64/node_pdb.7z +6b506b1fe654ca7161373916c7ba7e38f62545236698342fa97fd2faf39ebc4e win-x64/node_pdb.zip +36bf0f0a364ca8edc176776764831f9e88bef6d1e8056f6edc474a37b652a794 win-x86/node.exe +6a85c15a69238f0902b9a734d262bf36d211b273a46d5e3249857d4bb7f6d9b7 win-x86/node.lib +9256bdefae4491acfd523ca06d4f4344ddc4f1a28aac868b5efb6a72d8023e2a win-x86/node_pdb.7z +53c6b29afd58904e5143d9f3298b55695b8ecb2b6c08a9612ed30e9b0ed9589a win-x86/node_pdb.zip`; jest.mock('axios', () => ({ async get(url: string) { - expect(url).toBe('https://nodejs.org/dist/v8.9.4/SHASUMS256.txt'); + expect(url).toBe('https://nodejs.org/dist/v99.99.99/SHASUMS256.txt'); return { status: 200, data: mockResponse, @@ -84,12 +78,19 @@ import { getNodeShasums } from './node_shasums'; describe('src/dev/build/tasks/nodejs/node_shasums', () => { it('resolves to an object with shasums for node downloads for version', async () => { - const shasums = await getNodeShasums(new ToolingLog(), '8.9.4'); + const shasums = await getNodeShasums(new ToolingLog(), '99.99.99'); expect(shasums).toEqual( expect.objectContaining({ - 'node-v8.9.4.tar.gz': '729b44b32b2f82ecd5befac4f7518de0c4e3add34e8fe878f745740a66cbbc01', - 'node-v8.9.4-win-x64.zip': - '48946e99ac4484e071df25741d2300f3a656f476c5ff3f8116a4746c07ebe3b7', + 'node-v99.99.99-linux-x64.tar.gz': + 'fc83046a93d2189d919005a348db3b2372b598a145d84eb9781a3a4b0f032e95', + 'node-v99.99.99-linux-arm64.tar.gz': + 'dc3dfaee899ed21682e47eaf15525f85aff29013c392490e9b25219cd95b1c35', + 'node-v99.99.99-darwin-x64.tar.gz': + 'cd520da6e2e89fab881c66a3e9aff02cb0d61d68104b1d6a571dd71bef920870', + 'node-v99.99.99-darwin-arm64.tar.gz': + '82c7bb4869419ce7338669e6739a786dfc7e72f276ffbed663f85ffc905dcdb4', + 'node-v99.99.99-win-x64.zip': + '4b3bd4cb5570cc217490639e93a7e1b7a7a341981366661e514ce61941824a85', }) ); }); diff --git a/src/dev/build/tasks/nodejs/verify_existing_node_builds_task.test.ts b/src/dev/build/tasks/nodejs/verify_existing_node_builds_task.test.ts index 1d516ce457ad..1f4e6d9cb1c3 100644 --- a/src/dev/build/tasks/nodejs/verify_existing_node_builds_task.test.ts +++ b/src/dev/build/tasks/nodejs/verify_existing_node_builds_task.test.ts @@ -63,6 +63,7 @@ async function setup(actualShaSums?: Record) { linux: false, linuxArm: false, darwin: false, + darwinArm: false, windows: false, }, }); @@ -120,6 +121,7 @@ it('checks shasums for each downloaded node build', async () => { Object { "type": "return", "value": Object { + "darwin:darwin-arm64:downloadName": "valid shasum", "darwin:darwin-x64:downloadName": "valid shasum", "linux:linux-arm64:downloadName": "valid shasum", "linux:linux-x64:downloadName": "valid shasum", @@ -156,6 +158,14 @@ it('checks shasums for each downloaded node build', async () => { "name": "darwin", }, ], + Array [ + , + Platform { + "architecture": "arm64", + "buildName": "darwin-arm64", + "name": "darwin", + }, + ], Array [ , Platform { @@ -190,6 +200,14 @@ it('checks shasums for each downloaded node build', async () => { "version": "", }, }, + Object { + "type": "return", + "value": Object { + "downloadName": "darwin:darwin-arm64:downloadName", + "downloadPath": "darwin:darwin-arm64:downloadPath", + "version": "", + }, + }, Object { "type": "return", "value": Object { @@ -216,6 +234,10 @@ it('checks shasums for each downloaded node build', async () => { "darwin:darwin-x64:downloadPath", "sha256", ], + Array [ + "darwin:darwin-arm64:downloadPath", + "sha256", + ], Array [ "win32:win32-x64:downloadPath", "sha256", @@ -238,6 +260,10 @@ it('checks shasums for each downloaded node build', async () => { "type": "return", "value": "valid shasum", }, + Object { + "type": "return", + "value": "valid shasum", + }, ], } `); diff --git a/src/dev/i18n/README.md b/src/dev/i18n/README.md index 4a9e3f45eff0..7cfb938d38ec 100644 --- a/src/dev/i18n/README.md +++ b/src/dev/i18n/README.md @@ -4,7 +4,7 @@ ### Description -The tool is used to extract default messages from all `*.{js, ts, jsx, tsx, html }` files in provided plugins directories to a JSON file. +The tool is used to extract default messages from all `*.{js, ts, jsx, tsx }` files in provided plugins directories to a JSON file. It uses Babel to parse code and build an AST for each file or a single JS expression if whole file parsing is impossible. The tool is able to validate, extract and match IDs, default messages and descriptions only if they are defined statically and together, otherwise it will fail with detailed explanation. That means one can't define ID in one place and default message in another, or use function call to dynamically create default message etc. @@ -18,33 +18,6 @@ The `defaultMessage` must contain ICU references to all keys in the `values` and The `description` is optional, `values` is optional too unless `defaultMessage` references to it. -* **Angular (.html)** - - * **Filter** - - ``` - {{ ::'pluginNamespace.messageId' | i18n: { - defaultMessage: 'Default message string literal, {key}', - values: { key: 'value' }, - description: 'Message context or description' - } }} - ``` - - * Don't break `| i18n: {` with line breaks, and don't skip whitespaces around `i18n:`. - * `::` operator is optional. Omit it if you need data binding for the `values`. - - * **Directive** - - ```html -

- ``` - - * `html_` prefixes will be removed from `i18n-values` keys before validation. * **React (.jsx, .tsx)** diff --git a/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_1/test_file_4.html b/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_1/test_file_4.html deleted file mode 100644 index f725fa288405..000000000000 --- a/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_1/test_file_4.html +++ /dev/null @@ -1,8 +0,0 @@ - -
-
-
-
diff --git a/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_2/test_file.html b/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_2/test_file.html deleted file mode 100644 index c12843602b13..000000000000 --- a/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_2/test_file.html +++ /dev/null @@ -1 +0,0 @@ -

{{ ::'plugin_2.message-id' | i18n: { defaultMessage: 'Message text' } }}

diff --git a/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_2/test_file.jsx b/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_2/test_file.jsx new file mode 100644 index 000000000000..b3f0c8d2b9c1 --- /dev/null +++ b/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_2/test_file.jsx @@ -0,0 +1,6 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +i18n('plugin_2.message-id', { defaultMessage: 'Message text' }); diff --git a/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_4/test_file_4.jsx b/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_4/test_file_4.jsx new file mode 100644 index 000000000000..5ce7b916bcd4 --- /dev/null +++ b/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_4/test_file_4.jsx @@ -0,0 +1,18 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +/* eslint-disable */ +class Component extends PureComponent { + render() { + return ( +
+ +
+ ); + } +} \ No newline at end of file diff --git a/src/dev/i18n/__snapshots__/extract_default_translations.test.js.snap b/src/dev/i18n/__snapshots__/extract_default_translations.test.js.snap index 68ed0433f1ae..cf91d6252d05 100644 --- a/src/dev/i18n/__snapshots__/extract_default_translations.test.js.snap +++ b/src/dev/i18n/__snapshots__/extract_default_translations.test.js.snap @@ -33,6 +33,54 @@ Array [ ] `; +exports[`dev/i18n/extract_default_translations extracts messages from path to map 2`] = ` +Array [ + Array [ + "plugin_2.message-id", + Object { + "description": undefined, + "message": "Message text", + }, + ], +] +`; + +exports[`dev/i18n/extract_default_translations extracts messages from path to map 3`] = ` +Array [ + Array [ + "plugin_3.duplicate_id", + Object { + "description": undefined, + "message": "Message 1", + }, + ], +] +`; + +exports[`dev/i18n/extract_default_translations extracts messages from path to map 4`] = ` +Array [ + Array [ + "plugin_3.duplicate_id", + Object { + "description": undefined, + "message": "Message 1", + }, + ], +] +`; + +exports[`dev/i18n/extract_default_translations extracts messages from path to map 5`] = ` +Array [ + Array [ + "plugin_4.id_1", + Object { + "description": undefined, + "message": "Message 4", + }, + ], +] +`; + exports[`dev/i18n/extract_default_translations throws on id collision 1`] = ` Array [ " I18N ERROR  Error in src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_3/test_file.jsx diff --git a/src/dev/i18n/extract_default_translations.test.js b/src/dev/i18n/extract_default_translations.test.js index ea4754f3645e..c995ec562735 100644 --- a/src/dev/i18n/extract_default_translations.test.js +++ b/src/dev/i18n/extract_default_translations.test.js @@ -42,6 +42,7 @@ const pluginsPaths = [ path.join(fixturesPath, 'test_plugin_2'), path.join(fixturesPath, 'test_plugin_3'), path.join(fixturesPath, 'test_plugin_3_additional_path'), + path.join(fixturesPath, 'test_plugin_4'), ]; const config = { @@ -52,17 +53,19 @@ const config = { 'src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_3', 'src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_3_additional_path', ], + plugin_4: ['src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_4'], }, exclude: [], }; describe('dev/i18n/extract_default_translations', () => { test('extracts messages from path to map', async () => { - const [pluginPath] = pluginsPaths; - const resultMap = new Map(); + for (const pluginPath of pluginsPaths) { + const resultMap = new Map(); - await extractMessagesFromPathToMap(pluginPath, resultMap, config, new ErrorReporter()); - expect([...resultMap].sort()).toMatchSnapshot(); + await extractMessagesFromPathToMap(pluginPath, resultMap, config, new ErrorReporter()); + expect([...resultMap].sort()).toMatchSnapshot(); + } }); test('throws on id collision', async () => { @@ -88,11 +91,11 @@ describe('dev/i18n/extract_default_translations', () => { const id = 'plugin_3.message-id'; const filePath1 = path.resolve( __dirname, - '__fixtures__/extract_default_translations/test_plugin_3/test_file.html' + '__fixtures__/extract_default_translations/test_plugin_3/test_file.jsx' ); const filePath2 = path.resolve( __dirname, - '__fixtures__/extract_default_translations/test_plugin_3_additional_path/test_file.html' + '__fixtures__/extract_default_translations/test_plugin_3_additional_path/test_file.jsx' ); expect(() => validateMessageNamespace(id, filePath1, config.paths)).not.toThrow(); expect(() => validateMessageNamespace(id, filePath2, config.paths)).not.toThrow(); @@ -103,7 +106,7 @@ describe('dev/i18n/extract_default_translations', () => { const id = 'wrong_plugin_namespace.message-id'; const filePath = path.resolve( __dirname, - '__fixtures__/extract_default_translations/test_plugin_2/test_file.html' + '__fixtures__/extract_default_translations/test_plugin_2/test_file.jsx' ); expect(() => validateMessageNamespace(id, filePath, config.paths, { report })).not.toThrow(); diff --git a/src/dev/jest/config.js b/src/dev/jest/config.js index 03eb4d7b4ba4..c76cb7bfd343 100644 --- a/src/dev/jest/config.js +++ b/src/dev/jest/config.js @@ -186,7 +186,7 @@ export default { transformIgnorePatterns: [ // ignore all node_modules except those which require babel transforms to handle dynamic import() // since ESM modules are not natively supported in Jest yet (https://github.com/facebook/jest/issues/4842) - '[/\\\\]node_modules(?![\\/\\\\](monaco-editor|weak-lru-cache|ordered-binary|d3-color))[/\\\\].+\\.js$', + '[/\\\\]node_modules(?![\\/\\\\](monaco-editor|weak-lru-cache|ordered-binary|d3-color|axios))[/\\\\].+\\.js$', 'packages/osd-pm/dist/index.js', ], snapshotSerializers: [ diff --git a/src/plugins/advanced_settings/server/plugin.ts b/src/plugins/advanced_settings/server/plugin.ts index 46ea4b3f8961..094d975acf91 100644 --- a/src/plugins/advanced_settings/server/plugin.ts +++ b/src/plugins/advanced_settings/server/plugin.ts @@ -49,6 +49,14 @@ export class AdvancedSettingsServerPlugin implements Plugin { core.capabilities.registerProvider(capabilitiesProvider); + core.capabilities.registerSwitcher(async (request, capabilites) => { + return await core.security.readonlyService().hideForReadonly(request, capabilites, { + advancedSettings: { + save: false, + }, + }); + }); + return {}; } diff --git a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.test.tsx b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.test.tsx index 0cbe7f929fe3..8bf887a54fe4 100644 --- a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.test.tsx +++ b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.test.tsx @@ -53,7 +53,7 @@ import { getEndpointFromPosition } from '../../../../../lib/autocomplete/get_end import * as consoleMenuActions from '../console_menu_actions'; import { Editor } from './editor'; -describe('Legacy (Ace) Console Editor Component Smoke Test', () => { +describe('Legacy (Ace) Console Editor Component Smoke Test with dataSourceId', () => { let mockedAppContextValue: ContextValue; const sandbox = sinon.createSandbox(); @@ -63,7 +63,70 @@ describe('Legacy (Ace) Console Editor Component Smoke Test', () => { - + + + + + + ); + + beforeEach(() => { + document.queryCommandSupported = sinon.fake(() => true); + mockedAppContextValue = serviceContextMock.create(); + }); + + afterEach(() => { + jest.clearAllMocks(); + sandbox.restore(); + }); + + it('calls send current request to OpenSearch', async () => { + (getEndpointFromPosition as jest.Mock).mockReturnValue({ patterns: [] }); + (sendRequestToOpenSearch as jest.Mock).mockRejectedValue({}); + const editor = doMount(); + act(() => { + editor.find('[data-test-subj~="sendRequestButton"]').simulate('click'); + }); + await nextTick(); + expect(sendRequestToOpenSearch).toBeCalledTimes(1); + }); + + it('opens docs', () => { + const stub = sandbox.stub(consoleMenuActions, 'getDocumentation'); + const editor = doMount(); + const consoleMenuToggle = editor.find('[data-test-subj~="toggleConsoleMenu"]').last(); + consoleMenuToggle.simulate('click'); + + const docsButton = editor.find('[data-test-subj~="consoleMenuOpenDocs"]').last(); + docsButton.simulate('click'); + + expect(stub.callCount).toBe(1); + }); + + it('prompts auto-indent', () => { + const stub = sandbox.stub(consoleMenuActions, 'autoIndent'); + const editor = doMount(); + const consoleMenuToggle = editor.find('[data-test-subj~="toggleConsoleMenu"]').last(); + consoleMenuToggle.simulate('click'); + + const autoIndentButton = editor.find('[data-test-subj~="consoleMenuAutoIndent"]').last(); + autoIndentButton.simulate('click'); + + expect(stub.callCount).toBe(1); + }); +}); + +describe('Legacy (Ace) Console Editor Component Smoke Test with empty dataSourceId (Local Cluster)', () => { + let mockedAppContextValue: ContextValue; + const sandbox = sinon.createSandbox(); + + const doMount = () => + mount( + + + + + @@ -115,3 +178,73 @@ describe('Legacy (Ace) Console Editor Component Smoke Test', () => { expect(stub.callCount).toBe(1); }); }); + +describe('Legacy (Ace) Console Editor Component Smoke Test with dataSouceId undefined', () => { + let mockedAppContextValue: ContextValue; + const sandbox = sinon.createSandbox(); + + const doMount = () => + mount( + + + + + + + + + + ); + + beforeEach(() => { + document.queryCommandSupported = sinon.fake(() => true); + mockedAppContextValue = serviceContextMock.create(); + }); + + afterEach(() => { + jest.clearAllMocks(); + sandbox.restore(); + }); + + it('diasbles send request button', async () => { + (getEndpointFromPosition as jest.Mock).mockReturnValue({ patterns: [] }); + (sendRequestToOpenSearch as jest.Mock).mockRejectedValue({}); + const editor = doMount(); + expect(editor.find('[data-test-subj~="sendRequestButton"]').get(0).props.disabled); + }); + + it('not able to send current request to OpenSearch', async () => { + (getEndpointFromPosition as jest.Mock).mockReturnValue({ patterns: [] }); + (sendRequestToOpenSearch as jest.Mock).mockRejectedValue({}); + const editor = doMount(); + act(() => { + editor.find('[data-test-subj~="sendRequestButton"]').simulate('click'); + }); + await nextTick(); + expect(sendRequestToOpenSearch).toBeCalledTimes(0); + }); + + it('opens docs', () => { + const stub = sandbox.stub(consoleMenuActions, 'getDocumentation'); + const editor = doMount(); + const consoleMenuToggle = editor.find('[data-test-subj~="toggleConsoleMenu"]').last(); + consoleMenuToggle.simulate('click'); + + const docsButton = editor.find('[data-test-subj~="consoleMenuOpenDocs"]').last(); + docsButton.simulate('click'); + + expect(stub.callCount).toBe(1); + }); + + it('prompts auto-indent', () => { + const stub = sandbox.stub(consoleMenuActions, 'autoIndent'); + const editor = doMount(); + const consoleMenuToggle = editor.find('[data-test-subj~="toggleConsoleMenu"]').last(); + consoleMenuToggle.simulate('click'); + + const autoIndentButton = editor.find('[data-test-subj~="consoleMenuAutoIndent"]').last(); + autoIndentButton.simulate('click'); + + expect(stub.callCount).toBe(1); + }); +}); diff --git a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx index 1c47cc41e920..4422cbc4743f 100644 --- a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx +++ b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx @@ -228,6 +228,11 @@ function EditorUI({ initialTextValue, dataSourceId }: EditorProps) { }); }, [sendCurrentRequestToOpenSearch, openDocumentation]); + const tooltipDefaultMessage = + dataSourceId === undefined ? `Select a data source` : `Click to send request`; + + const toolTipButtonDiasbled = dataSourceId === undefined; + return (
@@ -240,16 +245,17 @@ function EditorUI({ initialTextValue, dataSourceId }: EditorProps) { diff --git a/src/plugins/console/public/application/hooks/use_send_current_request_to_opensearch/send_request_to_opensearch.test.ts b/src/plugins/console/public/application/hooks/use_send_current_request_to_opensearch/send_request_to_opensearch.test.ts index fbeadf4cc7de..47a73e4af98a 100644 --- a/src/plugins/console/public/application/hooks/use_send_current_request_to_opensearch/send_request_to_opensearch.test.ts +++ b/src/plugins/console/public/application/hooks/use_send_current_request_to_opensearch/send_request_to_opensearch.test.ts @@ -9,53 +9,14 @@ * GitHub history for details. */ -import { - HttpFetchError, - HttpFetchOptionsWithPath, - HttpResponse, - HttpSetup, -} from '../../../../../../core/public'; +import { HttpFetchError, HttpSetup } from '../../../../../../core/public'; import { OpenSearchRequestArgs, sendRequestToOpenSearch } from './send_request_to_opensearch'; import * as opensearch from '../../../lib/opensearch/opensearch'; +import { + createMockHttpResponse, + createMockResponse, +} from '../../../lib/opensearch/http_response.mock'; -const createMockResponse = ( - statusCode: number, - statusText: string, - headers: Array<[string, string]> -): Response => { - return { - // headers: {} as Headers, - headers: new Headers(headers), - ok: true, - redirected: false, - status: statusCode, - statusText, - type: 'basic', - url: '', - clone: jest.fn(), - body: (jest.fn() as unknown) as ReadableStream, - bodyUsed: true, - arrayBuffer: jest.fn(), - blob: jest.fn(), - text: jest.fn(), - formData: jest.fn(), - json: jest.fn(), - }; -}; - -const createMockHttpResponse = ( - statusCode: number, - statusText: string, - headers: Array<[string, string]>, - body: any -): HttpResponse => { - return { - fetchOptions: (jest.fn() as unknown) as Readonly, - request: (jest.fn() as unknown) as Readonly, - response: createMockResponse(statusCode, statusText, headers), - body, - }; -}; const dummyArgs: OpenSearchRequestArgs = { http: ({ post: jest.fn(), diff --git a/src/plugins/console/public/lib/mappings/__tests__/mapping.test.ts b/src/plugins/console/public/lib/mappings/__tests__/mapping.test.ts index b5b1975a6a5c..8e023df6c1ce 100644 --- a/src/plugins/console/public/lib/mappings/__tests__/mapping.test.ts +++ b/src/plugins/console/public/lib/mappings/__tests__/mapping.test.ts @@ -30,7 +30,10 @@ import { Field } from '../mappings'; import '../../../application/models/sense_editor/sense_editor.test.mocks'; +import * as opensearch from '../../opensearch/opensearch'; import * as mappings from '../mappings'; +import { createMockHttpResponse } from '../../opensearch/http_response.mock'; +import { serviceContextMock } from '../../../application/contexts/services_context.mock'; describe('Mappings', () => { beforeEach(() => { @@ -291,3 +294,99 @@ describe('Mappings', () => { expect(mappings.getTypes()).toEqual(['properties']); }); }); + +describe('Auto Complete Info', () => { + let response = {}; + + const mockHttpResponse = createMockHttpResponse( + 200, + 'ok', + [['Content-Type', 'application/json, utf-8']], + response + ); + + beforeEach(() => { + mappings.clear(); + response = { + body: { + 'sample-ecommerce': { + mappings: { + properties: { + timestamp: { + type: 'date', + }, + }, + }, + }, + }, + }; + jest.resetAllMocks(); + jest.useFakeTimers(); // Enable automatic mocking of timers + }); + + afterEach(() => { + jest.clearAllMocks(); + jest.useRealTimers(); + }); + + test('Retrieve AutoComplete Info for Mappings, Aliases and Templates', async () => { + const dataSourceId = 'mock-data-source-id'; + const sendSpy = jest.spyOn(opensearch, 'send').mockResolvedValue(mockHttpResponse); + + const { + services: { http, settings: settingsService }, + } = serviceContextMock.create(); + + mappings.retrieveAutoCompleteInfo( + http, + settingsService, + { + fields: true, + indices: true, + templates: true, + }, + dataSourceId + ); + + // Fast-forward until all timers have been executed + jest.runAllTimers(); + + expect(sendSpy).toHaveBeenCalledTimes(3); + + // Ensure send is called with different arguments + expect(sendSpy).toHaveBeenCalledWith(http, 'GET', '_mapping', null, dataSourceId); + expect(sendSpy).toHaveBeenCalledWith(http, 'GET', '_aliases', null, dataSourceId); + expect(sendSpy).toHaveBeenCalledWith(http, 'GET', '_template', null, dataSourceId); + }); + + test('Retrieve AutoComplete Info for Specified Fields from the Settings', async () => { + const dataSourceId = 'mock-data-source-id'; + const sendSpy = jest.spyOn(opensearch, 'send').mockResolvedValue(mockHttpResponse); + + const { + services: { http, settings: settingsService }, + } = serviceContextMock.create(); + + mappings.retrieveAutoCompleteInfo( + http, + settingsService, + { + fields: true, + indices: false, + templates: false, + }, + dataSourceId + ); + + // Fast-forward until all timers have been executed + jest.runAllTimers(); + + // Resolve the promise chain + await Promise.resolve(); + + expect(sendSpy).toHaveBeenCalledTimes(1); + + // Ensure send is called with different arguments + expect(sendSpy).toHaveBeenCalledWith(http, 'GET', '_mapping', null, dataSourceId); + }); +}); diff --git a/src/plugins/console/public/lib/mappings/mappings.ts b/src/plugins/console/public/lib/mappings/mappings.ts index 2bd425ade95e..4cc188a9875f 100644 --- a/src/plugins/console/public/lib/mappings/mappings.ts +++ b/src/plugins/console/public/lib/mappings/mappings.ts @@ -76,9 +76,15 @@ interface IndexAliases { type IndicesOrAliases = string | string[] | null; +const SETTING_KEY_TO_PATH_MAP = { + fields: '_mapping', + indices: '_aliases', + templates: '_template', +}; + // NOTE: If this value ever changes to be a few seconds or less, it might introduce flakiness // due to timing issues in our app.js tests. -const POLL_INTERVAL = 60000; +export const POLL_INTERVAL = 60000; let pollTimeoutId: NodeJS.Timeout | null; let perIndexTypes: { [index: string]: { [type: string]: Field[] } } = {}; @@ -316,27 +322,16 @@ export function clear() { function retrieveSettings( http: HttpSetup, - settingsKey: string, + settingsKey: keyof typeof SETTING_KEY_TO_PATH_MAP, settingsToRetrieve: any, dataSourceId: string -): Promise> | Promise | Promise<{}> { - const settingKeyToPathMap: { [settingsKey: string]: string } = { - fields: '_mapping', - indices: '_aliases', - templates: '_template', - }; - +): Promise> | Promise { // Fetch autocomplete info if setting is set to true, and if user has made changes. if (settingsToRetrieve[settingsKey] === true) { - return opensearch.send(http, 'GET', settingKeyToPathMap[settingsKey], null, dataSourceId); + return opensearch.send(http, 'GET', SETTING_KEY_TO_PATH_MAP[settingsKey], null, dataSourceId); } else { - if (settingsToRetrieve[settingsKey] === false) { - // If the user doesn't want autocomplete suggestions, then clear any that exist - return Promise.resolve({}); - } else { - // If the user doesn't want autocomplete suggestions, then clear any that exist - return Promise.resolve(); - } + // If the user doesn't want autocomplete suggestions, then clear any that exist + return Promise.resolve(); } } @@ -385,7 +380,7 @@ const retrieveMappings = async (http: HttpSetup, settingsToRetrieve: any, dataSo }; const retrieveAliases = async (http: HttpSetup, settingsToRetrieve: any, dataSourceId: string) => { - const response = await retrieveSettings(http, 'fields', settingsToRetrieve, dataSourceId); + const response = await retrieveSettings(http, 'indices', settingsToRetrieve, dataSourceId); if (isHttpResponse(response) && response.body) { const aliases = response.body as IndexAliases; @@ -398,7 +393,7 @@ const retrieveTemplates = async ( settingsToRetrieve: any, dataSourceId: string ) => { - const response = await retrieveSettings(http, 'fields', settingsToRetrieve, dataSourceId); + const response = await retrieveSettings(http, 'templates', settingsToRetrieve, dataSourceId); if (isHttpResponse(response) && response.body) { const resTemplates = response.body; @@ -418,7 +413,6 @@ export function retrieveAutoCompleteInfo( dataSourceId: string ) { clearSubscriptions(); - Promise.allSettled([ retrieveMappings(http, settingsToRetrieve, dataSourceId), retrieveAliases(http, settingsToRetrieve, dataSourceId), diff --git a/src/plugins/console/public/lib/opensearch/http_response.mock.ts b/src/plugins/console/public/lib/opensearch/http_response.mock.ts new file mode 100644 index 000000000000..3ee40be979ab --- /dev/null +++ b/src/plugins/console/public/lib/opensearch/http_response.mock.ts @@ -0,0 +1,44 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { HttpFetchOptionsWithPath, HttpResponse } from '../../../../../core/public'; + +export const createMockResponse = ( + statusCode: number, + statusText: string, + headers: Array<[string, string]> +): Response => { + return { + headers: new Headers(headers), + ok: true, + redirected: false, + status: statusCode, + statusText, + type: 'basic', + url: '', + clone: jest.fn(), + body: (jest.fn() as unknown) as ReadableStream, + bodyUsed: true, + arrayBuffer: jest.fn(), + blob: jest.fn(), + text: jest.fn(), + formData: jest.fn(), + json: jest.fn(), + }; +}; + +export const createMockHttpResponse = ( + statusCode: number, + statusText: string, + headers: Array<[string, string]>, + body: any +): HttpResponse => { + return { + fetchOptions: (jest.fn() as unknown) as Readonly, + request: (jest.fn() as unknown) as Readonly, + response: createMockResponse(statusCode, statusText, headers), + body, + }; +}; diff --git a/src/plugins/console/public/lib/opensearch/opensearch.ts b/src/plugins/console/public/lib/opensearch/opensearch.ts index d1ba8797e474..907323611358 100644 --- a/src/plugins/console/public/lib/opensearch/opensearch.ts +++ b/src/plugins/console/public/lib/opensearch/opensearch.ts @@ -57,7 +57,7 @@ export async function send( body: data, prependBasePath: true, asResponse: true, - withLongNumerals: true, + withLongNumeralsSupport: true, }); } diff --git a/src/plugins/console/server/plugin.ts b/src/plugins/console/server/plugin.ts index 4c33bc1d6060..fa89863198cb 100644 --- a/src/plugins/console/server/plugin.ts +++ b/src/plugins/console/server/plugin.ts @@ -50,7 +50,7 @@ export class ConsoleServerPlugin implements Plugin { this.log = this.ctx.logger.get(); } - async setup({ http, capabilities, getStartServices, opensearch }: CoreSetup) { + async setup({ http, capabilities, opensearch, security }: CoreSetup) { capabilities.registerProvider(() => ({ dev_tools: { show: true, @@ -58,6 +58,14 @@ export class ConsoleServerPlugin implements Plugin { }, })); + capabilities.registerSwitcher(async (request, capabilites) => { + return await security.readonlyService().hideForReadonly(request, capabilites, { + dev_tools: { + save: false, + }, + }); + }); + const config = await this.ctx.config.create().pipe(first()).toPromise(); const globalConfig = await this.ctx.config.legacy.globalConfig$.pipe(first()).toPromise(); const proxyPathFilters = config.proxyFilter.map((str: string) => new RegExp(str)); diff --git a/src/plugins/dashboard/public/application/components/dashboard_listing/__snapshots__/dashboard_listing.test.tsx.snap b/src/plugins/dashboard/public/application/components/dashboard_listing/__snapshots__/dashboard_listing.test.tsx.snap index 9679c0cfdccf..a00b1e1429f4 100644 --- a/src/plugins/dashboard/public/application/components/dashboard_listing/__snapshots__/dashboard_listing.test.tsx.snap +++ b/src/plugins/dashboard/public/application/components/dashboard_listing/__snapshots__/dashboard_listing.test.tsx.snap @@ -797,6 +797,7 @@ exports[`dashboard listing hideWriteControls 1`] = ` "dataSource": Object { "guide": "https://opensearch.org/docs/mocked-test-branch/dashboards/discover/multi-data-sources/", }, + "devTools": "https://opensearch.org/docs/mocked-test-branch/dashboards/dev-tools/index-dev/", "dql": Object { "base": "https://opensearch.org/docs/mocked-test-branch/dashboards/dql", "boolean_query": "https://opensearch.org/docs/mocked-test-branch/dashboards/dql/#boolean-query", @@ -1933,6 +1934,7 @@ exports[`dashboard listing render table listing with initial filters from URL 1` "dataSource": Object { "guide": "https://opensearch.org/docs/mocked-test-branch/dashboards/discover/multi-data-sources/", }, + "devTools": "https://opensearch.org/docs/mocked-test-branch/dashboards/dev-tools/index-dev/", "dql": Object { "base": "https://opensearch.org/docs/mocked-test-branch/dashboards/dql", "boolean_query": "https://opensearch.org/docs/mocked-test-branch/dashboards/dql/#boolean-query", @@ -3130,6 +3132,7 @@ exports[`dashboard listing renders call to action when no dashboards exist 1`] = "dataSource": Object { "guide": "https://opensearch.org/docs/mocked-test-branch/dashboards/discover/multi-data-sources/", }, + "devTools": "https://opensearch.org/docs/mocked-test-branch/dashboards/dev-tools/index-dev/", "dql": Object { "base": "https://opensearch.org/docs/mocked-test-branch/dashboards/dql", "boolean_query": "https://opensearch.org/docs/mocked-test-branch/dashboards/dql/#boolean-query", @@ -4327,6 +4330,7 @@ exports[`dashboard listing renders table rows 1`] = ` "dataSource": Object { "guide": "https://opensearch.org/docs/mocked-test-branch/dashboards/discover/multi-data-sources/", }, + "devTools": "https://opensearch.org/docs/mocked-test-branch/dashboards/dev-tools/index-dev/", "dql": Object { "base": "https://opensearch.org/docs/mocked-test-branch/dashboards/dql", "boolean_query": "https://opensearch.org/docs/mocked-test-branch/dashboards/dql/#boolean-query", @@ -5524,6 +5528,7 @@ exports[`dashboard listing renders warning when listingLimit is exceeded 1`] = ` "dataSource": Object { "guide": "https://opensearch.org/docs/mocked-test-branch/dashboards/discover/multi-data-sources/", }, + "devTools": "https://opensearch.org/docs/mocked-test-branch/dashboards/dev-tools/index-dev/", "dql": Object { "base": "https://opensearch.org/docs/mocked-test-branch/dashboards/dql", "boolean_query": "https://opensearch.org/docs/mocked-test-branch/dashboards/dql/#boolean-query", diff --git a/src/plugins/dashboard/public/application/components/dashboard_top_nav/__snapshots__/dashboard_top_nav.test.tsx.snap b/src/plugins/dashboard/public/application/components/dashboard_top_nav/__snapshots__/dashboard_top_nav.test.tsx.snap index 5e05e64ba8cd..1fcd1c30b40b 100644 --- a/src/plugins/dashboard/public/application/components/dashboard_top_nav/__snapshots__/dashboard_top_nav.test.tsx.snap +++ b/src/plugins/dashboard/public/application/components/dashboard_top_nav/__snapshots__/dashboard_top_nav.test.tsx.snap @@ -689,6 +689,7 @@ exports[`Dashboard top nav render in embed mode 1`] = ` "dataSource": Object { "guide": "https://opensearch.org/docs/mocked-test-branch/dashboards/discover/multi-data-sources/", }, + "devTools": "https://opensearch.org/docs/mocked-test-branch/dashboards/dev-tools/index-dev/", "dql": Object { "base": "https://opensearch.org/docs/mocked-test-branch/dashboards/dql", "boolean_query": "https://opensearch.org/docs/mocked-test-branch/dashboards/dql/#boolean-query", @@ -1650,6 +1651,7 @@ exports[`Dashboard top nav render in embed mode, and force hide filter bar 1`] = "dataSource": Object { "guide": "https://opensearch.org/docs/mocked-test-branch/dashboards/discover/multi-data-sources/", }, + "devTools": "https://opensearch.org/docs/mocked-test-branch/dashboards/dev-tools/index-dev/", "dql": Object { "base": "https://opensearch.org/docs/mocked-test-branch/dashboards/dql", "boolean_query": "https://opensearch.org/docs/mocked-test-branch/dashboards/dql/#boolean-query", @@ -2611,6 +2613,7 @@ exports[`Dashboard top nav render in embed mode, components can be forced show b "dataSource": Object { "guide": "https://opensearch.org/docs/mocked-test-branch/dashboards/discover/multi-data-sources/", }, + "devTools": "https://opensearch.org/docs/mocked-test-branch/dashboards/dev-tools/index-dev/", "dql": Object { "base": "https://opensearch.org/docs/mocked-test-branch/dashboards/dql", "boolean_query": "https://opensearch.org/docs/mocked-test-branch/dashboards/dql/#boolean-query", @@ -3572,6 +3575,7 @@ exports[`Dashboard top nav render in full screen mode with appended URL param bu "dataSource": Object { "guide": "https://opensearch.org/docs/mocked-test-branch/dashboards/discover/multi-data-sources/", }, + "devTools": "https://opensearch.org/docs/mocked-test-branch/dashboards/dev-tools/index-dev/", "dql": Object { "base": "https://opensearch.org/docs/mocked-test-branch/dashboards/dql", "boolean_query": "https://opensearch.org/docs/mocked-test-branch/dashboards/dql/#boolean-query", @@ -4533,6 +4537,7 @@ exports[`Dashboard top nav render in full screen mode, no componenets should be "dataSource": Object { "guide": "https://opensearch.org/docs/mocked-test-branch/dashboards/discover/multi-data-sources/", }, + "devTools": "https://opensearch.org/docs/mocked-test-branch/dashboards/dev-tools/index-dev/", "dql": Object { "base": "https://opensearch.org/docs/mocked-test-branch/dashboards/dql", "boolean_query": "https://opensearch.org/docs/mocked-test-branch/dashboards/dql/#boolean-query", @@ -5494,6 +5499,7 @@ exports[`Dashboard top nav render with all components 1`] = ` "dataSource": Object { "guide": "https://opensearch.org/docs/mocked-test-branch/dashboards/discover/multi-data-sources/", }, + "devTools": "https://opensearch.org/docs/mocked-test-branch/dashboards/dev-tools/index-dev/", "dql": Object { "base": "https://opensearch.org/docs/mocked-test-branch/dashboards/dql", "boolean_query": "https://opensearch.org/docs/mocked-test-branch/dashboards/dql/#boolean-query", diff --git a/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx b/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx index dad2f196f1b8..d994e98142db 100644 --- a/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx +++ b/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx @@ -239,6 +239,7 @@ export class DashboardContainer extends Container { + refreshDashboardContainer({ + dashboardServices: services, + dashboardContainer, + savedDashboard: dashboard!, + appStateData: stateContainer.getState(), + }); + }); + + subscriptions.add(stopSyncingFromGlobalFilters); + unsubscribeFromDashboardContainer = () => { stopSyncingDashboardContainerInputs(); stopSyncingDashboardContainerOutputs(); diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx index 956bb5a7a836..dd874d3419f2 100644 --- a/src/plugins/dashboard/public/plugin.tsx +++ b/src/plugins/dashboard/public/plugin.tsx @@ -80,7 +80,6 @@ import { withNotifyOnErrors, } from '../../opensearch_dashboards_utils/public'; import { - initAngularBootstrap, OpenSearchDashboardsLegacySetup, OpenSearchDashboardsLegacyStart, } from '../../opensearch_dashboards_legacy/public'; @@ -452,9 +451,6 @@ export class DashboardPlugin }, }; - // TODO: delete this when discover de-angular is completed - initAngularBootstrap(); - core.application.register(app); urlForwarding.forwardApp( DashboardConstants.DASHBOARDS_ID, diff --git a/src/plugins/dashboard/server/plugin.ts b/src/plugins/dashboard/server/plugin.ts index 49eb29706b04..4e377e24bbce 100644 --- a/src/plugins/dashboard/server/plugin.ts +++ b/src/plugins/dashboard/server/plugin.ts @@ -53,6 +53,15 @@ export class DashboardPlugin implements Plugin { + return await core.security.readonlyService().hideForReadonly(request, capabilites, { + dashboard: { + createNew: false, + showWriteControls: false, + saveQuery: false, + }, + }); + }); return {}; } diff --git a/src/plugins/data/common/field_formats/content_types/html_content_type.ts b/src/plugins/data/common/field_formats/content_types/html_content_type.ts index 1e3195f256c1..904e6a09c6e8 100644 --- a/src/plugins/data/common/field_formats/content_types/html_content_type.ts +++ b/src/plugins/data/common/field_formats/content_types/html_content_type.ts @@ -72,7 +72,7 @@ export const setup = ( }; const wrap: HtmlContextTypeConvert = (value, options) => { - return `${recurse(value, options)}`; + return `${recurse(value, options)}`; }; return wrap; diff --git a/src/plugins/data/common/field_formats/converters/color.test.ts b/src/plugins/data/common/field_formats/converters/color.test.ts index 689abf0e3daa..ac1b0c9bc4e9 100644 --- a/src/plugins/data/common/field_formats/converters/color.test.ts +++ b/src/plugins/data/common/field_formats/converters/color.test.ts @@ -48,14 +48,14 @@ describe('Color Format', () => { jest.fn() ); - expect(colorer.convert(99, HTML_CONTEXT_TYPE)).toBe('99'); + expect(colorer.convert(99, HTML_CONTEXT_TYPE)).toBe('99'); expect(colorer.convert(100, HTML_CONTEXT_TYPE)).toBe( - '100' + '100' ); expect(colorer.convert(150, HTML_CONTEXT_TYPE)).toBe( - '150' + '150' ); - expect(colorer.convert(151, HTML_CONTEXT_TYPE)).toBe('151'); + expect(colorer.convert(151, HTML_CONTEXT_TYPE)).toBe('151'); }); test('should not convert invalid ranges', () => { @@ -73,7 +73,7 @@ describe('Color Format', () => { jest.fn() ); - expect(colorer.convert(99, HTML_CONTEXT_TYPE)).toBe('99'); + expect(colorer.convert(99, HTML_CONTEXT_TYPE)).toBe('99'); }); }); @@ -94,26 +94,26 @@ describe('Color Format', () => { ); const converter = colorer.getConverterFor(HTML_CONTEXT_TYPE) as Function; - expect(converter('B', HTML_CONTEXT_TYPE)).toBe('B'); + expect(converter('B', HTML_CONTEXT_TYPE)).toBe('B'); expect(converter('AAA', HTML_CONTEXT_TYPE)).toBe( - 'AAA' + 'AAA' ); expect(converter('AB', HTML_CONTEXT_TYPE)).toBe( - 'AB' + 'AB' ); - expect(converter('a', HTML_CONTEXT_TYPE)).toBe('a'); + expect(converter('a', HTML_CONTEXT_TYPE)).toBe('a'); - expect(converter('B', HTML_CONTEXT_TYPE)).toBe('B'); + expect(converter('B', HTML_CONTEXT_TYPE)).toBe('B'); expect(converter('AAA', HTML_CONTEXT_TYPE)).toBe( - 'AAA' + 'AAA' ); expect(converter('AB', HTML_CONTEXT_TYPE)).toBe( - 'AB' + 'AB' ); expect(converter('AB <', HTML_CONTEXT_TYPE)).toBe( - 'AB <' + 'AB <' ); - expect(converter('a', HTML_CONTEXT_TYPE)).toBe('a'); + expect(converter('a', HTML_CONTEXT_TYPE)).toBe('a'); }); test('returns original value (escaped) when regex is invalid', () => { @@ -132,7 +132,7 @@ describe('Color Format', () => { ); const converter = colorer.getConverterFor(HTML_CONTEXT_TYPE) as Function; - expect(converter('<', HTML_CONTEXT_TYPE)).toBe('<'); + expect(converter('<', HTML_CONTEXT_TYPE)).toBe('<'); }); }); }); diff --git a/src/plugins/data/common/field_formats/converters/numeral.ts b/src/plugins/data/common/field_formats/converters/numeral.ts index ad0b9773823f..7219b2e6ab40 100644 --- a/src/plugins/data/common/field_formats/converters/numeral.ts +++ b/src/plugins/data/common/field_formats/converters/numeral.ts @@ -56,11 +56,14 @@ export abstract class NumeralFormat extends FieldFormat { protected getConvertedValue(val: any): string { if (val === -Infinity) return '-∞'; if (val === +Infinity) return '+∞'; - if (typeof val !== 'number') { + + const isBigInt = typeof val === 'bigint'; + + if (typeof val !== 'number' && !isBigInt) { val = parseFloat(val); } - if (isNaN(val)) return ''; + if (!isBigInt && isNaN(val)) return ''; const previousLocale = numeral.language(); const defaultLocale = diff --git a/src/plugins/data/common/field_formats/converters/source.test.ts b/src/plugins/data/common/field_formats/converters/source.test.ts index 2b70e4f4f6a3..763987f05054 100644 --- a/src/plugins/data/common/field_formats/converters/source.test.ts +++ b/src/plugins/data/common/field_formats/converters/source.test.ts @@ -50,7 +50,7 @@ describe('Source Format', () => { }; expect(convertHtml(hit)).toBe( - '{"foo":"bar","number":42,"hello":"<h1>World</h1>","also":"with \\"quotes\\" or 'single quotes'"}' + '{"foo":"bar","number":42,"hello":"<h1>World</h1>","also":"with \\"quotes\\" or 'single quotes'"}' ); }); }); diff --git a/src/plugins/data/common/field_formats/converters/url.test.ts b/src/plugins/data/common/field_formats/converters/url.test.ts index f80dc4fd11b9..91acf522b89d 100644 --- a/src/plugins/data/common/field_formats/converters/url.test.ts +++ b/src/plugins/data/common/field_formats/converters/url.test.ts @@ -36,7 +36,7 @@ describe('UrlFormat', () => { const url = new UrlFormat({}); expect(url.convert('http://opensearch.org', HTML_CONTEXT_TYPE)).toBe( - 'http://opensearch.org' + 'http://opensearch.org' ); }); @@ -44,7 +44,7 @@ describe('UrlFormat', () => { const url = new UrlFormat({ type: 'audio' }); expect(url.convert('http://opensearch.org', HTML_CONTEXT_TYPE)).toBe( - '' + '' ); }); @@ -53,7 +53,7 @@ describe('UrlFormat', () => { const url = new UrlFormat({ type: 'img' }); expect(url.convert('http://opensearch.org', HTML_CONTEXT_TYPE)).toBe( - 'A dynamically-specified image located at http://opensearch.orgA dynamically-specified image located at http://opensearch.org' ); }); @@ -62,7 +62,7 @@ describe('UrlFormat', () => { const url = new UrlFormat({ type: 'img', width: '12', height: '55' }); expect(url.convert('http://opensearch.org', HTML_CONTEXT_TYPE)).toBe( - 'A dynamically-specified image located at http://opensearch.orgA dynamically-specified image located at http://opensearch.org' ); }); @@ -71,7 +71,7 @@ describe('UrlFormat', () => { const url = new UrlFormat({ type: 'img', height: '55' }); expect(url.convert('http://opensearch.org', HTML_CONTEXT_TYPE)).toBe( - 'A dynamically-specified image located at http://opensearch.orgA dynamically-specified image located at http://opensearch.org' ); }); @@ -80,7 +80,7 @@ describe('UrlFormat', () => { const url = new UrlFormat({ type: 'img', width: '22' }); expect(url.convert('http://opensearch.org', HTML_CONTEXT_TYPE)).toBe( - 'A dynamically-specified image located at http://opensearch.orgA dynamically-specified image located at http://opensearch.org' ); }); @@ -89,7 +89,7 @@ describe('UrlFormat', () => { const url = new UrlFormat({ type: 'img', width: 'not a number' }); expect(url.convert('http://opensearch.org', HTML_CONTEXT_TYPE)).toBe( - 'A dynamically-specified image located at http://opensearch.orgA dynamically-specified image located at http://opensearch.org' ); }); @@ -98,7 +98,7 @@ describe('UrlFormat', () => { const url = new UrlFormat({ type: 'img', height: 'not a number' }); expect(url.convert('http://opensearch.org', HTML_CONTEXT_TYPE)).toBe( - 'A dynamically-specified image located at http://opensearch.orgA dynamically-specified image located at http://opensearch.org' ); }); @@ -109,7 +109,7 @@ describe('UrlFormat', () => { const url = new UrlFormat({ urlTemplate: 'http://{{ value }}' }); expect(url.convert('url', HTML_CONTEXT_TYPE)).toBe( - 'http://url' + 'http://url' ); }); @@ -128,7 +128,7 @@ describe('UrlFormat', () => { }); expect(url.convert('php', HTML_CONTEXT_TYPE)).toBe( - 'extension: php' + 'extension: php' ); }); @@ -188,19 +188,19 @@ describe('UrlFormat', () => { const converter = url.getConverterFor(HTML_CONTEXT_TYPE) as Function; expect(converter('www.opensearch.org')).toBe( - 'www.opensearch.org' + 'www.opensearch.org' ); expect(converter('opensearch.org')).toBe( - 'opensearch.org' + 'opensearch.org' ); expect(converter('opensearch')).toBe( - 'opensearch' + 'opensearch' ); expect(converter('ftp://opensearch.org')).toBe( - 'ftp://opensearch.org' + 'ftp://opensearch.org' ); }); @@ -213,19 +213,19 @@ describe('UrlFormat', () => { const converter = url.getConverterFor(HTML_CONTEXT_TYPE) as Function; expect(converter('www.opensearch.org')).toBe( - 'www.opensearch.org' + 'www.opensearch.org' ); expect(converter('opensearch.org')).toBe( - 'opensearch.org' + 'opensearch.org' ); expect(converter('opensearch')).toBe( - 'opensearch' + 'opensearch' ); expect(converter('ftp://opensearch.org')).toBe( - 'ftp://opensearch.org' + 'ftp://opensearch.org' ); }); @@ -238,7 +238,7 @@ describe('UrlFormat', () => { const converter = url.getConverterFor(HTML_CONTEXT_TYPE) as Function; expect(converter('../app/opensearch-dashboards')).toBe( - '../app/opensearch-dashboards' + '../app/opensearch-dashboards' ); }); @@ -246,11 +246,11 @@ describe('UrlFormat', () => { const url = new UrlFormat({}); expect(url.convert('../app/opensearch-dashboards', HTML_CONTEXT_TYPE)).toBe( - '../app/opensearch-dashboards' + '../app/opensearch-dashboards' ); expect(url.convert('http://www.opensearch.org', HTML_CONTEXT_TYPE)).toBe( - 'http://www.opensearch.org' + 'http://www.opensearch.org' ); }); @@ -264,15 +264,15 @@ describe('UrlFormat', () => { const converter = url.getConverterFor(HTML_CONTEXT_TYPE) as Function; expect(converter('#/foo')).toBe( - '#/foo' + '#/foo' ); expect(converter('/nbc/app/discover#/')).toBe( - '/nbc/app/discover#/' + '/nbc/app/discover#/' ); expect(converter('../foo/bar')).toBe( - '../foo/bar' + '../foo/bar' ); }); @@ -285,23 +285,23 @@ describe('UrlFormat', () => { const converter = url.getConverterFor(HTML_CONTEXT_TYPE) as Function; expect(converter('10.22.55.66')).toBe( - '10.22.55.66' + '10.22.55.66' ); expect(converter('http://www.domain.name/app/opensearch-dashboards#/dashboard/')).toBe( - 'http://www.domain.name/app/opensearch-dashboards#/dashboard/' + 'http://www.domain.name/app/opensearch-dashboards#/dashboard/' ); expect(converter('/app/opensearch-dashboards')).toBe( - '/app/opensearch-dashboards' + '/app/opensearch-dashboards' ); expect(converter('opensearch-dashboards#/dashboard/')).toBe( - 'opensearch-dashboards#/dashboard/' + 'opensearch-dashboards#/dashboard/' ); expect(converter('#/dashboard/')).toBe( - '#/dashboard/' + '#/dashboard/' ); }); @@ -314,23 +314,23 @@ describe('UrlFormat', () => { const converter = url.getConverterFor(HTML_CONTEXT_TYPE) as Function; expect(converter('10.22.55.66')).toBe( - '10.22.55.66' + '10.22.55.66' ); expect(converter('http://www.domain.name/app/kibana#/dashboard/')).toBe( - 'http://www.domain.name/app/kibana#/dashboard/' + 'http://www.domain.name/app/kibana#/dashboard/' ); expect(converter('/app/kibana')).toBe( - '/app/kibana' + '/app/kibana' ); expect(converter('kibana#/dashboard/')).toBe( - 'kibana#/dashboard/' + 'kibana#/dashboard/' ); expect(converter('#/dashboard/')).toBe( - '#/dashboard/' + '#/dashboard/' ); }); }); diff --git a/src/plugins/data/common/field_formats/field_format.test.ts b/src/plugins/data/common/field_formats/field_format.test.ts index 4dadca841120..7d3d83a9c49f 100644 --- a/src/plugins/data/common/field_formats/field_format.test.ts +++ b/src/plugins/data/common/field_formats/field_format.test.ts @@ -109,7 +109,7 @@ describe('FieldFormat class', () => { expect(text).not.toBe(html); expect(text && text('formatted')).toBe('formatted'); - expect(html && html('formatted')).toBe('formatted'); + expect(html && html('formatted')).toBe('formatted'); }); test('can be an object, with separate text and html converter', () => { @@ -119,7 +119,7 @@ describe('FieldFormat class', () => { expect(text).not.toBe(html); expect(text && text('formatted text')).toBe('formatted text'); - expect(html && html('formatted html')).toBe('formatted html'); + expect(html && html('formatted html')).toBe('formatted html'); }); test('does not escape the output of the text converter', () => { @@ -131,10 +131,7 @@ describe('FieldFormat class', () => { test('does escape the output of the text converter if used in an html context', () => { const f = getTestFormat(undefined, constant('')); - const expected = trimEnd( - trimStart(f.convert('', 'html'), ''), - '' - ); + const expected = trimEnd(trimStart(f.convert('', 'html'), ''), ''); expect(expected).not.toContain('<'); }); @@ -143,7 +140,7 @@ describe('FieldFormat class', () => { const f = getTestFormat(undefined, constant(''), constant('')); expect(f.convert('', 'text')).toBe(''); - expect(f.convert('', 'html')).toBe(''); + expect(f.convert('', 'html')).toBe(''); }); }); @@ -157,7 +154,7 @@ describe('FieldFormat class', () => { test('formats a value as html, when specified via second param', () => { const f = getTestFormat(undefined, constant('text'), constant('html')); - expect(f.convert('val', 'html')).toBe('html'); + expect(f.convert('val', 'html')).toBe('html'); }); test('formats a value as " - " when no value is specified', () => { diff --git a/src/plugins/data/common/index_patterns/index_patterns/flatten_hit.ts b/src/plugins/data/common/index_patterns/index_patterns/flatten_hit.ts index 63dc3eb453dd..73ace5e41a05 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/flatten_hit.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/flatten_hit.ts @@ -112,7 +112,7 @@ export function flattenHitWrapper( metaFields = {}, cache = new WeakMap() ) { - return function cachedFlatten(hit: Record, deep = false) { + return function cachedFlatten(hit: Record, deep = true) { const decorateFlattened = decorateFlattenedWrapper(hit, metaFields); const cached = cache.get(hit); const flattened = cached || flattenHit(indexPattern, hit, deep); diff --git a/src/plugins/data/common/index_patterns/index_patterns/format_hit.ts b/src/plugins/data/common/index_patterns/index_patterns/format_hit.ts index 204fea175728..7f30b7ddf3e0 100644 --- a/src/plugins/data/common/index_patterns/index_patterns/format_hit.ts +++ b/src/plugins/data/common/index_patterns/index_patterns/format_hit.ts @@ -52,8 +52,6 @@ export function formatHitProvider(indexPattern: IndexPattern, defaultFormat: any function formatHit(hit: Record, type: string = 'html') { if (type === 'text') { - // formatHit of type text is for react components to get rid of - // since it's currently just used at the discover's doc view table, caching is not necessary const flattened = indexPattern.flattenHit(hit); const result: Record = {}; for (const [key, value] of Object.entries(flattened)) { diff --git a/src/plugins/data/common/opensearch_query/filters/range_filter.test.ts b/src/plugins/data/common/opensearch_query/filters/range_filter.test.ts index c4c64de72aa7..e57471101976 100644 --- a/src/plugins/data/common/opensearch_query/filters/range_filter.test.ts +++ b/src/plugins/data/common/opensearch_query/filters/range_filter.test.ts @@ -63,6 +63,44 @@ describe('Range filter builder', () => { }); }); + it('should return a range filter when passed a standard field of type BigInt', () => { + const field = getField('bytes'); + + const lowerBigInt = BigInt(Number.MIN_SAFE_INTEGER) - 11n; + const upperBigInt = BigInt(Number.MAX_SAFE_INTEGER) + 11n; + + expect( + buildRangeFilter(field, { gte: `${lowerBigInt}`, lte: `${upperBigInt}` }, indexPattern) + ).toEqual({ + meta: { + index: 'id', + params: {}, + }, + range: { + bytes: { + gte: lowerBigInt, + lte: upperBigInt, + }, + }, + }); + }); + + it('should return a range filter when passed a scripted field using BigInt', () => { + const field = getField('script number'); + + const lowerBigInt = BigInt(Number.MIN_SAFE_INTEGER) - 11n; + const upperBigInt = BigInt(Number.MAX_SAFE_INTEGER) + 11n; + + const filter = buildRangeFilter( + field, + { gte: `${lowerBigInt}`, lt: `${upperBigInt}` }, + indexPattern + ); + + expect(filter.script!.script.params).toHaveProperty('gte', lowerBigInt); + expect(filter.script!.script.params).toHaveProperty('lt', upperBigInt); + }); + it('should return a script filter when passed a scripted field', () => { const field = getField('script number'); diff --git a/src/plugins/data/common/opensearch_query/filters/range_filter.ts b/src/plugins/data/common/opensearch_query/filters/range_filter.ts index 12d49f5cc71c..e7a6565cbc39 100644 --- a/src/plugins/data/common/opensearch_query/filters/range_filter.ts +++ b/src/plugins/data/common/opensearch_query/filters/range_filter.ts @@ -124,19 +124,24 @@ export const buildRangeFilter = ( filter.meta.formattedValue = formattedValue; } - params = mapValues(params, (value: any) => (field.type === 'number' ? parseFloat(value) : value)); + params = mapValues(params, (value: any) => + field.type === 'number' && typeof value !== 'bigint' + ? isFinite(value) && (value > Number.MAX_SAFE_INTEGER || value < Number.MIN_SAFE_INTEGER) + ? BigInt(value) + : parseFloat(value) + : value + ); if ('gte' in params && 'gt' in params) throw new Error('gte and gt are mutually exclusive'); if ('lte' in params && 'lt' in params) throw new Error('lte and lt are mutually exclusive'); const totalInfinite = ['gt', 'lt'].reduce((acc: number, op: any) => { - const key = op in params ? op : `${op}e`; - const isInfinite = Math.abs(get(params, key)) === Infinity; + const key: keyof RangeFilterParams = op in params ? op : `${op}e`; + const isInfinite = + typeof params[key] !== 'bigint' && Math.abs(get(params, key) as number) === Infinity; if (isInfinite) { acc++; - - // @ts-ignore delete params[key]; } diff --git a/src/plugins/data/common/opensearch_query/kuery/ast/_generated_/kuery.js b/src/plugins/data/common/opensearch_query/kuery/ast/_generated_/kuery.js index 7faf78fc757d..7396607e55c7 100644 --- a/src/plugins/data/common/opensearch_query/kuery/ast/_generated_/kuery.js +++ b/src/plugins/data/common/opensearch_query/kuery/ast/_generated_/kuery.js @@ -233,8 +233,14 @@ module.exports = (function() { if (sequence === 'true') return buildLiteralNode(true); if (sequence === 'false') return buildLiteralNode(false); if (chars.includes(wildcardSymbol)) return buildWildcardNode(sequence); - const isNumberPattern = /^(-?[1-9]+\d*([.]\d+)?)$|^(-?0[.]\d*[1-9]+)$|^0$|^0.0$|^[.]\d{1,}$/ - return buildLiteralNode(isNumberPattern.test(sequence) ? Number(sequence) : sequence); + const isNumberPattern = /^(-?[1-9]+\d*([.]\d+)?)$|^(-?0[.]\d*[1-9]+)$|^0$|^0.0$|^[.]\d{1,}$/; + return buildLiteralNode( + isNumberPattern.test(sequence) + ? isFinite(sequence) && (sequence > Number.MAX_SAFE_INTEGER || sequence < Number.MIN_SAFE_INTEGER) + ? BigInt(sequence) + : Number(sequence) + : sequence + ); }, peg$c50 = { type: "any", description: "any character" }, peg$c51 = "*", diff --git a/src/plugins/data/common/opensearch_query/kuery/ast/ast.test.ts b/src/plugins/data/common/opensearch_query/kuery/ast/ast.test.ts index 3e5744ed72cd..2841e07c9adc 100644 --- a/src/plugins/data/common/opensearch_query/kuery/ast/ast.test.ts +++ b/src/plugins/data/common/opensearch_query/kuery/ast/ast.test.ts @@ -182,6 +182,21 @@ describe('kuery AST API', () => { expect(actual).toEqual(expected); }); + test('should support long numerals', () => { + const lowerBigInt = BigInt(Number.MIN_SAFE_INTEGER) - 11n; + const upperBigInt = BigInt(Number.MAX_SAFE_INTEGER) + 11n; + const expected = nodeTypes.function.buildNode('and', [ + nodeTypes.function.buildNode('range', 'bytes', { + gt: lowerBigInt, + }), + nodeTypes.function.buildNode('range', 'bytes', { + lt: upperBigInt, + }), + ]); + const actual = fromKueryExpression(`bytes > ${lowerBigInt} and bytes < ${upperBigInt}`); + expect(actual).toEqual(expected); + }); + test('should support inclusive range operators', () => { const expected = nodeTypes.function.buildNode('and', [ nodeTypes.function.buildNode('range', 'bytes', { @@ -284,6 +299,7 @@ describe('kuery AST API', () => { const booleanFalseLiteral = nodeTypes.literal.buildNode(false); const booleanTrueLiteral = nodeTypes.literal.buildNode(true); const numberLiteral = nodeTypes.literal.buildNode(42); + const upperBigInt = BigInt(Number.MAX_SAFE_INTEGER) + 11n; expect(fromLiteralExpression('foo')).toEqual(stringLiteral); expect(fromLiteralExpression('true')).toEqual(booleanTrueLiteral); @@ -302,6 +318,7 @@ describe('kuery AST API', () => { expect(fromLiteralExpression('790.9').value).toEqual(790.9); expect(fromLiteralExpression('0.0001').value).toEqual(0.0001); expect(fromLiteralExpression('96565646732345').value).toEqual(96565646732345); + expect(fromLiteralExpression(upperBigInt.toString()).value).toEqual(upperBigInt); expect(fromLiteralExpression('..4').value).toEqual('..4'); expect(fromLiteralExpression('.3text').value).toEqual('.3text'); @@ -316,6 +333,7 @@ describe('kuery AST API', () => { expect(fromLiteralExpression('-.4').value).toEqual('-.4'); expect(fromLiteralExpression('-0').value).toEqual('-0'); expect(fromLiteralExpression('00949').value).toEqual('00949'); + expect(fromLiteralExpression(`00${upperBigInt}`).value).toEqual(`00${upperBigInt}`); }); test('should allow escaping of special characters with a backslash', () => { diff --git a/src/plugins/data/common/opensearch_query/kuery/ast/kuery.peg b/src/plugins/data/common/opensearch_query/kuery/ast/kuery.peg index 625c5069f936..bc5e4a77ff19 100644 --- a/src/plugins/data/common/opensearch_query/kuery/ast/kuery.peg +++ b/src/plugins/data/common/opensearch_query/kuery/ast/kuery.peg @@ -247,8 +247,14 @@ UnquotedLiteral if (sequence === 'true') return buildLiteralNode(true); if (sequence === 'false') return buildLiteralNode(false); if (chars.includes(wildcardSymbol)) return buildWildcardNode(sequence); - const isNumberPattern = /^(-?[1-9]+\d*([.]\d+)?)$|^(-?0[.]\d*[1-9]+)$|^0$|^0.0$|^[.]\d{1,}$/ - return buildLiteralNode(isNumberPattern.test(sequence) ? Number(sequence) : sequence); + const isNumberPattern = /^(-?[1-9]+\d*([.]\d+)?)$|^(-?0[.]\d*[1-9]+)$|^0$|^0.0$|^[.]\d{1,}$/; + return buildLiteralNode( + isNumberPattern.test(sequence) + ? isFinite(sequence) && (sequence > Number.MAX_SAFE_INTEGER || sequence < Number.MIN_SAFE_INTEGER) + ? BigInt(sequence) + : Number(sequence) + : sequence + ); } UnquotedCharacter diff --git a/src/plugins/data/common/opensearch_query/opensearch_query/filter_matches_index.test.ts b/src/plugins/data/common/opensearch_query/opensearch_query/filter_matches_index.test.ts index f610b1e7179f..ad68e14b2c54 100644 --- a/src/plugins/data/common/opensearch_query/opensearch_query/filter_matches_index.test.ts +++ b/src/plugins/data/common/opensearch_query/opensearch_query/filter_matches_index.test.ts @@ -66,4 +66,18 @@ describe('filterMatchesIndex', () => { expect(filterMatchesIndex(filter, indexPattern)).toBe(true); }); + + it('should return false if the custom filter is a different index id', () => { + const filter = { meta: { index: 'foo', key: 'bar', type: 'custom' } } as Filter; + const indexPattern = { id: 'bar', fields: [{ name: 'foo' }] } as IIndexPattern; + + expect(filterMatchesIndex(filter, indexPattern)).toBe(false); + }); + + it('should return true if the custom filter is the same index id', () => { + const filter = { meta: { index: 'foo', key: 'bar', type: 'custom' } } as Filter; + const indexPattern = { id: 'foo', fields: [{ name: 'barf' }] } as IIndexPattern; + + expect(filterMatchesIndex(filter, indexPattern)).toBe(true); + }); }); diff --git a/src/plugins/data/common/opensearch_query/opensearch_query/filter_matches_index.ts b/src/plugins/data/common/opensearch_query/opensearch_query/filter_matches_index.ts index f8c2ab67ee95..529e68609aeb 100644 --- a/src/plugins/data/common/opensearch_query/opensearch_query/filter_matches_index.ts +++ b/src/plugins/data/common/opensearch_query/opensearch_query/filter_matches_index.ts @@ -31,14 +31,14 @@ import { IIndexPattern, IFieldType } from '../../index_patterns'; import { Filter } from '../filters'; -/* - * TODO: We should base this on something better than `filter.meta.key`. We should probably modify - * this to check if `filter.meta.index` matches `indexPattern.id` instead, but that's a breaking - * change. - */ export function filterMatchesIndex(filter: Filter, indexPattern?: IIndexPattern | null) { if (!filter.meta?.key || !indexPattern) { return true; } + + if (filter.meta?.type === 'custom') { + return filter.meta.index === indexPattern.id; + } + return indexPattern.fields.some((field: IFieldType) => field.name === filter.meta.key); } diff --git a/src/plugins/data/common/search/aggs/utils/prop_filter.ts b/src/plugins/data/common/search/aggs/utils/prop_filter.ts index 341032e47bf6..2670e3d26b82 100644 --- a/src/plugins/data/common/search/aggs/utils/prop_filter.ts +++ b/src/plugins/data/common/search/aggs/utils/prop_filter.ts @@ -37,7 +37,7 @@ type FilterFunc

= (item: T[P]) => boolean; * - fieldType filters a list of fields by their type property * - aggFilter filters a list of aggs by their name property * - * @returns the filter function which can be registered with angular + * @returns the filter function */ export function propFilter

(prop: P) { /** diff --git a/src/plugins/data/common/search/aggs/utils/to_angular_json.ts b/src/plugins/data/common/search/aggs/utils/to_angular_json.ts index 0efafa7884a1..3eac6a1fcfe4 100644 --- a/src/plugins/data/common/search/aggs/utils/to_angular_json.ts +++ b/src/plugins/data/common/search/aggs/utils/to_angular_json.ts @@ -33,6 +33,7 @@ * https://github.com/angular/angular.js/blob/master/src/Angular.js#L1312 * * @internal + * @deprecated This function will be removed in the next major version. */ export function toAngularJSON(obj: any, pretty?: any): string { if (obj === undefined) return ''; diff --git a/src/plugins/data/common/search/opensearch_search/types.ts b/src/plugins/data/common/search/opensearch_search/types.ts index a45d4d0660ce..3b93177bf201 100644 --- a/src/plugins/data/common/search/opensearch_search/types.ts +++ b/src/plugins/data/common/search/opensearch_search/types.ts @@ -33,6 +33,7 @@ import { Search } from '@opensearch-project/opensearch/api/requestParams'; import { IOpenSearchDashboardsSearchRequest, IOpenSearchDashboardsSearchResponse } from '../types'; export const OPENSEARCH_SEARCH_STRATEGY = 'opensearch'; +export const OPENSEARCH_SEARCH_WITH_LONG_NUMERALS_STRATEGY = 'opensearch-with-long-numerals'; export interface ISearchOptions { /** @@ -43,6 +44,10 @@ export interface ISearchOptions { * Use this option to force using a specific server side search strategy. Leave empty to use the default strategy. */ strategy?: string; + /** + * Use this option to enable support for long numerals. + */ + withLongNumeralsSupport?: boolean; } export type ISearchRequestParams> = { diff --git a/src/plugins/data/common/search/search_source/parse_json.ts b/src/plugins/data/common/search/search_source/parse_json.ts index 80a58d62ff7f..5e03234ce355 100644 --- a/src/plugins/data/common/search/search_source/parse_json.ts +++ b/src/plugins/data/common/search/search_source/parse_json.ts @@ -28,6 +28,7 @@ * under the License. */ +import { parse } from '@osd/std'; import { SearchSourceFields } from './types'; import { InvalidJSONProperty } from '../../../../opensearch_dashboards_utils/common'; @@ -35,7 +36,7 @@ export const parseSearchSourceJSON = (searchSourceJSON: string) => { // if we have a searchSource, set its values based on the searchSourceJson field let searchSourceValues: SearchSourceFields; try { - searchSourceValues = JSON.parse(searchSourceJSON); + searchSourceValues = parse(searchSourceJSON); } catch (e) { throw new InvalidJSONProperty( `Invalid JSON in search source. ${e.message} JSON: ${searchSourceJSON}` diff --git a/src/plugins/data/common/search/search_source/search_source.ts b/src/plugins/data/common/search/search_source/search_source.ts index 9a85ec85ce87..abe6fa1b5cb4 100644 --- a/src/plugins/data/common/search/search_source/search_source.ts +++ b/src/plugins/data/common/search/search_source/search_source.ts @@ -81,6 +81,7 @@ */ import { setWith } from '@elastic/safer-lodash-set'; +import { stringify } from '@osd/std'; import { uniqueId, uniq, extend, pick, difference, omit, isObject, keys, isFunction } from 'lodash'; import { normalizeSortRequest } from './normalize_sort_request'; import { filterDocvalueFields } from './filter_docvalue_fields'; @@ -561,7 +562,7 @@ export class SearchSource { * @public */ public serialize() { const [searchSourceFields, references] = extractReferences(this.getSerializedFields()); - return { searchSourceJSON: JSON.stringify(searchSourceFields), references }; + return { searchSourceJSON: stringify(searchSourceFields), references }; } private getFilters(filterField: SearchSourceFields['filter']): Filter[] { diff --git a/src/plugins/data/common/search/types.ts b/src/plugins/data/common/search/types.ts index 5c4de4b0e426..e05c0adb46f0 100644 --- a/src/plugins/data/common/search/types.ts +++ b/src/plugins/data/common/search/types.ts @@ -76,6 +76,11 @@ export interface IOpenSearchDashboardsSearchResponse { */ isPartial?: boolean; + /** + * Indicates whether the results returned need long numerals treatment + */ + withLongNumeralsSupport?: boolean; + rawResponse: RawResponse; } diff --git a/src/plugins/data/public/data_sources/datasource/datasource.ts b/src/plugins/data/public/data_sources/datasource/datasource.ts new file mode 100644 index 000000000000..a2159c562064 --- /dev/null +++ b/src/plugins/data/public/data_sources/datasource/datasource.ts @@ -0,0 +1,79 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * Abstract class representing a data source. This class provides foundational + * interfaces for specific data sources. Any data source connection needs to extend + * and implement from this base class + * + * DataSourceMetaData: Represents metadata associated with the data source. + * SourceDataSet: Represents the dataset associated with the data source. + * DataSourceQueryResult: Represents the result from querying the data source. + */ + +import { ConnectionStatus } from './types'; + +/** + * @experimental this class is experimental and might change in future releases. + */ +export abstract class DataSource< + DataSourceMetaData, + DataSetParams, + SourceDataSet, + DataSourceQueryParams, + DataSourceQueryResult +> { + constructor( + private readonly name: string, + private readonly type: string, + private readonly metadata: DataSourceMetaData + ) {} + + getName() { + return this.name; + } + + getType() { + return this.type; + } + + getMetadata() { + return this.metadata; + } + + /** + * Abstract method to get the dataset associated with the data source. + * Implementing classes need to provide the specific implementation. + * + * Data source selector needs to display data sources with pattern + * group (connection name) - a list of datasets. For example, get + * all available tables for flint datasources, and get all index + * patterns for OpenSearch data source + * + * @experimental This API is experimental and might change in future releases. + * @returns {SourceDataSet} Dataset associated with the data source. + */ + abstract getDataSet(dataSetParams?: DataSetParams): SourceDataSet; + + /** + * Abstract method to run a query against the data source. + * Implementing classes need to provide the specific implementation. + * + * @experimental This API is experimental and might change in future releases. + * @returns {DataSourceQueryResult} Result from querying the data source. + */ + abstract runQuery(queryParams: DataSourceQueryParams): DataSourceQueryResult; + + /** + * Abstract method to test the connection to the data source. + * Implementing classes should provide the specific logic to determine + * the connection status, typically indicating success or failure. + * + * @experimental This API is experimental and might change in future releases. + * @returns {ConnectionStatus | Promise} Status of the connection test. + * @experimental + */ + abstract testConnection(): ConnectionStatus | Promise; +} diff --git a/src/plugins/data/public/data_sources/datasource/factory.test.ts b/src/plugins/data/public/data_sources/datasource/factory.test.ts new file mode 100644 index 000000000000..0f9ea016748f --- /dev/null +++ b/src/plugins/data/public/data_sources/datasource/factory.test.ts @@ -0,0 +1,93 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { DataSourceFactory } from './factory'; +import { DataSource } from './datasource'; +import { IndexPattern, IndexPatternsService } from '../../index_patterns'; + +class MockDataSource extends DataSource { + private readonly indexPatterns; + + constructor({ + name, + type, + metadata, + indexPatterns, + }: { + name: string; + type: string; + metadata: any; + indexPatterns: IndexPatternsService; + }) { + super(name, type, metadata); + this.indexPatterns = indexPatterns; + } + + async getDataSet(dataSetParams?: any) { + await this.indexPatterns.ensureDefaultIndexPattern(); + return await this.indexPatterns.getCache(); + } + + async testConnection(): Promise { + return true; + } + + async runQuery(queryParams: any) { + return undefined; + } +} + +describe('DataSourceFactory', () => { + beforeEach(() => { + // Reset the DataSourceFactory's singleton instance before each test for isolation + (DataSourceFactory as any).factory = undefined; + }); + + it('returns a singleton instance', () => { + const instance1 = DataSourceFactory.getInstance(); + const instance2 = DataSourceFactory.getInstance(); + expect(instance1).toBe(instance2); + }); + + it('registers a new data source type correctly', () => { + const factory = DataSourceFactory.getInstance(); + expect(() => { + factory.registerDataSourceType('mock', MockDataSource); + }).not.toThrow(); + }); + + it('throws error when registering an already registered data source type', () => { + const factory = DataSourceFactory.getInstance(); + factory.registerDataSourceType('mock', MockDataSource); + expect(() => { + factory.registerDataSourceType('mock', MockDataSource); + }).toThrow('This data source type has already been registered'); + }); + + it('creates and returns an instance of the registered data source type', () => { + const factory = DataSourceFactory.getInstance(); + const mockIndexPattern = {} as IndexPattern; + const config = { + name: 'test_datasource', + type: 'mock', + metadata: null, + indexPattern: mockIndexPattern, + }; + factory.registerDataSourceType('mock', MockDataSource); + + const instance = factory.getDataSourceInstance('mock', config); + expect(instance).toBeInstanceOf(MockDataSource); + expect(instance.getName()).toEqual(config.name); + expect(instance.getType()).toEqual(config.type); + expect(instance.getMetadata()).toEqual(config.metadata); + }); + + it('throws error when trying to get an instance of an unregistered data source type', () => { + const factory = DataSourceFactory.getInstance(); + expect(() => { + factory.getDataSourceInstance('unregistered', {}); + }).toThrow('Unsupported data source type'); + }); +}); diff --git a/src/plugins/data/public/data_sources/datasource/factory.ts b/src/plugins/data/public/data_sources/datasource/factory.ts new file mode 100644 index 000000000000..f0b4e36cfb82 --- /dev/null +++ b/src/plugins/data/public/data_sources/datasource/factory.ts @@ -0,0 +1,80 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * The DataSourceFactory is responsible for managing the registration and creation of data source classes. + * It serves as a registry for different data source types and provides a way to instantiate them. + */ + +import { DataSourceType } from '../datasource_services'; +import { DataSource } from '../datasource'; + +type DataSourceClass< + MetaData = any, + SetParams = any, + DataSet = any, + QueryParams = any, + QueryResult = any +> = new (config: any) => DataSource; + +export class DataSourceFactory { + // Holds the singleton instance of the DataSourceFactory. + private static factory: DataSourceFactory; + + // A dictionary holding the data source type as the key and its corresponding class constructor as the value. + private dataSourceClasses: { [type: string]: DataSourceClass } = {}; + + /** + * Private constructor to ensure only one instance of DataSourceFactory is created. + */ + private constructor() {} + + /** + * Returns the singleton instance of the DataSourceFactory. If it doesn't exist, it creates one. + * + * @experimental This API is experimental and might change in future releases. + * @returns {DataSourceFactory} The single instance of DataSourceFactory. + */ + static getInstance(): DataSourceFactory { + if (!this.factory) { + this.factory = new DataSourceFactory(); + } + return this.factory; + } + + /** + * Registers a new data source type with its associated class. + * If the type has already been registered, an error is thrown. + * + * @experimental This API is experimental and might change in future releases. + * @param {string} type - The identifier for the data source type. + * @param {DataSourceClass} dataSourceClass - The constructor of the data source class. + * @throws {Error} Throws an error if the data source type has already been registered. + */ + registerDataSourceType(type: string, dataSourceClass: DataSourceClass): void { + if (this.dataSourceClasses[type]) { + throw new Error('This data source type has already been registered'); + } + this.dataSourceClasses[type] = dataSourceClass; + } + + /** + * Creates and returns an instance of the specified data source type with the given configuration. + * If the type hasn't been registered, an error is thrown. + * + * @experimental This API is experimental and might change in future releases. + * @param {string} type - The identifier for the data source type. + * @param {any} config - The configuration for the data source instance. + * @returns {DataSourceType} An instance of the specified data source type. + * @throws {Error} Throws an error if the data source type is not supported. + */ + getDataSourceInstance(type: string, config: any): DataSourceType { + const DataSourceClass = this.dataSourceClasses[type]; + if (!DataSourceClass) { + throw new Error('Unsupported data source type'); + } + return new DataSourceClass(config); + } +} diff --git a/src/plugins/data/public/data_sources/datasource/index.ts b/src/plugins/data/public/data_sources/datasource/index.ts new file mode 100644 index 000000000000..10af40fdcfa2 --- /dev/null +++ b/src/plugins/data/public/data_sources/datasource/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export { DataSource } from './datasource'; +export { + IDataSourceMetaData, + ISourceDataSet, + IDataSetParams, + IDataSourceQueryParams, + IDataSourceQueryResult, + ConnectionStatus, + DataSourceConfig, + IndexPatternOption, +} from './types'; +export { DataSourceFactory } from './factory'; diff --git a/src/plugins/data/public/data_sources/datasource/types.ts b/src/plugins/data/public/data_sources/datasource/types.ts new file mode 100644 index 000000000000..bf77ef123a30 --- /dev/null +++ b/src/plugins/data/public/data_sources/datasource/types.ts @@ -0,0 +1,51 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @experimental These interfaces are experimental and might change in future releases. + */ + +import { IndexPatternsService } from '../../index_patterns'; +import { DataSourceType } from '../datasource_services'; + +export interface IndexPatternOption { + title: string; + id: string; +} + +export interface IDataSourceMetaData { + name: string; +} + +export interface IDataSourceGroup { + name: string; +} + +export interface ISourceDataSet { + ds: DataSourceType; + data_sets: Array; +} + +// to-dos: add common interfaces for datasource +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface IDataSetParams {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface IDataSourceQueryParams {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface IDataSourceQueryResult {} + +export interface ConnectionStatus { + success: boolean; + info: string; +} + +export interface DataSourceConfig { + name: string; + type: string; + metadata: any; + indexPatterns: IndexPatternsService; +} diff --git a/src/plugins/data/public/data_sources/datasource_selector/datasource_selectable.test.tsx b/src/plugins/data/public/data_sources/datasource_selector/datasource_selectable.test.tsx new file mode 100644 index 000000000000..3e6c99e420d6 --- /dev/null +++ b/src/plugins/data/public/data_sources/datasource_selector/datasource_selectable.test.tsx @@ -0,0 +1,184 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { render, act, screen, fireEvent } from '@testing-library/react'; +import { DataSourceSelectable } from './datasource_selectable'; +import { DataSourceType, GenericDataSource } from '../datasource_services'; +import { DataSourceGroup, DataSourceOption } from './types'; + +describe('DataSourceSelectable', () => { + let dataSourcesMock: GenericDataSource[]; + let dataSourceOptionListMock: DataSourceGroup[]; + let selectedSourcesMock: DataSourceOption[]; + let setSelectedSourcesMock: (sources: DataSourceOption[]) => void = jest.fn(); + let setDataSourceOptionListMock: (sources: DataSourceGroup[]) => void = jest.fn(); + let onFetchDataSetErrorMock: (error: Error) => void = jest.fn(); + + beforeEach(() => { + dataSourcesMock = [ + ({ + getDataSet: jest.fn().mockResolvedValue([]), + getType: jest.fn().mockReturnValue('DEFAULT_INDEX_PATTERNS'), + getName: jest.fn().mockReturnValue('SomeName'), + } as unknown) as DataSourceType, + ]; + + dataSourceOptionListMock = []; + selectedSourcesMock = []; + setSelectedSourcesMock = jest.fn(); + setDataSourceOptionListMock = jest.fn(); + onFetchDataSetErrorMock = jest.fn(); + }); + + it('renders without crashing', () => { + render( + + ); + }); + + it('fetches data sets on mount', async () => { + await act(async () => { + render( + + ); + }); + + expect(dataSourcesMock[0].getDataSet).toHaveBeenCalled(); + }); + + it('handles data set fetch errors', async () => { + (dataSourcesMock[0].getDataSet as jest.Mock).mockRejectedValue(new Error('Fetch error')); + + await act(async () => { + render( + + ); + }); + + expect(onFetchDataSetErrorMock).toHaveBeenCalledWith(new Error('Fetch error')); + }); + + it('should sort index patterns list alphabetically', async () => { + const mockDataSourceOptionList = [ + { + label: 'Index patterns', + options: [ + { label: 'logstash-*' }, + { label: '000-*' }, + { label: 'p000-1' }, + { label: 'pattern_archive' }, + { label: 'index_main' }, + { label: 'index-2024' }, + ], + }, + ] as any; + + render( + + ); + + const button = screen.getByLabelText('Open list of options'); + fireEvent.click(button); + expect( + screen.getByTestId('comboBoxOptionsList dataExplorerDSSelect-optionsList') + ).toBeInTheDocument(); + const defaultDSOptions = document.querySelectorAll('.euiComboBoxOption__content'); + const optionTexts = Array.from(defaultDSOptions).map((option) => option.innerHTML); + const expectedIndexPatternSortedOrder = [ + '000-*', + 'index_main', + 'index-2024', + 'logstash-*', + 'p000-1', + 'pattern_archive', + ]; + expect(optionTexts).toEqual(expectedIndexPatternSortedOrder); + }); + + it('should sort non index patterns list alphabetically', async () => { + const mockDataSourceOptionList = [ + { + label: 'Amazon S3', + options: [ + { label: 'mys3' }, + { label: '*starred' }, + { label: 'alpha-test-s3' }, + { label: '@special' }, + { label: 's3-2024' }, + { label: 'S3_Archive' }, + ], + }, + ] as any; + + render( + + ); + + const button = screen.getByLabelText('Open list of options'); + fireEvent.click(button); + expect( + screen.getByTestId('comboBoxOptionsList dataExplorerDSSelect-optionsList') + ).toBeInTheDocument(); + const defaultDSOptions = document.querySelectorAll('.euiComboBoxOption__content'); + const optionTexts = Array.from(defaultDSOptions).map((option) => option.innerHTML); + const expectedIndexPatternSortedOrder = [ + '@special', + '*starred', + 'alpha-test-s3', + 'mys3', + 'S3_Archive', + 's3-2024', + ]; + expect(optionTexts).toEqual(expectedIndexPatternSortedOrder); + }); +}); diff --git a/src/plugins/data/public/data_sources/datasource_selector/datasource_selectable.tsx b/src/plugins/data/public/data_sources/datasource_selector/datasource_selectable.tsx new file mode 100644 index 000000000000..1c6876815a0e --- /dev/null +++ b/src/plugins/data/public/data_sources/datasource_selector/datasource_selectable.tsx @@ -0,0 +1,147 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React, { useEffect, useCallback, useMemo } from 'react'; +import { EuiComboBox } from '@elastic/eui'; +import { i18n } from '@osd/i18n'; +import { ISourceDataSet, IndexPatternOption } from '../datasource'; +import { DataSourceType, GenericDataSource } from '../datasource_services'; +import { DataSourceGroup, DataSourceSelectableProps } from './types'; + +type DataSourceTypeKey = 'DEFAULT_INDEX_PATTERNS' | 's3glue' | 'spark'; + +// Mapping between datasource type and its display name. +const DATASOURCE_TYPE_DISPLAY_NAME_MAP: Record = { + DEFAULT_INDEX_PATTERNS: 'Index patterns', + s3glue: 'Amazon S3', + spark: 'Spark', +}; + +type DataSetType = ISourceDataSet['data_sets'][number]; + +// Get data sets for a given datasource and returns it along with the source. +const getDataSetWithSource = async (ds: GenericDataSource): Promise => { + const dataSet = await ds.getDataSet(); + return { + ds, + data_sets: dataSet, + }; +}; + +// Map through all data sources and get their respective data sets. +const getDataSets = (dataSources: GenericDataSource[]) => + dataSources.map((ds) => getDataSetWithSource(ds)); + +export const isIndexPatterns = (dataSet: DataSetType): dataSet is IndexPatternOption => { + if (typeof dataSet === 'string') return false; + + return !!(dataSet.title && dataSet.id); +}; + +// Get the option format for the combo box from the dataSource and dataSet. +export const getSourceOptions = (dataSource: DataSourceType, dataSet: DataSetType) => { + const optionContent = { + type: dataSource.getType(), + name: dataSource.getName(), + ds: dataSource, + }; + if (isIndexPatterns(dataSet)) { + return { + ...optionContent, + label: dataSet.title, + value: dataSet.id, + }; + } + return { + ...optionContent, + label: dataSource.getName(), + value: dataSource.getName(), + }; +}; + +// Convert data sets into a structured format suitable for selector rendering. +const getSourceList = (allDataSets: ISourceDataSet[]) => { + const finalList = [] as DataSourceGroup[]; + allDataSets.forEach((curDataSet) => { + const typeKey = curDataSet.ds.getType() as DataSourceTypeKey; + const groupName = DATASOURCE_TYPE_DISPLAY_NAME_MAP[typeKey] || 'Default Group'; + + const existingGroup = finalList.find((item) => item.label === groupName); + const mappedOptions = curDataSet.data_sets.map((dataSet) => + getSourceOptions(curDataSet.ds, dataSet) + ); + + // check if add new datasource group or add to existing one + if (existingGroup) { + // options deduplication + const existingOptionIds = new Set(existingGroup.options.map((opt) => opt.label)); + const nonDuplicateOptions = mappedOptions.filter((opt) => !existingOptionIds.has(opt.label)); + + // 'existingGroup' directly references an item in the finalList + // pushing options to 'existingGroup' updates the corresponding item in finalList + existingGroup.options.push(...nonDuplicateOptions); + } else { + finalList.push({ + label: groupName, + options: mappedOptions, + }); + } + }); + return finalList; +}; + +/** + * @experimental This component is experimental and might change in future releases. + */ +export const DataSourceSelectable = ({ + dataSources, // list of all available datasource connections. + dataSourceOptionList, // combo box renderable option list derived from dataSources + selectedSources, // current selected datasource in the form of [{ label: xxx, value: xxx }] + onDataSourceSelect, + setDataSourceOptionList, + onGetDataSetError, // onGetDataSetError, Callback for handling get data set errors. Ensure it's memoized. + singleSelection = { asPlainText: true }, + ...comboBoxProps +}: DataSourceSelectableProps) => { + // This effect gets data sets and prepares the datasource list for UI rendering. + useEffect(() => { + Promise.all(getDataSets(dataSources)) + .then((results) => { + setDataSourceOptionList(getSourceList(results)); + }) + .catch((e) => onGetDataSetError(e)); + }, [dataSources, setDataSourceOptionList, onGetDataSetError]); + + const handleSourceChange = useCallback( + (selectedOptions: any) => onDataSourceSelect(selectedOptions), + [onDataSourceSelect] + ); + + const memorizedDataSourceOptionList = useMemo(() => { + return dataSourceOptionList.map((dsGroup: DataSourceGroup) => { + return { + ...dsGroup, + options: [...dsGroup.options].sort((ds1, ds2) => { + return ds1.label.localeCompare(ds2.label, undefined, { sensitivity: 'base' }); + }), + }; + }); + }, [dataSourceOptionList]); + + return ( + + ); +}; diff --git a/src/plugins/data/public/data_sources/datasource_selector/index.tsx b/src/plugins/data/public/data_sources/datasource_selector/index.tsx new file mode 100644 index 000000000000..763c83069a6f --- /dev/null +++ b/src/plugins/data/public/data_sources/datasource_selector/index.tsx @@ -0,0 +1,7 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export { DataSourceSelectable } from './datasource_selectable'; +export { DataSourceSelectableProps, DataSourceGroup, DataSourceOption } from './types'; diff --git a/src/plugins/data/public/data_sources/datasource_selector/types.ts b/src/plugins/data/public/data_sources/datasource_selector/types.ts new file mode 100644 index 000000000000..0fa1bf21cb19 --- /dev/null +++ b/src/plugins/data/public/data_sources/datasource_selector/types.ts @@ -0,0 +1,33 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @experimental These interfaces are experimental and might change in future releases. + */ + +import { EuiComboBoxProps, EuiComboBoxSingleSelectionShape } from '@elastic/eui'; +import { GenericDataSource } from '../datasource_services'; + +export interface DataSourceGroup { + label: string; + options: DataSourceOption[]; +} + +export interface DataSourceOption { + label: string; + value: string; + type: string; + ds: GenericDataSource; +} + +export interface DataSourceSelectableProps extends Pick, 'fullWidth'> { + dataSources: GenericDataSource[]; + onDataSourceSelect: (dataSourceOption: DataSourceOption[]) => void; + singleSelection?: boolean | EuiComboBoxSingleSelectionShape; + onGetDataSetError: (error: Error) => void; + dataSourceOptionList: DataSourceGroup[]; + selectedSources: DataSourceOption[]; + setDataSourceOptionList: (dataSourceList: DataSourceGroup[]) => void; +} diff --git a/src/plugins/data/public/data_sources/datasource_services/datasource_service.test.ts b/src/plugins/data/public/data_sources/datasource_services/datasource_service.test.ts new file mode 100644 index 000000000000..2c8f393d7093 --- /dev/null +++ b/src/plugins/data/public/data_sources/datasource_services/datasource_service.test.ts @@ -0,0 +1,118 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { DataSource } from '../datasource'; +import { IndexPatternsService } from '../../index_patterns'; +import { DataSourceService } from '../datasource_services'; + +class MockDataSource extends DataSource { + private readonly indexPattern; + + constructor({ + name, + type, + metadata, + indexPattern, + }: { + name: string; + type: string; + metadata: any; + indexPattern: IndexPatternsService; + }) { + super(name, type, metadata); + this.indexPattern = indexPattern; + } + + async getDataSet(dataSetParams?: any) { + await this.indexPattern.ensureDefaultIndexPattern(); + return await this.indexPattern.getCache(); + } + + async testConnection(): Promise { + return true; + } + + async runQuery(queryParams: any) { + return undefined; + } +} + +const mockIndexPattern = {} as IndexPatternsService; + +const mockConfig1 = { + name: 'test_datasource1', + type: 'mock1', + metadata: null, + indexPattern: mockIndexPattern, +}; + +const mockConfig2 = { + name: 'test_datasource2', + type: 'mock1', + metadata: null, + indexPattern: mockIndexPattern, +}; + +describe('DataSourceService', () => { + beforeEach(() => { + // Reset the DataSourceService's singleton instance before each test for isolation + (DataSourceService as any).dataSourceService = undefined; + }); + + it('returns a singleton instance', () => { + const instance1 = DataSourceService.getInstance(); + const instance2 = DataSourceService.getInstance(); + expect(instance1).toBe(instance2); + }); + + it('registers a new data source correctly', async () => { + const service = DataSourceService.getInstance(); + const ds = new MockDataSource(mockConfig1); + const result = await service.registerDataSource(ds); + expect(result.success).toBe(true); + }); + + it('throws error when registering an already registered data source', async () => { + const service = DataSourceService.getInstance(); + const ds = new MockDataSource(mockConfig1); + await service.registerDataSource(ds); + await expect(service.registerDataSource(ds)).rejects.toThrow( + 'Unable to register datasource test_datasource1, error: datasource name exists.' + ); + }); + + it('registers multiple data sources correctly', () => { + const service = DataSourceService.getInstance(); + const ds1 = new MockDataSource(mockConfig1); + const ds2 = new MockDataSource(mockConfig2); + const results = service.registerMultipleDataSources([ds1, ds2]); + results.then((regResults) => { + expect(regResults).toHaveLength(2); + expect(regResults[0].success).toBe(true); + expect(regResults[1].success).toBe(true); + }); + }); + + it('retrieves registered data sources based on filters', () => { + const service = DataSourceService.getInstance(); + const ds1 = new MockDataSource(mockConfig1); + const ds2 = new MockDataSource(mockConfig2); + service.registerMultipleDataSources([ds1, ds2]); + const filter = { names: ['test_datasource1'] }; + const retrievedDataSources = service.getDataSources(filter); + expect(retrievedDataSources).toHaveProperty('test_datasource1'); + expect(retrievedDataSources).not.toHaveProperty('test_datasource2'); + }); + + it('returns all data sources if no filters provided', () => { + const service = DataSourceService.getInstance(); + const ds1 = new MockDataSource(mockConfig1); + const ds2 = new MockDataSource(mockConfig2); + service.registerMultipleDataSources([ds1, ds2]); + const retrievedDataSources = service.getDataSources(); + expect(retrievedDataSources).toHaveProperty('test_datasource1'); + expect(retrievedDataSources).toHaveProperty('test_datasource2'); + }); +}); diff --git a/src/plugins/data/public/data_sources/datasource_services/datasource_service.ts b/src/plugins/data/public/data_sources/datasource_services/datasource_service.ts new file mode 100644 index 000000000000..9cf674585366 --- /dev/null +++ b/src/plugins/data/public/data_sources/datasource_services/datasource_service.ts @@ -0,0 +1,88 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { BehaviorSubject } from 'rxjs'; +import { + DataSourceRegistrationError, + GenericDataSource, + IDataSourceFilter, + IDataSourceRegistrationResult, +} from './types'; + +export class DataSourceService { + private static dataSourceService: DataSourceService; + // A record to store all registered data sources, using the data source name as the key. + private dataSources: Record = {}; + private dataSourcesSubject: BehaviorSubject>; + + private constructor() { + this.dataSourcesSubject = new BehaviorSubject(this.dataSources); + } + + static getInstance(): DataSourceService { + if (!this.dataSourceService) { + this.dataSourceService = new DataSourceService(); + } + return this.dataSourceService; + } + + /** + * Register multiple data sources at once. + * + * @experimental This API is experimental and might change in future releases. + * @param datasources - An array of data sources to be registered. + * @returns An array of registration results, one for each data source. + */ + async registerMultipleDataSources( + datasources: GenericDataSource[] + ): Promise { + return Promise.all(datasources.map((ds) => this.registerDataSource(ds))); + } + + /** + * Register a single data source. + * Throws an error if a data source with the same name is already registered. + * + * @experimental This API is experimental and might change in future releases. + * @param ds - The data source to be registered. + * @returns A registration result indicating success or failure. + * @throws {DataSourceRegistrationError} Throws an error if a data source with the same name already exists. + */ + async registerDataSource(ds: GenericDataSource): Promise { + const dsName = ds.getName(); + if (dsName in this.dataSources) { + throw new DataSourceRegistrationError( + `Unable to register datasource ${dsName}, error: datasource name exists.` + ); + } else { + this.dataSources[dsName] = ds; + this.dataSourcesSubject.next(this.dataSources); + return { success: true, info: '' } as IDataSourceRegistrationResult; + } + } + + public get dataSources$() { + return this.dataSourcesSubject.asObservable(); + } + + /** + * Retrieve the registered data sources based on provided filters. + * If no filters are provided, all registered data sources are returned. + * @experimental This API is experimental and might change in future releases. + * @param filter - An optional object with filter criteria (e.g., names of data sources). + * @returns A record of filtered data sources. + */ + getDataSources(filter?: IDataSourceFilter): Record { + if (!filter || !Array.isArray(filter.names) || filter.names.length === 0) + return this.dataSources; + + return filter.names.reduce>((filteredDataSources, dsName) => { + if (dsName in this.dataSources) { + filteredDataSources[dsName] = this.dataSources[dsName]; + } + return filteredDataSources; + }, {} as Record); + } +} diff --git a/src/plugins/data/public/data_sources/datasource_services/index.ts b/src/plugins/data/public/data_sources/datasource_services/index.ts new file mode 100644 index 000000000000..14db278b47a5 --- /dev/null +++ b/src/plugins/data/public/data_sources/datasource_services/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export { DataSourceService } from './datasource_service'; +export { + IDataSourceFilter, + IDataSourceRegistrationResult, + DataSourceRegistrationError, + DataSourceType, + GenericDataSource, +} from './types'; diff --git a/src/plugins/data/public/data_sources/datasource_services/types.ts b/src/plugins/data/public/data_sources/datasource_services/types.ts new file mode 100644 index 000000000000..cb5bb31500b4 --- /dev/null +++ b/src/plugins/data/public/data_sources/datasource_services/types.ts @@ -0,0 +1,54 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @experimental These interfaces, types and classes are experimental and might change + * in future releases. + */ + +import { + DataSource, + DataSourceFactory, + IDataSetParams, + IDataSourceMetaData, + IDataSourceQueryParams, + IDataSourceQueryResult, + ISourceDataSet, +} from '../datasource'; +import { DataSourceService } from './datasource_service'; + +export interface IDataSourceFilter { + names: string[]; +} + +export interface IDataSourceRegistrationResult { + success: boolean; + info: string; +} + +export class DataSourceRegistrationError extends Error { + success: boolean; + info: string; + constructor(message: string) { + super(message); + this.success = false; + this.info = message; + } +} + +export interface DataSourceStart { + dataSourceService: DataSourceService; + dataSourceFactory: DataSourceFactory; +} + +export type DataSourceType = DataSource< + IDataSourceMetaData, + IDataSetParams, + ISourceDataSet, + IDataSourceQueryParams, + IDataSourceQueryResult +>; + +export type GenericDataSource = DataSource; diff --git a/src/plugins/data/public/data_sources/default_datasource/default_datasource.test.ts b/src/plugins/data/public/data_sources/default_datasource/default_datasource.test.ts new file mode 100644 index 000000000000..aedc6cd3853a --- /dev/null +++ b/src/plugins/data/public/data_sources/default_datasource/default_datasource.test.ts @@ -0,0 +1,55 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { IndexPatternsService } from '../../index_patterns'; +import { DefaultDslDataSource } from './default_datasource'; + +describe('DefaultDslDataSource', () => { + let indexPatternsMock: IndexPatternsService; + + beforeEach(() => { + indexPatternsMock = ({ + ensureDefaultIndexPattern: jest.fn(), + getCache: jest.fn(), + } as unknown) as IndexPatternsService; + }); + + it('should ensure default index pattern and get cache', async () => { + const dataSource = new DefaultDslDataSource({ + name: 'testName', + type: 'testType', + metadata: {}, + indexPatterns: indexPatternsMock, + }); + + await dataSource.getDataSet(); + + expect(indexPatternsMock.ensureDefaultIndexPattern).toHaveBeenCalledTimes(1); + expect(indexPatternsMock.getCache).toHaveBeenCalledTimes(1); + }); + + it('should throw an error', async () => { + const dataSource = new DefaultDslDataSource({ + name: 'testName', + type: 'testType', + metadata: {}, + indexPatterns: indexPatternsMock, + }); + + await expect(dataSource.testConnection()).resolves.toBe(true); + }); + + it('should return null', async () => { + const dataSource = new DefaultDslDataSource({ + name: 'testName', + type: 'testType', + metadata: {}, + indexPatterns: indexPatternsMock, + }); + + const result = await dataSource.runQuery({}); + expect(result).toBeUndefined(); + }); +}); diff --git a/src/plugins/data/public/data_sources/default_datasource/default_datasource.ts b/src/plugins/data/public/data_sources/default_datasource/default_datasource.ts new file mode 100644 index 000000000000..c3b5d2a4cf99 --- /dev/null +++ b/src/plugins/data/public/data_sources/default_datasource/default_datasource.ts @@ -0,0 +1,47 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { SavedObject } from '../../../../../core/public'; +import { IndexPatternSavedObjectAttrs } from '../../index_patterns/index_patterns'; +import { DataSource, DataSourceConfig, IndexPatternOption } from '../datasource'; + +export class DefaultDslDataSource extends DataSource< + any, + any, + Promise, + any, + any +> { + private readonly indexPatterns; + + constructor({ name, type, metadata, indexPatterns }: DataSourceConfig) { + super(name, type, metadata); + this.indexPatterns = indexPatterns; + } + + async getDataSet(dataSetParams?: any) { + await this.indexPatterns.ensureDefaultIndexPattern(); + const savedObjectLst = await this.indexPatterns.getCache(); + + if (!Array.isArray(savedObjectLst)) { + return undefined; + } + + return savedObjectLst.map((savedObject: SavedObject) => { + return { + id: savedObject.id, + title: savedObject.attributes.title, + }; + }); + } + + async testConnection(): Promise { + return true; + } + + async runQuery(queryParams: any) { + return undefined; + } +} diff --git a/src/plugins/data/public/data_sources/default_datasource/index.ts b/src/plugins/data/public/data_sources/default_datasource/index.ts new file mode 100644 index 000000000000..f612fa42f03f --- /dev/null +++ b/src/plugins/data/public/data_sources/default_datasource/index.ts @@ -0,0 +1,6 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export { DefaultDslDataSource } from './default_datasource'; diff --git a/src/plugins/data/public/data_sources/register_default_datasource.ts b/src/plugins/data/public/data_sources/register_default_datasource.ts new file mode 100644 index 000000000000..8dece27e82eb --- /dev/null +++ b/src/plugins/data/public/data_sources/register_default_datasource.ts @@ -0,0 +1,27 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { i18n } from '@osd/i18n'; +import { DataPublicPluginStart } from '../types'; +import { DefaultDslDataSource } from './default_datasource'; + +export const DEFAULT_DATASOURCE_TYPE = 'DEFAULT_INDEX_PATTERNS'; +export const DEFAULT_DATASOURCE_NAME = i18n.translate('data.datasource.type.openSearchDefault', { + defaultMessage: 'OpenSearch Default', +}); + +export const registerDefaultDatasource = (data: Omit) => { + // Datasources registrations for index patterns datasource + const { dataSourceService, dataSourceFactory } = data.dataSources; + dataSourceFactory.registerDataSourceType(DEFAULT_DATASOURCE_TYPE, DefaultDslDataSource); + dataSourceService.registerDataSource( + dataSourceFactory.getDataSourceInstance(DEFAULT_DATASOURCE_TYPE, { + name: DEFAULT_DATASOURCE_NAME, + type: DEFAULT_DATASOURCE_TYPE, + metadata: null, + indexPatterns: data.indexPatterns, + }) + ); +}; diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index 3f96955e22b6..3b559f9e6c63 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -496,3 +496,29 @@ export { // Export plugin after all other imports export { DataPublicPlugin as Plugin }; + +// Export datasources +export { + DataSource, + IDataSourceMetaData, + IDataSetParams, + IDataSourceQueryParams, + IDataSourceQueryResult, + ISourceDataSet, + ConnectionStatus, + DataSourceFactory, + DataSourceConfig, +} from './data_sources/datasource'; +export { + DataSourceRegistrationError, + DataSourceService, + DataSourceType, + IDataSourceFilter, + IDataSourceRegistrationResult, +} from './data_sources/datasource_services'; +export { + DataSourceSelectable, + DataSourceSelectableProps, + DataSourceGroup, + DataSourceOption, +} from './data_sources/datasource_selector'; diff --git a/src/plugins/data/public/plugin.ts b/src/plugins/data/public/plugin.ts index 5b7a262960b5..179b6c0a8c83 100644 --- a/src/plugins/data/public/plugin.ts +++ b/src/plugins/data/public/plugin.ts @@ -88,6 +88,9 @@ import { import { SavedObjectsClientPublicToCommon } from './index_patterns'; import { indexPatternLoad } from './index_patterns/expressions/load_index_pattern'; +import { DataSourceService } from './data_sources/datasource_services'; +import { DataSourceFactory } from './data_sources/datasource'; +import { registerDefaultDatasource } from './data_sources/register_default_datasource'; declare module '../../ui_actions/public' { export interface ActionContextMapping { @@ -212,6 +215,10 @@ export class DataPublicPlugin uiActions.getAction(ACTION_GLOBAL_APPLY_FILTER) ); + // Create or fetch the singleton instance + const dataSourceService = DataSourceService.getInstance(); + const dataSourceFactory = DataSourceFactory.getInstance(); + const dataServices = { actions: { createFiltersFromValueClickAction, @@ -222,8 +229,14 @@ export class DataPublicPlugin indexPatterns, query, search, + dataSources: { + dataSourceService, + dataSourceFactory, + }, }; + registerDefaultDatasource(dataServices); + const SearchBar = createSearchBar({ core, data: dataServices, diff --git a/src/plugins/data/public/search/search_interceptor.test.ts b/src/plugins/data/public/search/search_interceptor.test.ts index c19e92e3872e..3c75b74d2359 100644 --- a/src/plugins/data/public/search/search_interceptor.test.ts +++ b/src/plugins/data/public/search/search_interceptor.test.ts @@ -32,7 +32,11 @@ import { setImmediate } from 'timers'; import { CoreSetup } from '../../../../core/public'; import { coreMock } from '../../../../core/public/mocks'; -import { IOpenSearchSearchRequest } from '../../common/search'; +import { + IOpenSearchSearchRequest, + OPENSEARCH_SEARCH_STRATEGY, + OPENSEARCH_SEARCH_WITH_LONG_NUMERALS_STRATEGY, +} from '../../common'; import { SearchInterceptor } from './search_interceptor'; import { AbortError } from '../../common'; import { SearchTimeoutError, PainlessError } from './errors'; @@ -204,5 +208,82 @@ describe('SearchInterceptor', () => { }; response.subscribe({ error }); }); + + test('Should use the default strategy when no strategy or long-numerals support is requested', async () => { + const mockResponse: any = { result: 200 }; + mockCoreSetup.http.fetch.mockResolvedValueOnce(mockResponse); + const mockRequest: IOpenSearchSearchRequest = { + params: {}, + }; + const response = searchInterceptor.search(mockRequest); + + await response.toPromise(); + + expect(mockCoreSetup.http.fetch).toBeCalledWith( + expect.objectContaining({ + path: `/internal/search/${OPENSEARCH_SEARCH_STRATEGY}`, + }) + ); + }); + + test('Should use the correct strategy when long-numerals support with no specific strategy is requested', async () => { + const mockResponse: any = { result: 200 }; + mockCoreSetup.http.fetch.mockResolvedValueOnce(mockResponse); + const mockRequest: IOpenSearchSearchRequest = { + params: {}, + }; + const response = searchInterceptor.search(mockRequest, { + withLongNumeralsSupport: true, + }); + + await response.toPromise(); + + expect(mockCoreSetup.http.fetch).toBeCalledWith( + expect.objectContaining({ + path: `/internal/search/${OPENSEARCH_SEARCH_WITH_LONG_NUMERALS_STRATEGY}`, + }) + ); + }); + + test('Should use the requested strategy when no long-numerals support is requested', async () => { + const mockResponse: any = { result: 200 }; + mockCoreSetup.http.fetch.mockResolvedValueOnce(mockResponse); + const mockRequest: IOpenSearchSearchRequest = { + params: {}, + }; + + const strategy = 'unregistered-strategy'; + const response = searchInterceptor.search(mockRequest, { strategy }); + + await response.toPromise(); + + expect(mockCoreSetup.http.fetch).toBeCalledWith( + expect.objectContaining({ + path: `/internal/search/${strategy}`, + }) + ); + }); + + test('Should use the requested strategy even when long-numerals support is requested', async () => { + const mockResponse: any = { result: 200 }; + mockCoreSetup.http.fetch.mockResolvedValueOnce(mockResponse); + const mockRequest: IOpenSearchSearchRequest = { + params: {}, + }; + + const strategy = 'unregistered-strategy'; + const response = searchInterceptor.search(mockRequest, { + strategy, + withLongNumeralsSupport: true, + }); + + await response.toPromise(); + + expect(mockCoreSetup.http.fetch).toBeCalledWith( + expect.objectContaining({ + path: `/internal/search/${strategy}`, + }) + ); + }); }); }); diff --git a/src/plugins/data/public/search/search_interceptor.ts b/src/plugins/data/public/search/search_interceptor.ts index 1c2fdb9c371c..153ac80a249c 100644 --- a/src/plugins/data/public/search/search_interceptor.ts +++ b/src/plugins/data/public/search/search_interceptor.ts @@ -32,6 +32,7 @@ import { get, trimEnd, debounce } from 'lodash'; import { BehaviorSubject, throwError, timer, defer, from, Observable, NEVER } from 'rxjs'; import { catchError, finalize } from 'rxjs/operators'; import { CoreStart, CoreSetup, ToastsSetup } from 'opensearch-dashboards/public'; +import { stringify } from '@osd/std'; import { getCombinedSignal, AbortError, @@ -39,6 +40,7 @@ import { IOpenSearchDashboardsSearchResponse, ISearchOptions, OPENSEARCH_SEARCH_STRATEGY, + OPENSEARCH_SEARCH_WITH_LONG_NUMERALS_STRATEGY, } from '../../common'; import { SearchUsageCollector } from './collectors'; import { SearchTimeoutError, PainlessError, isPainlessError } from './errors'; @@ -113,20 +115,40 @@ export class SearchInterceptor { protected runSearch( request: IOpenSearchDashboardsSearchRequest, signal: AbortSignal, - strategy?: string + strategy?: string, + withLongNumeralsSupport?: boolean ): Observable { const { id, ...searchRequest } = request; const path = trimEnd( - `/internal/search/${strategy || OPENSEARCH_SEARCH_STRATEGY}/${id || ''}`, + `/internal/search/${ + strategy || + (withLongNumeralsSupport + ? OPENSEARCH_SEARCH_WITH_LONG_NUMERALS_STRATEGY + : OPENSEARCH_SEARCH_STRATEGY) + }/${id || ''}`, '/' ); - const body = JSON.stringify(searchRequest); + /* When long-numerals support is not explicitly requested, it indicates that the response handler + * is not capable of handling BigInts. Ideally, because this is the outward request, that wouldn't + * matter and `body = stringify(searchRequest)` would be the best option. However, to maintain the + * existing behavior, we use @osd/std/json.stringify only when long-numerals support is explicitly + * requested. Otherwise, JSON.stringify is used with imprecise numbers for BigInts. + * + * ToDo: With OSD v3, change to `body = stringify(searchRequest)`. + */ + const body = withLongNumeralsSupport + ? stringify(searchRequest) + : JSON.stringify(searchRequest, (key: string, value: any) => + typeof value === 'bigint' ? Number(value) : value + ); + return from( this.deps.http.fetch({ method: 'POST', path, body, signal, + withLongNumeralsSupport, }) ); } @@ -214,7 +236,12 @@ export class SearchInterceptor { }); this.pendingCount$.next(this.pendingCount$.getValue() + 1); - return this.runSearch(request, combinedSignal, options?.strategy).pipe( + return this.runSearch( + request, + combinedSignal, + options?.strategy, + options?.withLongNumeralsSupport + ).pipe( catchError((e: any) => { return throwError( this.handleSearchError(e, request, timeoutSignal, options?.abortSignal) diff --git a/src/plugins/data/public/types.ts b/src/plugins/data/public/types.ts index bd1499879134..5870ea7def8e 100644 --- a/src/plugins/data/public/types.ts +++ b/src/plugins/data/public/types.ts @@ -41,6 +41,7 @@ import { QuerySetup, QueryStart } from './query'; import { IndexPatternsContract } from './index_patterns'; import { IndexPatternSelectProps, StatefulSearchBarProps } from './ui'; import { UsageCollectionSetup } from '../../usage_collection/public'; +import { DataSourceStart } from './data_sources/datasource_services/types'; export interface DataPublicPluginEnhancements { search: SearchEnhancements; @@ -125,6 +126,11 @@ export interface DataPublicPluginStart { * {@link DataPublicPluginStartUi} */ ui: DataPublicPluginStartUi; + /** + * multiple datasources + * {@link DataSourceStart} + */ + dataSources: DataSourceStart; } export interface IDataPluginServices extends Partial { diff --git a/src/plugins/data/public/ui/filter_bar/_global_filter_item.scss b/src/plugins/data/public/ui/filter_bar/_global_filter_item.scss index 19c55a2d259c..25cdce24aa8e 100644 --- a/src/plugins/data/public/ui/filter_bar/_global_filter_item.scss +++ b/src/plugins/data/public/ui/filter_bar/_global_filter_item.scss @@ -6,8 +6,8 @@ line-height: $euiSize; border: none; color: $euiTextColor; - padding-top: $euiSizeM / 2; - padding-bottom: $euiSizeM / 2; + padding-top: calc($euiSizeM / 2); + padding-bottom: calc($euiSizeM / 2); white-space: normal; /* 1 */ .euiBadge__childButton { @@ -67,8 +67,8 @@ left: 0; width: $euiSizeXS; background-color: $osdGlobalFilterItemBorderColor; - border-top-left-radius: $euiBorderRadius / 2; - border-bottom-left-radius: $euiBorderRadius / 2; + border-top-left-radius: calc($euiBorderRadius / 2); + border-bottom-left-radius: calc($euiBorderRadius / 2); } } diff --git a/src/plugins/data/public/ui/filter_bar/filter_bar.tsx b/src/plugins/data/public/ui/filter_bar/filter_bar.tsx index 94f9c6590b91..c44ba583c3b4 100644 --- a/src/plugins/data/public/ui/filter_bar/filter_bar.tsx +++ b/src/plugins/data/public/ui/filter_bar/filter_bar.tsx @@ -38,6 +38,7 @@ import { import { FormattedMessage, InjectedIntl, injectI18n } from '@osd/i18n/react'; import classNames from 'classnames'; import React, { useState } from 'react'; +import { stringify } from '@osd/std'; import { FilterEditor } from './filter_editor'; import { FilterItem } from './filter_item'; @@ -143,7 +144,7 @@ function FilterBarUI(props: Props) { indexPatterns={props.indexPatterns} onSubmit={onAdd} onCancel={() => setIsAddFilterPopoverOpen(false)} - key={JSON.stringify(newFilter)} + key={stringify(newFilter)} />

diff --git a/src/plugins/data/public/ui/filter_bar/filter_editor/index.tsx b/src/plugins/data/public/ui/filter_bar/filter_editor/index.tsx index 7a4bb433bd10..b73eeb84df35 100644 --- a/src/plugins/data/public/ui/filter_bar/filter_editor/index.tsx +++ b/src/plugins/data/public/ui/filter_bar/filter_editor/index.tsx @@ -47,6 +47,7 @@ import { i18n } from '@osd/i18n'; import { FormattedMessage, InjectedIntl, injectI18n } from '@osd/i18n/react'; import { get } from 'lodash'; import React, { Component } from 'react'; +import { parse, stringify } from '@osd/std'; import { GenericComboBox, GenericComboBoxProps } from './generic_combo_box'; import { getFieldFromFilter, @@ -99,7 +100,7 @@ class FilterEditorUI extends Component { params: getFilterParams(props.filter), useCustomLabel: props.filter.meta.alias !== null, customLabel: props.filter.meta.alias, - queryDsl: JSON.stringify(cleanFilter(props.filter), null, 2), + queryDsl: stringify(cleanFilter(props.filter), null, 2), isCustomEditorOpen: this.isUnknownFilterType(), }; } @@ -430,7 +431,7 @@ class FilterEditorUI extends Component { if (isCustomEditorOpen) { try { - return Boolean(JSON.parse(queryDsl)); + return Boolean(parse(queryDsl)); } catch (e) { return false; } @@ -501,7 +502,7 @@ class FilterEditorUI extends Component { if (isCustomEditorOpen) { const { index, disabled, negate } = this.props.filter.meta; const newIndex = index || this.props.indexPatterns[0].id!; - const body = JSON.parse(queryDsl); + const body = parse(queryDsl); const filter = buildCustomFilter(newIndex, body, disabled, negate, alias, $state.store); this.props.onSubmit(filter); } else if (indexPattern && field && operator) { diff --git a/src/plugins/data/public/ui/filter_bar/filter_editor/lib/filter_label.tsx b/src/plugins/data/public/ui/filter_bar/filter_editor/lib/filter_label.tsx index 3861c8dbce4a..529053ffd042 100644 --- a/src/plugins/data/public/ui/filter_bar/filter_editor/lib/filter_label.tsx +++ b/src/plugins/data/public/ui/filter_bar/filter_editor/lib/filter_label.tsx @@ -31,6 +31,7 @@ import React, { Fragment } from 'react'; import { EuiTextColor } from '@elastic/eui'; import { i18n } from '@osd/i18n'; +import { stringify } from '@osd/std'; import { existsOperator, isOneOfOperator } from './filter_operators'; import { Filter, FILTERS } from '../../../../../common'; import type { FilterLabelStatus } from '../../filter_item'; @@ -119,7 +120,7 @@ export default function FilterLabel({ filter, valueLabel, filterLabelStatus }: F return ( {prefix} - {getValue(`${JSON.stringify(filter.query) || filter.meta.value}`)} + {getValue(`${stringify(filter.query) || filter.meta.value}`)} ); } diff --git a/src/plugins/data/public/ui/filter_bar/filter_editor/value_input_type.tsx b/src/plugins/data/public/ui/filter_bar/filter_editor/value_input_type.tsx index 360eecc91674..1bd1a653e027 100644 --- a/src/plugins/data/public/ui/filter_bar/filter_editor/value_input_type.tsx +++ b/src/plugins/data/public/ui/filter_bar/filter_editor/value_input_type.tsx @@ -68,7 +68,13 @@ class ValueInputTypeUI extends Component { { it('Should display the suggestion and use the provided ariaId', () => { const component = shallow( @@ -135,4 +143,37 @@ describe('SuggestionComponent', () => { component.simulate('mouseenter'); expect(mockHandler).toHaveBeenCalledTimes(1); }); + + it('Should return null for empty suggestion text', () => { + const component = shallow( + + ); + + expect(component.isEmptyRender()).toBeTruthy(); + }); + + it('Should return null for suggestion text with only whitespace', () => { + const whitespaceSuggestion = { ...mockEmptySuggestion, text: ' ' }; + const component = shallow( + + ); + + expect(component.isEmptyRender()).toBeTruthy(); + }); }); diff --git a/src/plugins/data/public/ui/typeahead/suggestion_component.tsx b/src/plugins/data/public/ui/typeahead/suggestion_component.tsx index 7d97f5b58283..67243df6415a 100644 --- a/src/plugins/data/public/ui/typeahead/suggestion_component.tsx +++ b/src/plugins/data/public/ui/typeahead/suggestion_component.tsx @@ -61,6 +61,13 @@ interface Props { } export function SuggestionComponent(props: Props) { + // Removing empty suggestions from the history is for maintaining a clean user experience. + // Empty suggestions, which typically result from inadvertent keystrokes or incomplete queries, + // do not provide value to the user. + if (!props.suggestion.text.trim()) { + return null; + } + return ( // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/interactive-supports-focus
{ + return await core.security.readonlyService().hideForReadonly(request, capabilites, { + indexPatterns: { + save: false, + }, + }); + }); registerRoutes(core.http); } diff --git a/src/plugins/data/server/search/opensearch_search/decide_client.ts b/src/plugins/data/server/search/opensearch_search/decide_client.ts index 5f38b82ffb4b..2ff2339add44 100644 --- a/src/plugins/data/server/search/opensearch_search/decide_client.ts +++ b/src/plugins/data/server/search/opensearch_search/decide_client.ts @@ -8,12 +8,15 @@ import { IOpenSearchSearchRequest } from '..'; export const decideClient = async ( context: RequestHandlerContext, - request: IOpenSearchSearchRequest + request: IOpenSearchSearchRequest, + withLongNumeralsSupport: boolean = false ): Promise => { // if data source feature is disabled, return default opensearch client of current user const client = request.dataSourceId && context.dataSource ? await context.dataSource.opensearch.getClient(request.dataSourceId) + : withLongNumeralsSupport + ? context.core.opensearch.client.asCurrentUserWithLongNumeralsSupport : context.core.opensearch.client.asCurrentUser; return client; }; diff --git a/src/plugins/data/server/search/opensearch_search/opensearch_search_strategy.test.ts b/src/plugins/data/server/search/opensearch_search/opensearch_search_strategy.test.ts index fe95e3d7d4eb..39c367a04a41 100644 --- a/src/plugins/data/server/search/opensearch_search/opensearch_search_strategy.test.ts +++ b/src/plugins/data/server/search/opensearch_search/opensearch_search_strategy.test.ts @@ -31,6 +31,8 @@ import { RequestHandlerContext } from '../../../../../core/server'; import { pluginInitializerContextConfigMock } from '../../../../../core/server/mocks'; import { opensearchSearchStrategyProvider } from './opensearch_search_strategy'; +import { DataSourceError } from '../../../../data_source/server/lib/error'; +import { DataSourcePluginSetup } from '../../../../data_source/server'; describe('OpenSearch search strategy', () => { const mockLogger: any = { diff --git a/src/plugins/data/server/search/opensearch_search/opensearch_search_strategy.ts b/src/plugins/data/server/search/opensearch_search/opensearch_search_strategy.ts index ba50740e6baf..5eb290517792 100644 --- a/src/plugins/data/server/search/opensearch_search/opensearch_search_strategy.ts +++ b/src/plugins/data/server/search/opensearch_search/opensearch_search_strategy.ts @@ -49,11 +49,11 @@ export const opensearchSearchStrategyProvider = ( config$: Observable, logger: Logger, usage?: SearchUsage, - dataSource?: DataSourcePluginSetup + dataSource?: DataSourcePluginSetup, + withLongNumeralsSupport?: boolean ): ISearchStrategy => { return { search: async (context, request, options) => { - logger.debug(`search ${request.params?.index}`); const config = await config$.pipe(first()).toPromise(); const uiSettingsClient = await context.core.uiSettings.client; @@ -73,7 +73,7 @@ export const opensearchSearchStrategyProvider = ( }); try { - const client = await decideClient(context, request); + const client = await decideClient(context, request, withLongNumeralsSupport); const promise = shimAbortSignal(client.search(params), options?.abortSignal); const { body: rawResponse } = (await promise) as ApiResponse>; @@ -87,6 +87,7 @@ export const opensearchSearchStrategyProvider = ( isRunning: false, rawResponse, ...getTotalLoaded(rawResponse._shards), + withLongNumeralsSupport, }; } catch (e) { if (usage) usage.trackError(); diff --git a/src/plugins/data/server/search/routes/search.ts b/src/plugins/data/server/search/routes/search.ts index 1fa4c00e2463..d569de539f39 100644 --- a/src/plugins/data/server/search/routes/search.ts +++ b/src/plugins/data/server/search/routes/search.ts @@ -60,7 +60,7 @@ export function registerSearchRoute( const [, , selfStart] = await getStartServices(); try { - const response = await selfStart.search.search( + const { withLongNumeralsSupport, ...response } = await selfStart.search.search( context, { ...searchRequest, id }, { @@ -76,6 +76,7 @@ export function registerSearchRoute( rawResponse: shimHitsTotal(response.rawResponse), }, }, + withLongNumeralsSupport, }); } catch (err) { return res.customError({ diff --git a/src/plugins/data/server/search/search_service.ts b/src/plugins/data/server/search/search_service.ts index 6620b88a0fe3..feb1a3157794 100644 --- a/src/plugins/data/server/search/search_service.ts +++ b/src/plugins/data/server/search/search_service.ts @@ -65,6 +65,7 @@ import { SearchSourceDependencies, SearchSourceService, searchSourceRequiredUiSettings, + OPENSEARCH_SEARCH_WITH_LONG_NUMERALS_STRATEGY, } from '../../common/search'; import { getShardDelayBucketAgg, @@ -133,6 +134,17 @@ export class SearchService implements Plugin { ) ); + this.registerSearchStrategy( + OPENSEARCH_SEARCH_WITH_LONG_NUMERALS_STRATEGY, + opensearchSearchStrategyProvider( + this.initializerContext.config.legacy.globalConfig$, + this.logger, + usage, + dataSource, + true + ) + ); + core.savedObjects.registerType(searchTelemetry); if (usageCollection) { registerUsageCollector(usageCollection, this.initializerContext); @@ -242,7 +254,6 @@ export class SearchService implements Plugin { name: string, strategy: ISearchStrategy ) => { - this.logger.debug(`Register strategy ${name}`); this.searchStrategies[name] = strategy; }; @@ -265,7 +276,6 @@ export class SearchService implements Plugin { >( name: string ): ISearchStrategy => { - this.logger.debug(`Get strategy ${name}`); const strategy = this.searchStrategies[name]; if (!strategy) { throw new Error(`Search strategy ${name} not found`); diff --git a/src/plugins/data_explorer/README.md b/src/plugins/data_explorer/README.md index 8ea14c17d428..0e8377d73add 100755 --- a/src/plugins/data_explorer/README.md +++ b/src/plugins/data_explorer/README.md @@ -1,11 +1,76 @@ -# dataExplorer +# Data Explorer -A OpenSearch Dashboards plugin +## Overview ---- +Data Explorer is an integral enhancement of the OpenSearch Dashboards that seeks to consolidate various data exploration facets into a unified platform. Built to provide an efficient data exploration experience, Data Explorer merges capabilities of different applications like Discover, Visbuilder, and Event Analytics into a singular platform. + +## Key Features + +1. **Unified Data Exploration**: Data Explorer acts as a consolidated platform for all data exploration tasks, aiming to provide users with a seamless and efficient environment. +2. **Extensibility**: Provides an architecture that allows existing exploration apps to migrate with minimal changes. +3. **Shared Utilities**: Offers components and utilities that can be shared across different views. + +## Architecture and Integration + +Data Explorer, at its core, is a shell for data exploration views. Here's a breakdown of the architecture and how it manages responsibilities: + +### Data Explorer Responsibilities: + +1. **Data Source**: Acts as the central point for the data source being explored. +2. **View Registry**: Allows apps to register themselves as views and manages their display based on user selection. +3. **State Management**: Provides shared state management and hooks for underlying apps to register their state reducers. +4. **Shared Utilities**: Contains components and utilities that can be reused by various views. + +### View Responsibilities: + +1. **Metadata Storage**: Handles the logic for storing metadata and its retrieval (Saved objects). +2. **Data Fetching**: Manages the logic for fetching data from the data source. +3. **View specific state management**: Handles view-specific state management and hooks into Data Explorer's state management. +4. **Nav Options & Search/Query Bar**: Manages the navigation options, time filter, and search bar. +5. **View Specific Logic**: Contains view-specific rendering and application logic. +6. **Embeddables**: Responsible for registering their embeddables. -## Development +### Migrating Existing Applications: + +Existing applications can migrate their data exploration views to Data Explorer. Such migrations involve: + +1. Registering the application as a view. +2. Using Data Explorer's state management and data source. +3. Modifying routes to utilize Data Explorer's routes. +4. Adapting the UI to match Data Explorer's panel and canvas components. + +#### Routing: + +Existing routes for each view are expected to redirect to new routes prefixed with `/data_explorer`. E.g., existing Discover route will redirect to `/data_explorer/discover`. + +### View Registry: + +For an application to be registered as a view within Data Explorer, it needs to adhere to the following data model: + +```ts +interface ViewDefinition { + readonly id: string; + readonly title: string; + readonly ui?: { + defaults: DefaultViewState | (() => DefaultViewState) | (() => Promise); + slice: Slice; + }; + readonly Canvas: LazyExoticComponent<(props: ViewProps) => React.ReactElement>; + readonly Panel: LazyExoticComponent<(props: ViewProps) => React.ReactElement>; + readonly Context: LazyExoticComponent< + (props: React.PropsWithChildren) => React.ReactElement + >; + readonly defaultPath: string; + readonly appExtentions: { + savedObject: { + docTypes: [string]; + toListItem: (obj: { id: string; title: string }) => ViewListItem; + }; + }; + readonly shouldShow?: (state: any) => boolean; +} +``` + +--- -See the [OpenSearch Dashboards contributing -guide](https://github.com/opensearch-project/OpenSearch-Dashboards/blob/main/CONTRIBUTING.md) for instructions -setting up your development environment. +Original proposal: https://github.com/opensearch-project/OpenSearch-Dashboards/issues/4165 diff --git a/src/plugins/data_explorer/public/components/app_container.scss b/src/plugins/data_explorer/public/components/app_container.scss index d289e7d4be3e..7bd5ed6f69f6 100644 --- a/src/plugins/data_explorer/public/components/app_container.scss +++ b/src/plugins/data_explorer/public/components/app_container.scss @@ -1,8 +1,12 @@ $osdHeaderOffset: $euiHeaderHeightCompensation; .deSidebar { - max-width: 462px; - min-width: 400px; + height: 100%; + + @include ouiBreakpoint("xs", "s", "m") { + max-width: initial; + width: 100%; + } } .deLayout { diff --git a/src/plugins/data_explorer/public/components/app_container.tsx b/src/plugins/data_explorer/public/components/app_container.tsx index 8f37e9c1230f..529829140057 100644 --- a/src/plugins/data_explorer/public/components/app_container.tsx +++ b/src/plugins/data_explorer/public/components/app_container.tsx @@ -3,8 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -import React from 'react'; -import { EuiPage, EuiPageBody } from '@elastic/eui'; +import React, { memo } from 'react'; +import { EuiPage, EuiPageBody, EuiResizableContainer, useIsWithinBreakpoints } from '@elastic/eui'; import { Suspense } from 'react'; import { AppMountParameters } from '../../../../core/public'; import { Sidebar } from './sidebar'; @@ -13,6 +13,7 @@ import { View } from '../services/view_service/view'; import './app_container.scss'; export const AppContainer = ({ view, params }: { view?: View; params: AppMountParameters }) => { + const isMobile = useIsWithinBreakpoints(['xs', 's', 'm']); // TODO: Make this more robust. if (!view) { return ; @@ -20,18 +21,38 @@ export const AppContainer = ({ view, params }: { view?: View; params: AppMountPa const { Canvas, Panel, Context } = view; + const MemoizedPanel = memo(Panel); + const MemoizedCanvas = memo(Canvas); + // Render the application DOM. return ( {/* TODO: improve fallback state */} Loading...
}> - - - - - - + + {(EuiResizablePanel, EuiResizableButton) => ( + <> + + + + + + + + + + + + + + )} + diff --git a/src/plugins/data_explorer/public/components/no_view.tsx b/src/plugins/data_explorer/public/components/no_view.tsx index a341e9d0564e..20bdf83f1de6 100644 --- a/src/plugins/data_explorer/public/components/no_view.tsx +++ b/src/plugins/data_explorer/public/components/no_view.tsx @@ -11,7 +11,7 @@ export const NoView = () => { return ( { const { indexPattern: indexPatternId } = useTypedSelector((state) => state.metadata); const dispatch = useTypedDispatch(); - const [options, setOptions] = useState>>([]); - const [selectedOption, setSelectedOption] = useState>(); - const { view, viewRegistry } = useView(); - const views = viewRegistry.all(); - const viewOptions = useMemo( - () => - views.map(({ id, title }) => ({ - value: id, - text: title, - })), - [views] - ); + const [selectedSources, setSelectedSources] = useState([]); + const [dataSourceOptionList, setDataSourceOptionList] = useState([]); + const [activeDataSources, setActiveDataSources] = useState([]); const { services: { - data: { indexPatterns }, + data: { indexPatterns, dataSources }, notifications: { toasts }, + application, }, } = useOpenSearchDashboards(); useEffect(() => { let isMounted = true; - const fetchIndexPatterns = async () => { - await indexPatterns.ensureDefaultIndexPattern(); - const cache = await indexPatterns.getCache(); - const currentOptions = (cache || []).map((indexPattern) => ({ - label: indexPattern.attributes.title, - value: indexPattern.id, - })); - if (isMounted) { - setOptions(currentOptions); + const subscription = dataSources.dataSourceService.dataSources$.subscribe( + (currentDataSources) => { + if (isMounted) { + setActiveDataSources(Object.values(currentDataSources)); + } } - }; - fetchIndexPatterns(); + ); return () => { + subscription.unsubscribe(); isMounted = false; }; - }, [indexPatterns]); + }, [indexPatterns, dataSources]); + + const getMatchedOption = (dataSourceList: DataSourceGroup[], ipId: string) => { + for (const dsGroup of dataSourceList) { + const matchedOption = dsGroup.options.find((item) => item.value === ipId); + if (matchedOption !== undefined) return matchedOption; + } + return undefined; + }; - // Set option to the current index pattern useEffect(() => { if (indexPatternId) { - const option = options.find((o) => o.value === indexPatternId); - setSelectedOption(option); + const option = getMatchedOption(dataSourceOptionList, indexPatternId); + setSelectedSources(option ? [option] : []); } - }, [indexPatternId, options]); + }, [indexPatternId, activeDataSources, dataSourceOptionList]); + + const handleSourceSelection = useCallback( + (selectedDataSources: DataSourceOption[]) => { + if (selectedDataSources.length === 0) { + setSelectedSources(selectedDataSources); + return; + } + // Temporary redirection solution for 2.11, where clicking non-index-pattern datasource + // will redirect user to Observability event explorer + if (selectedDataSources[0]?.ds?.getType() !== 'DEFAULT_INDEX_PATTERNS') { + return application.navigateToUrl( + `../observability-logs#/explorer?datasourceName=${selectedDataSources[0].label}&datasourceType=${selectedDataSources[0].type}` + ); + } + setSelectedSources(selectedDataSources); + dispatch(setIndexPattern(selectedDataSources[0].value)); + }, + [application, dispatch] + ); + + const handleGetDataSetError = useCallback( + () => (error: Error) => { + toasts.addError(error, { + title: + i18n.translate('dataExplorer.sidebar.failedToGetDataSetErrorDescription', { + defaultMessage: 'Failed to get data set: ', + }) + (error.message || error.name), + }); + }, + [toasts] + ); return ( - - - + + { - // TODO: There are many issues with this approach, but it's a start - // 1. Combo box can delete a selected index pattern. This should not be possible - // 2. Combo box is severely truncated. This should be fixed in the EUI component - // 3. The onchange can fire with a option that is not valid. discuss where to handle this. - // 4. value is optional. If the combobox needs to act as a slecet, this should be required. - const { value } = selected[0] || {}; - - if (!value) { - toasts.addWarning({ - id: 'index-pattern-not-found', - title: i18n.translate('dataExplorer.indexPatternError', { - defaultMessage: 'Index pattern not found', - }), - }); - return; - } - - dispatch(setIndexPattern(value)); - }} /> - {/* Hidden for the 2.10 release of Data Explorer. Uncomment when Data explorer is released */} - {/* - { - dispatch(setView(e.target.value)); - }} - fullWidth - /> */} - + {children} diff --git a/src/plugins/data_explorer/public/index.scss b/src/plugins/data_explorer/public/index.scss deleted file mode 100644 index 8389e31b426a..000000000000 --- a/src/plugins/data_explorer/public/index.scss +++ /dev/null @@ -1,9 +0,0 @@ -$osdHeaderOffset: $euiHeaderHeightCompensation; - -.dePageTemplate { - height: calc(100vh - #{$osdHeaderOffset}); -} - -.headerIsExpanded .dePageTemplate { - height: calc(100vh - #{$osdHeaderOffset * 2}); -} diff --git a/src/plugins/data_explorer/public/index.ts b/src/plugins/data_explorer/public/index.ts index 635a0ec285db..f8adda434ced 100644 --- a/src/plugins/data_explorer/public/index.ts +++ b/src/plugins/data_explorer/public/index.ts @@ -3,8 +3,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -import './index.scss'; - import { DataExplorerPlugin } from './plugin'; // This exports static code and TypeScript types, @@ -14,4 +12,10 @@ export function plugin() { } export { DataExplorerPluginSetup, DataExplorerPluginStart, DataExplorerServices } from './types'; export { ViewProps, ViewDefinition, DefaultViewState } from './services/view_service'; -export { RootState, useTypedSelector, useTypedDispatch } from './utils/state_management'; +export { + RootState, + Store, + useTypedSelector, + useTypedDispatch, + setIndexPattern, +} from './utils/state_management'; diff --git a/src/plugins/data_explorer/public/utils/state_management/store.ts b/src/plugins/data_explorer/public/utils/state_management/store.ts index cd967d25fc20..daf0b3d7e369 100644 --- a/src/plugins/data_explorer/public/utils/state_management/store.ts +++ b/src/plugins/data_explorer/public/utils/state_management/store.ts @@ -9,6 +9,13 @@ import { reducer as metadataReducer } from './metadata_slice'; import { loadReduxState, persistReduxState } from './redux_persistence'; import { DataExplorerServices } from '../../types'; +const HYDRATE = 'HYDRATE'; + +export const hydrate = (newState: RootState) => ({ + type: HYDRATE, + payload: newState, +}); + const commonReducers = { metadata: metadataReducer, }; @@ -22,9 +29,20 @@ let dynamicReducers: { const rootReducer = combineReducers(dynamicReducers); +const createRootReducer = (): Reducer => { + const combinedReducer = combineReducers(dynamicReducers); + + return (state: RootState | undefined, action: any): RootState => { + if (action.type === HYDRATE) { + return action.payload; + } + return combinedReducer(state, action); + }; +}; + export const configurePreloadedStore = (preloadedState: PreloadedState) => { // After registering the slices the root reducer needs to be updated - const updatedRootReducer = combineReducers(dynamicReducers); + const updatedRootReducer = createRootReducer(); return configureStore({ reducer: updatedRootReducer, @@ -62,6 +80,18 @@ export const getPreloadedStore = async (services: DataExplorerServices) => { // the store subscriber will automatically detect changes and call handleChange function const unsubscribe = store.subscribe(handleChange); + // This is necessary because browser navigation updates URL state that isnt reflected in the redux state + services.scopedHistory.listen(async (location, action) => { + const urlState = await loadReduxState(services); + const currentState = store.getState(); + + // If the url state is different from the current state, then we need to update the store + // the state should have a view property if it was loaded from the url + if (action === 'POP' && urlState.metadata?.view && !isEqual(urlState, currentState)) { + store.dispatch(hydrate(urlState as RootState)); + } + }); + const onUnsubscribe = () => { dynamicReducers = { ...commonReducers, diff --git a/src/plugins/data_source/common/data_sources/types.ts b/src/plugins/data_source/common/data_sources/types.ts index 8763c5306c15..d30e5ee710c8 100644 --- a/src/plugins/data_source/common/data_sources/types.ts +++ b/src/plugins/data_source/common/data_sources/types.ts @@ -11,11 +11,15 @@ export interface DataSourceAttributes extends SavedObjectAttributes { endpoint: string; auth: { type: AuthType; - credentials: UsernamePasswordTypedContent | SigV4Content | undefined; + credentials: UsernamePasswordTypedContent | SigV4Content | undefined | AuthTypeContent; }; lastUpdatedTime?: string; } +export interface AuthTypeContent { + [key: string]: string; +} + /** * Multiple datasource supports authenticating as IAM user, it doesn't support IAM role. * Because IAM role session requires temporary security credentials through assuming role, diff --git a/src/plugins/data_source/config.ts b/src/plugins/data_source/config.ts index 09ce35978921..d5412d32f0ff 100644 --- a/src/plugins/data_source/config.ts +++ b/src/plugins/data_source/config.ts @@ -13,6 +13,7 @@ const WRAPPING_KEY_SIZE: number = 32; export const configSchema = schema.object({ enabled: schema.boolean({ defaultValue: false }), + hideLocalCluster: schema.boolean({ defaultValue: false }), encryption: schema.object({ wrappingKeyName: schema.string({ minLength: KEY_NAME_MIN_LENGTH, diff --git a/src/plugins/data_source/public/index.ts b/src/plugins/data_source/public/index.ts index b69e07784ff4..56bcd4c8bb15 100644 --- a/src/plugins/data_source/public/index.ts +++ b/src/plugins/data_source/public/index.ts @@ -3,12 +3,13 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { PluginInitializerContext } from 'opensearch-dashboards/public'; import { DataSourcePlugin } from './plugin'; // This exports static code and TypeScript types, // as well as, OpenSearch Dashboards Platform `plugin()` initializer. -export function plugin() { - return new DataSourcePlugin(); +export function plugin(initializerContext: PluginInitializerContext) { + return new DataSourcePlugin(initializerContext); } export { DataSourcePluginSetup, DataSourcePluginStart } from './types'; diff --git a/src/plugins/data_source/public/plugin.ts b/src/plugins/data_source/public/plugin.ts index 672db78e15cb..65bee912255e 100644 --- a/src/plugins/data_source/public/plugin.ts +++ b/src/plugins/data_source/public/plugin.ts @@ -3,16 +3,34 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { CoreSetup, CoreStart, Plugin } from '../../../core/public'; +import { + CoreSetup, + CoreStart, + Plugin, + PluginInitializerContext, +} from 'opensearch-dashboards/public'; import { DataSourcePluginSetup, DataSourcePluginStart } from './types'; +import { DataSourcePluginConfigType } from '../config'; export class DataSourcePlugin implements Plugin { + constructor( + private readonly initializerContext: PluginInitializerContext + ) {} + public setup(core: CoreSetup): DataSourcePluginSetup { - return {}; + const config = this.initializerContext.config.get(); + return { + dataSourceEnabled: config.enabled, + hideLocalCluster: config.hideLocalCluster, + }; } public start(core: CoreStart): DataSourcePluginStart { - return {}; + const config = this.initializerContext.config.get(); + return { + dataSourceEnabled: config.enabled, + hideLocalCluster: config.hideLocalCluster, + }; } public stop() {} diff --git a/src/plugins/data_source/public/types.ts b/src/plugins/data_source/public/types.ts index 0c57dd1ea1bb..030ea5b77097 100644 --- a/src/plugins/data_source/public/types.ts +++ b/src/plugins/data_source/public/types.ts @@ -3,8 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface DataSourcePluginSetup {} +export interface DataSourcePluginSetup { + dataSourceEnabled: boolean; + hideLocalCluster: boolean; +} -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface DataSourcePluginStart {} +export interface DataSourcePluginStart { + dataSourceEnabled: boolean; + hideLocalCluster: boolean; +} diff --git a/src/plugins/data_source/server/auth_registry/authentication_methods_registry.test.ts b/src/plugins/data_source/server/auth_registry/authentication_methods_registry.test.ts new file mode 100644 index 000000000000..c7692acee782 --- /dev/null +++ b/src/plugins/data_source/server/auth_registry/authentication_methods_registry.test.ts @@ -0,0 +1,108 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { AuthenticationMethodRegistery } from './authentication_methods_registry'; +import { AuthenticationMethod } from '../../server/types'; +import { AuthType } from '../../common/data_sources'; + +const createAuthenticationMethod = ( + authMethod: Partial +): AuthenticationMethod => ({ + name: 'unknown', + authType: AuthType.NoAuth, + credentialProvider: jest.fn(), + ...authMethod, +}); + +describe('AuthenticationMethodRegistery', () => { + let registry: AuthenticationMethodRegistery; + + beforeEach(() => { + registry = new AuthenticationMethodRegistery(); + }); + + it('allows to register authentication method', () => { + registry.registerAuthenticationMethod(createAuthenticationMethod({ name: 'typeA' })); + registry.registerAuthenticationMethod(createAuthenticationMethod({ name: 'typeB' })); + registry.registerAuthenticationMethod(createAuthenticationMethod({ name: 'typeC' })); + + expect( + registry + .getAllAuthenticationMethods() + .map((type) => type.name) + .sort() + ).toEqual(['typeA', 'typeB', 'typeC']); + }); + + it('throws when trying to register the same authentication method twice', () => { + registry.registerAuthenticationMethod(createAuthenticationMethod({ name: 'typeA' })); + registry.registerAuthenticationMethod(createAuthenticationMethod({ name: 'typeB' })); + expect(() => { + registry.registerAuthenticationMethod(createAuthenticationMethod({ name: 'typeA' })); + }).toThrowErrorMatchingInlineSnapshot(`"Authentication method 'typeA' is already registered"`); + }); + + describe('#getAuthenticationMethod', () => { + it(`retrieve a type by it's name`, () => { + const typeA = createAuthenticationMethod({ name: 'typeA' }); + const typeB = createAuthenticationMethod({ name: 'typeB' }); + registry.registerAuthenticationMethod(typeA); + registry.registerAuthenticationMethod(typeB); + registry.registerAuthenticationMethod(createAuthenticationMethod({ name: 'typeC' })); + + expect(registry.getAuthenticationMethod('typeA')).toEqual(typeA); + expect(registry.getAuthenticationMethod('typeB')).toEqual(typeB); + expect(registry.getAuthenticationMethod('unknownType')).toBeUndefined(); + }); + + it('forbids to mutate the registered types', () => { + registry.registerAuthenticationMethod( + createAuthenticationMethod({ + name: 'typeA', + authType: AuthType.NoAuth, + }) + ); + + const typeA = registry.getAuthenticationMethod('typeA')!; + + expect(() => { + typeA.authType = AuthType.SigV4; + }).toThrow(); + expect(() => { + typeA.name = 'foo'; + }).toThrow(); + expect(() => { + typeA.credentialProvider = jest.fn(); + }).toThrow(); + }); + }); + + describe('#getAllTypes', () => { + it('returns all registered types', () => { + const typeA = createAuthenticationMethod({ name: 'typeA' }); + const typeB = createAuthenticationMethod({ name: 'typeB' }); + const typeC = createAuthenticationMethod({ name: 'typeC' }); + registry.registerAuthenticationMethod(typeA); + registry.registerAuthenticationMethod(typeB); + + const registered = registry.getAllAuthenticationMethods(); + expect(registered.length).toEqual(2); + expect(registered).toContainEqual(typeA); + expect(registered).toContainEqual(typeB); + expect(registered).not.toContainEqual(typeC); + }); + + it('does not mutate the registered types when altering the list', () => { + registry.registerAuthenticationMethod(createAuthenticationMethod({ name: 'typeA' })); + registry.registerAuthenticationMethod(createAuthenticationMethod({ name: 'typeB' })); + registry.registerAuthenticationMethod(createAuthenticationMethod({ name: 'typeC' })); + + const types = registry.getAllAuthenticationMethods(); + types.splice(0, 3); + + expect(registry.getAllAuthenticationMethods().length).toEqual(3); + }); + }); +}); diff --git a/src/plugins/data_source/server/auth_registry/authentication_methods_registry.ts b/src/plugins/data_source/server/auth_registry/authentication_methods_registry.ts new file mode 100644 index 000000000000..e2f39498e007 --- /dev/null +++ b/src/plugins/data_source/server/auth_registry/authentication_methods_registry.ts @@ -0,0 +1,34 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { deepFreeze } from '@osd/std'; +import { AuthenticationMethod } from '../../server/types'; + +export type IAuthenticationMethodRegistery = Omit< + AuthenticationMethodRegistery, + 'registerAuthenticationMethod' +>; + +export class AuthenticationMethodRegistery { + private readonly authMethods = new Map(); + /** + * Register a authMethods with function to return credentials inside the registry. + * Authentication Method can only be registered once. subsequent calls with the same method name will throw an error. + */ + public registerAuthenticationMethod(method: AuthenticationMethod) { + if (this.authMethods.has(method.name)) { + throw new Error(`Authentication method '${method.name}' is already registered`); + } + this.authMethods.set(method.name, deepFreeze(method) as AuthenticationMethod); + } + + public getAllAuthenticationMethods() { + return [...this.authMethods.values()]; + } + + public getAuthenticationMethod(name: string) { + return this.authMethods.get(name); + } +} diff --git a/src/plugins/data_source/server/auth_registry/index.ts b/src/plugins/data_source/server/auth_registry/index.ts new file mode 100644 index 000000000000..9352afd8b661 --- /dev/null +++ b/src/plugins/data_source/server/auth_registry/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export { + IAuthenticationMethodRegistery, + AuthenticationMethodRegistery, +} from './authentication_methods_registry'; diff --git a/src/plugins/data_source/server/client/client_config.ts b/src/plugins/data_source/server/client/client_config.ts index 5973e5a0813f..61ffde2be748 100644 --- a/src/plugins/data_source/server/client/client_config.ts +++ b/src/plugins/data_source/server/client/client_config.ts @@ -15,7 +15,8 @@ import { DataSourcePluginConfigType } from '../../config'; export function parseClientOptions( // TODO: will use client configs, that comes from a merge result of user config and default opensearch client config, config: DataSourcePluginConfigType, - endpoint: string + endpoint: string, + registeredSchema: any[] ): ClientOptions { const clientOptions: ClientOptions = { node: endpoint, @@ -23,6 +24,7 @@ export function parseClientOptions( requestCert: true, rejectUnauthorized: true, }, + plugins: registeredSchema, }; return clientOptions; diff --git a/src/plugins/data_source/server/client/configure_client.test.ts b/src/plugins/data_source/server/client/configure_client.test.ts index aa367f0a6f89..dc0fc2691a83 100644 --- a/src/plugins/data_source/server/client/configure_client.test.ts +++ b/src/plugins/data_source/server/client/configure_client.test.ts @@ -22,6 +22,7 @@ import { opensearchClientMock } from '../../../../core/server/opensearch/client/ import { cryptographyServiceSetupMock } from '../cryptography_service.mocks'; import { CryptographyServiceSetup } from '../cryptography_service'; import { DataSourceClientParams } from '../types'; +import { CustomApiSchemaRegistry } from '../schema_registry'; const DATA_SOURCE_ID = 'a54b76ec86771ee865a0f74a305dfff8'; @@ -38,12 +39,14 @@ describe('configureClient', () => { let dataSourceClientParams: DataSourceClientParams; let usernamePasswordAuthContent: UsernamePasswordTypedContent; let sigV4AuthContent: SigV4Content; + let customApiSchemaRegistry: CustomApiSchemaRegistry; beforeEach(() => { dsClient = opensearchClientMock.createInternalClient(); logger = loggingSystemMock.createLogger(); savedObjectsMock = savedObjectsClientMock.create(); cryptographyMock = cryptographyServiceSetupMock.create(); + customApiSchemaRegistry = new CustomApiSchemaRegistry(); config = { enabled: true, @@ -95,6 +98,7 @@ describe('configureClient', () => { dataSourceId: DATA_SOURCE_ID, savedObjects: savedObjectsMock, cryptography: cryptographyMock, + customApiSchemaRegistryPromise: Promise.resolve(customApiSchemaRegistry), }; ClientMock.mockImplementation(() => dsClient); diff --git a/src/plugins/data_source/server/client/configure_client.ts b/src/plugins/data_source/server/client/configure_client.ts index acbdfddb3fc4..984d99565569 100644 --- a/src/plugins/data_source/server/client/configure_client.ts +++ b/src/plugins/data_source/server/client/configure_client.ts @@ -29,7 +29,13 @@ import { } from './configure_client_utils'; export const configureClient = async ( - { dataSourceId, savedObjects, cryptography, testClientDataSourceAttr }: DataSourceClientParams, + { + dataSourceId, + savedObjects, + cryptography, + testClientDataSourceAttr, + customApiSchemaRegistryPromise, + }: DataSourceClientParams, openSearchClientPoolSetup: OpenSearchClientPoolSetup, config: DataSourcePluginConfigType, logger: Logger @@ -64,10 +70,13 @@ export const configureClient = async ( dataSourceId ) as Client; + const registeredSchema = (await customApiSchemaRegistryPromise).getAll(); + return await getQueryClient( dataSource, openSearchClientPoolSetup.addClientToPool, config, + registeredSchema, cryptography, rootClient, dataSourceId, @@ -87,6 +96,7 @@ export const configureClient = async ( * * @param rootClient root client for the given data source. * @param dataSourceAttr data source saved object attributes + * @param registeredSchema registered API schema * @param cryptography cryptography service for password encryption / decryption * @param config data source config * @param addClientToPool function to add client to client pool @@ -98,6 +108,7 @@ const getQueryClient = async ( dataSourceAttr: DataSourceAttributes, addClientToPool: (endpoint: string, authType: AuthType, client: Client | LegacyClient) => void, config: DataSourcePluginConfigType, + registeredSchema: any[], cryptography?: CryptographyServiceSetup, rootClient?: Client, dataSourceId?: string, @@ -107,7 +118,7 @@ const getQueryClient = async ( auth: { type }, endpoint, } = dataSourceAttr; - const clientOptions = parseClientOptions(config, endpoint); + const clientOptions = parseClientOptions(config, endpoint, registeredSchema); const cacheKey = generateCacheKey(dataSourceAttr, dataSourceId); switch (type) { diff --git a/src/plugins/data_source/server/index.ts b/src/plugins/data_source/server/index.ts index f05b833817d6..b88b1beb863e 100644 --- a/src/plugins/data_source/server/index.ts +++ b/src/plugins/data_source/server/index.ts @@ -8,6 +8,10 @@ import { DataSourcePlugin } from './plugin'; import { configSchema, DataSourcePluginConfigType } from '../config'; export const config: PluginConfigDescriptor = { + exposeToBrowser: { + enabled: true, + hideLocalCluster: true, + }, schema: configSchema, }; diff --git a/src/plugins/data_source/server/legacy/client_config.ts b/src/plugins/data_source/server/legacy/client_config.ts index d9b1cc704e3a..eed052cf245d 100644 --- a/src/plugins/data_source/server/legacy/client_config.ts +++ b/src/plugins/data_source/server/legacy/client_config.ts @@ -15,13 +15,15 @@ import { DataSourcePluginConfigType } from '../../config'; export function parseClientOptions( // TODO: will use client configs, that comes from a merge result of user config and default legacy client config, config: DataSourcePluginConfigType, - endpoint: string + endpoint: string, + registeredSchema: any[] ): ConfigOptions { const configOptions: ConfigOptions = { host: endpoint, ssl: { rejectUnauthorized: true, }, + plugins: registeredSchema, }; return configOptions; diff --git a/src/plugins/data_source/server/legacy/configure_legacy_client.test.ts b/src/plugins/data_source/server/legacy/configure_legacy_client.test.ts index 59c110d06dc5..f5cae1307f5a 100644 --- a/src/plugins/data_source/server/legacy/configure_legacy_client.test.ts +++ b/src/plugins/data_source/server/legacy/configure_legacy_client.test.ts @@ -15,6 +15,7 @@ import { OpenSearchClientPoolSetup } from '../client'; import { ConfigOptions } from 'elasticsearch'; import { ClientMock, parseClientOptionsMock } from './configure_legacy_client.test.mocks'; import { configureLegacyClient } from './configure_legacy_client'; +import { CustomApiSchemaRegistry } from '../schema_registry'; const DATA_SOURCE_ID = 'a54b76ec86771ee865a0f74a305dfff8'; @@ -35,6 +36,7 @@ describe('configureLegacyClient', () => { }; let dataSourceClientParams: DataSourceClientParams; let callApiParams: LegacyClientCallAPIParams; + const customApiSchemaRegistry = new CustomApiSchemaRegistry(); const mockResponse = { data: 'ping' }; @@ -98,6 +100,7 @@ describe('configureLegacyClient', () => { dataSourceId: DATA_SOURCE_ID, savedObjects: savedObjectsMock, cryptography: cryptographyMock, + customApiSchemaRegistryPromise: Promise.resolve(customApiSchemaRegistry), }; ClientMock.mockImplementation(() => mockOpenSearchClientInstance); diff --git a/src/plugins/data_source/server/legacy/configure_legacy_client.ts b/src/plugins/data_source/server/legacy/configure_legacy_client.ts index 8341e844edc8..58905b33d85c 100644 --- a/src/plugins/data_source/server/legacy/configure_legacy_client.ts +++ b/src/plugins/data_source/server/legacy/configure_legacy_client.ts @@ -36,7 +36,12 @@ import { } from '../client/configure_client_utils'; export const configureLegacyClient = async ( - { dataSourceId, savedObjects, cryptography }: DataSourceClientParams, + { + dataSourceId, + savedObjects, + cryptography, + customApiSchemaRegistryPromise, + }: DataSourceClientParams, callApiParams: LegacyClientCallAPIParams, openSearchClientPoolSetup: OpenSearchClientPoolSetup, config: DataSourcePluginConfigType, @@ -50,12 +55,15 @@ export const configureLegacyClient = async ( dataSourceId ) as LegacyClient; + const registeredSchema = (await customApiSchemaRegistryPromise).getAll(); + return await getQueryClient( dataSourceAttr, cryptography, callApiParams, openSearchClientPoolSetup.addClientToPool, config, + registeredSchema, rootClient, dataSourceId ); @@ -75,6 +83,7 @@ export const configureLegacyClient = async ( * @param dataSourceAttr data source saved object attributes * @param cryptography cryptography service for password encryption / decryption * @param config data source config + * @param registeredSchema registered API schema * @param addClientToPool function to add client to client pool * @param dataSourceId id of data source saved Object * @returns child client. @@ -85,6 +94,7 @@ const getQueryClient = async ( { endpoint, clientParams, options }: LegacyClientCallAPIParams, addClientToPool: (endpoint: string, authType: AuthType, client: Client | LegacyClient) => void, config: DataSourcePluginConfigType, + registeredSchema: any[], rootClient?: LegacyClient, dataSourceId?: string ) => { @@ -92,7 +102,7 @@ const getQueryClient = async ( auth: { type }, endpoint: nodeUrl, } = dataSourceAttr; - const clientOptions = parseClientOptions(config, nodeUrl); + const clientOptions = parseClientOptions(config, nodeUrl, registeredSchema); const cacheKey = generateCacheKey(dataSourceAttr, dataSourceId); switch (type) { diff --git a/src/plugins/data_source/server/plugin.ts b/src/plugins/data_source/server/plugin.ts index 0f3c47be4b4c..6bccfbfad662 100644 --- a/src/plugins/data_source/server/plugin.ts +++ b/src/plugins/data_source/server/plugin.ts @@ -23,19 +23,24 @@ import { LoggingAuditor } from './audit/logging_auditor'; import { CryptographyService, CryptographyServiceSetup } from './cryptography_service'; import { DataSourceService, DataSourceServiceSetup } from './data_source_service'; import { DataSourceSavedObjectsClientWrapper, dataSource } from './saved_objects'; -import { DataSourcePluginSetup, DataSourcePluginStart } from './types'; +import { AuthenticationMethod, DataSourcePluginSetup, DataSourcePluginStart } from './types'; import { DATA_SOURCE_SAVED_OBJECT_TYPE } from '../common'; // eslint-disable-next-line @osd/eslint/no-restricted-paths import { ensureRawRequest } from '../../../../src/core/server/http/router'; import { createDataSourceError } from './lib/error'; import { registerTestConnectionRoute } from './routes/test_connection'; +import { AuthenticationMethodRegistery, IAuthenticationMethodRegistery } from './auth_registry'; +import { CustomApiSchemaRegistry } from './schema_registry'; export class DataSourcePlugin implements Plugin { private readonly logger: Logger; private readonly cryptographyService: CryptographyService; private readonly dataSourceService: DataSourceService; private readonly config$: Observable; + private started = false; + private authMethodsRegistry = new AuthenticationMethodRegistery(); + private customApiSchemaRegistry = new CustomApiSchemaRegistry(); constructor(private initializerContext: PluginInitializerContext) { this.logger = this.initializerContext.logger.get(); @@ -44,7 +49,7 @@ export class DataSourcePlugin implements Plugin(); } - public async setup(core: CoreSetup) { + public async setup(core: CoreSetup) { this.logger.debug('dataSource: Setup'); // Register data source saved object type @@ -95,6 +100,17 @@ export class DataSourcePlugin implements Plugin coreStart.auditTrail); const dataSourceService: DataSourceServiceSetup = await this.dataSourceService.setup(config); + + const authRegistryPromise = core.getStartServices().then(([, , selfStart]) => { + const dataSourcePluginStart = selfStart as DataSourcePluginStart; + return dataSourcePluginStart.getAuthenticationMethodRegistery(); + }); + + const customApiSchemaRegistryPromise = core.getStartServices().then(([, , selfStart]) => { + const dataSourcePluginStart = selfStart as DataSourcePluginStart; + return dataSourcePluginStart.getCustomApiSchemaRegistry(); + }); + // Register data source plugin context to route handler context core.http.registerRouteHandlerContext( 'dataSource', @@ -102,22 +118,42 @@ export class DataSourcePlugin implements Plugin { + this.logger.debug(`Registered Credential Provider for authType = ${method.name}`); + if (this.started) { + throw new Error('cannot call `registerCredentialProvider` after service startup.'); + } + this.authMethodsRegistry.registerAuthenticationMethod(method); + }; return { createDataSourceError: (e: any) => createDataSourceError(e), + registerCredentialProvider, + registerCustomApiSchema: (schema: any) => this.customApiSchemaRegistry.register(schema), }; } public start(core: CoreStart) { this.logger.debug('dataSource: Started'); - - return {}; + this.started = true; + return { + getAuthenticationMethodRegistery: () => this.authMethodsRegistry, + getCustomApiSchemaRegistry: () => this.customApiSchemaRegistry, + }; } public stop() { @@ -128,7 +164,9 @@ export class DataSourcePlugin implements Plugin + auditTrailPromise: Promise, + authRegistryPromise: Promise, + customApiSchemaRegistryPromise: Promise ): IContextProvider, 'dataSource'> => { return (context, req) => { return { @@ -142,6 +180,7 @@ export class DataSourcePlugin implements Plugin { + describe('Test datasource connection without SigV4 auth', () => { + test('Success: opensearch client response code is 200 and response body have cluster name', async () => { + const opensearchClient = opensearchServiceMock.createOpenSearchClient(); + opensearchClient.info.mockResolvedValue( + opensearchServiceMock.createApiResponse({ + statusCode: 200, + body: { + cluster_name: 'This is the cluster name', + }, + }) + ); + const dataSourceValidator = new DataSourceConnectionValidator(opensearchClient, {}); + const validateDataSourcesResponse = await dataSourceValidator.validate(); + expect(validateDataSourcesResponse.statusCode).toBe(200); + }); + + test('failure: opensearch client response code is 200 but response body not have cluster name', async () => { + try { + const opensearchClient = opensearchServiceMock.createOpenSearchClient(); + opensearchClient.info.mockResolvedValue( + opensearchServiceMock.createApiResponse({ + statusCode: 200, + body: { + Message: 'Response without cluster name.', + }, + }) + ); + const dataSourceValidator = new DataSourceConnectionValidator(opensearchClient, {}); + await dataSourceValidator.validate(); + } catch (e) { + expect(e).toBeTruthy(); + expect(e.message).toContain('Response without cluster name.'); + } + }); + + test('failure: opensearch client response code is other than 200', async () => { + const statusCodeList = [100, 202, 300, 400, 500]; + statusCodeList.forEach(async function (code) { + try { + const opensearchClient = opensearchServiceMock.createOpenSearchClient(); + opensearchClient.info.mockResolvedValue( + opensearchServiceMock.createApiResponse({ + statusCode: code, + body: { + Message: 'Your request is not correct.', + }, + }) + ); + const dataSourceValidator = new DataSourceConnectionValidator(opensearchClient, {}); + await dataSourceValidator.validate(); + } catch (e) { + expect(e).toBeTruthy(); + expect(e.message).toContain('Your request is not correct.'); + } + }); + }); + }); + + describe('Test datasource connection for SigV4 auth', () => { + test('Success: opensearch client response code is 200 and response body is not empty', async () => { + const opensearchClient = opensearchServiceMock.createOpenSearchClient(); + opensearchClient.cat.indices.mockResolvedValue(opensearchServiceMock.createApiResponse()); + const dataSourceValidator = new DataSourceConnectionValidator(opensearchClient, { + auth: { + credentials: { + service: SigV4ServiceName.OpenSearchServerless, + }, + }, + }); + const validateDataSourcesResponse = await dataSourceValidator.validate(); + expect(validateDataSourcesResponse.statusCode).toBe(200); + }); + + test('failure: opensearch client response code is 200 and response body is empty', async () => { + try { + const opensearchClient = opensearchServiceMock.createOpenSearchClient(); + opensearchClient.cat.indices.mockResolvedValue(opensearchServiceMock.createApiResponse()); + const dataSourceValidator = new DataSourceConnectionValidator(opensearchClient, { + auth: { + statusCode: 200, + body: '', + credentials: { + service: SigV4ServiceName.OpenSearchServerless, + }, + }, + }); + const validateDataSourcesResponse = await dataSourceValidator.validate(); + expect(validateDataSourcesResponse.statusCode).toBe(200); + } catch (e) { + expect(e).toBeTruthy(); + } + }); + + test('failure: opensearch client response code is other than 200', async () => { + const statusCodeList = [100, 202, 300, 400, 500]; + statusCodeList.forEach(async function (code) { + try { + const opensearchClient = opensearchServiceMock.createOpenSearchClient(); + opensearchClient.cat.indices.mockResolvedValue( + opensearchServiceMock.createApiResponse({ + statusCode: code, + body: { + Message: 'Your request is not correct.', + }, + }) + ); + const dataSourceValidator = new DataSourceConnectionValidator(opensearchClient, { + auth: { + credentials: { + service: SigV4ServiceName.OpenSearchServerless, + }, + }, + }); + await dataSourceValidator.validate(); + } catch (e) { + expect(e).toBeTruthy(); + expect(e.message).toContain('Your request is not correct.'); + } + }); + }); + }); +}); diff --git a/src/plugins/data_source/server/routes/data_source_connection_validator.ts b/src/plugins/data_source/server/routes/data_source_connection_validator.ts index f3233859e854..735d1429414c 100644 --- a/src/plugins/data_source/server/routes/data_source_connection_validator.ts +++ b/src/plugins/data_source/server/routes/data_source_connection_validator.ts @@ -14,10 +14,23 @@ export class DataSourceConnectionValidator { async validate() { try { + let validationResponse; // Amazon OpenSearch Serverless does not support .info() API - if (this.dataSourceAttr.auth?.credentials?.service === SigV4ServiceName.OpenSearchServerless) - return await this.callDataCluster.cat.indices(); - return await this.callDataCluster.info(); + if ( + this.dataSourceAttr.auth?.credentials?.service === SigV4ServiceName.OpenSearchServerless + ) { + validationResponse = await this.callDataCluster.cat.indices(); + if (validationResponse?.statusCode === 200 && validationResponse?.body) { + return validationResponse; + } + } else { + validationResponse = await this.callDataCluster.info(); + if (validationResponse?.statusCode === 200 && validationResponse?.body?.cluster_name) { + return validationResponse; + } + } + + throw new Error(JSON.stringify(validationResponse?.body)); } catch (e) { throw createDataSourceError(e); } diff --git a/src/plugins/data_source/server/routes/test_connection.ts b/src/plugins/data_source/server/routes/test_connection.ts index cba42517e535..85eea97c933c 100644 --- a/src/plugins/data_source/server/routes/test_connection.ts +++ b/src/plugins/data_source/server/routes/test_connection.ts @@ -9,11 +9,13 @@ import { AuthType, DataSourceAttributes, SigV4ServiceName } from '../../common/d import { DataSourceConnectionValidator } from './data_source_connection_validator'; import { DataSourceServiceSetup } from '../data_source_service'; import { CryptographyServiceSetup } from '../cryptography_service'; +import { IAuthenticationMethodRegistery } from '../auth_registry'; export const registerTestConnectionRoute = ( router: IRouter, dataSourceServiceSetup: DataSourceServiceSetup, - cryptography: CryptographyServiceSetup + cryptography: CryptographyServiceSetup, + authRegistryPromise: Promise ) => { router.post( { diff --git a/src/plugins/data_source/server/schema_registry/custom_api_schema_registry.test.ts b/src/plugins/data_source/server/schema_registry/custom_api_schema_registry.test.ts new file mode 100644 index 000000000000..06bef7d1bd28 --- /dev/null +++ b/src/plugins/data_source/server/schema_registry/custom_api_schema_registry.test.ts @@ -0,0 +1,20 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { CustomApiSchemaRegistry } from './custom_api_schema_registry'; + +describe('CustomApiSchemaRegistry', () => { + let registry: CustomApiSchemaRegistry; + + beforeEach(() => { + registry = new CustomApiSchemaRegistry(); + }); + + it('allows to register and get api schema', () => { + const sqlPlugin = () => {}; + registry.register(sqlPlugin); + expect(registry.getAll()).toEqual([sqlPlugin]); + }); +}); diff --git a/src/plugins/data_source/server/schema_registry/custom_api_schema_registry.ts b/src/plugins/data_source/server/schema_registry/custom_api_schema_registry.ts new file mode 100644 index 000000000000..ef9cd3b1e633 --- /dev/null +++ b/src/plugins/data_source/server/schema_registry/custom_api_schema_registry.ts @@ -0,0 +1,19 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ +export class CustomApiSchemaRegistry { + private readonly schemaRegistry: any[]; + + constructor() { + this.schemaRegistry = new Array(); + } + + public register(schema: any) { + this.schemaRegistry.push(schema); + } + + public getAll(): any[] { + return this.schemaRegistry; + } +} diff --git a/src/plugins/data_source/server/schema_registry/index.ts b/src/plugins/data_source/server/schema_registry/index.ts new file mode 100644 index 000000000000..67a60bc90433 --- /dev/null +++ b/src/plugins/data_source/server/schema_registry/index.ts @@ -0,0 +1,6 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export { CustomApiSchemaRegistry } from './custom_api_schema_registry'; diff --git a/src/plugins/data_source/server/types.ts b/src/plugins/data_source/server/types.ts index 68a840ebbbcb..9bd70b142d8b 100644 --- a/src/plugins/data_source/server/types.ts +++ b/src/plugins/data_source/server/types.ts @@ -7,11 +7,19 @@ import { LegacyCallAPIOptions, OpenSearchClient, SavedObjectsClientContract, + OpenSearchDashboardsRequest, } from 'src/core/server'; -import { DataSourceAttributes } from '../common/data_sources'; +import { + DataSourceAttributes, + AuthType, + UsernamePasswordTypedContent, + SigV4Content, +} from '../common/data_sources'; import { CryptographyServiceSetup } from './cryptography_service'; import { DataSourceError } from './lib/error'; +import { IAuthenticationMethodRegistery } from './auth_registry'; +import { CustomApiSchemaRegistry } from './schema_registry'; export interface LegacyClientCallAPIParams { endpoint: string; @@ -27,6 +35,24 @@ export interface DataSourceClientParams { dataSourceId?: string; // required when creating test client testClientDataSourceAttr?: DataSourceAttributes; + // custom API schema registry promise, required for getting registered custom API schema + customApiSchemaRegistryPromise: Promise; +} + +export interface DataSourceCredentialsProviderOptions { + dataSourceAttr: DataSourceAttributes; + request?: OpenSearchDashboardsRequest; + cryptography?: CryptographyServiceSetup; +} + +export type DataSourceCredentialsProvider = ( + options: DataSourceCredentialsProviderOptions +) => Promise; + +export interface AuthenticationMethod { + name: string; + authType: AuthType; + credentialProvider: DataSourceCredentialsProvider; } export interface DataSourcePluginRequestContext { @@ -53,6 +79,11 @@ declare module 'src/core/server' { export interface DataSourcePluginSetup { createDataSourceError: (err: any) => DataSourceError; + registerCredentialProvider: (method: AuthenticationMethod) => void; + registerCustomApiSchema: (schema: any) => void; +} + +export interface DataSourcePluginStart { + getAuthenticationMethodRegistery: () => IAuthenticationMethodRegistery; + getCustomApiSchemaRegistry: () => CustomApiSchemaRegistry; } -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface DataSourcePluginStart {} diff --git a/src/plugins/data_source_management/opensearch_dashboards.json b/src/plugins/data_source_management/opensearch_dashboards.json index 6b58c63bb5a5..565ccff401dd 100644 --- a/src/plugins/data_source_management/opensearch_dashboards.json +++ b/src/plugins/data_source_management/opensearch_dashboards.json @@ -5,6 +5,6 @@ "ui": true, "requiredPlugins": ["dataSource", "indexPatternManagement"], "optionalPlugins": [], - "requiredBundles": ["opensearchDashboardsReact"], + "requiredBundles": ["opensearchDashboardsReact", "dataSource"], "extraPublicDirs": ["public/components/utils"] } diff --git a/src/plugins/data_source_management/public/auth_registry/authentication_methods_registry.test.ts b/src/plugins/data_source_management/public/auth_registry/authentication_methods_registry.test.ts new file mode 100644 index 000000000000..f2bd07af4dc5 --- /dev/null +++ b/src/plugins/data_source_management/public/auth_registry/authentication_methods_registry.test.ts @@ -0,0 +1,97 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { AuthenticationMethodRegistery } from './authentication_methods_registry'; +import React from 'react'; +import { createAuthenticationMethod } from '../mocks'; + +describe('AuthenticationMethodRegistery', () => { + let registry: AuthenticationMethodRegistery; + + beforeEach(() => { + registry = new AuthenticationMethodRegistery(); + }); + + it('allows to register authentication method', () => { + registry.registerAuthenticationMethod(createAuthenticationMethod({ name: 'typeA' })); + registry.registerAuthenticationMethod(createAuthenticationMethod({ name: 'typeB' })); + registry.registerAuthenticationMethod(createAuthenticationMethod({ name: 'typeC' })); + + expect( + registry + .getAllAuthenticationMethods() + .map((type) => type.name) + .sort() + ).toEqual(['typeA', 'typeB', 'typeC']); + }); + + it('throws when trying to register the same authentication method twice', () => { + registry.registerAuthenticationMethod(createAuthenticationMethod({ name: 'typeA' })); + registry.registerAuthenticationMethod(createAuthenticationMethod({ name: 'typeB' })); + expect(() => { + registry.registerAuthenticationMethod(createAuthenticationMethod({ name: 'typeA' })); + }).toThrowErrorMatchingInlineSnapshot(`"Authentication method 'typeA' is already registered"`); + }); + + describe('#getAuthenticationMethod', () => { + it(`retrieve a type by it's name`, () => { + const typeA = createAuthenticationMethod({ name: 'typeA' }); + const typeB = createAuthenticationMethod({ name: 'typeB' }); + registry.registerAuthenticationMethod(typeA); + registry.registerAuthenticationMethod(typeB); + registry.registerAuthenticationMethod(createAuthenticationMethod({ name: 'typeC' })); + + expect(registry.getAuthenticationMethod('typeA')).toEqual(typeA); + expect(registry.getAuthenticationMethod('typeB')).toEqual(typeB); + expect(registry.getAuthenticationMethod('unknownType')).toBeUndefined(); + }); + + it('forbids to mutate the registered types', () => { + registry.registerAuthenticationMethod( + createAuthenticationMethod({ + name: 'typeA', + }) + ); + + const typeA = registry.getAuthenticationMethod('typeA')!; + + expect(() => { + typeA.credentialForm = React.createElement('div', {}, 'Welcome!'); + }).toThrow(); + expect(() => { + typeA.credentialSourceOption = { + value: 'typeA', + }; + }).toThrow(); + }); + }); + + describe('#getAllTypes', () => { + it('returns all registered types', () => { + const typeA = createAuthenticationMethod({ name: 'typeA' }); + const typeB = createAuthenticationMethod({ name: 'typeB' }); + const typeC = createAuthenticationMethod({ name: 'typeC' }); + registry.registerAuthenticationMethod(typeA); + registry.registerAuthenticationMethod(typeB); + + const registered = registry.getAllAuthenticationMethods(); + expect(registered.length).toEqual(2); + expect(registered).toContainEqual(typeA); + expect(registered).toContainEqual(typeB); + expect(registered).not.toContainEqual(typeC); + }); + + it('does not mutate the registered types when altering the list', () => { + registry.registerAuthenticationMethod(createAuthenticationMethod({ name: 'typeA' })); + registry.registerAuthenticationMethod(createAuthenticationMethod({ name: 'typeB' })); + registry.registerAuthenticationMethod(createAuthenticationMethod({ name: 'typeC' })); + + const types = registry.getAllAuthenticationMethods(); + types.splice(0, 3); + + expect(registry.getAllAuthenticationMethods().length).toEqual(3); + }); + }); +}); diff --git a/src/plugins/data_source_management/public/auth_registry/authentication_methods_registry.ts b/src/plugins/data_source_management/public/auth_registry/authentication_methods_registry.ts new file mode 100644 index 000000000000..98cff913483f --- /dev/null +++ b/src/plugins/data_source_management/public/auth_registry/authentication_methods_registry.ts @@ -0,0 +1,40 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { deepFreeze } from '@osd/std'; +import { EuiSuperSelectOption } from '@elastic/eui'; + +export interface AuthenticationMethod { + name: string; + credentialForm: React.JSX.Element; + credentialSourceOption: EuiSuperSelectOption; +} + +export type IAuthenticationMethodRegistery = Omit< + AuthenticationMethodRegistery, + 'registerAuthenticationMethod' +>; + +export class AuthenticationMethodRegistery { + private readonly authMethods = new Map(); + /** + * Register a authMethods with function to return credentials inside the registry. + * Authentication Method can only be registered once. subsequent calls with the same method name will throw an error. + */ + public registerAuthenticationMethod(method: AuthenticationMethod) { + if (this.authMethods.has(method.name)) { + throw new Error(`Authentication method '${method.name}' is already registered`); + } + this.authMethods.set(method.name, deepFreeze(method) as AuthenticationMethod); + } + + public getAllAuthenticationMethods() { + return [...this.authMethods.values()]; + } + + public getAuthenticationMethod(name: string) { + return this.authMethods.get(name); + } +} diff --git a/src/plugins/data_source_management/public/auth_registry/index.ts b/src/plugins/data_source_management/public/auth_registry/index.ts new file mode 100644 index 000000000000..5cbadd12a51a --- /dev/null +++ b/src/plugins/data_source_management/public/auth_registry/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export { + IAuthenticationMethodRegistery, + AuthenticationMethod, + AuthenticationMethodRegistery, +} from './authentication_methods_registry'; diff --git a/src/plugins/data_source_management/public/components/cluster_selector/__snapshots__/cluster_selector.test.tsx.snap b/src/plugins/data_source_management/public/components/cluster_selector/__snapshots__/cluster_selector.test.tsx.snap new file mode 100644 index 000000000000..34c1df89f050 --- /dev/null +++ b/src/plugins/data_source_management/public/components/cluster_selector/__snapshots__/cluster_selector.test.tsx.snap @@ -0,0 +1,114 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ClusterSelector should render normally with local cluster is hidden 1`] = ` + +`; + +exports[`ClusterSelector should render normally with local cluster not hidden 1`] = ` + +`; + +exports[`ClusterSelector: check dataSource options should always place local cluster option as the first option when local cluster not hidden 1`] = ` + +`; diff --git a/src/plugins/data_source_management/public/components/cluster_selector/cluster_selector.test.tsx b/src/plugins/data_source_management/public/components/cluster_selector/cluster_selector.test.tsx new file mode 100644 index 000000000000..6a4448874ab9 --- /dev/null +++ b/src/plugins/data_source_management/public/components/cluster_selector/cluster_selector.test.tsx @@ -0,0 +1,97 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { ShallowWrapper, shallow } from 'enzyme'; +import { ClusterSelector } from './cluster_selector'; +import { SavedObjectsClientContract } from '../../../../../core/public'; +import { notificationServiceMock } from '../../../../../core/public/mocks'; +import React from 'react'; +import { getDataSourcesResponse, mockResponseForSavedObjectsCalls } from '../../mocks'; + +describe('ClusterSelector', () => { + let component: ShallowWrapper, React.Component<{}, {}, any>>; + + let client: SavedObjectsClientContract; + const { toasts } = notificationServiceMock.createStartContract(); + + beforeEach(() => { + client = { + find: jest.fn().mockResolvedValue([]), + } as any; + }); + + it('should render normally with local cluster not hidden', () => { + component = shallow( + + ); + expect(component).toMatchSnapshot(); + expect(client.find).toBeCalledWith({ + fields: ['id', 'description', 'title'], + perPage: 10000, + type: 'data-source', + }); + expect(toasts.addWarning).toBeCalledTimes(0); + }); + + it('should render normally with local cluster is hidden', () => { + component = shallow( + + ); + expect(component).toMatchSnapshot(); + expect(client.find).toBeCalledWith({ + fields: ['id', 'description', 'title'], + perPage: 10000, + type: 'data-source', + }); + expect(toasts.addWarning).toBeCalledTimes(0); + }); +}); + +describe('ClusterSelector: check dataSource options', () => { + let component: ShallowWrapper, React.Component<{}, {}, any>>; + let client: SavedObjectsClientContract; + const { toasts } = notificationServiceMock.createStartContract(); + const nextTick = () => new Promise((res) => process.nextTick(res)); + + beforeEach(async () => { + client = { + find: jest.fn().mockResolvedValue([]), + } as any; + + mockResponseForSavedObjectsCalls(client, 'find', getDataSourcesResponse); + }); + + it('should always place local cluster option as the first option when local cluster not hidden', async () => { + component = shallow( + + ); + + component.instance().componentDidMount!(); + await nextTick(); + expect(component).toMatchSnapshot(); + expect(toasts.addWarning).toBeCalledTimes(0); + }); +}); diff --git a/src/plugins/data_source_management/public/components/cluster_selector/cluster_selector.tsx b/src/plugins/data_source_management/public/components/cluster_selector/cluster_selector.tsx new file mode 100644 index 000000000000..59f73f1b8795 --- /dev/null +++ b/src/plugins/data_source_management/public/components/cluster_selector/cluster_selector.tsx @@ -0,0 +1,115 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { i18n } from '@osd/i18n'; +import { EuiComboBox } from '@elastic/eui'; +import { SavedObjectsClientContract, ToastsStart } from 'opensearch-dashboards/public'; +import { getDataSources } from '../utils'; + +export const LocalCluster: ClusterOption = { + label: i18n.translate('dataSource.localCluster', { + defaultMessage: 'Local cluster', + }), + id: '', +}; + +interface ClusterSelectorProps { + savedObjectsClient: SavedObjectsClientContract; + notifications: ToastsStart; + onSelectedDataSource: (clusterOption: ClusterOption[]) => void; + disabled: boolean; + hideLocalCluster: boolean; + fullWidth: boolean; +} + +interface ClusterSelectorState { + clusterOptions: ClusterOption[]; + selectedOption: ClusterOption[]; +} + +export interface ClusterOption { + label: string; + id: string; +} + +export class ClusterSelector extends React.Component { + private _isMounted: boolean = false; + + constructor(props: ClusterSelectorProps) { + super(props); + + this.state = { + clusterOptions: this.props.hideLocalCluster ? [] : [LocalCluster], + selectedOption: this.props.hideLocalCluster ? [] : [LocalCluster], + }; + } + + componentWillUnmount() { + this._isMounted = false; + } + + async componentDidMount() { + this._isMounted = true; + getDataSources(this.props.savedObjectsClient) + .then((fetchedDataSources) => { + if (fetchedDataSources?.length) { + const clusterOptions = fetchedDataSources.map((dataSource) => ({ + id: dataSource.id, + label: dataSource.title, + })); + + if (!this.props.hideLocalCluster) { + clusterOptions.unshift(LocalCluster); + } + + if (!this._isMounted) return; + this.setState({ + ...this.state, + clusterOptions, + }); + } + }) + .catch(() => { + this.props.notifications.addWarning( + i18n.translate('dataSource.fetchDataSourceError', { + defaultMessage: 'Unable to fetch existing data sources', + }) + ); + }); + } + + onChange(e) { + if (!this._isMounted) return; + this.setState({ + selectedOption: e, + }); + this.props.onSelectedDataSource(e); + } + + render() { + return ( + this.onChange(e)} + prepend={i18n.translate('clusterSelectorComboBoxPrepend', { + defaultMessage: 'Data source', + })} + compressed + isDisabled={this.props.disabled} + fullWidth={this.props.fullWidth || false} + data-test-subj={'clusterSelectorComboBox'} + /> + ); + } +} diff --git a/src/plugins/data_source_management/public/components/cluster_selector/index.ts b/src/plugins/data_source_management/public/components/cluster_selector/index.ts new file mode 100644 index 000000000000..8e65dc8dc698 --- /dev/null +++ b/src/plugins/data_source_management/public/components/cluster_selector/index.ts @@ -0,0 +1,6 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export { ClusterSelector } from './cluster_selector'; diff --git a/src/plugins/data_source_management/public/components/create_data_source_wizard/components/create_form/create_data_source_form.test.tsx b/src/plugins/data_source_management/public/components/create_data_source_wizard/components/create_form/create_data_source_form.test.tsx index ad1d02c87db4..c3c34cbdab1d 100644 --- a/src/plugins/data_source_management/public/components/create_data_source_wizard/components/create_form/create_data_source_form.test.tsx +++ b/src/plugins/data_source_management/public/components/create_data_source_wizard/components/create_form/create_data_source_form.test.tsx @@ -53,11 +53,8 @@ describe('Datasource Management: Create Datasource form', () => { }; const setAuthTypeValue = (testSubjId: string, value: string) => { - component.find(testSubjId).last().simulate('change', { - target: { - value, - }, - }); + component.find(testSubjId).last().simulate('click'); + component.find({ id: value }).last().simulate('click'); }; beforeEach(() => { @@ -103,7 +100,7 @@ describe('Datasource Management: Create Datasource form', () => { const { authType, username, password } = getFields(component); - expect(authType.prop('value')).toBe(AuthType.NoAuth); + expect(authType.prop('valueOfSelected')).toBe(AuthType.NoAuth); expect(username.exists()).toBeFalsy(); // username field does not exist when No Auth option is selected expect(password.exists()).toBeFalsy(); // password field does not exist when No Auth option is selected }); diff --git a/src/plugins/data_source_management/public/components/create_data_source_wizard/components/create_form/create_data_source_form.tsx b/src/plugins/data_source_management/public/components/create_data_source_wizard/components/create_form/create_data_source_form.tsx index 3107ee84006a..86052cff8995 100644 --- a/src/plugins/data_source_management/public/components/create_data_source_wizard/components/create_form/create_data_source_form.tsx +++ b/src/plugins/data_source_management/public/components/create_data_source_wizard/components/create_form/create_data_source_form.tsx @@ -15,7 +15,7 @@ import { EuiForm, EuiFormRow, EuiPageContent, - EuiSelect, + EuiSuperSelect, EuiSpacer, EuiText, } from '@elastic/eui'; @@ -124,8 +124,7 @@ export class CreateDataSourceForm extends React.Component< }); }; - onChangeAuthType = (e: React.ChangeEvent) => { - const authType = e.target.value as AuthType; + onChangeAuthType = (authType: AuthType) => { this.setState({ auth: { ...this.state.auth, @@ -140,13 +139,13 @@ export class CreateDataSourceForm extends React.Component< }); }; - onChangeSigV4ServiceName = (e: React.ChangeEvent) => { + onChangeSigV4ServiceName = (service: SigV4ServiceName) => { this.setState({ auth: { ...this.state.auth, credentials: { ...this.state.auth.credentials, - service: e.target.value as SigV4ServiceName, + service, }, }, }); @@ -425,10 +424,10 @@ export class CreateDataSourceForm extends React.Component< defaultMessage: 'Service Name', })} > - this.onChangeSigV4ServiceName(e)} + valueOfSelected={this.state.auth.credentials.service} + onChange={(value) => this.onChangeSigV4ServiceName(value)} name="ServiceName" data-test-subj="createDataSourceFormSigV4ServiceTypeSelect" /> @@ -598,10 +597,10 @@ export class CreateDataSourceForm extends React.Component< {/* Credential source */} - this.onChangeAuthType(e)} + valueOfSelected={this.state.auth.type} + onChange={(value) => this.onChangeAuthType(value)} name="Credential" data-test-subj="createDataSourceFormAuthTypeSelect" /> diff --git a/src/plugins/data_source_management/public/components/edit_data_source/components/edit_form/edit_data_source_form.test.tsx b/src/plugins/data_source_management/public/components/edit_data_source/components/edit_form/edit_data_source_form.test.tsx index 41133f65035a..c57b5b414ed3 100644 --- a/src/plugins/data_source_management/public/components/edit_data_source/components/edit_form/edit_data_source_form.test.tsx +++ b/src/plugins/data_source_management/public/components/edit_data_source/components/edit_form/edit_data_source_form.test.tsx @@ -50,11 +50,8 @@ describe('Datasource Management: Edit Datasource Form', () => { }; const setAuthTypeValue = (testSubjId: string, value: string) => { - component.find(testSubjId).last().simulate('change', { - target: { - value, - }, - }); + component.find(testSubjId).last().simulate('click'); + component.find({ id: value }).last().simulate('click'); }; describe('Case 1: With Username & Password', () => { diff --git a/src/plugins/data_source_management/public/components/edit_data_source/components/edit_form/edit_data_source_form.tsx b/src/plugins/data_source_management/public/components/edit_data_source/components/edit_form/edit_data_source_form.tsx index e05813ca6f04..9af843a64071 100644 --- a/src/plugins/data_source_management/public/components/edit_data_source/components/edit_form/edit_data_source_form.tsx +++ b/src/plugins/data_source_management/public/components/edit_data_source/components/edit_form/edit_data_source_form.tsx @@ -17,7 +17,7 @@ import { EuiFormRow, EuiHorizontalRule, EuiPanel, - EuiSelect, + EuiSuperSelect, EuiSpacer, EuiText, } from '@elastic/eui'; @@ -166,8 +166,7 @@ export class EditDataSourceForm extends React.Component) => { - const authType = e.target.value as AuthType; + onChangeAuthType = (authType: AuthType) => { this.setState( { auth: { @@ -241,13 +240,13 @@ export class EditDataSourceForm extends React.Component) => { + onChangeSigV4ServiceName = (service: SigV4ServiceName) => { this.setState({ auth: { ...this.state.auth, credentials: { ...this.state.auth.credentials, - service: e.target.value as SigV4ServiceName, + service, } as SigV4Content, }, }); @@ -772,9 +771,9 @@ export class EditDataSourceForm extends React.Component - - this.onChangeSigV4ServiceName(e)} + valueOfSelected={this.state.auth.credentials?.service} + onChange={(value) => this.onChangeSigV4ServiceName(value)} name="ServiceName" data-test-subj="editDataSourceFormSigV4ServiceTypeSelect" /> diff --git a/src/plugins/data_source_management/public/index.ts b/src/plugins/data_source_management/public/index.ts index acae34449ce3..e58e8f9bce5e 100644 --- a/src/plugins/data_source_management/public/index.ts +++ b/src/plugins/data_source_management/public/index.ts @@ -11,3 +11,5 @@ export function plugin() { return new DataSourceManagementPlugin(); } export { DataSourceManagementPluginStart } from './types'; +export { ClusterSelector } from './components/cluster_selector'; +export { DataSourceManagementPlugin, DataSourceManagementPluginSetup } from './plugin'; diff --git a/src/plugins/data_source_management/public/mocks.ts b/src/plugins/data_source_management/public/mocks.ts index c078247956e0..7b170c4a7c79 100644 --- a/src/plugins/data_source_management/public/mocks.ts +++ b/src/plugins/data_source_management/public/mocks.ts @@ -3,10 +3,19 @@ * SPDX-License-Identifier: Apache-2.0 */ +import React from 'react'; import { throwError } from 'rxjs'; import { SavedObjectsClientContract } from 'opensearch-dashboards/public'; import { AuthType } from './types'; import { coreMock } from '../../../core/public/mocks'; +import { + DataSourceManagementPlugin, + DataSourceManagementPluginSetup, + DataSourceManagementPluginStart, +} from './plugin'; +import { managementPluginMock } from '../../management/public/mocks'; +import { mockManagementPlugin as indexPatternManagementPluginMock } from '../../index_pattern_management/public/mocks'; +import { AuthenticationMethod } from './auth_registry'; /* Mock Types */ @@ -197,3 +206,35 @@ export const mockErrorResponseForSavedObjectsCalls = ( throwError(new Error('Error while fetching data sources')) ); }; + +export interface TestPluginReturn { + setup: DataSourceManagementPluginSetup; + doStart: () => DataSourceManagementPluginStart; +} + +export const testDataSourceManagementPlugin = ( + coreSetup: any, + coreStart: any +): TestPluginReturn => { + const plugin = new DataSourceManagementPlugin(); + const setup = plugin.setup(coreSetup, { + management: managementPluginMock.createSetupContract(), + indexPatternManagement: indexPatternManagementPluginMock.createSetupContract(), + }); + const doStart = () => { + const start = plugin.start(coreStart); + return start; + }; + return { setup, doStart }; +}; + +export const createAuthenticationMethod = ( + authMethod: Partial +): AuthenticationMethod => ({ + name: 'unknown', + credentialForm: React.createElement('div', {}, 'Hello, world!'), + credentialSourceOption: { + value: 'unknown', + }, + ...authMethod, +}); diff --git a/src/plugins/data_source_management/public/plugin.test.ts b/src/plugins/data_source_management/public/plugin.test.ts new file mode 100644 index 000000000000..98615119a0c1 --- /dev/null +++ b/src/plugins/data_source_management/public/plugin.test.ts @@ -0,0 +1,25 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ +import { coreMock } from '../../../core/public/mocks'; +import { DataSourceManagementPluginStart } from './plugin'; +import { testDataSourceManagementPlugin, createAuthenticationMethod } from './mocks'; + +describe('#dataSourceManagement', () => { + let coreSetup: any; + let coreStart: any; + let mockDataSourceManagementPluginStart: MockedKeys; + beforeEach(() => { + coreSetup = coreMock.createSetup({ pluginStartContract: mockDataSourceManagementPluginStart }); + coreStart = coreMock.createStart(); + }); + it('can register custom authentication method', () => { + const { setup, doStart } = testDataSourceManagementPlugin(coreSetup, coreStart); + const typeA = createAuthenticationMethod({ name: 'typeA' }); + setup.registerAuthenticationMethod(createAuthenticationMethod(typeA)); + const start = doStart(); + const registry = start.getAuthenticationMethodRegistery(); + expect(registry.getAuthenticationMethod('typeA')).toEqual(typeA); + }); +}); diff --git a/src/plugins/data_source_management/public/plugin.ts b/src/plugins/data_source_management/public/plugin.ts index d0c900effce2..2bf9d112ee7e 100644 --- a/src/plugins/data_source_management/public/plugin.ts +++ b/src/plugins/data_source_management/public/plugin.ts @@ -18,17 +18,41 @@ import { ManagementSetup } from '../../management/public'; import { IndexPatternManagementSetup } from '../../index_pattern_management/public'; import { DataSourceColumn } from './components/data_source_column/data_source_column'; import { DataSourceManagementStartDependencies } from './types'; +import { + AuthenticationMethod, + IAuthenticationMethodRegistery, + AuthenticationMethodRegistery, +} from './auth_registry'; export interface DataSourceManagementSetupDependencies { management: ManagementSetup; indexPatternManagement: IndexPatternManagementSetup; } +export interface DataSourceManagementPluginSetup { + registerAuthenticationMethod: (authMethodValues: AuthenticationMethod) => void; +} + +export interface DataSourceManagementPluginStart { + getAuthenticationMethodRegistery: () => IAuthenticationMethodRegistery; +} + const DSM_APP_ID = 'dataSources'; export class DataSourceManagementPlugin - implements Plugin { - public setup(core: CoreSetup, { indexPatternManagement }: DataSourceManagementSetupDependencies) { + implements + Plugin< + DataSourceManagementPluginSetup, + DataSourceManagementPluginStart, + DataSourceManagementSetupDependencies + > { + private started = false; + private authMethodsRegistry = new AuthenticationMethodRegistery(); + + public setup( + core: CoreSetup, + { indexPatternManagement }: DataSourceManagementSetupDependencies + ) { const savedObjectPromise = core .getStartServices() .then(([coreStart]) => coreStart.savedObjects); @@ -50,9 +74,25 @@ export class DataSourceManagementPlugin ); }, }); + + const registerAuthenticationMethod = (authMethod: AuthenticationMethod) => { + if (this.started) { + throw new Error( + 'cannot call `registerAuthenticationMethod` after data source management startup.' + ); + } + this.authMethodsRegistry.registerAuthenticationMethod(authMethod); + }; + + return { registerAuthenticationMethod }; } - public start(core: CoreStart) {} + public start(core: CoreStart) { + this.started = true; + return { + getAuthenticationMethodRegistery: () => this.authMethodsRegistry, + }; + } public stop() {} } diff --git a/src/plugins/data_source_management/public/types.ts b/src/plugins/data_source_management/public/types.ts index be2b7725d7ed..65e1b604d02c 100644 --- a/src/plugins/data_source_management/public/types.ts +++ b/src/plugins/data_source_management/public/types.ts @@ -62,19 +62,19 @@ export enum AuthType { export const credentialSourceOptions = [ { value: AuthType.NoAuth, - text: i18n.translate('dataSourceManagement.credentialSourceOptions.NoAuthentication', { + inputDisplay: i18n.translate('dataSourceManagement.credentialSourceOptions.NoAuthentication', { defaultMessage: 'No authentication', }), }, { value: AuthType.UsernamePasswordType, - text: i18n.translate('dataSourceManagement.credentialSourceOptions.UsernamePassword', { + inputDisplay: i18n.translate('dataSourceManagement.credentialSourceOptions.UsernamePassword', { defaultMessage: 'Username & Password', }), }, { value: AuthType.SigV4, - text: i18n.translate('dataSourceManagement.credentialSourceOptions.AwsSigV4', { + inputDisplay: i18n.translate('dataSourceManagement.credentialSourceOptions.AwsSigV4', { defaultMessage: 'AWS SigV4', }), }, @@ -83,13 +83,13 @@ export const credentialSourceOptions = [ export const sigV4ServiceOptions = [ { value: SigV4ServiceName.OpenSearch, - text: i18n.translate('dataSourceManagement.SigV4ServiceOptions.OpenSearch', { + inputDisplay: i18n.translate('dataSourceManagement.SigV4ServiceOptions.OpenSearch', { defaultMessage: 'Amazon OpenSearch Service', }), }, { value: SigV4ServiceName.OpenSearchServerless, - text: i18n.translate('dataSourceManagement.SigV4ServiceOptions.OpenSearchServerless', { + inputDisplay: i18n.translate('dataSourceManagement.SigV4ServiceOptions.OpenSearchServerless', { defaultMessage: 'Amazon OpenSearch Serverless', }), }, diff --git a/src/plugins/dev_tools/public/application.tsx b/src/plugins/dev_tools/public/application.tsx index 8c1c936de646..e9b088284537 100644 --- a/src/plugins/dev_tools/public/application.tsx +++ b/src/plugins/dev_tools/public/application.tsx @@ -28,18 +28,10 @@ * under the License. */ -import React, { useEffect, useRef, useState } from 'react'; +import React, { useEffect, useRef } from 'react'; import ReactDOM from 'react-dom'; import { HashRouter as Router, Switch, Route, Redirect } from 'react-router-dom'; -import { - EuiTab, - EuiTabs, - EuiToolTip, - EuiComboBox, - EuiFlexGroup, - EuiFlexItem, - EuiComboBoxOptionOption, -} from '@elastic/eui'; +import { EuiTab, EuiTabs, EuiToolTip, EuiComboBoxOptionOption } from '@elastic/eui'; import { I18nProvider } from '@osd/i18n/react'; import { i18n } from '@osd/i18n'; @@ -52,11 +44,10 @@ import { ScopedHistory, } from 'src/core/public'; -import { useEffectOnce } from 'react-use'; -// eslint-disable-next-line @osd/eslint/no-restricted-paths -import { getDataSources } from '../../data_source_management/public/components/utils'; +import { ClusterSelector } from '../../data_source_management/public'; import { DevToolApp } from './dev_tool'; import { DevToolsSetupDependencies } from './plugin'; +import { addHelpMenuToAppChrome } from './utils/util'; interface DevToolsWrapperProps { devTools: readonly DevToolApp[]; @@ -65,6 +56,7 @@ interface DevToolsWrapperProps { savedObjects: SavedObjectsStart; notifications: NotificationsStart; dataSourceEnabled: boolean; + hideLocalCluster: boolean; } interface MountedDevToolDescriptor { @@ -73,11 +65,6 @@ interface MountedDevToolDescriptor { unmountHandler: () => void; } -interface DataSourceOption extends EuiComboBoxOptionOption { - id: string; - label: string; -} - function DevToolsWrapper({ devTools, activeDevTool, @@ -85,10 +72,9 @@ function DevToolsWrapper({ savedObjects, notifications: { toasts }, dataSourceEnabled, + hideLocalCluster, }: DevToolsWrapperProps) { const mountedTool = useRef(null); - const [dataSources, setDataSources] = useState([]); - const [selectedOptions, setSelectedOptions] = useState([]); useEffect( () => () => { @@ -99,33 +85,8 @@ function DevToolsWrapper({ [] ); - useEffectOnce(() => { - fetchDataSources(); - }); - - const fetchDataSources = () => { - getDataSources(savedObjects.client) - .then((fetchedDataSources) => { - if (fetchedDataSources?.length) { - const dataSourceOptions = fetchedDataSources.map((dataSource) => ({ - id: dataSource.id, - label: dataSource.title, - })); - setDataSources(dataSourceOptions); - } - }) - .catch(() => { - toasts.addDanger( - i18n.translate('devTools.devToolWrapper.fetchDataSourceError', { - defaultMessage: 'Unable to fetch existing data sources', - }) - ); - }); - }; - const onChange = async (e: Array>) => { const dataSourceId = e[0] ? e[0].id : undefined; - setSelectedOptions(e); await remount(mountedTool.current!.mountpoint, dataSourceId); }; @@ -171,21 +132,14 @@ function DevToolsWrapper({ ))} {dataSourceEnabled ? ( -
- +
) : null} @@ -202,7 +156,12 @@ function DevToolsWrapper({ mountedTool.current.devTool !== activeDevTool || mountedTool.current.mountpoint !== element) ) { - await remount(element); + let initialDataSourceId; + if (!dataSourceEnabled || (dataSourceEnabled && !hideLocalCluster)) { + initialDataSourceId = ''; + } + + await remount(element, initialDataSourceId); } }} /> @@ -254,17 +213,19 @@ function setBreadcrumbs(chrome: ChromeStart) { } export function renderApp( - { application, chrome, savedObjects, notifications }: CoreStart, + { application, chrome, docLinks, savedObjects, notifications }: CoreStart, element: HTMLElement, history: ScopedHistory, devTools: readonly DevToolApp[], { dataSource }: DevToolsSetupDependencies ) { const dataSourceEnabled = !!dataSource; + const hideLocalCluster = dataSource?.hideLocalCluster ?? false; if (redirectOnMissingCapabilities(application)) { return () => {}; } + addHelpMenuToAppChrome(chrome, docLinks); setBadge(application, chrome); setBreadcrumbs(chrome); setTitle(chrome); @@ -289,6 +250,7 @@ export function renderApp( savedObjects={savedObjects} notifications={notifications} dataSourceEnabled={dataSourceEnabled} + hideLocalCluster={hideLocalCluster} /> )} /> diff --git a/src/plugins/dev_tools/public/index.scss b/src/plugins/dev_tools/public/index.scss index 38bb0d31f166..a526bde3b659 100644 --- a/src/plugins/dev_tools/public/index.scss +++ b/src/plugins/dev_tools/public/index.scss @@ -22,7 +22,7 @@ flex-grow: 1; } -.devAppDataSourcePicker { +.devAppClusterSelector { margin: 7px 8px 0 0; min-width: 400px; } diff --git a/src/plugins/dev_tools/public/utils/util.ts b/src/plugins/dev_tools/public/utils/util.ts new file mode 100644 index 000000000000..7738e84bd15f --- /dev/null +++ b/src/plugins/dev_tools/public/utils/util.ts @@ -0,0 +1,24 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { i18n } from '@osd/i18n'; +import { CoreStart } from 'opensearch-dashboards/public'; + +export function addHelpMenuToAppChrome( + chrome: CoreStart['chrome'], + docLinks: CoreStart['docLinks'] +) { + chrome.setHelpExtension({ + appName: i18n.translate('devTools.helpMenu.appName', { + defaultMessage: 'Dev Tools', + }), + links: [ + { + linkType: 'documentation', + href: `${docLinks.links.opensearchDashboards.devTools}`, + }, + ], + }); +} diff --git a/src/plugins/discover/common/index.ts b/src/plugins/discover/common/index.ts index 0cac73333e25..45887df880ae 100644 --- a/src/plugins/discover/common/index.ts +++ b/src/plugins/discover/common/index.ts @@ -4,7 +4,6 @@ */ export const PLUGIN_ID = 'discover'; -export const NEW_DISCOVER_APP = 'discover:v2'; export const DEFAULT_COLUMNS_SETTING = 'defaultColumns'; export const SAMPLE_SIZE_SETTING = 'discover:sampleSize'; export const AGGS_TERMS_SIZE_SETTING = 'discover:aggs:terms:size'; diff --git a/src/plugins/discover/public/application/components/chart/chart.tsx b/src/plugins/discover/public/application/components/chart/chart.tsx index 8ba188af3dd9..f21ce3e4ef19 100644 --- a/src/plugins/discover/public/application/components/chart/chart.tsx +++ b/src/plugins/discover/public/application/components/chart/chart.tsx @@ -21,8 +21,8 @@ import { useDiscoverContext } from '../../view_components/context'; import { setInterval, useDispatch, useSelector } from '../../utils/state_management'; interface DiscoverChartProps { - bucketInterval: TimechartHeaderBucketInterval; - chartData: Chart; + bucketInterval?: TimechartHeaderBucketInterval; + chartData?: Chart; config: IUiSettingsClient; data: DataPublicPluginStart; hits: number; diff --git a/src/plugins/discover/public/application/components/chart/histogram/histogram.tsx b/src/plugins/discover/public/application/components/chart/histogram/histogram.tsx index 51d3f1f1b706..87917c113c2f 100644 --- a/src/plugins/discover/public/application/components/chart/histogram/histogram.tsx +++ b/src/plugins/discover/public/application/components/chart/histogram/histogram.tsx @@ -306,6 +306,14 @@ export class DiscoverHistogram extends Component { expect(result.ordered.interval.asHours()).toBe(1); expect(result.ordered.intervalOpenSearchUnit).toBe('h'); expect(result.ordered.intervalOpenSearchValue).toBe(1); - expect(result.ordered.min.format()).toBe('2023-01-01T00:00:00+00:00'); - expect(result.ordered.max.format()).toBe('2023-01-02T00:00:00+00:00'); + expect(result.ordered.min.format()).toBe(moment('2023-01-01T00:00:00+00:00').format()); + expect(result.ordered.max.format()).toBe(moment('2023-01-02T00:00:00+00:00').format()); expect(result.yAxisLabel).toEqual('Y Axis'); expect(result.values).toEqual([ { x: 10, y: 100 }, diff --git a/src/plugins/discover/public/application/components/data_grid/_data_grid_table.scss b/src/plugins/discover/public/application/components/data_grid/_data_grid_table.scss new file mode 100644 index 000000000000..7f3fa66e0520 --- /dev/null +++ b/src/plugins/discover/public/application/components/data_grid/_data_grid_table.scss @@ -0,0 +1,32 @@ +.discoverDataGrid { + // stylelint-disable-next-line @osd/stylelint/no_modifying_global_selectors + .euiDataGrid__content { + @include ouiCodeFont; + + font-size: 12px; + } + + // stylelint-disable-next-line @osd/stylelint/no_modifying_global_selectors + .euiDataGridRowCell { + font-size: inherit; + line-height: 1.6666em; + + .source { + dd, + dl, + dt { + font-size: inherit !important; + } + } + } + + // stylelint-disable-next-line @osd/stylelint/no_modifying_global_selectors + .euiDataGridHeaderCell { + font-size: inherit; + } + + // stylelint-disable-next-line @osd/stylelint/no_modifying_global_selectors + .euiDataGridRowCell__expandButton { + top: calc(1.6666em / 2 + 6px); + } +} diff --git a/src/plugins/discover/public/application/components/data_grid/constants.ts b/src/plugins/discover/public/application/components/data_grid/constants.ts deleted file mode 100644 index be96468a3a09..000000000000 --- a/src/plugins/discover/public/application/components/data_grid/constants.ts +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -export const toolbarVisibility = { - showColumnSelector: { - allowHide: false, - allowReorder: true, - }, - showStyleSelector: false, - showFullScreenSelector: false, -}; diff --git a/src/plugins/discover/public/application/components/data_grid/data_grid_table.tsx b/src/plugins/discover/public/application/components/data_grid/data_grid_table.tsx index 596e0b97ae07..a0c1851e7716 100644 --- a/src/plugins/discover/public/application/components/data_grid/data_grid_table.tsx +++ b/src/plugins/discover/public/application/components/data_grid/data_grid_table.tsx @@ -3,35 +3,49 @@ * SPDX-License-Identifier: Apache-2.0 */ +import './_data_grid_table.scss'; + import React, { useState, useMemo, useCallback } from 'react'; import { EuiDataGrid, EuiDataGridSorting, EuiPanel } from '@elastic/eui'; -import { IndexPattern } from '../../../opensearch_dashboards_services'; +import { IndexPattern, getServices } from '../../../opensearch_dashboards_services'; import { fetchTableDataCell } from './data_grid_table_cell_value'; import { buildDataGridColumns, computeVisibleColumns } from './data_grid_table_columns'; import { DocViewInspectButton } from './data_grid_table_docview_inspect_button'; import { DataGridFlyout } from './data_grid_table_flyout'; import { DiscoverGridContextProvider } from './data_grid_table_context'; -import { toolbarVisibility } from './constants'; import { DocViewFilterFn, OpenSearchSearchHit } from '../../doc_views/doc_views_types'; -import { DiscoverServices } from '../../../build_services'; import { usePagination } from '../utils/use_pagination'; -import { SortOrder } from '../../../saved_searches/types'; import { buildColumns } from '../../utils/columns'; +import { + DOC_HIDE_TIME_COLUMN_SETTING, + SAMPLE_SIZE_SETTING, + SORT_DEFAULT_ORDER_SETTING, +} from '../../../../common'; +import { UI_SETTINGS } from '../../../../../data/common'; +import { LegacyDiscoverTable } from '../default_discover_table/default_discover_table'; +import { getNewDiscoverSetting } from '../utils/local_storage'; +import { SortDirection, SortOrder } from '../../../saved_searches/types'; +import { useToolbarOptions } from './data_grid_toolbar'; export interface DataGridTableProps { columns: string[]; indexPattern: IndexPattern; onAddColumn: (column: string) => void; onFilter: DocViewFilterFn; + onMoveColumn: (colName: string, destination: number) => void; onRemoveColumn: (column: string) => void; - onSort: (sort: SortOrder[]) => void; + hits?: number; + onSort: (s: SortOrder[]) => void; rows: OpenSearchSearchHit[]; onSetColumns: (columns: string[]) => void; sort: SortOrder[]; displayTimeColumn: boolean; - services: DiscoverServices; + title?: string; + description?: string; isToolbarVisible?: boolean; isContextView?: boolean; + isLoading?: boolean; + showPagination?: boolean; } export const DataGridTable = ({ @@ -39,18 +53,34 @@ export const DataGridTable = ({ indexPattern, onAddColumn, onFilter, + onMoveColumn, onRemoveColumn, onSetColumns, onSort, sort, + hits, rows, displayTimeColumn, + title = '', + description = '', isToolbarVisible = true, isContextView = false, + isLoading = false, + showPagination, }: DataGridTableProps) => { + const services = getServices(); const [inspectedHit, setInspectedHit] = useState(); const rowCount = useMemo(() => (rows ? rows.length : 0), [rows]); - const pagination = usePagination(rowCount); + const { toolbarOptions, lineCount } = useToolbarOptions(); + const [pageSizeLimit, isShortDots, hideTimeColumn, defaultSortOrder] = useMemo(() => { + return [ + services.uiSettings.get(SAMPLE_SIZE_SETTING), + services.uiSettings.get(UI_SETTINGS.SHORT_DOTS_ENABLE), + services.uiSettings.get(DOC_HIDE_TIME_COLUMN_SETTING), + services.uiSettings.get(SORT_DEFAULT_ORDER_SETTING, 'desc') as SortDirection, + ]; + }, [services.uiSettings]); + const pagination = usePagination({ rowCount, pageSizeLimit }); let adjustedColumns = buildColumns(columns); // handle case where the user removes selected filed and leaves only time column @@ -67,10 +97,10 @@ export const DataGridTable = ({ const rowHeightsOptions = useMemo( () => ({ defaultHeight: { - lineCount: adjustedColumns.includes('_source') ? 3 : 1, + lineCount: lineCount || (includeSourceInColumns ? 3 : 1), }, }), - [adjustedColumns] + [includeSourceInColumns, lineCount] ); const onColumnSort = useCallback( @@ -80,12 +110,13 @@ export const DataGridTable = ({ [onSort] ); - const renderCellValue = useMemo(() => fetchTableDataCell(indexPattern, rows), [ + const renderCellValue = useMemo(() => fetchTableDataCell(indexPattern, rows, isShortDots), [ indexPattern, + isShortDots, rows, ]); - const dataGridTableColumns = useMemo( + const displayedTableColumns = useMemo( () => buildDataGridColumns( adjustedColumns, @@ -127,11 +158,53 @@ export const DataGridTable = ({ ]; }, []); - const table = useMemo( + const newDiscoverEnabled = getNewDiscoverSetting(services.storage); + + const legacyDiscoverTable = useMemo( + () => ( + setInspectedHit(undefined)} + sampleSize={pageSizeLimit} + showPagination={showPagination} + isShortDots={isShortDots} + hideTimeColumn={hideTimeColumn} + defaultSortOrder={defaultSortOrder} + /> + ), + [ + adjustedColumns, + hits, + rows, + indexPattern, + sort, + onSort, + onRemoveColumn, + onMoveColumn, + onAddColumn, + onFilter, + pageSizeLimit, + showPagination, + defaultSortOrder, + hideTimeColumn, + isShortDots, + ] + ); + + const dataGridTable = useMemo( () => ( ), [ - dataGridTableColumns, + displayedTableColumns, dataGridTableColumnsVisibility, leadingControlColumns, pagination, @@ -152,10 +226,27 @@ export const DataGridTable = ({ rowCount, sorting, isToolbarVisible, + toolbarOptions, rowHeightsOptions, ] ); + const tablePanelProps = newDiscoverEnabled + ? { + paddingSize: 'none' as const, + style: { + margin: '8px', + }, + color: 'transparent' as const, + } + : { + paddingSize: 'none' as const, + style: { + margin: '0px', + }, + color: 'transparent' as const, + }; + return ( - <> - - - {table} - +
+ + {newDiscoverEnabled ? dataGridTable : legacyDiscoverTable} - {inspectedHit && ( + {newDiscoverEnabled && inspectedHit && ( setInspectedHit(undefined)} /> )} - +
); }; diff --git a/src/plugins/discover/public/application/components/data_grid/data_grid_table_cell_value.test.tsx b/src/plugins/discover/public/application/components/data_grid/data_grid_table_cell_value.test.tsx index a4b640a34a30..d877b0db71e4 100644 --- a/src/plugins/discover/public/application/components/data_grid/data_grid_table_cell_value.test.tsx +++ b/src/plugins/discover/public/application/components/data_grid/data_grid_table_cell_value.test.tsx @@ -66,7 +66,7 @@ const customizedIndexPatternMock = getMockedIndexPatternWithCustomizedFields( describe('Testing fetchTableDataCell function', () => { it('should display empty span if no data', () => { - const DataGridTableCellValue = fetchTableDataCell(indexPatternMock, dataRowsMock); + const DataGridTableCellValue = fetchTableDataCell(indexPatternMock, dataRowsMock, false); const comp = shallow( { }); it('should display empty span if field is not defined in index pattern', () => { - const DataGridTableCellValue = fetchTableDataCell(indexPatternMock, dataRowsMock); + const DataGridTableCellValue = fetchTableDataCell(indexPatternMock, dataRowsMock, false); const comp = shallow( { }); it('should display JSON string representation of the data if columnId is _source and isDetails is false', () => { - const DataGridTableCellValue = fetchTableDataCell(customizedIndexPatternMock, dataRowsMock); + const DataGridTableCellValue = fetchTableDataCell( + customizedIndexPatternMock, + dataRowsMock, + false + ); const comp = shallow( { }); it('should display EuiDescriptionList if columnId is _source and isDetails is false', () => { - const DataGridTableCellValue = fetchTableDataCell(customizedIndexPatternMock, dataRowsMock); + const DataGridTableCellValue = fetchTableDataCell( + customizedIndexPatternMock, + dataRowsMock, + false + ); const comp = shallow( { expect(comp).toMatchInlineSnapshot(` - order_date + order_date: { }); it('should correctly display data if columnId is in index pattern and is not _source', () => { - const DataGridTableCellValue = fetchTableDataCell(customizedIndexPatternMock, dataRowsMock); + const DataGridTableCellValue = fetchTableDataCell( + customizedIndexPatternMock, + dataRowsMock, + false + ); const comp = shallow( , columnId: string, - isDetails: boolean + isDetails: boolean, + isShortDots: boolean ) { if (isDetails) { - return {JSON.stringify(row[columnId], null, 2)}; + return {stringify(row[columnId], null, 2)}; } const formattedRow = idxPattern.formatHit(row); + const rawKeys = Object.keys(formattedRow); + const keys = isShortDots ? rawKeys.map((k) => shortenDottedString(k)) : rawKeys; return ( - - {Object.keys(formattedRow).map((key) => ( + + {keys.map((key, index) => ( - {key} + {key + ':'} + {index !== keys.length - 1 && ' '} ))} @@ -45,7 +51,8 @@ function fetchSourceTypeDataCell( export const fetchTableDataCell = ( idxPattern: IndexPattern, - dataRows: OpenSearchSearchHit[] | undefined + dataRows: OpenSearchSearchHit[] | undefined, + isShortDots: boolean ) => ({ rowIndex, columnId, isDetails }: EuiDataGridCellValueElementProps) => { const singleRow = dataRows ? (dataRows[rowIndex] as Record) : undefined; const flattenedRows = dataRows ? dataRows.map((hit) => idxPattern.flattenHit(hit)) : []; @@ -58,23 +65,23 @@ export const fetchTableDataCell = ( return -; } - if (!fieldInfo?.type && flattenedRow && typeof flattenedRow[columnId] === 'object') { + if (!fieldInfo?.type && typeof flattenedRow?.[columnId] === 'object') { if (isDetails) { - return {JSON.stringify(flattenedRow[columnId], null, 2)}; + return {stringify(flattenedRow[columnId], null, 2)}; } - return {JSON.stringify(flattenedRow[columnId])}; + return {stringify(flattenedRow[columnId])}; } if (fieldInfo?.type === '_source') { - return fetchSourceTypeDataCell(idxPattern, singleRow, columnId, isDetails); + return fetchSourceTypeDataCell(idxPattern, singleRow, columnId, isDetails, isShortDots); } const formattedValue = idxPattern.formatField(singleRow, columnId); if (typeof formattedValue === 'undefined') { return -; } else { - const sanitizedCellValue = dompurify.sanitize(idxPattern.formatField(singleRow, columnId)); + const sanitizedCellValue = dompurify.sanitize(formattedValue); return ( // eslint-disable-next-line react/no-danger diff --git a/src/plugins/discover/public/application/components/data_grid/data_grid_table_docview_inspect_button.tsx b/src/plugins/discover/public/application/components/data_grid/data_grid_table_docview_inspect_button.tsx index df5e691240e3..88341aad1b08 100644 --- a/src/plugins/discover/public/application/components/data_grid/data_grid_table_docview_inspect_button.tsx +++ b/src/plugins/discover/public/application/components/data_grid/data_grid_table_docview_inspect_button.tsx @@ -22,6 +22,7 @@ export const DocViewInspectButton = ({ rowIndex }: EuiDataGridCellValueElementPr onClick={() => setInspectedHit(isCurrentInspected ? undefined : currentInspected)} iconType={isCurrentInspected ? 'minimize' : 'inspect'} aria-label={inspectHintMsg} + data-test-subj={`docTableExpandToggleColumn-${rowIndex}`} /> ); diff --git a/src/plugins/discover/public/application/components/data_grid/data_grid_table_flyout.tsx b/src/plugins/discover/public/application/components/data_grid/data_grid_table_flyout.tsx index 957679a2faef..315d5ca9c006 100644 --- a/src/plugins/discover/public/application/components/data_grid/data_grid_table_flyout.tsx +++ b/src/plugins/discover/public/application/components/data_grid/data_grid_table_flyout.tsx @@ -13,14 +13,15 @@ import { EuiFlexItem, EuiTitle, } from '@elastic/eui'; +import { FormattedMessage } from '@osd/i18n/react'; import { DocViewer } from '../doc_viewer/doc_viewer'; import { IndexPattern } from '../../../opensearch_dashboards_services'; -import { DocViewFilterFn } from '../../doc_views/doc_views_types'; +import { DocViewFilterFn, OpenSearchSearchHit } from '../../doc_views/doc_views_types'; import { DocViewerLinks } from '../doc_viewer_links/doc_viewer_links'; interface Props { columns: string[]; - hit: any; + hit: OpenSearchSearchHit; indexPattern: IndexPattern; onAddColumn: (column: string) => void; onClose: () => void; @@ -40,10 +41,12 @@ export function DataGridFlyout({ // TODO: replace EuiLink with doc_view_links registry // TODO: Also move the flyout higher in the react tree to prevent redrawing the table component and slowing down page performance return ( - + -

Document Details

+

+ +

diff --git a/src/plugins/discover/public/application/components/data_grid/data_grid_toolbar.tsx b/src/plugins/discover/public/application/components/data_grid/data_grid_toolbar.tsx new file mode 100644 index 000000000000..07204560040e --- /dev/null +++ b/src/plugins/discover/public/application/components/data_grid/data_grid_toolbar.tsx @@ -0,0 +1,81 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + EuiButtonEmpty, + EuiDataGridToolBarVisibilityOptions, + EuiFormRow, + EuiPopover, + EuiRange, +} from '@elastic/eui'; +import React, { useState } from 'react'; +import { useLocalStorage } from 'react-use'; + +const AddtitionalControls = ({ + setLineCount, + lineCount, +}: { + setLineCount: (lineCount: number) => void; + lineCount: number; +}) => { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + + const handleRangeChange = (e: any) => { + setLineCount(Number(e.target.value)); + }; + + const onButtonClick = () => setIsPopoverOpen((open) => !open); + const closePopover = () => {}; + + const button = ( + + Display + + ); + + return ( + + + + + + ); +}; + +export const useToolbarOptions = (): { + toolbarOptions: EuiDataGridToolBarVisibilityOptions | boolean; + lineCount: number; +} => { + const [lineCount, setLineCount] = useLocalStorage('discover:lineCount', 1); + + const toolbarOptions = { + showColumnSelector: { + allowHide: false, + allowReorder: true, + }, + showStyleSelector: false, + showFullScreenSelector: false, + additionalControls: , + }; + + return { + toolbarOptions, + lineCount, + }; +}; diff --git a/src/plugins/discover_legacy/public/application/angular/doc_table/_doc_table.scss b/src/plugins/discover/public/application/components/default_discover_table/_doc_table.scss similarity index 82% rename from src/plugins/discover_legacy/public/application/angular/doc_table/_doc_table.scss rename to src/plugins/discover/public/application/components/default_discover_table/_doc_table.scss index bc75d4171004..1e780a7e4d8a 100644 --- a/src/plugins/discover_legacy/public/application/angular/doc_table/_doc_table.scss +++ b/src/plugins/discover/public/application/components/default_discover_table/_doc_table.scss @@ -42,11 +42,18 @@ doc-table { .osd-table, .osdDocTable { + @include ouiCodeFont; + + // To fight intruding styles that conflict with OUI's + & > tbody > tr > td { + line-height: inherit; + } + /** - * Style OpenSearch document _source in table view
key:
value
- * Use alpha so this will stand out against non-white backgrounds, e.g. the highlighted - * row in the Context Log. - */ + * Style OpenSearch document _source in table view
key:
value
+ * Use alpha so this will stand out against non-white backgrounds, e.g. the highlighted + * row in the Context Log. + */ dl.source { margin-bottom: 0; @@ -59,9 +66,9 @@ doc-table { } dt { - background-color: tintOrShade($euiColorPrimary, 90%, 70%); + background-color: transparentize(shade($euiColorPrimary, 20%), 0.9); color: $euiTextColor; - padding: ($euiSizeXS / 2) $euiSizeXS; + padding: calc($euiSizeXS / 2) $euiSizeXS; margin-right: $euiSizeXS; word-break: normal; border-radius: $euiBorderRadius; diff --git a/src/plugins/discover/public/application/components/default_discover_table/_pagination.scss b/src/plugins/discover/public/application/components/default_discover_table/_pagination.scss new file mode 100644 index 000000000000..793e707e2085 --- /dev/null +++ b/src/plugins/discover/public/application/components/default_discover_table/_pagination.scss @@ -0,0 +1,7 @@ +.osdDocTable_pagination { + width: 100%; + + & ~ table { + margin-bottom: 0; + } +} diff --git a/src/plugins/discover/public/application/components/default_discover_table/_table_cell.scss b/src/plugins/discover/public/application/components/default_discover_table/_table_cell.scss new file mode 100644 index 000000000000..c960e87a9477 --- /dev/null +++ b/src/plugins/discover/public/application/components/default_discover_table/_table_cell.scss @@ -0,0 +1,97 @@ +.osdDocTable__detailsParent { + border-top: none !important; +} + +// stylelint-disable-next-line @osd/stylelint/no_modifying_global_selectors +.euiFlexItem.osdDocTable__detailsIconContainer { + margin-right: 0; +} + +.osd-table td.osdDocTableCell__toggleDetails { + padding: 5px 0 0 4px; +} + +/** + * 1. Align icon with text in cell. + * 2. Use opacity to make this element accessible to screen readers and keyboard. + * 3. Show on focus to enable keyboard accessibility. + */ + +.osdDocTableCell { + position: relative; + + &__filter { + position: absolute; + display: flex; + flex-grow: 0; + + // Vertically align the button group with the first line of text + // 8px is set by .table and 2em is the line-height + top: calc(2em / 2 + 8px); + transform: translateY(-50%); + + // Stick it to the right but use the padding of the container to distance it from the edge (below) + right: 0; + + // Just to have some distance from the content behind it; larger for left so we can show a gradiant + // 8px is set by .table + padding: 8px 8px 8px 16px; + + &::before { + content: ""; + position: absolute; + display: block; + right: 0; + top: 0; + height: 100%; + width: 100%; + background-image: linear-gradient(to right, transparent 0, $ouiColorEmptyShade 16px); + z-index: 1; + } + + & > * { + // So they will appear over the background in ::before + z-index: 2; + } + } + + &__filterButton, + &__filter { + opacity: 0; + transition: opacity $euiAnimSpeedFast; + + @include ouiBreakpoint("xs", "s", "m") { + opacity: 1; + } + } + + &:hover &__filterButton, + &:focus &__filterButton, + &:hover &__filter, + &:focus &__filter { + opacity: 1; + } + + .osdDescriptionListFieldTitle { + margin: 0 4px 0 0 !important; + } + + // stylelint-disable-next-line @osd/stylelint/no_modifying_global_selectors + &.eui-textNoWrap { + // To make sure the time-series column never stretches + width: 1%; + } +} + +.osdDocTableCell__source { + .truncate-by-height { + transform: translateY(-1.5px); + margin-bottom: -1.5px; + } + + dd, + dl, + dt { + font-size: inherit !important; + } +} diff --git a/src/plugins/discover/public/application/components/default_discover_table/_table_header.scss b/src/plugins/discover/public/application/components/default_discover_table/_table_header.scss new file mode 100644 index 000000000000..cd29c1e54ad0 --- /dev/null +++ b/src/plugins/discover/public/application/components/default_discover_table/_table_header.scss @@ -0,0 +1,36 @@ +.osdDocTableHeader { + white-space: nowrap; + text-align: left; +} + +.osdDocTableHeader button { + margin-left: $euiSizeXS; +} + +.osdDocTableHeader__move, +.osdDocTableHeader__sortChange { + opacity: 0; + + &:focus, + th:hover & { + opacity: 1; + } +} + +.docTableHeaderField { + &__actionButton { + opacity: 0; + height: 10px; + width: 10px; + transition: opacity $euiAnimSpeedFast; + + @include ouiBreakpoint("xs", "s", "m") { + opacity: 1; + } + } + + &:hover &__actionButton, + &:focus &__actionButton { + opacity: 1; + } +} diff --git a/src/plugins/discover/public/application/components/default_discover_table/default_discover_table.tsx b/src/plugins/discover/public/application/components/default_discover_table/default_discover_table.tsx new file mode 100644 index 000000000000..fe8092ed8c9c --- /dev/null +++ b/src/plugins/discover/public/application/components/default_discover_table/default_discover_table.tsx @@ -0,0 +1,195 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import './_doc_table.scss'; + +import React, { useEffect, useRef, useState } from 'react'; +import { EuiButtonEmpty, EuiCallOut, EuiProgress } from '@elastic/eui'; +import { FormattedMessage } from '@osd/i18n/react'; +import { TableHeader } from './table_header'; +import { DocViewFilterFn, OpenSearchSearchHit } from '../../doc_views/doc_views_types'; +import { TableRow } from './table_rows'; +import { IndexPattern } from '../../../opensearch_dashboards_services'; +import { Pagination } from './pagination'; +import { getLegacyDisplayedColumns } from './helper'; +import { SortDirection, SortOrder } from '../../../saved_searches/types'; + +export interface DefaultDiscoverTableProps { + columns: string[]; + hits?: number; + rows: OpenSearchSearchHit[]; + indexPattern: IndexPattern; + sort: SortOrder[]; + onSort: (s: SortOrder[]) => void; + onRemoveColumn: (column: string) => void; + onMoveColumn: (colName: string, destination: number) => void; + onAddColumn: (column: string) => void; + onFilter: DocViewFilterFn; + onClose: () => void; + sampleSize: number; + isShortDots: boolean; + hideTimeColumn: boolean; + defaultSortOrder: SortDirection; + showPagination?: boolean; +} + +export const LegacyDiscoverTable = ({ + columns, + hits, + rows, + indexPattern, + sort, + onSort, + onRemoveColumn, + onMoveColumn, + onAddColumn, + onFilter, + onClose, + sampleSize, + isShortDots, + hideTimeColumn, + defaultSortOrder, + showPagination, +}: DefaultDiscoverTableProps) => { + const displayedColumns = getLegacyDisplayedColumns( + columns, + indexPattern, + hideTimeColumn, + isShortDots + ); + const displayedColumnNames = displayedColumns.map((column) => column.name); + const pageSize = 50; + const [renderedRowCount, setRenderedRowCount] = useState(50); // Start with 50 rows + const [displayedRows, setDisplayedRows] = useState(rows.slice(0, pageSize)); + const [currentRowCounts, setCurrentRowCounts] = useState({ + startRow: 0, + endRow: rows.length < pageSize ? rows.length : pageSize, + }); + const observerRef = useRef(null); + const sentinelRef = useRef(null); + + const loadMoreRows = () => { + setRenderedRowCount((prevRowCount) => prevRowCount + 50); // Load 50 more rows + }; + + useEffect(() => { + const sentinel = sentinelRef.current; + observerRef.current = new IntersectionObserver( + (entries) => { + if (entries[0].isIntersecting) { + loadMoreRows(); + } + }, + { threshold: 1.0 } + ); + + if (sentinelRef.current) { + observerRef.current.observe(sentinelRef.current); + } + + return () => { + if (observerRef.current && sentinel) { + observerRef.current.unobserve(sentinel); + } + }; + }, []); + + const [activePage, setActivePage] = useState(0); + const pageCount = Math.ceil(rows.length / pageSize); + + const goToPage = (pageNumber: number) => { + const startRow = pageNumber * pageSize; + const endRow = + rows.length < pageNumber * pageSize + pageSize + ? rows.length + : pageNumber * pageSize + pageSize; + setCurrentRowCounts({ + startRow, + endRow, + }); + setDisplayedRows(rows.slice(startRow, endRow)); + setActivePage(pageNumber); + }; + + return ( + indexPattern && ( + <> + {showPagination ? ( + + ) : null} + + + + + + {(showPagination ? displayedRows : rows.slice(0, renderedRowCount)).map( + (row: OpenSearchSearchHit, index: number) => { + return ( + + ); + } + )} + +
+ {!showPagination && renderedRowCount < rows.length && ( +
+ +
+ )} + {!showPagination && rows.length === sampleSize && ( + + + + window.scrollTo(0, 0)}> + + + + )} + {showPagination ? ( + + ) : null} + + ) + ); +}; diff --git a/src/plugins/discover_legacy/public/application/angular/doc_table/components/table_header/helpers.tsx b/src/plugins/discover/public/application/components/default_discover_table/helper.tsx similarity index 88% rename from src/plugins/discover_legacy/public/application/angular/doc_table/components/table_header/helpers.tsx rename to src/plugins/discover/public/application/components/default_discover_table/helper.tsx index 477b72f5b199..82ac73acd784 100644 --- a/src/plugins/discover_legacy/public/application/angular/doc_table/components/table_header/helpers.tsx +++ b/src/plugins/discover/public/application/components/default_discover_table/helper.tsx @@ -28,10 +28,18 @@ * under the License. */ -import { IndexPattern } from '../../../../../opensearch_dashboards_services'; -import { shortenDottedString } from '../../../../helpers'; +import { IndexPattern } from '../../../opensearch_dashboards_services'; +import { shortenDottedString } from '../../helpers'; + +export interface LegacyDisplayedColumn { + name: string; + displayName: string; + isSortable: boolean; + isRemoveable: boolean; + colLeftIdx: number; + colRightIdx: number; +} -export type SortOrder = [string, string]; export interface ColumnProps { name: string; displayName: string; @@ -66,12 +74,12 @@ export function getTimeColumn(timeFieldName: string): ColumnProps { * @param hideTimeField * @param isShortDots */ -export function getDisplayedColumns( +export function getLegacyDisplayedColumns( columns: string[], indexPattern: IndexPattern, hideTimeField: boolean, isShortDots: boolean -) { +): LegacyDisplayedColumn[] { if (!Array.isArray(columns) || typeof indexPattern !== 'object' || !indexPattern.getFieldByName) { return []; } diff --git a/src/plugins/discover/public/application/components/default_discover_table/pagination.tsx b/src/plugins/discover/public/application/components/default_discover_table/pagination.tsx new file mode 100644 index 000000000000..feec7631f735 --- /dev/null +++ b/src/plugins/discover/public/application/components/default_discover_table/pagination.tsx @@ -0,0 +1,63 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { EuiFlexGroup, EuiFlexItem, EuiPagination, EuiTextColor } from '@elastic/eui'; +import React from 'react'; +import { FormattedMessage } from '@osd/i18n/react'; +import './_pagination.scss'; + +interface Props { + pageCount: number; + activePage: number; + goToPage: (page: number) => void; + startItem: number; + endItem: number; + totalItems?: number; + sampleSize: number; +} + +export const Pagination = ({ + pageCount, + activePage, + goToPage, + startItem, + endItem, + totalItems = 0, + sampleSize, +}: Props) => { + return ( + + {endItem >= sampleSize && ( + + + + + + )} + + + + + goToPage(currentPage)} + /> + + + ); +}; diff --git a/src/plugins/discover/public/application/components/default_discover_table/table_cell.tsx b/src/plugins/discover/public/application/components/default_discover_table/table_cell.tsx new file mode 100644 index 000000000000..a542e70ff646 --- /dev/null +++ b/src/plugins/discover/public/application/components/default_discover_table/table_cell.tsx @@ -0,0 +1,86 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Any modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +import './_table_cell.scss'; + +import React from 'react'; +import { EuiButtonIcon, EuiToolTip } from '@elastic/eui'; +import { i18n } from '@osd/i18n'; +import { DocViewFilterFn } from '../../doc_views/doc_views_types'; + +export interface TableCellProps { + columnId: string; + isTimeField?: boolean; + onFilter: DocViewFilterFn; + filterable?: boolean; + fieldMapping?: any; + sanitizedCellValue: string; +} + +export const TableCell = ({ + columnId, + isTimeField, + onFilter, + fieldMapping, + sanitizedCellValue, +}: TableCellProps) => { + const content = ( + <> + {/* eslint-disable-next-line react/no-danger */} + + + + onFilter(columnId, fieldMapping, '+')} + iconType="plusInCircle" + aria-label={i18n.translate('discover.filterForValueLabel', { + defaultMessage: 'Filter for value', + })} + data-test-subj="filterForValue" + className="osdDocTableCell__filterButton" + /> + + + onFilter(columnId, fieldMapping, '-')} + iconType="minusInCircle" + aria-label={i18n.translate('discover.filterOutValueLabel', { + defaultMessage: 'Filter out value', + })} + data-test-subj="filterOutValue" + className="osdDocTableCell__filterButton" + /> + + + + ); + + return isTimeField ? ( + + {content} + + ) : ( + +
{content}
+ + ); +}; diff --git a/src/plugins/discover/public/application/components/default_discover_table/table_header.tsx b/src/plugins/discover/public/application/components/default_discover_table/table_header.tsx new file mode 100644 index 000000000000..52ef078387c8 --- /dev/null +++ b/src/plugins/discover/public/application/components/default_discover_table/table_header.tsx @@ -0,0 +1,59 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Any modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +import './_table_header.scss'; + +import React from 'react'; +import { IndexPattern } from '../../../opensearch_dashboards_services'; +import { TableHeaderColumn } from './table_header_column'; +import { LegacyDisplayedColumn } from './helper'; +import { getDefaultSort } from '../../view_components/utils/get_default_sort'; +import { SortDirection, SortOrder } from '../../../saved_searches/types'; + +interface Props { + displayedColumns: LegacyDisplayedColumn[]; + defaultSortOrder: SortDirection; + indexPattern: IndexPattern; + onChangeSortOrder?: (sortOrder: SortOrder[]) => void; + onRemoveColumn?: (name: string) => void; + onMoveColumn?: (colName: string, destination: number) => void; + sortOrder: SortOrder[]; +} + +export function TableHeader({ + displayedColumns, + defaultSortOrder, + indexPattern, + onChangeSortOrder, + onMoveColumn, + onRemoveColumn, + sortOrder, +}: Props) { + return ( + + + {displayedColumns.map((col) => { + return ( + + ); + })} + + ); +} diff --git a/src/plugins/discover_legacy/public/application/angular/doc_table/components/table_header/table_header_column.tsx b/src/plugins/discover/public/application/components/default_discover_table/table_header_column.tsx similarity index 78% rename from src/plugins/discover_legacy/public/application/angular/doc_table/components/table_header/table_header_column.tsx rename to src/plugins/discover/public/application/components/default_discover_table/table_header_column.tsx index 81dad59c6d34..9316eb81817e 100644 --- a/src/plugins/discover_legacy/public/application/angular/doc_table/components/table_header/table_header_column.tsx +++ b/src/plugins/discover/public/application/components/default_discover_table/table_header_column.tsx @@ -9,47 +9,30 @@ * GitHub history for details. */ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ +import './_table_header.scss'; -import React from 'react'; +import React, { ReactNode } from 'react'; import { i18n } from '@osd/i18n'; -import { EuiToolTip } from '@elastic/eui'; -import { SortOrder } from './helpers'; +import { EuiButtonIcon, EuiToolTip } from '@elastic/eui'; +import { SortOrder } from '../../../saved_searches/types'; interface Props { colLeftIdx: number; // idx of the column to the left, -1 if moving is not possible colRightIdx: number; // idx of the column to the right, -1 if moving is not possible - displayName: string; + displayName: ReactNode; isRemoveable: boolean; - isSortable: boolean; + isSortable?: boolean; name: string; onChangeSortOrder?: (sortOrder: SortOrder[]) => void; - onMoveColumn?: (name: string, idx: number) => void; + onMoveColumn?: (colName: string, destination: number) => void; onRemoveColumn?: (name: string) => void; sortOrder: SortOrder[]; } const sortDirectionToIcon: Record = { - desc: 'fa fa-sort-down', - asc: 'fa fa-sort-up', - '': 'fa fa-sort', + desc: 'sortDown', + asc: 'sortUp', + '': 'sortable', }; export function TableHeaderColumn({ @@ -64,14 +47,15 @@ export function TableHeaderColumn({ onRemoveColumn, sortOrder, }: Props) { - const [, sortDirection = ''] = sortOrder.find((sortPair) => name === sortPair[0]) || []; const currentSortWithoutColumn = sortOrder.filter((pair) => pair[0] !== name); const currentColumnSort = sortOrder.find((pair) => pair[0] === name); const currentColumnSortDirection = (currentColumnSort && currentColumnSort[1]) || ''; - const btnSortIcon = sortDirectionToIcon[sortDirection]; + const btnSortIcon = sortDirectionToIcon[currentColumnSortDirection]; const btnSortClassName = - sortDirection !== '' ? btnSortIcon : `osdDocTableHeader__sortChange ${btnSortIcon}`; + currentColumnSortDirection !== '' + ? btnSortIcon + : `osdDocTableHeader__sortChange ${btnSortIcon}`; const handleChangeSortOrder = () => { if (!onChangeSortOrder) return; @@ -84,7 +68,7 @@ export function TableHeaderColumn({ } else if (currentColumnSortDirection === 'desc' && currentSortWithoutColumn.length === 0) { // If we're at the end of the cycle and this is the only existing sort, we switch // back to ascending sort instead of removing it. - onChangeSortOrder([[name, 'asc']]); + onChangeSortOrder([...currentSortWithoutColumn, [name, 'asc']]); } else { onChangeSortOrder(currentSortWithoutColumn); } @@ -115,9 +99,9 @@ export function TableHeaderColumn({ if (currentColumnSort === undefined) { return sortAscendingMessage; - } else if (sortDirection === 'asc') { + } else if (currentColumnSortDirection === 'asc') { return sortDescendingMessage; - } else if (sortDirection === 'desc' && currentSortWithoutColumn.length === 0) { + } else if (currentColumnSortDirection === 'desc' && currentSortWithoutColumn.length === 0) { return sortAscendingMessage; } else { return stopSortingMessage; @@ -134,6 +118,7 @@ export function TableHeaderColumn({ onClick: handleChangeSortOrder, testSubject: `docTableHeaderFieldSort_${name}`, tooltip: getSortButtonAriaLabel(), + iconType: btnSortIcon, }, // Remove Button { @@ -148,6 +133,7 @@ export function TableHeaderColumn({ tooltip: i18n.translate('discover.docTable.tableHeader.removeColumnButtonTooltip', { defaultMessage: 'Remove Column', }), + iconType: 'cross', }, // Move Left Button { @@ -162,6 +148,7 @@ export function TableHeaderColumn({ tooltip: i18n.translate('discover.docTable.tableHeader.moveColumnLeftButtonTooltip', { defaultMessage: 'Move column to the left', }), + iconType: 'sortLeft', }, // Move Right Button { @@ -176,11 +163,12 @@ export function TableHeaderColumn({ tooltip: i18n.translate('discover.docTable.tableHeader.moveColumnRightButtonTooltip', { defaultMessage: 'Move column to the right', }), + iconType: 'sortRight', }, ]; return ( - + {displayName} {buttons @@ -191,11 +179,13 @@ export function TableHeaderColumn({ content={button.tooltip} key={`button-${idx}`} > - - -
-`; diff --git a/src/plugins/discover_legacy/public/application/angular/doc_table/components/pager/__snapshots__/tool_bar_pager_text.test.tsx.snap b/src/plugins/discover_legacy/public/application/angular/doc_table/components/pager/__snapshots__/tool_bar_pager_text.test.tsx.snap deleted file mode 100644 index fe168c013cb1..000000000000 --- a/src/plugins/discover_legacy/public/application/angular/doc_table/components/pager/__snapshots__/tool_bar_pager_text.test.tsx.snap +++ /dev/null @@ -1,10 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`it renders ToolBarPagerText without crashing 1`] = ` -
- 1–2 of 3 -
-`; diff --git a/src/plugins/discover_legacy/public/application/angular/doc_table/components/pager/index.ts b/src/plugins/discover_legacy/public/application/angular/doc_table/components/pager/index.ts deleted file mode 100644 index a0f5278d7881..000000000000 --- a/src/plugins/discover_legacy/public/application/angular/doc_table/components/pager/index.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { ToolBarPagerText } from './tool_bar_pager_text'; -import { ToolBarPagerButtons } from './tool_bar_pager_buttons'; - -export function createToolBarPagerTextDirective(reactDirective: any) { - return reactDirective(ToolBarPagerText); -} - -export function createToolBarPagerButtonsDirective(reactDirective: any) { - return reactDirective(ToolBarPagerButtons); -} diff --git a/src/plugins/discover_legacy/public/application/angular/doc_table/components/pager/tool_bar_pager_buttons.test.tsx b/src/plugins/discover_legacy/public/application/angular/doc_table/components/pager/tool_bar_pager_buttons.test.tsx deleted file mode 100644 index 2ac06b2b6ebf..000000000000 --- a/src/plugins/discover_legacy/public/application/angular/doc_table/components/pager/tool_bar_pager_buttons.test.tsx +++ /dev/null @@ -1,73 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { mountWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers'; -import { ToolBarPagerButtons } from './tool_bar_pager_buttons'; -import { findTestSubject } from 'test_utils/helpers'; - -test('it renders ToolBarPagerButtons', () => { - const props = { - hasPreviousPage: true, - hasNextPage: true, - onPageNext: jest.fn(), - onPagePrevious: jest.fn(), - }; - const wrapper = shallowWithIntl(); - expect(wrapper).toMatchSnapshot(); -}); - -test('it renders ToolBarPagerButtons with clickable next and previous button', () => { - const props = { - hasPreviousPage: true, - hasNextPage: true, - onPageNext: jest.fn(), - onPagePrevious: jest.fn(), - }; - const wrapper = mountWithIntl(); - findTestSubject(wrapper, 'btnPrevPage').simulate('click'); - expect(props.onPagePrevious).toHaveBeenCalledTimes(1); - findTestSubject(wrapper, 'btnNextPage').simulate('click'); - expect(props.onPageNext).toHaveBeenCalledTimes(1); -}); - -test('it renders ToolBarPagerButtons with disabled next and previous button', () => { - const props = { - hasPreviousPage: false, - hasNextPage: false, - onPageNext: jest.fn(), - onPagePrevious: jest.fn(), - }; - const wrapper = mountWithIntl(); - findTestSubject(wrapper, 'btnPrevPage').simulate('click'); - expect(props.onPagePrevious).toHaveBeenCalledTimes(0); - findTestSubject(wrapper, 'btnNextPage').simulate('click'); - expect(props.onPageNext).toHaveBeenCalledTimes(0); -}); diff --git a/src/plugins/discover_legacy/public/application/angular/doc_table/components/pager/tool_bar_pager_buttons.tsx b/src/plugins/discover_legacy/public/application/angular/doc_table/components/pager/tool_bar_pager_buttons.tsx deleted file mode 100644 index 04956583291c..000000000000 --- a/src/plugins/discover_legacy/public/application/angular/doc_table/components/pager/tool_bar_pager_buttons.tsx +++ /dev/null @@ -1,74 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { i18n } from '@osd/i18n'; - -interface Props { - hasPreviousPage: boolean; - hasNextPage: boolean; - onPageNext: () => void; - onPagePrevious: () => void; -} - -export function ToolBarPagerButtons(props: Props) { - return ( -
- - -
- ); -} diff --git a/src/plugins/discover_legacy/public/application/angular/doc_table/components/pager/tool_bar_pager_text.test.tsx b/src/plugins/discover_legacy/public/application/angular/doc_table/components/pager/tool_bar_pager_text.test.tsx deleted file mode 100644 index 4b13de634f04..000000000000 --- a/src/plugins/discover_legacy/public/application/angular/doc_table/components/pager/tool_bar_pager_text.test.tsx +++ /dev/null @@ -1,43 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { renderWithIntl } from 'test_utils/enzyme_helpers'; -import { ToolBarPagerText } from './tool_bar_pager_text'; - -test('it renders ToolBarPagerText without crashing', () => { - const props = { - startItem: 1, - endItem: 2, - totalItems: 3, - }; - const wrapper = renderWithIntl(); - expect(wrapper).toMatchSnapshot(); -}); diff --git a/src/plugins/discover_legacy/public/application/angular/doc_table/components/pager/tool_bar_pager_text.tsx b/src/plugins/discover_legacy/public/application/angular/doc_table/components/pager/tool_bar_pager_text.tsx deleted file mode 100644 index 28110891a2f9..000000000000 --- a/src/plugins/discover_legacy/public/application/angular/doc_table/components/pager/tool_bar_pager_text.tsx +++ /dev/null @@ -1,52 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { FormattedMessage, I18nProvider } from '@osd/i18n/react'; - -interface Props { - startItem: number; - endItem: number; - totalItems: number; -} - -export function ToolBarPagerText({ startItem, endItem, totalItems }: Props) { - return ( - -
- -
-
- ); -} diff --git a/src/plugins/discover_legacy/public/application/angular/doc_table/components/row_headers.test.js b/src/plugins/discover_legacy/public/application/angular/doc_table/components/row_headers.test.js deleted file mode 100644 index 1b0e03653d0d..000000000000 --- a/src/plugins/discover_legacy/public/application/angular/doc_table/components/row_headers.test.js +++ /dev/null @@ -1,508 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import angular from 'angular'; -import 'angular-mocks'; -import 'angular-sanitize'; -import 'angular-route'; -import _ from 'lodash'; -import sinon from 'sinon'; -import { getFakeRow } from 'fixtures/fake_row'; -import $ from 'jquery'; -import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; -import { - setScopedHistory, - setServices, - setDocViewsRegistry, - setDocViewsLinksRegistry, -} from '../../../../opensearch_dashboards_services'; -import { coreMock } from '../../../../../../../core/public/mocks'; -import { dataPluginMock } from '../../../../../../data/public/mocks'; -import { navigationPluginMock } from '../../../../../../navigation/public/mocks'; -import { getInnerAngularModule } from '../../../../get_inner_angular'; -import { createBrowserHistory } from 'history'; - -const fakeRowVals = { - time: 'time_formatted', - bytes: 'bytes_formatted', - '@timestamp': '@timestamp_formatted', - request_body: 'request_body_formatted', -}; - -describe('Doc Table', () => { - const core = coreMock.createStart(); - const dataMock = dataPluginMock.createStartContract(); - let $parentScope; - let $scope; - let $elementScope; - let timeout; - let registry = []; - - // Stub out a minimal mapping of 4 fields - let mapping; - - beforeAll(() => setScopedHistory(createBrowserHistory())); - beforeEach(() => { - angular.element.prototype.slice = jest.fn(function (index) { - return $(this).slice(index); - }); - angular.element.prototype.filter = jest.fn(function (condition) { - return $(this).filter(condition); - }); - angular.element.prototype.toggle = jest.fn(function (name) { - return $(this).toggle(name); - }); - angular.element.prototype.is = jest.fn(function (name) { - return $(this).is(name); - }); - setServices({ - uiSettings: core.uiSettings, - filterManager: dataMock.query.filterManager, - }); - - setDocViewsRegistry({ - addDocView(view) { - registry.push(view); - }, - getDocViewsSorted() { - return registry; - }, - resetRegistry: () => { - registry = []; - }, - }); - - setDocViewsLinksRegistry({ - addDocViewLink(view) { - registry.push(view); - }, - getDocViewsLinksSorted() { - return registry; - }, - resetRegistry: () => { - registry = []; - }, - }); - - getInnerAngularModule( - 'app/discover', - core, - { - data: dataMock, - navigation: navigationPluginMock.createStartContract(), - }, - coreMock.createPluginInitializerContext() - ); - angular.mock.module('app/discover'); - }); - beforeEach( - angular.mock.inject(function ($rootScope, Private, $timeout) { - $parentScope = $rootScope; - timeout = $timeout; - $parentScope.indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); - mapping = $parentScope.indexPattern.fields; - - // Stub `getConverterFor` for a field in the indexPattern to return mock data. - - const convertFn = (value, type, options) => { - const fieldName = _.get(options, 'field.name', null); - return fakeRowVals[fieldName] || ''; - }; - $parentScope.indexPattern.getFormatterForField = () => ({ - convert: convertFn, - getConverterFor: () => convertFn, - }); - }) - ); - - afterEach(() => { - delete angular.element.prototype.slice; - delete angular.element.prototype.filter; - delete angular.element.prototype.toggle; - delete angular.element.prototype.is; - }); - - // Sets up the directive, take an element, and a list of properties to attach to the parent scope. - const init = function ($elem, props) { - angular.mock.inject(function ($compile) { - _.assign($parentScope, props); - const el = $compile($elem)($parentScope); - $elementScope = el.scope(); - el.scope().$digest(); - $scope = el.isolateScope(); - }); - }; - - const destroy = () => { - $scope.$destroy(); - $parentScope.$destroy(); - }; - - // For testing column removing/adding for the header and the rows - const columnTests = function (elemType, parentElem) { - test('should create a time column if the timefield is defined', () => { - const childElems = parentElem.find(elemType); - expect(childElems.length).toBe(1); - }); - - test('should be able to add and remove columns', () => { - let childElems; - - // Should include a column for toggling and the time column by default - $parentScope.columns = ['bytes']; - $elementScope.$digest(); - childElems = parentElem.find(elemType); - expect(childElems.length).toBe(2); - expect($(childElems[1]).text()).toContain('bytes'); - - $parentScope.columns = ['bytes', 'request_body']; - $elementScope.$digest(); - childElems = parentElem.find(elemType); - expect(childElems.length).toBe(3); - expect($(childElems[2]).text()).toContain('request_body'); - - $parentScope.columns = ['request_body']; - $elementScope.$digest(); - childElems = parentElem.find(elemType); - expect(childElems.length).toBe(2); - expect($(childElems[1]).text()).toContain('request_body'); - }); - - test('should create only the toggle column if there is no timeField', () => { - delete $scope.indexPattern.timeFieldName; - $scope.$digest(); - timeout.flush(); - - const childElems = parentElem.find(elemType); - expect(childElems.length).toBe(0); - }); - }; - - describe('osdTableRow', () => { - const $elem = $( - '' - ); - let row; - - beforeEach(() => { - row = getFakeRow(0, mapping); - - init($elem, { - row, - columns: [], - sorting: [], - filter: sinon.spy(), - maxLength: 50, - }); - }); - afterEach(() => { - destroy(); - }); - - describe('adding and removing columns', () => { - columnTests('[data-test-subj~="docTableField"]', $elem); - }); - - describe('details row', () => { - test('should be an empty tr by default', () => { - expect($elem.next().is('tr')).toBe(true); - expect($elem.next().text()).toBe(''); - }); - - test('should expand the detail row when the toggle arrow is clicked', () => { - $elem.children(':first-child').click(); - expect($elem.next().text()).not.toBe(''); - }); - - describe('expanded', () => { - let $details; - beforeEach(() => { - // Open the row - $scope.toggleRow(); - timeout.flush(); - $details = $elem.next(); - }); - afterEach(() => { - // Close the row - $scope.toggleRow(); - }); - - test('should be a tr with something in it', () => { - expect($details.is('tr')).toBe(true); - expect($details.text()).toBeTruthy(); - }); - }); - }); - }); - - describe('osdTableRow meta', () => { - const $elem = angular.element( - '' - ); - let row; - - beforeEach(() => { - row = getFakeRow(0, mapping); - - init($elem, { - row: row, - columns: [], - sorting: [], - filtering: sinon.spy(), - maxLength: 50, - }); - - // Open the row - $scope.toggleRow(); - $scope.$digest(); - timeout.flush(); - $elem.next(); - }); - - afterEach(() => { - destroy(); - }); - - /** this no longer works with the new plugin approach - test('should render even when the row source contains a field with the same name as a meta field', () => { - setTimeout(() => { - //this should be overridden by later changes - }, 100); - expect($details.find('tr').length).toBe(_.keys($parentScope.indexPattern.flattenHit($scope.row)).length); - }); */ - }); - - describe('row diffing', () => { - let $row; - let $scope; - let $root; - let $before; - - beforeEach( - angular.mock.inject(function ($rootScope, $compile, Private) { - $root = $rootScope; - $root.row = getFakeRow(0, mapping); - $root.columns = ['_source']; - $root.sorting = []; - $root.filtering = sinon.spy(); - $root.maxLength = 50; - $root.mapping = mapping; - $root.indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); - - $row = $('').attr({ - 'osd-table-row': 'row', - columns: 'columns', - sorting: 'sorting', - filtering: 'filtering', - 'index-pattern': 'indexPattern', - }); - - $scope = $root.$new(); - $compile($row)($scope); - $root.$apply(); - - $before = $row.find('td'); - expect($before).toHaveLength(3); - expect($before.eq(0).text().trim()).toBe(''); - expect($before.eq(1).text().trim()).toMatch(/^time_formatted/); - }) - ); - - afterEach(() => { - $row.remove(); - }); - - test('handles a new column', () => { - $root.columns.push('bytes'); - $root.$apply(); - - const $after = $row.find('td'); - expect($after).toHaveLength(4); - expect($after[0].outerHTML).toBe($before[0].outerHTML); - expect($after[1].outerHTML).toBe($before[1].outerHTML); - expect($after[2].outerHTML).toBe($before[2].outerHTML); - expect($after.eq(3).text().trim()).toMatch(/^bytes_formatted/); - }); - - test('handles two new columns at once', () => { - $root.columns.push('bytes'); - $root.columns.push('request_body'); - $root.$apply(); - - const $after = $row.find('td'); - expect($after).toHaveLength(5); - expect($after[0].outerHTML).toBe($before[0].outerHTML); - expect($after[1].outerHTML).toBe($before[1].outerHTML); - expect($after[2].outerHTML).toBe($before[2].outerHTML); - expect($after.eq(3).text().trim()).toMatch(/^bytes_formatted/); - expect($after.eq(4).text().trim()).toMatch(/^request_body_formatted/); - }); - - test('handles three new columns in odd places', () => { - $root.columns = ['@timestamp', 'bytes', '_source', 'request_body']; - $root.$apply(); - - const $after = $row.find('td'); - expect($after).toHaveLength(6); - expect($after[0].outerHTML).toBe($before[0].outerHTML); - expect($after[1].outerHTML).toBe($before[1].outerHTML); - expect($after.eq(2).text().trim()).toMatch(/^@timestamp_formatted/); - expect($after.eq(3).text().trim()).toMatch(/^bytes_formatted/); - expect($after[4].outerHTML).toBe($before[2].outerHTML); - expect($after.eq(5).text().trim()).toMatch(/^request_body_formatted/); - }); - - test('handles a removed column', () => { - _.pull($root.columns, '_source'); - $root.$apply(); - - const $after = $row.find('td'); - expect($after).toHaveLength(2); - expect($after[0].outerHTML).toBe($before[0].outerHTML); - expect($after[1].outerHTML).toBe($before[1].outerHTML); - }); - - test('handles two removed columns', () => { - // first add a column - $root.columns.push('@timestamp'); - $root.$apply(); - - const $mid = $row.find('td'); - expect($mid).toHaveLength(4); - - $root.columns.pop(); - $root.columns.pop(); - $root.$apply(); - - const $after = $row.find('td'); - expect($after).toHaveLength(2); - expect($after[0].outerHTML).toBe($before[0].outerHTML); - expect($after[1].outerHTML).toBe($before[1].outerHTML); - }); - - test('handles three removed random columns', () => { - // first add two column - $root.columns.push('@timestamp', 'bytes'); - $root.$apply(); - - const $mid = $row.find('td'); - expect($mid).toHaveLength(5); - - $root.columns[0] = false; // _source - $root.columns[2] = false; // bytes - $root.columns = $root.columns.filter(Boolean); - $root.$apply(); - - const $after = $row.find('td'); - expect($after).toHaveLength(3); - expect($after[0].outerHTML).toBe($before[0].outerHTML); - expect($after[1].outerHTML).toBe($before[1].outerHTML); - expect($after.eq(2).text().trim()).toMatch(/^@timestamp_formatted/); - }); - - test('handles two columns with the same content', () => { - const tempVal = fakeRowVals.request_body; - fakeRowVals.request_body = 'bytes_formatted'; - - $root.columns.length = 0; - $root.columns.push('bytes'); - $root.columns.push('request_body'); - $root.$apply(); - - const $after = $row.find('td'); - expect($after).toHaveLength(4); - expect($after.eq(2).text().trim()).toMatch(/^bytes_formatted/); - expect($after.eq(3).text().trim()).toMatch(/^bytes_formatted/); - fakeRowVals.request_body = tempVal; - }); - - test('handles two columns swapping position', () => { - $root.columns.push('bytes'); - $root.$apply(); - - const $mid = $row.find('td'); - expect($mid).toHaveLength(4); - - $root.columns.reverse(); - $root.$apply(); - - const $after = $row.find('td'); - expect($after).toHaveLength(4); - expect($after[0].outerHTML).toBe($before[0].outerHTML); - expect($after[1].outerHTML).toBe($before[1].outerHTML); - expect($after[2].outerHTML).toBe($mid[3].outerHTML); - expect($after[3].outerHTML).toBe($mid[2].outerHTML); - }); - - test('handles four columns all reversing position', () => { - $root.columns.push('bytes', 'response', '@timestamp'); - $root.$apply(); - - const $mid = $row.find('td'); - expect($mid).toHaveLength(6); - - $root.columns.reverse(); - $root.$apply(); - - const $after = $row.find('td'); - expect($after).toHaveLength(6); - expect($after[0].outerHTML).toBe($before[0].outerHTML); - expect($after[1].outerHTML).toBe($before[1].outerHTML); - expect($after[2].outerHTML).toBe($mid[5].outerHTML); - expect($after[3].outerHTML).toBe($mid[4].outerHTML); - expect($after[4].outerHTML).toBe($mid[3].outerHTML); - expect($after[5].outerHTML).toBe($mid[2].outerHTML); - }); - - test('handles multiple columns with the same name', () => { - $root.columns.push('bytes', 'bytes', 'bytes'); - $root.$apply(); - - const $after = $row.find('td'); - expect($after).toHaveLength(6); - expect($after[0].outerHTML).toBe($before[0].outerHTML); - expect($after[1].outerHTML).toBe($before[1].outerHTML); - expect($after[2].outerHTML).toBe($before[2].outerHTML); - expect($after.eq(3).text().trim()).toMatch(/^bytes_formatted/); - expect($after.eq(4).text().trim()).toMatch(/^bytes_formatted/); - expect($after.eq(5).text().trim()).toMatch(/^bytes_formatted/); - }); - }); -}); diff --git a/src/plugins/discover_legacy/public/application/angular/doc_table/components/table_header.ts b/src/plugins/discover_legacy/public/application/angular/doc_table/components/table_header.ts deleted file mode 100644 index 1c60685d279e..000000000000 --- a/src/plugins/discover_legacy/public/application/angular/doc_table/components/table_header.ts +++ /dev/null @@ -1,58 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { TableHeader } from './table_header/table_header'; -import { getServices } from '../../../../opensearch_dashboards_services'; -import { SORT_DEFAULT_ORDER_SETTING, DOC_HIDE_TIME_COLUMN_SETTING } from '../../../../../common'; -import { UI_SETTINGS } from '../../../../../../data/public'; - -export function createTableHeaderDirective(reactDirective: any) { - const { uiSettings: config } = getServices(); - - return reactDirective( - TableHeader, - [ - ['columns', { watchDepth: 'collection' }], - ['hideTimeColumn', { watchDepth: 'value' }], - ['indexPattern', { watchDepth: 'reference' }], - ['isShortDots', { watchDepth: 'value' }], - ['onChangeSortOrder', { watchDepth: 'reference' }], - ['onMoveColumn', { watchDepth: 'reference' }], - ['onRemoveColumn', { watchDepth: 'reference' }], - ['sortOrder', { watchDepth: 'collection' }], - ], - { restrict: 'A' }, - { - hideTimeColumn: config.get(DOC_HIDE_TIME_COLUMN_SETTING, false), - isShortDots: config.get(UI_SETTINGS.SHORT_DOTS_ENABLE), - defaultSortOrder: config.get(SORT_DEFAULT_ORDER_SETTING, 'desc'), - } - ); -} diff --git a/src/plugins/discover_legacy/public/application/angular/doc_table/components/table_header/__snapshots__/table_header.test.tsx.snap b/src/plugins/discover_legacy/public/application/angular/doc_table/components/table_header/__snapshots__/table_header.test.tsx.snap deleted file mode 100644 index 2aeb8951a60a..000000000000 --- a/src/plugins/discover_legacy/public/application/angular/doc_table/components/table_header/__snapshots__/table_header.test.tsx.snap +++ /dev/null @@ -1,221 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`TableHeader with time column renders correctly 1`] = ` - - - - - Time - - - - <% } %> - - \ No newline at end of file diff --git a/src/plugins/discover_legacy/public/application/angular/doc_table/components/table_row/details.html b/src/plugins/discover_legacy/public/application/angular/doc_table/components/table_row/details.html deleted file mode 100644 index a28f1b9906cb..000000000000 --- a/src/plugins/discover_legacy/public/application/angular/doc_table/components/table_row/details.html +++ /dev/null @@ -1,37 +0,0 @@ - -
-
-
-
- -
-
-

-
-
-
-
- -
-
-
- -
- - diff --git a/src/plugins/discover_legacy/public/application/angular/doc_table/components/table_row/open.html b/src/plugins/discover_legacy/public/application/angular/doc_table/components/table_row/open.html deleted file mode 100644 index d9e15c37f02c..000000000000 --- a/src/plugins/discover_legacy/public/application/angular/doc_table/components/table_row/open.html +++ /dev/null @@ -1,7 +0,0 @@ - - - diff --git a/src/plugins/discover_legacy/public/application/angular/doc_table/components/table_row/truncate_by_height.html b/src/plugins/discover_legacy/public/application/angular/doc_table/components/table_row/truncate_by_height.html deleted file mode 100644 index cf13f10e7006..000000000000 --- a/src/plugins/discover_legacy/public/application/angular/doc_table/components/table_row/truncate_by_height.html +++ /dev/null @@ -1,3 +0,0 @@ -
- <%= body %> -
\ No newline at end of file diff --git a/src/plugins/discover_legacy/public/application/angular/doc_table/create_doc_table_react.tsx b/src/plugins/discover_legacy/public/application/angular/doc_table/create_doc_table_react.tsx deleted file mode 100644 index 8b68dc7ae466..000000000000 --- a/src/plugins/discover_legacy/public/application/angular/doc_table/create_doc_table_react.tsx +++ /dev/null @@ -1,142 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import angular, { auto, ICompileService, IScope } from 'angular'; -import { render } from 'react-dom'; -import React, { useRef, useEffect } from 'react'; -import { getServices, IIndexPattern } from '../../../opensearch_dashboards_services'; -import { IndexPatternField } from '../../../../../data/common/index_patterns'; -export type AngularScope = IScope; - -export interface AngularDirective { - template: string; -} - -/** - * Compiles and injects the give angular template into the given dom node - * returns a function to cleanup the injected angular element - */ -export async function injectAngularElement( - domNode: Element, - template: string, - scopeProps: any, - getInjector: () => Promise -): Promise<() => void> { - const $injector = await getInjector(); - const rootScope: AngularScope = $injector.get('$rootScope'); - const $compile: ICompileService = $injector.get('$compile'); - const newScope = Object.assign(rootScope.$new(), scopeProps); - - const $target = angular.element(domNode); - const $element = angular.element(template); - - newScope.$apply(() => { - const linkFn = $compile($element); - $target.empty().append($element); - linkFn(newScope); - }); - - return () => { - newScope.$destroy(); - }; -} - -/** - * Converts a given legacy angular directive to a render function - * for usage in a react component. Note that the rendering is async - */ -export function convertDirectiveToRenderFn( - directive: AngularDirective, - getInjector: () => Promise -) { - return (domNode: Element, props: any) => { - let rejected = false; - - const cleanupFnPromise = injectAngularElement(domNode, directive.template, props, getInjector); - cleanupFnPromise.catch(() => { - rejected = true; - render(
error
, domNode); - }); - - return () => { - if (!rejected) { - // for cleanup - cleanupFnPromise.then((cleanup) => cleanup()); - } - }; - }; -} - -export interface DocTableLegacyProps { - columns: string[]; - searchDescription?: string; - searchTitle?: string; - onFilter: (field: IndexPatternField | string, value: string, type: '+' | '-') => void; - rows: Array>; - indexPattern: IIndexPattern; - minimumVisibleRows: number; - onAddColumn: (column: string) => void; - onSort: (sort: string[][]) => void; - onMoveColumn: (columns: string, newIdx: number) => void; - onRemoveColumn: (column: string) => void; - sort?: string[][]; -} - -export function DocTableLegacy(renderProps: DocTableLegacyProps) { - const renderFn = convertDirectiveToRenderFn( - { - template: ``, - }, - () => getServices().getEmbeddableInjector() - ); - const ref = useRef(null); - useEffect(() => { - if (ref && ref.current) { - return renderFn(ref.current, renderProps); - } - }, [renderFn, renderProps]); - return
; -} diff --git a/src/plugins/discover_legacy/public/application/angular/doc_table/doc_table.html b/src/plugins/discover_legacy/public/application/angular/doc_table/doc_table.html deleted file mode 100644 index e7a66ce50299..000000000000 --- a/src/plugins/discover_legacy/public/application/angular/doc_table/doc_table.html +++ /dev/null @@ -1,67 +0,0 @@ -
-
-
-
-
- {{ limitedResultsWarning }} -
- - - - -
-
-
- - - - - - -
-
- - -
- - - - - - -
- -
- -
-
- - -
- -

-

-
-
\ No newline at end of file diff --git a/src/plugins/discover_legacy/public/application/angular/doc_table/doc_table.test.js b/src/plugins/discover_legacy/public/application/angular/doc_table/doc_table.test.js deleted file mode 100644 index bd087ac3547f..000000000000 --- a/src/plugins/discover_legacy/public/application/angular/doc_table/doc_table.test.js +++ /dev/null @@ -1,158 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import angular from 'angular'; -import _ from 'lodash'; -import 'angular-mocks'; -import 'angular-sanitize'; -import 'angular-route'; -import { createBrowserHistory } from 'history'; -import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; -import hits from 'fixtures/real_hits'; -import { coreMock } from '../../../../../../core/public/mocks'; -import { dataPluginMock } from '../../../../../data/public/mocks'; -import { navigationPluginMock } from '../../../../../navigation/public/mocks'; -import { setScopedHistory, setServices } from '../../../opensearch_dashboards_services'; -import { getInnerAngularModule } from '../../../get_inner_angular'; - -let $parentScope; - -let $scope; - -let $timeout; - -let indexPattern; - -const init = function ($elem, props) { - angular.mock.inject(function ($rootScope, $compile, _$timeout_) { - $timeout = _$timeout_; - $parentScope = $rootScope; - _.assign($parentScope, props); - - $compile($elem)($parentScope); - - // I think the prereq requires this? - $timeout(() => { - $elem.scope().$digest(); - }, 0); - - $scope = $elem.isolateScope(); - }); -}; - -const destroy = () => { - $scope.$destroy(); - $parentScope.$destroy(); -}; - -describe('docTable', () => { - const core = coreMock.createStart(); - let $elem; - - beforeAll(() => setScopedHistory(createBrowserHistory())); - beforeEach(() => { - angular.element.prototype.slice = jest.fn(() => { - return null; - }); - angular.element.prototype.filter = jest.fn(() => { - return { - remove: jest.fn(), - }; - }); - setServices({ - uiSettings: core.uiSettings, - }); - getInnerAngularModule( - 'app/discover', - core, - { - data: dataPluginMock.createStartContract(), - navigation: navigationPluginMock.createStartContract(), - }, - coreMock.createPluginInitializerContext() - ); - angular.mock.module('app/discover'); - }); - beforeEach(() => { - $elem = angular.element(` - - `); - angular.mock.inject(function (Private) { - indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); - }); - init($elem, { - indexPattern, - hits: [...hits], - totalHitCount: hits.length, - columns: [], - sorting: ['@timestamp', 'desc'], - }); - $scope.$digest(); - }); - - afterEach(() => { - delete angular.element.prototype.slice; - delete angular.element.prototype.filter; - destroy(); - }); - - test('should compile', () => { - expect($elem.text()).toBeTruthy(); - }); - - test('should have an addRows function that increases the row count', () => { - expect($scope.addRows).toBeInstanceOf(Function); - $scope.$digest(); - expect($scope.limit).toBe(50); - $scope.addRows(); - expect($scope.limit).toBe(100); - }); - - test('should reset the row limit when results are received', () => { - $scope.limit = 100; - expect($scope.limit).toBe(100); - $scope.hits = [...hits]; - $scope.$digest(); - expect($scope.limit).toBe(50); - }); - - test('should have a header and a table element', () => { - $scope.$digest(); - - expect($elem.find('thead').length).toBe(1); - expect($elem.find('table').length).toBe(1); - }); -}); diff --git a/src/plugins/discover_legacy/public/application/angular/doc_table/doc_table.ts b/src/plugins/discover_legacy/public/application/angular/doc_table/doc_table.ts deleted file mode 100644 index 2c68ad4a89ff..000000000000 --- a/src/plugins/discover_legacy/public/application/angular/doc_table/doc_table.ts +++ /dev/null @@ -1,113 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import html from './doc_table.html'; -import { dispatchRenderComplete } from '../../../../../opensearch_dashboards_utils/public'; -import { SAMPLE_SIZE_SETTING } from '../../../../common'; -// @ts-ignore -import { getLimitedSearchResultsMessage } from './doc_table_strings'; -import { getServices } from '../../../opensearch_dashboards_services'; -import './index.scss'; - -export interface LazyScope extends ng.IScope { - [key: string]: any; -} - -export function createDocTableDirective(pagerFactory: any, $filter: any) { - return { - restrict: 'E', - template: html, - scope: { - sorting: '=', - columns: '=', - hits: '=', - totalHitCount: '=', - indexPattern: '=', - isLoading: '=?', - infiniteScroll: '=?', - filter: '=?', - minimumVisibleRows: '=?', - onAddColumn: '=?', - onChangeSortOrder: '=?', - onMoveColumn: '=?', - onRemoveColumn: '=?', - inspectorAdapters: '=?', - }, - link: ($scope: LazyScope, $el: JQuery) => { - $scope.persist = { - sorting: $scope.sorting, - columns: $scope.columns, - }; - - const limitTo = $filter('limitTo'); - const calculateItemsOnPage = () => { - $scope.pager.setTotalItems($scope.hits.length); - $scope.pageOfItems = limitTo($scope.hits, $scope.pager.pageSize, $scope.pager.startIndex); - }; - - $scope.limitedResultsWarning = getLimitedSearchResultsMessage( - getServices().uiSettings.get(SAMPLE_SIZE_SETTING, 500) - ); - - $scope.addRows = function () { - $scope.limit += 50; - }; - - $scope.$watch('hits', (hits: any) => { - if (!hits) return; - - // Reset infinite scroll limit - $scope.limit = $scope.minimumVisibleRows || 50; - - if (hits.length === 0) { - dispatchRenderComplete($el[0]); - } - - if ($scope.infiniteScroll) return; - $scope.pager = pagerFactory.create(hits.length, 50, 1); - calculateItemsOnPage(); - }); - - $scope.pageOfItems = []; - $scope.onPageNext = () => { - $scope.pager.nextPage(); - calculateItemsOnPage(); - }; - - $scope.onPagePrevious = () => { - $scope.pager.previousPage(); - calculateItemsOnPage(); - }; - - $scope.shouldShowLimitedResultsWarning = () => - !$scope.pager.hasNextPage && $scope.pager.totalItems < $scope.totalHitCount; - }, - }; -} diff --git a/src/plugins/discover_legacy/public/application/angular/doc_table/doc_table_strings.js b/src/plugins/discover_legacy/public/application/angular/doc_table/doc_table_strings.js deleted file mode 100644 index 4a0457638faa..000000000000 --- a/src/plugins/discover_legacy/public/application/angular/doc_table/doc_table_strings.js +++ /dev/null @@ -1,43 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; - -/** - * A message letting the user know the results that have been retrieved is limited - * to a certain size. - * @param resultCount {Number} - */ -export function getLimitedSearchResultsMessage(resultCount) { - return i18n.translate('discover.docTable.limitedSearchResultLabel', { - defaultMessage: 'Limited to {resultCount} results. Refine your search.', - values: { resultCount }, - }); -} diff --git a/src/plugins/discover_legacy/public/application/angular/doc_table/index.scss b/src/plugins/discover_legacy/public/application/angular/doc_table/index.scss deleted file mode 100644 index 5b9dc82adb16..000000000000 --- a/src/plugins/discover_legacy/public/application/angular/doc_table/index.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import "doc_table"; -@import "components/index"; diff --git a/src/plugins/discover_legacy/public/application/angular/doc_table/index.ts b/src/plugins/discover_legacy/public/application/angular/doc_table/index.ts deleted file mode 100644 index c51ce33cc28f..000000000000 --- a/src/plugins/discover_legacy/public/application/angular/doc_table/index.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export { createDocTableDirective } from './doc_table'; -export { getSort, getSortArray } from './lib/get_sort'; -export { getSortForSearchSource } from './lib/get_sort_for_search_source'; diff --git a/src/plugins/discover_legacy/public/application/angular/doc_table/infinite_scroll.ts b/src/plugins/discover_legacy/public/application/angular/doc_table/infinite_scroll.ts deleted file mode 100644 index 2a59d94aa5c4..000000000000 --- a/src/plugins/discover_legacy/public/application/angular/doc_table/infinite_scroll.ts +++ /dev/null @@ -1,79 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import $ from 'jquery'; - -interface LazyScope extends ng.IScope { - [key: string]: any; -} - -export function createInfiniteScrollDirective() { - return { - restrict: 'E', - scope: { - more: '=', - }, - link: ($scope: LazyScope, $element: JQuery) => { - const $window = $(window); - let checkTimer: any; - - function onScroll() { - if (!$scope.more) return; - - const winHeight = Number($window.height()); - const winBottom = Number(winHeight) + Number($window.scrollTop()); - const offset = $element.offset(); - const elTop = offset ? offset.top : 0; - const remaining = elTop - winBottom; - - if (remaining <= winHeight * 0.5) { - $scope[$scope.$$phase ? '$eval' : '$apply'](function () { - $scope.more(); - }); - } - } - - function scheduleCheck() { - if (checkTimer) return; - checkTimer = setTimeout(function () { - checkTimer = null; - onScroll(); - }, 50); - } - - $window.on('scroll', scheduleCheck); - $scope.$on('$destroy', function () { - clearTimeout(checkTimer); - $window.off('scroll', scheduleCheck); - }); - scheduleCheck(); - }, - }; -} diff --git a/src/plugins/discover_legacy/public/application/angular/doc_table/lib/get_default_sort.ts b/src/plugins/discover_legacy/public/application/angular/doc_table/lib/get_default_sort.ts deleted file mode 100644 index d22c963d219c..000000000000 --- a/src/plugins/discover_legacy/public/application/angular/doc_table/lib/get_default_sort.ts +++ /dev/null @@ -1,49 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { IndexPattern } from '../../../../opensearch_dashboards_services'; -// @ts-ignore -import { isSortable } from './get_sort'; -import { SortOrder } from '../components/table_header/helpers'; - -/** - * use in case the user didn't manually sort. - * the default sort is returned depending of the index pattern - */ -export function getDefaultSort( - indexPattern: IndexPattern, - defaultSortOrder: string = 'desc' -): SortOrder[] { - if (indexPattern.timeFieldName && isSortable(indexPattern.timeFieldName, indexPattern)) { - return [[indexPattern.timeFieldName, defaultSortOrder]]; - } else { - return [['_score', defaultSortOrder]]; - } -} diff --git a/src/plugins/discover_legacy/public/application/angular/doc_table/lib/get_sort.test.ts b/src/plugins/discover_legacy/public/application/angular/doc_table/lib/get_sort.test.ts deleted file mode 100644 index 30c302bccd72..000000000000 --- a/src/plugins/discover_legacy/public/application/angular/doc_table/lib/get_sort.test.ts +++ /dev/null @@ -1,107 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { getSort, getSortArray } from './get_sort'; -// @ts-ignore -import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; -import { IndexPattern } from '../../../../opensearch_dashboards_services'; - -describe('docTable', function () { - let indexPattern: IndexPattern; - - beforeEach(() => { - indexPattern = FixturesStubbedLogstashIndexPatternProvider() as IndexPattern; - }); - - describe('getSort function', function () { - test('should be a function', function () { - expect(typeof getSort === 'function').toBeTruthy(); - }); - - test('should return an array of objects', function () { - expect(getSort([['bytes', 'desc']], indexPattern)).toEqual([{ bytes: 'desc' }]); - - delete indexPattern.timeFieldName; - expect(getSort([['bytes', 'desc']], indexPattern)).toEqual([{ bytes: 'desc' }]); - }); - - test('should passthrough arrays of objects', () => { - expect(getSort([{ bytes: 'desc' }], indexPattern)).toEqual([{ bytes: 'desc' }]); - }); - - test('should return an empty array when passed an unsortable field', function () { - expect(getSort([['non-sortable', 'asc']], indexPattern)).toEqual([]); - expect(getSort([['lol_nope', 'asc']], indexPattern)).toEqual([]); - - delete indexPattern.timeFieldName; - expect(getSort([['non-sortable', 'asc']], indexPattern)).toEqual([]); - }); - - test('should return an empty array ', function () { - expect(getSort([], indexPattern)).toEqual([]); - expect(getSort([['foo', 'bar']], indexPattern)).toEqual([]); - expect(getSort([{ foo: 'bar' }], indexPattern)).toEqual([]); - }); - - test('should convert a legacy sort to an array of objects', function () { - expect(getSort(['foo', 'desc'], indexPattern)).toEqual([{ foo: 'desc' }]); - expect(getSort(['foo', 'asc'], indexPattern)).toEqual([{ foo: 'asc' }]); - }); - }); - - describe('getSortArray function', function () { - test('should have an array method', function () { - expect(getSortArray).toBeInstanceOf(Function); - }); - - test('should return an array of arrays for sortable fields', function () { - expect(getSortArray([['bytes', 'desc']], indexPattern)).toEqual([['bytes', 'desc']]); - }); - - test('should return an array of arrays from an array of elasticsearch sort objects', function () { - expect(getSortArray([{ bytes: 'desc' }], indexPattern)).toEqual([['bytes', 'desc']]); - }); - - test('should sort by an empty array when an unsortable field is given', function () { - expect(getSortArray([{ 'non-sortable': 'asc' }], indexPattern)).toEqual([]); - expect(getSortArray([{ lol_nope: 'asc' }], indexPattern)).toEqual([]); - - delete indexPattern.timeFieldName; - expect(getSortArray([{ 'non-sortable': 'asc' }], indexPattern)).toEqual([]); - }); - - test('should return an empty array when passed an empty sort array', () => { - expect(getSortArray([], indexPattern)).toEqual([]); - - delete indexPattern.timeFieldName; - expect(getSortArray([], indexPattern)).toEqual([]); - }); - }); -}); diff --git a/src/plugins/discover_legacy/public/application/angular/doc_table/lib/get_sort.ts b/src/plugins/discover_legacy/public/application/angular/doc_table/lib/get_sort.ts deleted file mode 100644 index 2a65d7be5de1..000000000000 --- a/src/plugins/discover_legacy/public/application/angular/doc_table/lib/get_sort.ts +++ /dev/null @@ -1,92 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import _ from 'lodash'; -import { IndexPattern } from '../../../../../../data/public'; - -export type SortPairObj = Record; -export type SortPairArr = [string, string]; -export type SortPair = SortPairArr | SortPairObj; -export type SortInput = SortPair | SortPair[]; - -export function isSortable(fieldName: string, indexPattern: IndexPattern) { - const field = indexPattern.getFieldByName(fieldName); - return field && field.sortable; -} - -function createSortObject( - sortPair: SortInput, - indexPattern: IndexPattern -): SortPairObj | undefined { - if ( - Array.isArray(sortPair) && - sortPair.length === 2 && - isSortable(String(sortPair[0]), indexPattern) - ) { - const [field, direction] = sortPair as SortPairArr; - return { [field]: direction }; - } else if (_.isPlainObject(sortPair) && isSortable(Object.keys(sortPair)[0], indexPattern)) { - return sortPair as SortPairObj; - } -} - -export function isLegacySort(sort: SortPair[] | SortPair): sort is SortPair { - return ( - sort.length === 2 && typeof sort[0] === 'string' && (sort[1] === 'desc' || sort[1] === 'asc') - ); -} - -/** - * Take a sorting array and make it into an object - * @param {array} sort two dimensional array [[fieldToSort, directionToSort]] - * or an array of objects [{fieldToSort: directionToSort}] - * @param {object} indexPattern used for determining default sort - * @returns Array<{object}> an array of sort objects - */ -export function getSort(sort: SortPair[] | SortPair, indexPattern: IndexPattern): SortPairObj[] { - if (Array.isArray(sort)) { - if (isLegacySort(sort)) { - // To stay compatible with legacy sort, which just supported a single sort field - return [{ [sort[0]]: sort[1] }]; - } - return sort - .map((sortPair: SortPair) => createSortObject(sortPair, indexPattern)) - .filter((sortPairObj) => typeof sortPairObj === 'object') as SortPairObj[]; - } - return []; -} - -/** - * compared to getSort it doesn't return an array of objects, it returns an array of arrays - * [[fieldToSort: directionToSort]] - */ -export function getSortArray(sort: SortPair[], indexPattern: IndexPattern) { - return getSort(sort, indexPattern).map((sortPair) => Object.entries(sortPair).pop()); -} diff --git a/src/plugins/discover_legacy/public/application/angular/doc_table/lib/get_sort_for_search_source.ts b/src/plugins/discover_legacy/public/application/angular/doc_table/lib/get_sort_for_search_source.ts deleted file mode 100644 index f83c764565c5..000000000000 --- a/src/plugins/discover_legacy/public/application/angular/doc_table/lib/get_sort_for_search_source.ts +++ /dev/null @@ -1,65 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { OpenSearchQuerySortValue, IndexPattern } from '../../../../opensearch_dashboards_services'; -import { SortOrder } from '../components/table_header/helpers'; -import { getSort } from './get_sort'; -import { getDefaultSort } from './get_default_sort'; - -/** - * Prepares sort for search source, that's sending the request to OpenSearch - * - Adds default sort if necessary - * - Handles the special case when there's sorting by date_nanos typed fields - * the addon of the numeric_type guarantees the right sort order - * when there are indices with date and indices with date_nanos field - */ -export function getSortForSearchSource( - sort?: SortOrder[], - indexPattern?: IndexPattern, - defaultDirection: string = 'desc' -): OpenSearchQuerySortValue[] { - if (!sort || !indexPattern) { - return []; - } else if (Array.isArray(sort) && sort.length === 0) { - sort = getDefaultSort(indexPattern, defaultDirection); - } - const { timeFieldName } = indexPattern; - return getSort(sort, indexPattern).map((sortPair: Record) => { - if (indexPattern.isTimeNanosBased() && timeFieldName && sortPair[timeFieldName]) { - return { - [timeFieldName]: { - order: sortPair[timeFieldName], - numeric_type: 'date_nanos', - }, - } as OpenSearchQuerySortValue; - } - return sortPair as OpenSearchQuerySortValue; - }); -} diff --git a/src/plugins/discover_legacy/public/application/angular/doc_table/lib/pager/index.js b/src/plugins/discover_legacy/public/application/angular/doc_table/lib/pager/index.js deleted file mode 100644 index e400b3b52226..000000000000 --- a/src/plugins/discover_legacy/public/application/angular/doc_table/lib/pager/index.js +++ /dev/null @@ -1,32 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import './pager_factory'; -export { Pager } from './pager'; diff --git a/src/plugins/discover_legacy/public/application/angular/doc_table/lib/pager/pager.js b/src/plugins/discover_legacy/public/application/angular/doc_table/lib/pager/pager.js deleted file mode 100644 index 55b8c670317a..000000000000 --- a/src/plugins/discover_legacy/public/application/angular/doc_table/lib/pager/pager.js +++ /dev/null @@ -1,88 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -function clamp(val, min, max) { - return Math.min(Math.max(min, val), max); -} - -export class Pager { - constructor(totalItems, pageSize, startingPage) { - this.currentPage = startingPage; - this.totalItems = totalItems; - this.pageSize = pageSize; - this.startIndex = 0; - this.updateMeta(); - } - - get pageCount() { - return Math.ceil(this.totalItems / this.pageSize); - } - - get hasNextPage() { - return this.currentPage < this.totalPages; - } - - get hasPreviousPage() { - return this.currentPage > 1; - } - - nextPage() { - this.currentPage += 1; - this.updateMeta(); - } - - previousPage() { - this.currentPage -= 1; - this.updateMeta(); - } - - setTotalItems(count) { - this.totalItems = count; - this.updateMeta(); - } - - setPageSize(count) { - this.pageSize = count; - this.updateMeta(); - } - - updateMeta() { - this.totalPages = Math.ceil(this.totalItems / this.pageSize); - this.currentPage = clamp(this.currentPage, 1, this.totalPages); - - this.startItem = (this.currentPage - 1) * this.pageSize + 1; - this.startItem = clamp(this.startItem, 0, this.totalItems); - - this.endItem = this.startItem - 1 + this.pageSize; - this.endItem = clamp(this.endItem, 0, this.totalItems); - - this.startIndex = this.startItem - 1; - } -} diff --git a/src/plugins/discover_legacy/public/application/angular/doc_table/lib/pager/pager_factory.ts b/src/plugins/discover_legacy/public/application/angular/doc_table/lib/pager/pager_factory.ts deleted file mode 100644 index febb6428e53b..000000000000 --- a/src/plugins/discover_legacy/public/application/angular/doc_table/lib/pager/pager_factory.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -// @ts-ignore -import { Pager } from './pager'; - -export function createPagerFactory() { - return { - create(...args: unknown[]) { - return new Pager(...args); - }, - }; -} diff --git a/src/plugins/discover_legacy/public/application/angular/doc_viewer.tsx b/src/plugins/discover_legacy/public/application/angular/doc_viewer.tsx deleted file mode 100644 index 9072594bf478..000000000000 --- a/src/plugins/discover_legacy/public/application/angular/doc_viewer.tsx +++ /dev/null @@ -1,59 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { DocViewer } from '../components/doc_viewer/doc_viewer'; - -export function createDocViewerDirective(reactDirective: any) { - return reactDirective( - (props: any) => { - return ; - }, - [ - 'hit', - ['indexPattern', { watchDepth: 'reference' }], - ['filter', { watchDepth: 'reference' }], - ['columns', { watchDepth: 'collection' }], - ['onAddColumn', { watchDepth: 'reference' }], - ['onRemoveColumn', { watchDepth: 'reference' }], - ], - { - restrict: 'E', - scope: { - hit: '=', - indexPattern: '=', - filter: '=?', - columns: '=?', - onAddColumn: '=?', - onRemoveColumn: '=?', - }, - } - ); -} diff --git a/src/plugins/discover_legacy/public/application/angular/doc_viewer_links.tsx b/src/plugins/discover_legacy/public/application/angular/doc_viewer_links.tsx deleted file mode 100644 index 763a75e51300..000000000000 --- a/src/plugins/discover_legacy/public/application/angular/doc_viewer_links.tsx +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import React from 'react'; -import { DocViewerLinks } from '../components/doc_viewer_links/doc_viewer_links'; - -export function createDocViewerLinksDirective(reactDirective: any) { - return reactDirective( - (props: any) => { - return ; - }, - [ - 'hit', - ['indexPattern', { watchDepth: 'reference' }], - ['columns', { watchDepth: 'collection' }], - ], - { - restrict: 'E', - scope: { - hit: '=', - indexPattern: '=', - columns: '=?', - }, - } - ); -} diff --git a/src/plugins/discover_legacy/public/application/angular/helpers/index.ts b/src/plugins/discover_legacy/public/application/angular/helpers/index.ts deleted file mode 100644 index eb569f6985d6..000000000000 --- a/src/plugins/discover_legacy/public/application/angular/helpers/index.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export { buildPointSeriesData } from './point_series'; diff --git a/src/plugins/discover_legacy/public/application/angular/helpers/point_series.ts b/src/plugins/discover_legacy/public/application/angular/helpers/point_series.ts deleted file mode 100644 index 34b35453cb55..000000000000 --- a/src/plugins/discover_legacy/public/application/angular/helpers/point_series.ts +++ /dev/null @@ -1,122 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { uniq } from 'lodash'; -import { Duration, Moment } from 'moment'; -import { Unit } from '@elastic/datemath'; - -import { SerializedFieldFormat } from '../../../../../expressions/common/types'; - -export interface Column { - id: string; - name: string; -} - -export interface Row { - [key: string]: number | 'NaN'; -} - -export interface Table { - columns: Column[]; - rows: Row[]; -} - -interface HistogramParams { - date: true; - interval: Duration; - intervalOpenSearchValue: number; - intervalOpenSearchUnit: Unit; - format: string; - bounds: { - min: Moment; - max: Moment; - }; -} -export interface Dimension { - accessor: 0 | 1; - format: SerializedFieldFormat<{ pattern: string }>; -} - -export interface Dimensions { - x: Dimension & { params: HistogramParams }; - y: Dimension; -} - -interface Ordered { - date: true; - interval: Duration; - intervalOpenSearchUnit: string; - intervalOpenSearchValue: number; - min: Moment; - max: Moment; -} -export interface Chart { - values: Array<{ - x: number; - y: number; - }>; - xAxisOrderedValues: number[]; - xAxisFormat: Dimension['format']; - xAxisLabel: Column['name']; - yAxisLabel?: Column['name']; - ordered: Ordered; -} - -export const buildPointSeriesData = (table: Table, dimensions: Dimensions) => { - const { x, y } = dimensions; - const xAccessor = table.columns[x.accessor].id; - const yAccessor = table.columns[y.accessor].id; - const chart = {} as Chart; - - chart.xAxisOrderedValues = uniq(table.rows.map((r) => r[xAccessor] as number)); - chart.xAxisFormat = x.format; - chart.xAxisLabel = table.columns[x.accessor].name; - - const { intervalOpenSearchUnit, intervalOpenSearchValue, interval, bounds } = x.params; - chart.ordered = { - date: true, - interval, - intervalOpenSearchUnit, - intervalOpenSearchValue, - min: bounds.min, - max: bounds.max, - }; - - chart.yAxisLabel = table.columns[y.accessor].name; - - chart.values = table.rows - .filter((row) => row && row[yAccessor] !== 'NaN') - .map((row) => ({ - x: row[xAccessor] as number, - y: row[yAccessor] as number, - })); - - return chart; -}; diff --git a/src/plugins/discover_legacy/public/application/angular/index.ts b/src/plugins/discover_legacy/public/application/angular/index.ts deleted file mode 100644 index f9b905759c06..000000000000 --- a/src/plugins/discover_legacy/public/application/angular/index.ts +++ /dev/null @@ -1,41 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -// required for i18nIdDirective -import 'angular-sanitize'; -// required for ngRoute -import 'angular-route'; - -import './discover'; -import './doc'; -import './context'; -import './doc_viewer'; -import './redirect'; -import './directives'; diff --git a/src/plugins/discover_legacy/public/application/angular/redirect.ts b/src/plugins/discover_legacy/public/application/angular/redirect.ts deleted file mode 100644 index d8c03e42abb7..000000000000 --- a/src/plugins/discover_legacy/public/application/angular/redirect.ts +++ /dev/null @@ -1,49 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { getAngularModule, getServices, getUrlTracker } from '../../opensearch_dashboards_services'; - -getAngularModule().config(($routeProvider: any) => { - $routeProvider.otherwise({ - resolveRedirectTo: ($rootScope: any) => { - const path = window.location.hash.substr(1); - getUrlTracker().restorePreviousUrl(); - $rootScope.$applyAsync(() => { - const { urlForwarding } = getServices(); - const { navigated } = urlForwarding.navigateToLegacyOpenSearchDashboardsUrl(path); - if (!navigated) { - urlForwarding.navigateToDefaultApp(); - } - }); - // prevent angular from completing the navigation - return new Promise(() => {}); - }, - }); -}); diff --git a/src/plugins/discover_legacy/public/application/angular/response_handler.js b/src/plugins/discover_legacy/public/application/angular/response_handler.js deleted file mode 100644 index 853de0086168..000000000000 --- a/src/plugins/discover_legacy/public/application/angular/response_handler.js +++ /dev/null @@ -1,131 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { getServices } from '../../opensearch_dashboards_services'; -import { buildPointSeriesData } from './helpers'; - -function tableResponseHandler(table, dimensions) { - const converted = { tables: [] }; - const split = dimensions.splitColumn || dimensions.splitRow; - - if (split) { - converted.direction = dimensions.splitRow ? 'row' : 'column'; - const splitColumnIndex = split[0].accessor; - const splitColumnFormatter = getServices().data.fieldFormats.deserialize(split[0].format); - const splitColumn = table.columns[splitColumnIndex]; - const splitMap = {}; - let splitIndex = 0; - - table.rows.forEach((row, rowIndex) => { - const splitValue = row[splitColumn.id]; - - if (!splitMap.hasOwnProperty(splitValue)) { - splitMap[splitValue] = splitIndex++; - const tableGroup = { - $parent: converted, - title: `${splitColumnFormatter.convert(splitValue)}: ${splitColumn.name}`, - name: splitColumn.name, - key: splitValue, - column: splitColumnIndex, - row: rowIndex, - table, - tables: [], - }; - tableGroup.tables.push({ - $parent: tableGroup, - columns: table.columns, - rows: [], - }); - - converted.tables.push(tableGroup); - } - - const tableIndex = splitMap[splitValue]; - converted.tables[tableIndex].tables[0].rows.push(row); - }); - } else { - converted.tables.push({ - columns: table.columns, - rows: table.rows, - }); - } - - return converted; -} - -function convertTableGroup(tableGroup, convertTable) { - const tables = tableGroup.tables; - - if (!tables.length) return; - - const firstChild = tables[0]; - if (firstChild.columns) { - const chart = convertTable(firstChild); - // if chart is within a split, assign group title to its label - if (tableGroup.$parent) { - chart.label = tableGroup.title; - } - return chart; - } - - const out = {}; - let outList; - - tables.forEach(function (table) { - if (!outList) { - const direction = tableGroup.direction === 'row' ? 'rows' : 'columns'; - outList = out[direction] = []; - } - - let output; - if ((output = convertTableGroup(table, convertTable))) { - outList.push(output); - } - }); - - return out; -} - -export const discoverResponseHandler = (response, dimensions) => { - const tableGroup = tableResponseHandler(response, dimensions); - - let converted = convertTableGroup(tableGroup, (table) => { - return buildPointSeriesData(table, dimensions); - }); - if (!converted) { - // mimic a row of tables that doesn't have any tables - // https://github.com/opensearch-project/OpenSearch-Dashboards/blob/7bfb68cd24ed42b1b257682f93c50cd8d73e2520/src/kibana/components/vislib/components/zero_injection/inject_zeros.js#L32 - converted = { rows: [] }; - } - - converted.hits = response.rows.length; - - return converted; -}; diff --git a/src/plugins/discover_legacy/public/application/application.ts b/src/plugins/discover_legacy/public/application/application.ts deleted file mode 100644 index 49fd743d76ef..000000000000 --- a/src/plugins/discover_legacy/public/application/application.ts +++ /dev/null @@ -1,56 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import './index.scss'; -import angular from 'angular'; -import { getServices } from '../opensearch_dashboards_services'; - -/** - * Here's where Discover's inner angular is mounted and rendered - */ -export async function renderApp(moduleName: string, element: HTMLElement) { - // do not wait for fontawesome - getServices().opensearchDashboardsLegacy.loadFontAwesome(); - await import('./angular'); - const $injector = mountDiscoverApp(moduleName, element); - return () => $injector.get('$rootScope').$destroy(); -} - -function mountDiscoverApp(moduleName: string, element: HTMLElement) { - const mountpoint = document.createElement('div'); - const appWrapper = document.createElement('div'); - appWrapper.setAttribute('ng-view', ''); - mountpoint.appendChild(appWrapper); - // bootstrap angular into detached element and attach it later to - // make angular-within-angular possible - const $injector = angular.bootstrap(mountpoint, [moduleName]); - element.appendChild(mountpoint); - return $injector; -} diff --git a/src/plugins/discover_legacy/public/application/components/context_error_message/context_error_message.test.tsx b/src/plugins/discover_legacy/public/application/components/context_error_message/context_error_message.test.tsx deleted file mode 100644 index a1ef06b81cf2..000000000000 --- a/src/plugins/discover_legacy/public/application/components/context_error_message/context_error_message.test.tsx +++ /dev/null @@ -1,65 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { mountWithIntl } from 'test_utils/enzyme_helpers'; -import { ReactWrapper } from 'enzyme'; -import { ContextErrorMessage } from './context_error_message'; -// @ts-ignore -import { FAILURE_REASONS, LOADING_STATUS } from '../../angular/context/query'; -import { findTestSubject } from 'test_utils/helpers'; - -describe('loading spinner', function () { - let component: ReactWrapper; - - it('ContextErrorMessage does not render on loading', () => { - component = mountWithIntl(); - expect(findTestSubject(component, 'contextErrorMessageTitle').length).toBe(0); - }); - - it('ContextErrorMessage does not render on success loading', () => { - component = mountWithIntl(); - expect(findTestSubject(component, 'contextErrorMessageTitle').length).toBe(0); - }); - - it('ContextErrorMessage renders just the title if the reason is not specifically handled', () => { - component = mountWithIntl(); - expect(findTestSubject(component, 'contextErrorMessageTitle').length).toBe(1); - expect(findTestSubject(component, 'contextErrorMessageBody').text()).toBe(''); - }); - - it('ContextErrorMessage renders the reason for unknown errors', () => { - component = mountWithIntl( - - ); - expect(findTestSubject(component, 'contextErrorMessageTitle').length).toBe(1); - expect(findTestSubject(component, 'contextErrorMessageBody').length).toBe(1); - }); -}); diff --git a/src/plugins/discover_legacy/public/application/components/context_error_message/context_error_message.tsx b/src/plugins/discover_legacy/public/application/components/context_error_message/context_error_message.tsx deleted file mode 100644 index 8fe36f592275..000000000000 --- a/src/plugins/discover_legacy/public/application/components/context_error_message/context_error_message.tsx +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { EuiCallOut, EuiText } from '@elastic/eui'; -import { FormattedMessage, I18nProvider } from '@osd/i18n/react'; -// @ts-ignore -import { FAILURE_REASONS, LOADING_STATUS } from '../../angular/context/query'; - -export interface ContextErrorMessageProps { - /** - * the status of the loading action - */ - status: string; - /** - * the reason of the error - */ - reason?: string; -} - -export function ContextErrorMessage({ status, reason }: ContextErrorMessageProps) { - if (status !== LOADING_STATUS.FAILED) { - return null; - } - return ( - - - } - color="danger" - iconType="alert" - data-test-subj="contextErrorMessageTitle" - > - - {reason === FAILURE_REASONS.UNKNOWN && ( - - )} - - - - ); -} diff --git a/src/plugins/discover_legacy/public/application/components/context_error_message/context_error_message_directive.ts b/src/plugins/discover_legacy/public/application/components/context_error_message/context_error_message_directive.ts deleted file mode 100644 index 077682c67d06..000000000000 --- a/src/plugins/discover_legacy/public/application/components/context_error_message/context_error_message_directive.ts +++ /dev/null @@ -1,38 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { ContextErrorMessage } from './context_error_message'; - -export function createContextErrorMessageDirective(reactDirective: any) { - return reactDirective(ContextErrorMessage, [ - ['status', { watchDepth: 'reference' }], - ['reason', { watchDepth: 'reference' }], - ]); -} diff --git a/src/plugins/discover_legacy/public/application/components/context_error_message/index.ts b/src/plugins/discover_legacy/public/application/components/context_error_message/index.ts deleted file mode 100644 index 99cd662ecca3..000000000000 --- a/src/plugins/discover_legacy/public/application/components/context_error_message/index.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export { ContextErrorMessage } from './context_error_message'; -export { createContextErrorMessageDirective } from './context_error_message_directive'; diff --git a/src/plugins/discover_legacy/public/application/components/create_discover_legacy_directive.ts b/src/plugins/discover_legacy/public/application/components/create_discover_legacy_directive.ts deleted file mode 100644 index 09cc33964862..000000000000 --- a/src/plugins/discover_legacy/public/application/components/create_discover_legacy_directive.ts +++ /dev/null @@ -1,67 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { DiscoverLegacy } from './discover_legacy'; - -export function createDiscoverLegacyDirective(reactDirective: any) { - return reactDirective(DiscoverLegacy, [ - ['addColumn', { watchDepth: 'reference' }], - ['fetch', { watchDepth: 'reference' }], - ['fetchCounter', { watchDepth: 'reference' }], - ['fetchError', { watchDepth: 'reference' }], - ['fieldCounts', { watchDepth: 'reference' }], - ['histogramData', { watchDepth: 'reference' }], - ['hits', { watchDepth: 'reference' }], - ['indexPattern', { watchDepth: 'reference' }], - ['minimumVisibleRows', { watchDepth: 'reference' }], - ['onAddFilter', { watchDepth: 'reference' }], - ['onChangeInterval', { watchDepth: 'reference' }], - ['onMoveColumn', { watchDepth: 'reference' }], - ['onRemoveColumn', { watchDepth: 'reference' }], - ['onSetColumns', { watchDepth: 'reference' }], - ['onSkipBottomButtonClick', { watchDepth: 'reference' }], - ['onSort', { watchDepth: 'reference' }], - ['opts', { watchDepth: 'reference' }], - ['resetQuery', { watchDepth: 'reference' }], - ['resultState', { watchDepth: 'reference' }], - ['rows', { watchDepth: 'reference' }], - ['savedSearch', { watchDepth: 'reference' }], - ['searchSource', { watchDepth: 'reference' }], - ['setIndexPattern', { watchDepth: 'reference' }], - ['showSaveQuery', { watchDepth: 'reference' }], - ['state', { watchDepth: 'reference' }], - ['timefilterUpdateHandler', { watchDepth: 'reference' }], - ['timeRange', { watchDepth: 'reference' }], - ['topNavMenu', { watchDepth: 'reference' }], - ['updateQuery', { watchDepth: 'reference' }], - ['updateSavedQueryId', { watchDepth: 'reference' }], - ['vis', { watchDepth: 'reference' }], - ]); -} diff --git a/src/plugins/discover_legacy/public/application/components/discover_legacy.tsx b/src/plugins/discover_legacy/public/application/components/discover_legacy.tsx deleted file mode 100644 index d4a3d235a188..000000000000 --- a/src/plugins/discover_legacy/public/application/components/discover_legacy.tsx +++ /dev/null @@ -1,368 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React, { useState, useCallback, useEffect } from 'react'; -import classNames from 'classnames'; -import { EuiButtonEmpty, EuiButtonIcon, EuiCallOut, EuiLink } from '@elastic/eui'; -import { i18n } from '@osd/i18n'; -import { FormattedMessage, I18nProvider } from '@osd/i18n/react'; -import { IUiSettingsClient, MountPoint } from 'opensearch-dashboards/public'; -import { HitsCounter } from './hits_counter'; -import { TimechartHeader } from './timechart_header'; -import { DiscoverSidebar } from './sidebar'; -import { getServices, IndexPattern } from '../../opensearch_dashboards_services'; -// @ts-ignore -import { DiscoverNoResults } from '../angular/directives/no_results'; -import { DiscoverUninitialized } from '../angular/directives/uninitialized'; -import { DiscoverHistogram } from '../angular/directives/histogram'; -import { LoadingSpinner } from './loading_spinner/loading_spinner'; -import { DocTableLegacy } from '../angular/doc_table/create_doc_table_react'; -import { SkipBottomButton } from './skip_bottom_button'; -import { - IndexPatternField, - search, - ISearchSource, - TimeRange, - Query, - IndexPatternAttributes, -} from '../../../../data/public'; -import { Chart } from '../angular/helpers/point_series'; -import { AppState } from '../angular/discover_state'; -import { SavedSearch } from '../../saved_searches'; - -import { SavedObject } from '../../../../../core/types'; -import { Vis } from '../../../../visualizations/public'; -import { TopNavMenuData } from '../../../../navigation/public'; - -export interface DiscoverLegacyProps { - addColumn: (column: string) => void; - fetch: () => void; - fetchCounter: number; - fieldCounts: Record; - histogramData: Chart; - hits: number; - indexPattern: IndexPattern; - minimumVisibleRows: number; - onAddFilter: (field: IndexPatternField | string, value: string, type: '+' | '-') => void; - onChangeInterval: (interval: string) => void; - onMoveColumn: (columns: string, newIdx: number) => void; - onRemoveColumn: (column: string) => void; - onSetColumns: (columns: string[]) => void; - onSkipBottomButtonClick: () => void; - onSort: (sort: string[][]) => void; - opts: { - savedSearch: SavedSearch; - config: IUiSettingsClient; - indexPatternList: Array>; - timefield: string; - sampleSize: number; - fixedScroll: (el: HTMLElement) => void; - setHeaderActionMenu: (menuMount: MountPoint | undefined) => void; - }; - resetQuery: () => void; - resultState: string; - rows: Array>; - searchSource: ISearchSource; - setIndexPattern: (id: string) => void; - showSaveQuery: boolean; - state: AppState; - timefilterUpdateHandler: (ranges: { from: number; to: number }) => void; - timeRange?: { from: string; to: string }; - topNavMenu: TopNavMenuData[]; - updateQuery: (payload: { dateRange: TimeRange; query?: Query }, isUpdate?: boolean) => void; - updateSavedQueryId: (savedQueryId?: string) => void; - vis?: Vis; -} - -const KEY_SHOW_NOTICE = 'discover:deprecation-notice:show'; - -export function DiscoverLegacy({ - addColumn, - fetch, - fetchCounter, - fieldCounts, - histogramData, - hits, - indexPattern, - minimumVisibleRows, - onAddFilter, - onChangeInterval, - onMoveColumn, - onRemoveColumn, - onSkipBottomButtonClick, - onSort, - opts, - resetQuery, - resultState, - rows, - searchSource, - setIndexPattern, - showSaveQuery, - state, - timefilterUpdateHandler, - timeRange, - topNavMenu, - updateQuery, - updateSavedQueryId, - vis, -}: DiscoverLegacyProps) { - const [isSidebarClosed, setIsSidebarClosed] = useState(false); - const [isCallOutVisible, setIsCallOutVisible] = useState( - localStorage.getItem(KEY_SHOW_NOTICE) !== 'false' - ); - const { TopNavMenu } = getServices().navigation.ui; - const { savedSearch, indexPatternList } = opts; - const bucketAggConfig = vis?.data?.aggs?.aggs[1]; - const bucketInterval = - bucketAggConfig && search.aggs.isDateHistogramBucketAggConfig(bucketAggConfig) - ? bucketAggConfig.buckets?.getInterval() - : undefined; - const [fixedScrollEl, setFixedScrollEl] = useState(); - - const closeCallOut = () => { - localStorage.setItem(KEY_SHOW_NOTICE, 'false'); - setIsCallOutVisible(false); - }; - - let callOut; - - if (isCallOutVisible) { - callOut = ( -
- -

- To provide feedback,{' '} - - open an issue - - . -

-
-
- ); - } - - useEffect(() => (fixedScrollEl ? opts.fixedScroll(fixedScrollEl) : undefined), [ - fixedScrollEl, - opts, - ]); - const fixedScrollRef = useCallback( - (node: HTMLElement) => { - if (node !== null) { - setFixedScrollEl(node); - } - }, - [setFixedScrollEl] - ); - const sidebarClassName = classNames({ - closed: isSidebarClosed, - }); - - const mainSectionClassName = classNames({ - 'col-md-10': !isSidebarClosed, - 'col-md-12': isSidebarClosed, - }); - - return ( - -
-

{savedSearch.title}

-
- -
-
-
-
- {!isSidebarClosed && ( -
- -
- )} - setIsSidebarClosed(!isSidebarClosed)} - data-test-subj="collapseSideBarButton" - aria-controls="discover-sidebar" - aria-expanded={isSidebarClosed ? 'false' : 'true'} - aria-label="Toggle sidebar" - className="dscCollapsibleSidebar__collapseButton euiButtonIcon--auto" - /> -
-
- {callOut} - {resultState === 'none' && ( - - )} - {resultState === 'uninitialized' && } - {/* @TODO: Solved in the Angular way to satisfy functional test - should be improved*/} - -
- -
-
- {resultState === 'ready' && ( -
- - 0 ? hits : 0} - showResetButton={!!(savedSearch && savedSearch.id)} - onResetQuery={resetQuery} - /> - {opts.timefield && ( - - )} - - {opts.timefield && ( -
- {vis && rows.length !== 0 && ( -
- -
- )} -
- )} - -
-
-

- -

- {rows && rows.length && ( -
- - - ​ - - {rows.length === opts.sampleSize && ( -
- - - window.scrollTo(0, 0)}> - - -
- )} -
- )} -
-
-
- )} -
-
-
-
-
- ); -} diff --git a/src/plugins/discover_legacy/public/application/components/doc/doc.test.tsx b/src/plugins/discover_legacy/public/application/components/doc/doc.test.tsx deleted file mode 100644 index 4a3fb740492a..000000000000 --- a/src/plugins/discover_legacy/public/application/components/doc/doc.test.tsx +++ /dev/null @@ -1,150 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { throwError, of } from 'rxjs'; -import React from 'react'; -import { act } from 'react-dom/test-utils'; -import { mountWithIntl } from 'test_utils/enzyme_helpers'; -import { ReactWrapper } from 'enzyme'; -import { findTestSubject } from 'test_utils/helpers'; -import { Doc, DocProps } from './doc'; - -const mockSearchApi = jest.fn(); - -jest.mock('../../../opensearch_dashboards_services', () => { - let registry: any[] = []; - - return { - getServices: () => ({ - metadata: { - branch: 'test', - }, - data: { - search: { - search: mockSearchApi, - }, - }, - }), - getDocViewsRegistry: () => ({ - addDocView(view: any) { - registry.push(view); - }, - getDocViewsSorted() { - return registry; - }, - resetRegistry: () => { - registry = []; - }, - }), - getDocViewsLinksRegistry: () => ({ - addDocViewLink(view: any) { - registry.push(view); - }, - getDocViewsLinksSorted() { - return registry; - }, - resetRegistry: () => { - registry = []; - }, - }), - }; -}); - -beforeEach(() => { - jest.clearAllMocks(); -}); - -const waitForPromises = async () => - act(async () => { - await new Promise((resolve) => setTimeout(resolve)); - }); - -/** - * this works but logs ugly error messages until we're using React 16.9 - * should be adapted when we upgrade - */ -async function mountDoc(update = false, indexPatternGetter: any = null) { - const indexPattern = { - getComputedFields: () => [], - }; - const indexPatternService = { - get: indexPatternGetter ? indexPatternGetter : jest.fn(() => Promise.resolve(indexPattern)), - } as any; - - const props = { - id: '1', - index: 'index1', - indexPatternId: 'xyz', - indexPatternService, - } as DocProps; - let comp!: ReactWrapper; - await act(async () => { - comp = mountWithIntl(); - if (update) comp.update(); - }); - if (update) { - await waitForPromises(); - comp.update(); - } - return comp; -} - -describe('Test of of Discover', () => { - test('renders loading msg', async () => { - const comp = await mountDoc(); - expect(findTestSubject(comp, 'doc-msg-loading').length).toBe(1); - }); - - test('renders IndexPattern notFound msg', async () => { - const indexPatternGetter = jest.fn(() => Promise.reject({ savedObjectId: '007' })); - const comp = await mountDoc(true, indexPatternGetter); - expect(findTestSubject(comp, 'doc-msg-notFoundIndexPattern').length).toBe(1); - }); - - test('renders notFound msg', async () => { - mockSearchApi.mockImplementation(() => throwError({ status: 404 })); - const comp = await mountDoc(true); - expect(findTestSubject(comp, 'doc-msg-notFound').length).toBe(1); - }); - - test('renders error msg', async () => { - mockSearchApi.mockImplementation(() => throwError({ error: 'something else' })); - const comp = await mountDoc(true); - expect(findTestSubject(comp, 'doc-msg-error').length).toBe(1); - }); - - test('renders opensearch hit ', async () => { - mockSearchApi.mockImplementation(() => - of({ rawResponse: { hits: { total: 1, hits: [{ _id: 1, _source: { test: 1 } }] } } }) - ); - const comp = await mountDoc(true); - expect(findTestSubject(comp, 'doc-hit').length).toBe(1); - }); -}); diff --git a/src/plugins/discover_legacy/public/application/components/doc/doc.tsx b/src/plugins/discover_legacy/public/application/components/doc/doc.tsx deleted file mode 100644 index 204a16d64757..000000000000 --- a/src/plugins/discover_legacy/public/application/components/doc/doc.tsx +++ /dev/null @@ -1,140 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { FormattedMessage, I18nProvider } from '@osd/i18n/react'; -import { EuiCallOut, EuiLink, EuiLoadingSpinner, EuiPageContent } from '@elastic/eui'; -import { IndexPatternsContract } from 'src/plugins/data/public'; -import { OpenSearchRequestState, useOpenSearchDocSearch } from './use_opensearch_doc_search'; -import { DocViewer } from '../doc_viewer/doc_viewer'; - -export interface DocProps { - /** - * Id of the doc in OpenSearch - */ - id: string; - /** - * Index in OpenSearch to query - */ - index: string; - /** - * IndexPattern ID used to get IndexPattern entity - * that's used for adding additional fields (stored_fields, script_fields, docvalue_fields) - */ - indexPatternId: string; - /** - * IndexPatternService to get a given index pattern by ID - */ - indexPatternService: IndexPatternsContract; -} - -export function Doc(props: DocProps) { - const [reqState, hit, indexPattern] = useOpenSearchDocSearch(props); - return ( - - - {reqState === OpenSearchRequestState.NotFoundIndexPattern && ( - - } - /> - )} - {reqState === OpenSearchRequestState.NotFound && ( - - } - > - - - )} - - {reqState === OpenSearchRequestState.Error && ( - - } - > - {' '} - - - - - )} - - {reqState === OpenSearchRequestState.Loading && ( - - {' '} - - - )} - - {reqState === OpenSearchRequestState.Found && hit !== null && indexPattern && ( -
- -
- )} -
-
- ); -} diff --git a/src/plugins/discover_legacy/public/application/components/doc/use_opensearch_doc_search.test.tsx b/src/plugins/discover_legacy/public/application/components/doc/use_opensearch_doc_search.test.tsx deleted file mode 100644 index cb716a4f17cb..000000000000 --- a/src/plugins/discover_legacy/public/application/components/doc/use_opensearch_doc_search.test.tsx +++ /dev/null @@ -1,98 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { renderHook, act } from '@testing-library/react-hooks'; -import { - buildSearchBody, - useOpenSearchDocSearch, - OpenSearchRequestState, -} from './use_opensearch_doc_search'; -import { DocProps } from './doc'; -import { Observable } from 'rxjs'; - -const mockSearchResult = new Observable(); - -jest.mock('../../../opensearch_dashboards_services', () => ({ - getServices: () => ({ - data: { - search: { - search: jest.fn(() => { - return mockSearchResult; - }), - }, - }, - }), -})); - -describe('Test of helper / hook', () => { - test('buildSearchBody', () => { - const indexPattern = { - getComputedFields: () => ({ storedFields: [], scriptFields: [], docvalueFields: [] }), - } as any; - const actual = buildSearchBody('1', indexPattern); - expect(actual).toMatchInlineSnapshot(` - Object { - "_source": true, - "docvalue_fields": Array [], - "query": Object { - "ids": Object { - "values": Array [ - "1", - ], - }, - }, - "script_fields": Array [], - "stored_fields": Array [], - } - `); - }); - - test('useOpenSearchDocSearch', async () => { - const indexPattern = { - getComputedFields: () => [], - }; - const indexPatternService = { - get: jest.fn(() => Promise.resolve(indexPattern)), - } as any; - const props = { - id: '1', - index: 'index1', - indexPatternId: 'xyz', - indexPatternService, - } as DocProps; - let hook; - await act(async () => { - hook = renderHook((p: DocProps) => useOpenSearchDocSearch(p), { initialProps: props }); - }); - // @ts-ignore - expect(hook.result.current).toEqual([OpenSearchRequestState.Loading, null, indexPattern]); - expect(indexPatternService.get).toHaveBeenCalled(); - }); -}); diff --git a/src/plugins/discover_legacy/public/application/components/doc/use_opensearch_doc_search.ts b/src/plugins/discover_legacy/public/application/components/doc/use_opensearch_doc_search.ts deleted file mode 100644 index b5ca9fec1c2f..000000000000 --- a/src/plugins/discover_legacy/public/application/components/doc/use_opensearch_doc_search.ts +++ /dev/null @@ -1,114 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { useEffect, useState } from 'react'; -import { IndexPattern, getServices } from '../../../opensearch_dashboards_services'; -import { DocProps } from './doc'; -import { OpenSearchSearchHit } from '../../doc_views/doc_views_types'; - -export enum OpenSearchRequestState { - Loading, - NotFound, - Found, - Error, - NotFoundIndexPattern, -} - -/** - * helper function to build a query body for OpenSearch - * https://opensearch.org/docs/latest/opensearch/query-dsl/index/ - */ -export function buildSearchBody(id: string, indexPattern: IndexPattern): Record { - const computedFields = indexPattern.getComputedFields(); - - return { - query: { - ids: { - values: [id], - }, - }, - stored_fields: computedFields.storedFields, - _source: true, - script_fields: computedFields.scriptFields, - docvalue_fields: computedFields.docvalueFields, - }; -} - -/** - * Custom react hook for querying a single doc in OpenSearch - */ -export function useOpenSearchDocSearch({ - id, - index, - indexPatternId, - indexPatternService, -}: DocProps): [OpenSearchRequestState, OpenSearchSearchHit | null, IndexPattern | null] { - const [indexPattern, setIndexPattern] = useState(null); - const [status, setStatus] = useState(OpenSearchRequestState.Loading); - const [hit, setHit] = useState(null); - - useEffect(() => { - async function requestData() { - try { - const indexPatternEntity = await indexPatternService.get(indexPatternId); - setIndexPattern(indexPatternEntity); - - const { rawResponse } = await getServices() - .data.search.search({ - dataSourceId: indexPatternEntity.dataSourceRef?.id, - params: { - index, - body: buildSearchBody(id, indexPatternEntity), - }, - }) - .toPromise(); - - const hits = rawResponse.hits; - - if (hits?.hits?.[0]) { - setStatus(OpenSearchRequestState.Found); - setHit(hits.hits[0]); - } else { - setStatus(OpenSearchRequestState.NotFound); - } - } catch (err) { - if (err.savedObjectId) { - setStatus(OpenSearchRequestState.NotFoundIndexPattern); - } else if (err.status === 404) { - setStatus(OpenSearchRequestState.NotFound); - } else { - setStatus(OpenSearchRequestState.Error); - } - } - } - requestData(); - }, [id, index, indexPatternId, indexPatternService]); - return [status, hit, indexPattern]; -} diff --git a/src/plugins/discover_legacy/public/application/components/doc_viewer/__snapshots__/doc_viewer.test.tsx.snap b/src/plugins/discover_legacy/public/application/components/doc_viewer/__snapshots__/doc_viewer.test.tsx.snap deleted file mode 100644 index cc1647fe264e..000000000000 --- a/src/plugins/discover_legacy/public/application/components/doc_viewer/__snapshots__/doc_viewer.test.tsx.snap +++ /dev/null @@ -1,56 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Render with 3 different tabs 1`] = ` -
- , - "id": "osd_doc_viewer_tab_0", - "name": "Render function", - }, - Object { - "content": , - "id": "osd_doc_viewer_tab_1", - "name": "React component", - }, - Object { - "content": , - "id": "osd_doc_viewer_tab_2", - "name": "Invalid doc view", - }, - ] - } - /> -
-`; diff --git a/src/plugins/discover_legacy/public/application/components/doc_viewer/__snapshots__/doc_viewer_render_tab.test.tsx.snap b/src/plugins/discover_legacy/public/application/components/doc_viewer/__snapshots__/doc_viewer_render_tab.test.tsx.snap deleted file mode 100644 index 31509659ce41..000000000000 --- a/src/plugins/discover_legacy/public/application/components/doc_viewer/__snapshots__/doc_viewer_render_tab.test.tsx.snap +++ /dev/null @@ -1,20 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Mounting and unmounting DocViewerRenderTab 1`] = ` -[MockFunction] { - "calls": Array [ - Array [ -
, - Object { - "hit": Object {}, - }, - ], - ], - "results": Array [ - Object { - "type": "return", - "value": [MockFunction], - }, - ], -} -`; diff --git a/src/plugins/discover_legacy/public/application/components/doc_viewer/doc_viewer.scss b/src/plugins/discover_legacy/public/application/components/doc_viewer/doc_viewer.scss deleted file mode 100644 index 91b66fc84297..000000000000 --- a/src/plugins/discover_legacy/public/application/components/doc_viewer/doc_viewer.scss +++ /dev/null @@ -1,72 +0,0 @@ -.osdDocViewerTable { - margin-top: $euiSizeS; -} - -.osdDocViewer { - pre, - .osdDocViewer__value { - display: inline-block; - word-break: break-all; - word-wrap: break-word; - white-space: pre-wrap; - color: $euiColorFullShade; - vertical-align: top; - padding-top: 2px; - } - - .osdDocViewer__field { - padding-top: 8px; - } - - .dscFieldName { - color: $euiColorDarkShade; - } - - td, - pre { - font-family: $euiCodeFontFamily; - } - - tr:first-child td { - border-top-color: transparent; - } - - tr:hover { - .osdDocViewer__actionButton { - opacity: 1; - } - } -} - -.osdDocViewer__buttons, -.osdDocViewer__field { - white-space: nowrap; -} - -.osdDocViewer__buttons { - width: 60px; - - // Show all icons if one is focused, - // IE doesn't support, but the fallback is just the focused button becomes visible - &:focus-within { - .osdDocViewer__actionButton { - opacity: 1; - } - } -} - -.osdDocViewer__field { - width: 160px; -} - -.osdDocViewer__actionButton { - opacity: 0; - - &:focus { - opacity: 1; - } -} - -.osdDocViewer__warning { - margin-right: $euiSizeS; -} diff --git a/src/plugins/discover_legacy/public/application/components/doc_viewer/doc_viewer.test.tsx b/src/plugins/discover_legacy/public/application/components/doc_viewer/doc_viewer.test.tsx deleted file mode 100644 index ccab0be41ed2..000000000000 --- a/src/plugins/discover_legacy/public/application/components/doc_viewer/doc_viewer.test.tsx +++ /dev/null @@ -1,94 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { mount, shallow } from 'enzyme'; -import { DocViewer } from './doc_viewer'; -import { findTestSubject } from 'test_utils/helpers'; -import { getDocViewsRegistry } from '../../../opensearch_dashboards_services'; -import { DocViewRenderProps } from '../../doc_views/doc_views_types'; - -jest.mock('../../../opensearch_dashboards_services', () => { - let registry: any[] = []; - return { - getDocViewsRegistry: () => ({ - addDocView(view: any) { - registry.push(view); - }, - getDocViewsSorted() { - return registry; - }, - resetRegistry: () => { - registry = []; - }, - }), - }; -}); - -beforeEach(() => { - (getDocViewsRegistry() as any).resetRegistry(); - jest.clearAllMocks(); -}); - -test('Render with 3 different tabs', () => { - const registry = getDocViewsRegistry(); - registry.addDocView({ order: 10, title: 'Render function', render: jest.fn() }); - registry.addDocView({ order: 20, title: 'React component', component: () =>
test
}); - registry.addDocView({ order: 30, title: 'Invalid doc view' }); - - const renderProps = { hit: {} } as DocViewRenderProps; - - const wrapper = shallow(); - - expect(wrapper).toMatchSnapshot(); -}); - -test('Render with 1 tab displaying error message', () => { - function SomeComponent() { - // this is just a placeholder - return null; - } - - const registry = getDocViewsRegistry(); - registry.addDocView({ - order: 10, - title: 'React component', - component: SomeComponent, - }); - - const renderProps = { hit: {} } as DocViewRenderProps; - const errorMsg = 'Catch me if you can!'; - - const wrapper = mount(); - const error = new Error(errorMsg); - wrapper.find(SomeComponent).simulateError(error); - const errorMsgComponent = findTestSubject(wrapper, 'docViewerError'); - expect(errorMsgComponent.text()).toMatch(new RegExp(`${errorMsg}`)); -}); diff --git a/src/plugins/discover_legacy/public/application/components/doc_viewer/doc_viewer.tsx b/src/plugins/discover_legacy/public/application/components/doc_viewer/doc_viewer.tsx deleted file mode 100644 index d165c9bd05b8..000000000000 --- a/src/plugins/discover_legacy/public/application/components/doc_viewer/doc_viewer.tsx +++ /dev/null @@ -1,75 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import './doc_viewer.scss'; -import React from 'react'; -import { EuiTabbedContent } from '@elastic/eui'; -import { getDocViewsRegistry } from '../../../opensearch_dashboards_services'; -import { DocViewerTab } from './doc_viewer_tab'; -import { DocView, DocViewRenderProps } from '../../doc_views/doc_views_types'; - -/** - * Rendering tabs with different views of 1 OpenSearch hit in Discover. - * The tabs are provided by the `docs_views` registry. - * A view can contain a React `component`, or any JS framework by using - * a `render` function. - */ -export function DocViewer(renderProps: DocViewRenderProps) { - const docViewsRegistry = getDocViewsRegistry(); - const tabs = docViewsRegistry - .getDocViewsSorted(renderProps.hit) - .map(({ title, render, component }: DocView, idx: number) => { - return { - id: `osd_doc_viewer_tab_${idx}`, - name: title, - content: ( - - ), - }; - }); - - if (!tabs.length) { - // There there's a minimum of 2 tabs active in Discover. - // This condition takes care of unit tests with 0 tabs. - return null; - } - - return ( -
- -
- ); -} diff --git a/src/plugins/discover_legacy/public/application/components/doc_viewer/doc_viewer_render_error.tsx b/src/plugins/discover_legacy/public/application/components/doc_viewer/doc_viewer_render_error.tsx deleted file mode 100644 index 1cb14d191a57..000000000000 --- a/src/plugins/discover_legacy/public/application/components/doc_viewer/doc_viewer_render_error.tsx +++ /dev/null @@ -1,48 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { EuiCallOut, EuiCodeBlock } from '@elastic/eui'; -import { formatMsg, formatStack } from '../../../../../opensearch_dashboards_legacy/public'; - -interface Props { - error: Error | string; -} - -export function DocViewerError({ error }: Props) { - const errMsg = formatMsg(error); - const errStack = typeof error === 'object' ? formatStack(error) : ''; - - return ( - - {errStack && {errStack}} - - ); -} diff --git a/src/plugins/discover_legacy/public/application/components/doc_viewer/doc_viewer_render_tab.test.tsx b/src/plugins/discover_legacy/public/application/components/doc_viewer/doc_viewer_render_tab.test.tsx deleted file mode 100644 index 83d857b24fc5..000000000000 --- a/src/plugins/discover_legacy/public/application/components/doc_viewer/doc_viewer_render_tab.test.tsx +++ /dev/null @@ -1,52 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { mount } from 'enzyme'; -import { DocViewRenderTab } from './doc_viewer_render_tab'; -import { DocViewRenderProps } from '../../doc_views/doc_views_types'; - -test('Mounting and unmounting DocViewerRenderTab', () => { - const unmountFn = jest.fn(); - const renderFn = jest.fn(() => unmountFn); - const renderProps = { - hit: {}, - }; - - const wrapper = mount( - - ); - - expect(renderFn).toMatchSnapshot(); - - wrapper.unmount(); - - expect(unmountFn).toBeCalled(); -}); diff --git a/src/plugins/discover_legacy/public/application/components/doc_viewer/doc_viewer_render_tab.tsx b/src/plugins/discover_legacy/public/application/components/doc_viewer/doc_viewer_render_tab.tsx deleted file mode 100644 index edc7f40c5e43..000000000000 --- a/src/plugins/discover_legacy/public/application/components/doc_viewer/doc_viewer_render_tab.tsx +++ /dev/null @@ -1,52 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React, { useRef, useEffect } from 'react'; -import { DocViewRenderFn, DocViewRenderProps } from '../../doc_views/doc_views_types'; - -interface Props { - render: DocViewRenderFn; - renderProps: DocViewRenderProps; -} -/** - * Responsible for rendering a tab provided by a render function. - * So any other framework can be used (E.g. legacy Angular 3rd party plugin code) - * The provided `render` function is called with a reference to the - * component's `HTMLDivElement` as 1st arg and `renderProps` as 2nd arg - */ -export function DocViewRenderTab({ render, renderProps }: Props) { - const ref = useRef(null); - useEffect(() => { - if (ref && ref.current) { - return render(ref.current, renderProps); - } - }, [render, renderProps]); - return
; -} diff --git a/src/plugins/discover_legacy/public/application/components/doc_viewer/doc_viewer_tab.tsx b/src/plugins/discover_legacy/public/application/components/doc_viewer/doc_viewer_tab.tsx deleted file mode 100644 index 6e7a5f1ac434..000000000000 --- a/src/plugins/discover_legacy/public/application/components/doc_viewer/doc_viewer_tab.tsx +++ /dev/null @@ -1,101 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { I18nProvider } from '@osd/i18n/react'; -import { DocViewRenderTab } from './doc_viewer_render_tab'; -import { DocViewerError } from './doc_viewer_render_error'; -import { DocViewRenderFn, DocViewRenderProps } from '../../doc_views/doc_views_types'; - -interface Props { - component?: React.ComponentType; - id: number; - render?: DocViewRenderFn; - renderProps: DocViewRenderProps; - title: string; -} - -interface State { - error: Error | string; - hasError: boolean; -} -/** - * Renders the tab content of a doc view. - * Displays an error message when it encounters exceptions, thanks to - * Error Boundaries. - */ -export class DocViewerTab extends React.Component { - state = { - hasError: false, - error: '', - }; - - static getDerivedStateFromError(error: unknown) { - // Update state so the next render will show the fallback UI. - return { hasError: true, error }; - } - - shouldComponentUpdate(nextProps: Props, nextState: State) { - return ( - nextProps.renderProps.hit._id !== this.props.renderProps.hit._id || - nextProps.id !== this.props.id || - nextState.hasError - ); - } - - render() { - const { component, render, renderProps, title } = this.props; - const { hasError, error } = this.state; - - if (hasError && error) { - return ; - } else if (!render && !component) { - return ( - - ); - } - - if (render) { - // doc view is provided by a render function, e.g. for legacy Angular code - return ; - } - - // doc view is provided by a react component - - const Component = component as any; - return ( - - - - ); - } -} diff --git a/src/plugins/discover_legacy/public/application/components/doc_viewer_links/__snapshots__/doc_viewer_links.test.tsx.snap b/src/plugins/discover_legacy/public/application/components/doc_viewer_links/__snapshots__/doc_viewer_links.test.tsx.snap deleted file mode 100644 index 95fb0c377180..000000000000 --- a/src/plugins/discover_legacy/public/application/components/doc_viewer_links/__snapshots__/doc_viewer_links.test.tsx.snap +++ /dev/null @@ -1,34 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Dont Render if generateCb.hide 1`] = ` - -`; - -exports[`Render with 2 different links 1`] = ` - - - - - - - - -`; diff --git a/src/plugins/discover_legacy/public/application/components/doc_viewer_links/doc_viewer_links.test.tsx b/src/plugins/discover_legacy/public/application/components/doc_viewer_links/doc_viewer_links.test.tsx deleted file mode 100644 index 8aba555b3a37..000000000000 --- a/src/plugins/discover_legacy/public/application/components/doc_viewer_links/doc_viewer_links.test.tsx +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import React from 'react'; -import { shallow } from 'enzyme'; -import { DocViewerLinks } from './doc_viewer_links'; -import { getDocViewsLinksRegistry } from '../../../opensearch_dashboards_services'; -import { DocViewLinkRenderProps } from '../../doc_views_links/doc_views_links_types'; - -jest.mock('../../../opensearch_dashboards_services', () => { - let registry: any[] = []; - return { - getDocViewsLinksRegistry: () => ({ - addDocViewLink(view: any) { - registry.push(view); - }, - getDocViewsLinksSorted() { - return registry; - }, - resetRegistry: () => { - registry = []; - }, - }), - }; -}); - -beforeEach(() => { - (getDocViewsLinksRegistry() as any).resetRegistry(); - jest.clearAllMocks(); -}); - -test('Render with 2 different links', () => { - const registry = getDocViewsLinksRegistry(); - registry.addDocViewLink({ - order: 10, - label: 'generateCb link', - generateCb: () => ({ - url: 'aaa', - }), - }); - registry.addDocViewLink({ order: 20, label: 'href link', href: 'bbb' }); - - const renderProps = { hit: {} } as DocViewLinkRenderProps; - - const wrapper = shallow(); - - expect(wrapper).toMatchSnapshot(); -}); - -test('Dont Render if generateCb.hide', () => { - const registry = getDocViewsLinksRegistry(); - registry.addDocViewLink({ - order: 10, - label: 'generateCb link', - generateCb: () => ({ - url: 'aaa', - hide: true, - }), - }); - - const renderProps = { hit: {} } as DocViewLinkRenderProps; - - const wrapper = shallow(); - - expect(wrapper).toMatchSnapshot(); -}); diff --git a/src/plugins/discover_legacy/public/application/components/doc_viewer_links/doc_viewer_links.tsx b/src/plugins/discover_legacy/public/application/components/doc_viewer_links/doc_viewer_links.tsx deleted file mode 100644 index 9efb0693fde6..000000000000 --- a/src/plugins/discover_legacy/public/application/components/doc_viewer_links/doc_viewer_links.tsx +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import React from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiListGroupItem, EuiListGroupItemProps } from '@elastic/eui'; -import { getDocViewsLinksRegistry } from '../../../opensearch_dashboards_services'; -import { DocViewLinkRenderProps } from '../../doc_views_links/doc_views_links_types'; - -export function DocViewerLinks(renderProps: DocViewLinkRenderProps) { - const listItems = getDocViewsLinksRegistry() - .getDocViewsLinksSorted() - .filter((item) => !(item.generateCb && item.generateCb(renderProps)?.hide)) - .map((item) => { - const { generateCb, href, ...props } = item; - const listItem: EuiListGroupItemProps = { - 'data-test-subj': 'docTableRowAction', - ...props, - href: generateCb ? generateCb(renderProps).url : href, - }; - - return listItem; - }); - - return ( - - {listItems.map((item, index) => ( - - - - ))} - - ); -} diff --git a/src/plugins/discover_legacy/public/application/components/field_name/__snapshots__/field_name.test.tsx.snap b/src/plugins/discover_legacy/public/application/components/field_name/__snapshots__/field_name.test.tsx.snap deleted file mode 100644 index cfd81a66acae..000000000000 --- a/src/plugins/discover_legacy/public/application/components/field_name/__snapshots__/field_name.test.tsx.snap +++ /dev/null @@ -1,94 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`FieldName renders a geo field, useShortDots is set to true 1`] = ` -
-
- - - -
-
- - - t.t.test - - -
-
-`; - -exports[`FieldName renders a number field by providing a field record, useShortDots is set to false 1`] = ` -
-
- - - -
-
- - - test.test.test - - -
-
-`; - -exports[`FieldName renders a string field by providing fieldType and fieldName 1`] = ` -
-
- - - -
-
- - - test - - -
-
-`; diff --git a/src/plugins/discover_legacy/public/application/components/field_name/field_name.test.tsx b/src/plugins/discover_legacy/public/application/components/field_name/field_name.test.tsx deleted file mode 100644 index 54dc902837d0..000000000000 --- a/src/plugins/discover_legacy/public/application/components/field_name/field_name.test.tsx +++ /dev/null @@ -1,52 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { render } from 'enzyme'; -import { FieldName } from './field_name'; - -// Note that it currently provides just 2 basic tests, there should be more, but -// the components involved will soon change -test('FieldName renders a string field by providing fieldType and fieldName', () => { - const component = render(); - expect(component).toMatchSnapshot(); -}); - -test('FieldName renders a number field by providing a field record, useShortDots is set to false', () => { - const component = render(); - expect(component).toMatchSnapshot(); -}); - -test('FieldName renders a geo field, useShortDots is set to true', () => { - const component = render( - - ); - expect(component).toMatchSnapshot(); -}); diff --git a/src/plugins/discover_legacy/public/application/components/field_name/field_name.tsx b/src/plugins/discover_legacy/public/application/components/field_name/field_name.tsx deleted file mode 100644 index bbd9ab79d0fb..000000000000 --- a/src/plugins/discover_legacy/public/application/components/field_name/field_name.tsx +++ /dev/null @@ -1,75 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui'; - -import { FieldIcon, FieldIconProps } from '../../../../../opensearch_dashboards_react/public'; -import { shortenDottedString } from '../../helpers'; -import { getFieldTypeName } from './field_type_name'; - -// properties fieldType and fieldName are provided in osd_doc_view -// this should be changed when both components are deangularized -interface Props { - fieldName: string; - fieldType: string; - useShortDots?: boolean; - fieldIconProps?: Omit; - scripted?: boolean; -} - -export function FieldName({ - fieldName, - fieldType, - useShortDots, - fieldIconProps, - scripted = false, -}: Props) { - const typeName = getFieldTypeName(fieldType); - const displayName = useShortDots ? shortenDottedString(fieldName) : fieldName; - - return ( - - - - - - - {displayName} - - - - ); -} diff --git a/src/plugins/discover_legacy/public/application/components/field_name/field_type_name.ts b/src/plugins/discover_legacy/public/application/components/field_name/field_type_name.ts deleted file mode 100644 index 38b18792d3e4..000000000000 --- a/src/plugins/discover_legacy/public/application/components/field_name/field_type_name.ts +++ /dev/null @@ -1,85 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; - -export function getFieldTypeName(type: string) { - switch (type) { - case 'boolean': - return i18n.translate('discover.fieldNameIcons.booleanAriaLabel', { - defaultMessage: 'Boolean field', - }); - case 'conflict': - return i18n.translate('discover.fieldNameIcons.conflictFieldAriaLabel', { - defaultMessage: 'Conflicting field', - }); - case 'date': - return i18n.translate('discover.fieldNameIcons.dateFieldAriaLabel', { - defaultMessage: 'Date field', - }); - case 'geo_point': - return i18n.translate('discover.fieldNameIcons.geoPointFieldAriaLabel', { - defaultMessage: 'Geo point field', - }); - case 'geo_shape': - return i18n.translate('discover.fieldNameIcons.geoShapeFieldAriaLabel', { - defaultMessage: 'Geo shape field', - }); - case 'ip': - return i18n.translate('discover.fieldNameIcons.ipAddressFieldAriaLabel', { - defaultMessage: 'IP address field', - }); - case 'murmur3': - return i18n.translate('discover.fieldNameIcons.murmur3FieldAriaLabel', { - defaultMessage: 'Murmur3 field', - }); - case 'number': - return i18n.translate('discover.fieldNameIcons.numberFieldAriaLabel', { - defaultMessage: 'Number field', - }); - case 'source': - // Note that this type is currently not provided, type for _source is undefined - return i18n.translate('discover.fieldNameIcons.sourceFieldAriaLabel', { - defaultMessage: 'Source field', - }); - case 'string': - return i18n.translate('discover.fieldNameIcons.stringFieldAriaLabel', { - defaultMessage: 'String field', - }); - case 'nested': - return i18n.translate('discover.fieldNameIcons.nestedFieldAriaLabel', { - defaultMessage: 'Nested field', - }); - default: - return i18n.translate('discover.fieldNameIcons.unknownFieldAriaLabel', { - defaultMessage: 'Unknown field', - }); - } -} diff --git a/src/plugins/discover_legacy/public/application/components/help_menu/help_menu_util.js b/src/plugins/discover_legacy/public/application/components/help_menu/help_menu_util.js deleted file mode 100644 index 39ea94046d7c..000000000000 --- a/src/plugins/discover_legacy/public/application/components/help_menu/help_menu_util.js +++ /dev/null @@ -1,47 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { getServices } from '../../../opensearch_dashboards_services'; -const { docLinks } = getServices(); - -export function addHelpMenuToAppChrome(chrome) { - chrome.setHelpExtension({ - appName: i18n.translate('discover.helpMenu.appName', { - defaultMessage: 'Discover', - }), - links: [ - { - linkType: 'documentation', - href: `${docLinks.links.opensearchDashboards.introduction}`, - }, - ], - }); -} diff --git a/src/plugins/discover_legacy/public/application/components/hits_counter/hits_counter.test.tsx b/src/plugins/discover_legacy/public/application/components/hits_counter/hits_counter.test.tsx deleted file mode 100644 index 998ababbc47f..000000000000 --- a/src/plugins/discover_legacy/public/application/components/hits_counter/hits_counter.test.tsx +++ /dev/null @@ -1,80 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { mountWithIntl } from 'test_utils/enzyme_helpers'; -import { ReactWrapper } from 'enzyme'; -import { HitsCounter, HitsCounterProps } from './hits_counter'; -import { findTestSubject } from 'test_utils/helpers'; - -describe('hits counter', function () { - let props: HitsCounterProps; - let component: ReactWrapper; - - beforeAll(() => { - props = { - onResetQuery: jest.fn(), - showResetButton: true, - hits: 2, - }; - }); - - it('HitsCounter renders a button by providing the showResetButton property', () => { - component = mountWithIntl(); - expect(findTestSubject(component, 'resetSavedSearch').length).toBe(1); - }); - - it('HitsCounter not renders a button when the showResetButton property is false', () => { - component = mountWithIntl( - - ); - expect(findTestSubject(component, 'resetSavedSearch').length).toBe(0); - }); - - it('expect to render the number of hits', function () { - component = mountWithIntl(); - const hits = findTestSubject(component, 'discoverQueryHits'); - expect(hits.text()).toBe('2'); - }); - - it('expect to render 1,899 hits if 1899 hits given', function () { - component = mountWithIntl( - - ); - const hits = findTestSubject(component, 'discoverQueryHits'); - expect(hits.text()).toBe('1,899'); - }); - - it('should reset query', function () { - component = mountWithIntl(); - findTestSubject(component, 'resetSavedSearch').simulate('click'); - expect(props.onResetQuery).toHaveBeenCalled(); - }); -}); diff --git a/src/plugins/discover_legacy/public/application/components/hits_counter/hits_counter.tsx b/src/plugins/discover_legacy/public/application/components/hits_counter/hits_counter.tsx deleted file mode 100644 index 3355b733202b..000000000000 --- a/src/plugins/discover_legacy/public/application/components/hits_counter/hits_counter.tsx +++ /dev/null @@ -1,95 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; -import { FormattedMessage, I18nProvider } from '@osd/i18n/react'; -import { i18n } from '@osd/i18n'; -import { formatNumWithCommas } from '../../helpers'; - -export interface HitsCounterProps { - /** - * the number of query hits - */ - hits: number; - /** - * displays the reset button - */ - showResetButton: boolean; - /** - * resets the query - */ - onResetQuery: () => void; -} - -export function HitsCounter({ hits, showResetButton, onResetQuery }: HitsCounterProps) { - return ( - - - - - {formatNumWithCommas(hits)}{' '} - - - - {showResetButton && ( - - - - - - )} - - - ); -} diff --git a/src/plugins/discover_legacy/public/application/components/hits_counter/index.ts b/src/plugins/discover_legacy/public/application/components/hits_counter/index.ts deleted file mode 100644 index 213cf96e0cc8..000000000000 --- a/src/plugins/discover_legacy/public/application/components/hits_counter/index.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export { HitsCounter } from './hits_counter'; diff --git a/src/plugins/discover_legacy/public/application/components/json_code_block/__snapshots__/json_code_block.test.tsx.snap b/src/plugins/discover_legacy/public/application/components/json_code_block/__snapshots__/json_code_block.test.tsx.snap deleted file mode 100644 index 3897e22c50f1..000000000000 --- a/src/plugins/discover_legacy/public/application/components/json_code_block/__snapshots__/json_code_block.test.tsx.snap +++ /dev/null @@ -1,20 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`returns the \`JsonCodeEditor\` component 1`] = ` - - { - "_index": "test", - "_type": "doc", - "_id": "foo", - "_score": 1, - "_source": { - "test": 123 - } -} - -`; diff --git a/src/plugins/discover_legacy/public/application/components/json_code_block/json_code_block.test.tsx b/src/plugins/discover_legacy/public/application/components/json_code_block/json_code_block.test.tsx deleted file mode 100644 index 2cb700b4d2ac..000000000000 --- a/src/plugins/discover_legacy/public/application/components/json_code_block/json_code_block.test.tsx +++ /dev/null @@ -1,46 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { shallow } from 'enzyme'; -import { JsonCodeBlock } from './json_code_block'; -import { IndexPattern } from '../../../../../data/public'; - -it('returns the `JsonCodeEditor` component', () => { - const props = { - hit: { _index: 'test', _type: 'doc', _id: 'foo', _score: 1, _source: { test: 123 } }, - columns: [], - indexPattern: {} as IndexPattern, - filter: jest.fn(), - onAddColumn: jest.fn(), - onRemoveColumn: jest.fn(), - }; - expect(shallow()).toMatchSnapshot(); -}); diff --git a/src/plugins/discover_legacy/public/application/components/json_code_block/json_code_block.tsx b/src/plugins/discover_legacy/public/application/components/json_code_block/json_code_block.tsx deleted file mode 100644 index f33cae438cb2..000000000000 --- a/src/plugins/discover_legacy/public/application/components/json_code_block/json_code_block.tsx +++ /dev/null @@ -1,45 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { EuiCodeBlock } from '@elastic/eui'; -import { i18n } from '@osd/i18n'; -import { DocViewRenderProps } from '../../doc_views/doc_views_types'; - -export function JsonCodeBlock({ hit }: DocViewRenderProps) { - const label = i18n.translate('discover.docViews.json.codeEditorAriaLabel', { - defaultMessage: 'Read only JSON view of an opensearch document', - }); - return ( - - {JSON.stringify(hit, null, 2)} - - ); -} diff --git a/src/plugins/discover_legacy/public/application/components/loading_spinner/loading_spinner.test.tsx b/src/plugins/discover_legacy/public/application/components/loading_spinner/loading_spinner.test.tsx deleted file mode 100644 index fbc98e2550e0..000000000000 --- a/src/plugins/discover_legacy/public/application/components/loading_spinner/loading_spinner.test.tsx +++ /dev/null @@ -1,45 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { mountWithIntl } from 'test_utils/enzyme_helpers'; -import { ReactWrapper } from 'enzyme'; -import { LoadingSpinner } from './loading_spinner'; -import { findTestSubject } from 'test_utils/helpers'; - -describe('loading spinner', function () { - let component: ReactWrapper; - - it('LoadingSpinner renders a Searching text and a spinner', () => { - component = mountWithIntl(); - expect(findTestSubject(component, 'loadingSpinnerText').text()).toBe('Searching'); - expect(findTestSubject(component, 'loadingSpinner').length).toBe(1); - }); -}); diff --git a/src/plugins/discover_legacy/public/application/components/loading_spinner/loading_spinner.tsx b/src/plugins/discover_legacy/public/application/components/loading_spinner/loading_spinner.tsx deleted file mode 100644 index 697c7a136d60..000000000000 --- a/src/plugins/discover_legacy/public/application/components/loading_spinner/loading_spinner.tsx +++ /dev/null @@ -1,47 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { EuiLoadingSpinner, EuiTitle, EuiSpacer } from '@elastic/eui'; -import { FormattedMessage } from '@osd/i18n/react'; - -export function LoadingSpinner() { - return ( - <> - -

- -

-
- - - - ); -} diff --git a/src/plugins/discover_legacy/public/application/components/sidebar/__snapshots__/discover_index_pattern.test.tsx.snap b/src/plugins/discover_legacy/public/application/components/sidebar/__snapshots__/discover_index_pattern.test.tsx.snap deleted file mode 100644 index 42c11152e263..000000000000 --- a/src/plugins/discover_legacy/public/application/components/sidebar/__snapshots__/discover_index_pattern.test.tsx.snap +++ /dev/null @@ -1,3 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`DiscoverIndexPattern Invalid props dont cause an exception: "" 1`] = `""`; diff --git a/src/plugins/discover_legacy/public/application/components/sidebar/change_indexpattern.tsx b/src/plugins/discover_legacy/public/application/components/sidebar/change_indexpattern.tsx deleted file mode 100644 index 553031f06721..000000000000 --- a/src/plugins/discover_legacy/public/application/components/sidebar/change_indexpattern.tsx +++ /dev/null @@ -1,131 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import React, { useState } from 'react'; -import { - EuiButtonEmpty, - EuiPopover, - EuiPopoverTitle, - EuiSelectable, - EuiButtonEmptyProps, -} from '@elastic/eui'; -import { EuiSelectableProps } from '@elastic/eui/src/components/selectable/selectable'; -import { IndexPatternRef } from './types'; - -export type ChangeIndexPatternTriggerProps = EuiButtonEmptyProps & { - label: string; - title?: string; -}; - -export function ChangeIndexPattern({ - indexPatternRefs, - indexPatternId, - onChangeIndexPattern, - trigger, - selectableProps, -}: { - trigger: ChangeIndexPatternTriggerProps; - indexPatternRefs: IndexPatternRef[]; - onChangeIndexPattern: (newId: string) => void; - indexPatternId?: string; - selectableProps?: EuiSelectableProps; -}) { - const [isPopoverOpen, setPopoverIsOpen] = useState(false); - - const createTrigger = function () { - const { label, title, ...rest } = trigger; - return ( - setPopoverIsOpen(!isPopoverOpen)} - {...rest} - > - {label} - - ); - }; - - return ( - setPopoverIsOpen(false)} - className="eui-textTruncate" - anchorClassName="eui-textTruncate" - display="block" - panelPaddingSize="s" - ownFocus - > -
- - {i18n.translate('discover.fieldChooser.indexPattern.changeIndexPatternTitle', { - defaultMessage: 'Change index pattern', - })} - - ({ - label: title, - key: id, - value: id, - checked: id === indexPatternId ? 'on' : undefined, - }))} - onChange={(choices) => { - const choice = (choices.find(({ checked }) => checked) as unknown) as { - value: string; - }; - onChangeIndexPattern(choice.value); - setPopoverIsOpen(false); - }} - searchProps={{ - compressed: true, - ...(selectableProps ? selectableProps.searchProps : undefined), - }} - > - {(list, search) => ( - <> - {search} - {list} - - )} - -
-
- ); -} diff --git a/src/plugins/discover_legacy/public/application/components/sidebar/discover_field.scss b/src/plugins/discover_legacy/public/application/components/sidebar/discover_field.scss deleted file mode 100644 index 8e1dd41f66ab..000000000000 --- a/src/plugins/discover_legacy/public/application/components/sidebar/discover_field.scss +++ /dev/null @@ -1,4 +0,0 @@ -.dscSidebarItem__fieldPopoverPanel { - min-width: 260px; - max-width: 300px; -} diff --git a/src/plugins/discover_legacy/public/application/components/sidebar/discover_field.test.tsx b/src/plugins/discover_legacy/public/application/components/sidebar/discover_field.test.tsx deleted file mode 100644 index 1b384a4b5550..000000000000 --- a/src/plugins/discover_legacy/public/application/components/sidebar/discover_field.test.tsx +++ /dev/null @@ -1,152 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -// @ts-ignore -import { findTestSubject } from '@elastic/eui/lib/test'; -// @ts-ignore -import stubbedLogstashFields from 'fixtures/logstash_fields'; -import { mountWithIntl } from 'test_utils/enzyme_helpers'; -import { DiscoverField } from './discover_field'; -import { coreMock } from '../../../../../../core/public/mocks'; -import { IndexPatternField } from '../../../../../data/public'; -import { getStubIndexPattern } from '../../../../../data/public/test_utils'; - -jest.mock('../../../opensearch_dashboards_services', () => ({ - getServices: () => ({ - history: () => ({ - location: { - search: '', - }, - }), - capabilities: { - visualize: { - show: true, - }, - }, - uiSettings: { - get: (key: string) => { - if (key === 'fields:popularLimit') { - return 5; - } else if (key === 'shortDots:enable') { - return false; - } - }, - }, - }), -})); - -function getComponent({ - selected = false, - showDetails = false, - useShortDots = false, - field, -}: { - selected?: boolean; - showDetails?: boolean; - useShortDots?: boolean; - field?: IndexPatternField; -}) { - const indexPattern = getStubIndexPattern( - 'logstash-*', - (cfg: any) => cfg, - 'time', - stubbedLogstashFields(), - coreMock.createSetup() - ); - - const finalField = - field ?? - new IndexPatternField( - { - name: 'bytes', - type: 'number', - esTypes: ['long'], - count: 10, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: true, - }, - 'bytes' - ); - - const props = { - indexPattern, - columns: [], - field: finalField, - getDetails: jest.fn(() => ({ buckets: [], error: '', exists: 1, total: 1 })), - onAddFilter: jest.fn(), - onAddField: jest.fn(), - onRemoveField: jest.fn(), - showDetails, - selected, - useShortDots, - }; - const comp = mountWithIntl(); - return { comp, props }; -} - -describe('discover sidebar field', function () { - it('should allow selecting fields', function () { - const { comp, props } = getComponent({}); - findTestSubject(comp, 'fieldToggle-bytes').simulate('click'); - expect(props.onAddField).toHaveBeenCalledWith('bytes'); - }); - it('should allow deselecting fields', function () { - const { comp, props } = getComponent({ selected: true }); - findTestSubject(comp, 'fieldToggle-bytes').simulate('click'); - expect(props.onRemoveField).toHaveBeenCalledWith('bytes'); - }); - it('should trigger getDetails', function () { - const { comp, props } = getComponent({ selected: true }); - findTestSubject(comp, 'field-bytes-showDetails').simulate('click'); - expect(props.getDetails).toHaveBeenCalledWith(props.field); - }); - it('should not allow clicking on _source', function () { - const field = new IndexPatternField( - { - name: '_source', - type: '_source', - esTypes: ['_source'], - searchable: true, - aggregatable: true, - readFromDocValues: true, - }, - '_source' - ); - const { comp, props } = getComponent({ - selected: true, - field, - }); - findTestSubject(comp, 'field-_source-showDetails').simulate('click'); - expect(props.getDetails).not.toHaveBeenCalled(); - }); -}); diff --git a/src/plugins/discover_legacy/public/application/components/sidebar/discover_field.tsx b/src/plugins/discover_legacy/public/application/components/sidebar/discover_field.tsx deleted file mode 100644 index e807267435eb..000000000000 --- a/src/plugins/discover_legacy/public/application/components/sidebar/discover_field.tsx +++ /dev/null @@ -1,245 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React, { useState } from 'react'; -import { EuiPopover, EuiPopoverTitle, EuiButtonIcon, EuiToolTip } from '@elastic/eui'; -import { i18n } from '@osd/i18n'; -import { DiscoverFieldDetails } from './discover_field_details'; -import { FieldIcon, FieldButton } from '../../../../../opensearch_dashboards_react/public'; -import { FieldDetails } from './types'; -import { IndexPatternField, IndexPattern } from '../../../../../data/public'; -import { shortenDottedString } from '../../helpers'; -import { getFieldTypeName } from './lib/get_field_type_name'; -import './discover_field.scss'; - -export interface DiscoverFieldProps { - /** - * the selected columns displayed in the doc table in discover - */ - columns: string[]; - /** - * The displayed field - */ - field: IndexPatternField; - /** - * The currently selected index pattern - */ - indexPattern: IndexPattern; - /** - * Callback to add/select the field - */ - onAddField: (fieldName: string) => void; - /** - * Callback to add a filter to filter bar - */ - onAddFilter: (field: IndexPatternField | string, value: string, type: '+' | '-') => void; - /** - * Callback to remove/deselect a the field - * @param fieldName - */ - onRemoveField: (fieldName: string) => void; - /** - * Retrieve details data for the field - */ - getDetails: (field: IndexPatternField) => FieldDetails; - /** - * Determines whether the field is selected - */ - selected?: boolean; - /** - * Determines whether the field name is shortened test.sub1.sub2 = t.s.sub2 - */ - useShortDots?: boolean; -} - -export function DiscoverField({ - columns, - field, - indexPattern, - onAddField, - onRemoveField, - onAddFilter, - getDetails, - selected, - useShortDots, -}: DiscoverFieldProps) { - const addLabelAria = i18n.translate('discover.fieldChooser.discoverField.addButtonAriaLabel', { - defaultMessage: 'Add {field} to table', - values: { field: field.name }, - }); - const removeLabelAria = i18n.translate( - 'discover.fieldChooser.discoverField.removeButtonAriaLabel', - { - defaultMessage: 'Remove {field} from table', - values: { field: field.name }, - } - ); - - const [infoIsOpen, setOpen] = useState(false); - - const toggleDisplay = (f: IndexPatternField) => { - if (selected) { - onRemoveField(f.name); - } else { - onAddField(f.name); - } - }; - - function togglePopover() { - setOpen(!infoIsOpen); - } - - function wrapOnDot(str?: string) { - // u200B is a non-width white-space character, which allows - // the browser to efficiently word-wrap right after the dot - // without us having to draw a lot of extra DOM elements, etc - return str ? str.replace(/\./g, '.\u200B') : ''; - } - - const dscFieldIcon = ( - - ); - - const fieldName = ( - - {useShortDots ? wrapOnDot(shortenDottedString(field.name)) : wrapOnDot(field.displayName)} - - ); - - let actionButton; - if (field.name !== '_source' && !selected) { - actionButton = ( - - ) => { - if (ev.type === 'click') { - ev.currentTarget.focus(); - } - ev.preventDefault(); - ev.stopPropagation(); - toggleDisplay(field); - }} - data-test-subj={`fieldToggle-${field.name}`} - aria-label={addLabelAria} - /> - - ); - } else if (field.name !== '_source' && selected) { - actionButton = ( - - ) => { - if (ev.type === 'click') { - ev.currentTarget.focus(); - } - ev.preventDefault(); - ev.stopPropagation(); - toggleDisplay(field); - }} - data-test-subj={`fieldToggle-${field.name}`} - aria-label={removeLabelAria} - /> - - ); - } - - if (field.type === '_source') { - return ( - - ); - } - - return ( - { - togglePopover(); - }} - dataTestSubj={`field-${field.name}-showDetails`} - fieldIcon={dscFieldIcon} - fieldAction={actionButton} - fieldName={fieldName} - /> - } - isOpen={infoIsOpen} - closePopover={() => setOpen(false)} - anchorPosition="rightUp" - panelClassName="dscSidebarItem__fieldPopoverPanel" - > - - {' '} - {i18n.translate('discover.fieldChooser.discoverField.fieldTopValuesLabel', { - defaultMessage: 'Top 5 values', - })} - - {infoIsOpen && ( - - )} - - ); -} diff --git a/src/plugins/discover_legacy/public/application/components/sidebar/discover_field_bucket.scss b/src/plugins/discover_legacy/public/application/components/sidebar/discover_field_bucket.scss deleted file mode 100644 index 90b645f70084..000000000000 --- a/src/plugins/discover_legacy/public/application/components/sidebar/discover_field_bucket.scss +++ /dev/null @@ -1,4 +0,0 @@ -.dscFieldDetails__barContainer { - // Constrains value to the flex item, and allows for truncation when necessary - min-width: 0; -} diff --git a/src/plugins/discover_legacy/public/application/components/sidebar/discover_field_bucket.tsx b/src/plugins/discover_legacy/public/application/components/sidebar/discover_field_bucket.tsx deleted file mode 100644 index 6a4dbe295e50..000000000000 --- a/src/plugins/discover_legacy/public/application/components/sidebar/discover_field_bucket.tsx +++ /dev/null @@ -1,133 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { EuiText, EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; -import { i18n } from '@osd/i18n'; -import { StringFieldProgressBar } from './string_progress_bar'; -import { Bucket } from './types'; -import { IndexPatternField } from '../../../../../data/public'; -import './discover_field_bucket.scss'; - -interface Props { - bucket: Bucket; - field: IndexPatternField; - onAddFilter: (field: IndexPatternField | string, value: string, type: '+' | '-') => void; -} - -export function DiscoverFieldBucket({ field, bucket, onAddFilter }: Props) { - const emptyTxt = i18n.translate('discover.fieldChooser.detailViews.emptyStringText', { - defaultMessage: 'Empty string', - }); - const addLabel = i18n.translate('discover.fieldChooser.detailViews.filterValueButtonAriaLabel', { - defaultMessage: 'Filter for {field}: "{value}"', - values: { value: bucket.value, field: field.name }, - }); - const removeLabel = i18n.translate( - 'discover.fieldChooser.detailViews.filterOutValueButtonAriaLabel', - { - defaultMessage: 'Filter out {field}: "{value}"', - values: { value: bucket.value, field: field.name }, - } - ); - - return ( - <> - - - - - - {bucket.display === '' ? emptyTxt : bucket.display} - - - - - {bucket.percent.toFixed(1)}% - - - - - - {field.filterable && ( - -
- onAddFilter(field, bucket.value, '+')} - aria-label={addLabel} - data-test-subj={`plus-${field.name}-${bucket.value}`} - style={{ - minHeight: 'auto', - minWidth: 'auto', - paddingRight: 2, - paddingLeft: 2, - paddingTop: 0, - paddingBottom: 0, - }} - className={'euiButtonIcon--auto'} - /> - onAddFilter(field, bucket.value, '-')} - aria-label={removeLabel} - data-test-subj={`minus-${field.name}-${bucket.value}`} - style={{ - minHeight: 'auto', - minWidth: 'auto', - paddingTop: 0, - paddingBottom: 0, - paddingRight: 2, - paddingLeft: 2, - }} - className={'euiButtonIcon--auto'} - /> -
-
- )} -
- - - ); -} diff --git a/src/plugins/discover_legacy/public/application/components/sidebar/discover_field_details.scss b/src/plugins/discover_legacy/public/application/components/sidebar/discover_field_details.scss deleted file mode 100644 index 7bf0892d0148..000000000000 --- a/src/plugins/discover_legacy/public/application/components/sidebar/discover_field_details.scss +++ /dev/null @@ -1,6 +0,0 @@ -.dscFieldDetails__visualizeBtn { - @include euiFontSizeXS; - - height: $euiSizeL !important; - min-width: $euiSize * 4; -} diff --git a/src/plugins/discover_legacy/public/application/components/sidebar/discover_field_details.test.tsx b/src/plugins/discover_legacy/public/application/components/sidebar/discover_field_details.test.tsx deleted file mode 100644 index 63d5c7ace303..000000000000 --- a/src/plugins/discover_legacy/public/application/components/sidebar/discover_field_details.test.tsx +++ /dev/null @@ -1,312 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -// @ts-ignore -import { findTestSubject } from '@elastic/eui/lib/test'; -import { act } from '@testing-library/react'; -// @ts-ignore -import stubbedLogstashFields from 'fixtures/logstash_fields'; -import { mountWithIntl, nextTick } from 'test_utils/enzyme_helpers'; -import { DiscoverFieldDetails } from './discover_field_details'; -import { coreMock } from '../../../../../../core/public/mocks'; -import { IndexPatternField } from '../../../../../data/public'; -import { getStubIndexPattern } from '../../../../../data/public/test_utils'; - -const mockGetHref = jest.fn(); -const mockGetTriggerCompatibleActions = jest.fn(); - -jest.mock('../../../opensearch_dashboards_services', () => ({ - getUiActions: () => ({ - getTriggerCompatibleActions: mockGetTriggerCompatibleActions, - }), -})); - -const indexPattern = getStubIndexPattern( - 'logstash-*', - (cfg: any) => cfg, - 'time', - stubbedLogstashFields(), - coreMock.createSetup() -); - -describe('discover sidebar field details', function () { - const defaultProps = { - columns: [], - details: { buckets: [], error: '', exists: 1, total: 1 }, - indexPattern, - onAddFilter: jest.fn(), - }; - - beforeEach(() => { - mockGetHref.mockReturnValue('/foo/bar'); - mockGetTriggerCompatibleActions.mockReturnValue([ - { - getHref: mockGetHref, - }, - ]); - }); - - function mountComponent(field: IndexPatternField, props?: Record) { - const compProps = { ...defaultProps, ...props, field }; - return mountWithIntl(); - } - - it('should render buckets if they exist', async function () { - const visualizableField = new IndexPatternField( - { - name: 'bytes', - type: 'number', - esTypes: ['long'], - count: 10, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: true, - }, - 'bytes' - ); - const buckets = [1, 2, 3].map((n) => ({ - display: `display-${n}`, - value: `value-${n}`, - percent: 25, - count: 100, - })); - const comp = mountComponent(visualizableField, { - details: { ...defaultProps.details, buckets }, - }); - expect(findTestSubject(comp, 'fieldVisualizeError').length).toBe(0); - expect(findTestSubject(comp, 'fieldVisualizeBucketContainer').length).toBe(1); - expect(findTestSubject(comp, 'fieldVisualizeBucketContainer').children().length).toBe( - buckets.length - ); - // Visualize link should not be rendered until async hook update - expect(findTestSubject(comp, 'fieldVisualizeLink').length).toBe(0); - expect(findTestSubject(comp, 'fieldVisualize-bytes').length).toBe(0); - - // Complete async hook - await act(async () => { - await nextTick(); - comp.update(); - }); - expect(findTestSubject(comp, 'fieldVisualizeError').length).toBe(0); - expect(findTestSubject(comp, 'fieldVisualizeBucketContainer').length).toBe(1); - expect(findTestSubject(comp, 'fieldVisualizeBucketContainer').children().length).toBe( - buckets.length - ); - expect(findTestSubject(comp, 'fieldVisualizeLink').length).toBe(1); - expect(findTestSubject(comp, 'fieldVisualize-bytes').length).toBe(1); - }); - - it('should only render buckets if they exist', async function () { - const visualizableField = new IndexPatternField( - { - name: 'bytes', - type: 'number', - esTypes: ['long'], - count: 10, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: true, - }, - 'bytes' - ); - const comp = mountComponent(visualizableField); - expect(findTestSubject(comp, 'fieldVisualizeContainer').length).toBe(1); - expect(findTestSubject(comp, 'fieldVisualizeError').length).toBe(0); - expect(findTestSubject(comp, 'fieldVisualizeBucketContainer').length).toBe(0); - expect(findTestSubject(comp, 'fieldVisualizeLink').length).toBe(0); - expect(findTestSubject(comp, 'fieldVisualize-bytes').length).toBe(0); - - await act(async () => { - await nextTick(); - comp.update(); - }); - - expect(findTestSubject(comp, 'fieldVisualizeContainer').length).toBe(1); - expect(findTestSubject(comp, 'fieldVisualizeError').length).toBe(0); - expect(findTestSubject(comp, 'fieldVisualizeBucketContainer').length).toBe(0); - expect(findTestSubject(comp, 'fieldVisualizeLink').length).toBe(1); - expect(findTestSubject(comp, 'fieldVisualize-bytes').length).toBe(1); - }); - - it('should render a details error', async function () { - const visualizableField = new IndexPatternField( - { - name: 'bytes', - type: 'number', - esTypes: ['long'], - count: 10, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: true, - }, - 'bytes' - ); - const errText = 'Some error'; - const comp = mountComponent(visualizableField, { - details: { ...defaultProps.details, error: errText }, - }); - expect(findTestSubject(comp, 'fieldVisualizeContainer').length).toBe(1); - expect(findTestSubject(comp, 'fieldVisualizeBucketContainer').length).toBe(0); - expect(findTestSubject(comp, 'fieldVisualizeError').length).toBe(1); - expect(findTestSubject(comp, 'fieldVisualizeError').text()).toBe(errText); - - await act(async () => { - await nextTick(); - comp.update(); - }); - expect(findTestSubject(comp, 'fieldVisualizeLink').length).toBe(1); - expect(findTestSubject(comp, 'fieldVisualize-bytes').length).toBe(1); - }); - - it('should handle promise rejection from isFieldVisualizable', async function () { - mockGetTriggerCompatibleActions.mockRejectedValue(new Error('Async error')); - const visualizableField = new IndexPatternField( - { - name: 'bytes', - type: 'number', - esTypes: ['long'], - count: 10, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: true, - }, - 'bytes' - ); - const comp = mountComponent(visualizableField); - - await act(async () => { - await nextTick(); - comp.update(); - }); - expect(findTestSubject(comp, 'fieldVisualizeLink').length).toBe(0); - expect(findTestSubject(comp, 'fieldVisualize-bytes').length).toBe(0); - }); - - it('should handle promise rejection from getVisualizeHref', async function () { - mockGetHref.mockRejectedValue(new Error('Async error')); - const visualizableField = new IndexPatternField( - { - name: 'bytes', - type: 'number', - esTypes: ['long'], - count: 10, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: true, - }, - 'bytes' - ); - const comp = mountComponent(visualizableField); - - await act(async () => { - await nextTick(); - comp.update(); - }); - expect(findTestSubject(comp, 'fieldVisualizeLink').length).toBe(0); - expect(findTestSubject(comp, 'fieldVisualize-bytes').length).toBe(0); - }); - - it('should enable the visualize link for a number field', async function () { - const visualizableField = new IndexPatternField( - { - name: 'bytes', - type: 'number', - esTypes: ['long'], - count: 10, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: true, - }, - 'bytes' - ); - const comp = mountComponent(visualizableField); - - await act(async () => { - await nextTick(); - comp.update(); - }); - expect(findTestSubject(comp, 'fieldVisualizeLink').length).toBe(1); - expect(findTestSubject(comp, 'fieldVisualize-bytes').length).toBe(1); - }); - - it('should disable the visualize link for an _id field', async function () { - expect.assertions(1); - const conflictField = new IndexPatternField( - { - name: '_id', - type: 'string', - esTypes: ['_id'], - count: 0, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: true, - }, - 'test' - ); - const comp = mountComponent(conflictField); - - await act(async () => { - await nextTick(); - comp.update(); - }); - expect(findTestSubject(comp, 'fieldVisualize-_id').length).toBe(0); - }); - - it('should disable the visualize link for an unknown field', async function () { - const unknownField = new IndexPatternField( - { - name: 'test', - type: 'unknown', - esTypes: ['double'], - count: 0, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: true, - }, - 'test' - ); - const comp = mountComponent(unknownField); - - await act(async () => { - await nextTick(); - comp.update(); - }); - expect(findTestSubject(comp, 'fieldVisualize-test').length).toBe(0); - }); -}); diff --git a/src/plugins/discover_legacy/public/application/components/sidebar/discover_field_details.tsx b/src/plugins/discover_legacy/public/application/components/sidebar/discover_field_details.tsx deleted file mode 100644 index 906c173ed07d..000000000000 --- a/src/plugins/discover_legacy/public/application/components/sidebar/discover_field_details.tsx +++ /dev/null @@ -1,153 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React, { useState, useEffect } from 'react'; -import { EuiLink, EuiIconTip, EuiText, EuiPopoverFooter, EuiButton, EuiSpacer } from '@elastic/eui'; -import { FormattedMessage } from '@osd/i18n/react'; -import { DiscoverFieldBucket } from './discover_field_bucket'; -import { getWarnings } from './lib/get_warnings'; -import { - triggerVisualizeActions, - isFieldVisualizable, - getVisualizeHref, -} from './lib/visualize_trigger_utils'; -import { Bucket, FieldDetails } from './types'; -import { IndexPatternField, IndexPattern } from '../../../../../data/public'; -import './discover_field_details.scss'; - -interface DiscoverFieldDetailsProps { - columns: string[]; - details: FieldDetails; - field: IndexPatternField; - indexPattern: IndexPattern; - onAddFilter: (field: IndexPatternField | string, value: string, type: '+' | '-') => void; -} - -export function DiscoverFieldDetails({ - columns, - details, - field, - indexPattern, - onAddFilter, -}: DiscoverFieldDetailsProps) { - const warnings = getWarnings(field); - const [showVisualizeLink, setShowVisualizeLink] = useState(false); - const [visualizeLink, setVisualizeLink] = useState(''); - - useEffect(() => { - const checkIfVisualizable = async () => { - const visualizable = await isFieldVisualizable(field, indexPattern.id, columns).catch( - () => false - ); - - setShowVisualizeLink(visualizable); - if (visualizable) { - const href = await getVisualizeHref(field, indexPattern.id, columns).catch(() => ''); - setVisualizeLink(href || ''); - } - }; - checkIfVisualizable(); - }, [field, indexPattern.id, columns]); - - const handleVisualizeLinkClick = (event: React.MouseEvent) => { - // regular link click. let the uiActions code handle the navigation and show popup if needed - event.preventDefault(); - triggerVisualizeActions(field, indexPattern.id, columns); - }; - - return ( - <> -
- {details.error && ( - - {details.error} - - )} - - {!details.error && details.buckets.length > 0 && ( -
- {details.buckets.map((bucket: Bucket, idx: number) => ( - - ))} -
- )} - - {showVisualizeLink && visualizeLink && ( -
- - {/* eslint-disable-next-line @elastic/eui/href-or-on-click */} - handleVisualizeLinkClick(e)} - href={visualizeLink} - size="s" - className="dscFieldDetails__visualizeBtn" - data-test-subj={`fieldVisualize-${field.name}`} - > - - - {warnings.length > 0 && ( - - )} -
- )} -
- {!details.error && ( - - - {!indexPattern.metaFields.includes(field.name) && !field.scripted ? ( - onAddFilter('_exists_', field.name, '+')}> - {' '} - {details.exists} - - ) : ( - {details.exists} - )}{' '} - / {details.total}{' '} - - - - )} - - ); -} diff --git a/src/plugins/discover_legacy/public/application/components/sidebar/discover_field_search.test.tsx b/src/plugins/discover_legacy/public/application/components/sidebar/discover_field_search.test.tsx deleted file mode 100644 index f78505e11f1e..000000000000 --- a/src/plugins/discover_legacy/public/application/components/sidebar/discover_field_search.test.tsx +++ /dev/null @@ -1,160 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { act } from 'react-dom/test-utils'; -import { mountWithIntl } from 'test_utils/enzyme_helpers'; -import { findTestSubject } from 'test_utils/helpers'; -import { DiscoverFieldSearch, Props } from './discover_field_search'; -import { EuiButtonGroupProps, EuiPopover } from '@elastic/eui'; -import { ReactWrapper } from 'enzyme'; - -describe('DiscoverFieldSearch', () => { - const defaultProps = { - onChange: jest.fn(), - value: 'test', - types: ['any', 'string', '_source'], - }; - - function mountComponent(props?: Props) { - const compProps = props || defaultProps; - return mountWithIntl(); - } - - function findButtonGroup(component: ReactWrapper, id: string) { - return component.find(`[data-test-subj="${id}ButtonGroup"]`).first(); - } - - test('enter value', () => { - const component = mountComponent(); - const input = findTestSubject(component, 'fieldFilterSearchInput'); - input.simulate('change', { target: { value: 'new filter' } }); - expect(defaultProps.onChange).toBeCalledTimes(1); - }); - - test('change in active filters should change facet selection and call onChange', () => { - const onChange = jest.fn(); - const component = mountComponent({ ...defaultProps, ...{ onChange } }); - let btn = findTestSubject(component, 'toggleFieldFilterButton'); - expect(btn.hasClass('euiFacetButton--isSelected')).toBeFalsy(); - btn.simulate('click'); - const aggregatableButtonGroup = findButtonGroup(component, 'aggregatable'); - act(() => { - // @ts-ignore - (aggregatableButtonGroup.props() as EuiButtonGroupProps).onChange('aggregatable-true', null); - }); - component.update(); - btn = findTestSubject(component, 'toggleFieldFilterButton'); - expect(btn.hasClass('euiFacetButton--isSelected')).toBe(true); - expect(onChange).toBeCalledWith('aggregatable', true); - }); - - test('change in active filters should change filters count', () => { - const component = mountComponent(); - let btn = findTestSubject(component, 'toggleFieldFilterButton'); - btn.simulate('click'); - btn = findTestSubject(component, 'toggleFieldFilterButton'); - const badge = btn.find('.euiNotificationBadge'); - // no active filters - expect(badge.text()).toEqual('0'); - // change value of aggregatable select - const aggregatableButtonGroup = findButtonGroup(component, 'aggregatable'); - act(() => { - // @ts-ignore - (aggregatableButtonGroup.props() as EuiButtonGroupProps).onChange('aggregatable-true', null); - }); - component.update(); - expect(badge.text()).toEqual('1'); - // change value of searchable select - const searchableButtonGroup = findButtonGroup(component, 'searchable'); - act(() => { - // @ts-ignore - (searchableButtonGroup.props() as EuiButtonGroupProps).onChange('searchable-true', null); - }); - component.update(); - expect(badge.text()).toEqual('2'); - // change value of searchable select - act(() => { - // @ts-ignore - (searchableButtonGroup.props() as EuiButtonGroupProps).onChange('searchable-any', null); - }); - component.update(); - expect(badge.text()).toEqual('1'); - }); - - test('change in missing fields switch should not change filter count', () => { - const component = mountComponent(); - const btn = findTestSubject(component, 'toggleFieldFilterButton'); - btn.simulate('click'); - const badge = btn.find('.euiNotificationBadge'); - expect(badge.text()).toEqual('0'); - const missingSwitch = findTestSubject(component, 'missingSwitch'); - missingSwitch.simulate('change', { target: { value: false } }); - expect(badge.text()).toEqual('0'); - }); - - test('change in filters triggers onChange', () => { - const onChange = jest.fn(); - const component = mountComponent({ ...defaultProps, ...{ onChange } }); - const btn = findTestSubject(component, 'toggleFieldFilterButton'); - btn.simulate('click'); - const aggregtableButtonGroup = findButtonGroup(component, 'aggregatable'); - const missingSwitch = findTestSubject(component, 'missingSwitch'); - act(() => { - // @ts-ignore - (aggregtableButtonGroup.props() as EuiButtonGroupProps).onChange('aggregatable-true', null); - }); - missingSwitch.simulate('click'); - expect(onChange).toBeCalledTimes(2); - }); - - test('change in type filters triggers onChange with appropriate value', () => { - const onChange = jest.fn(); - const component = mountComponent({ ...defaultProps, ...{ onChange } }); - const btn = findTestSubject(component, 'toggleFieldFilterButton'); - btn.simulate('click'); - const typeSelector = findTestSubject(component, 'typeSelect'); - typeSelector.simulate('change', { target: { value: 'string' } }); - expect(onChange).toBeCalledWith('type', 'string'); - typeSelector.simulate('change', { target: { value: 'any' } }); - expect(onChange).toBeCalledWith('type', 'any'); - }); - - test('click on filter button should open and close popover', () => { - const component = mountComponent(); - const btn = findTestSubject(component, 'toggleFieldFilterButton'); - btn.simulate('click'); - let popover = component.find(EuiPopover); - expect(popover.prop('isOpen')).toBe(true); - btn.simulate('click'); - popover = component.find(EuiPopover); - expect(popover.prop('isOpen')).toBe(false); - }); -}); diff --git a/src/plugins/discover_legacy/public/application/components/sidebar/discover_field_search.tsx b/src/plugins/discover_legacy/public/application/components/sidebar/discover_field_search.tsx deleted file mode 100644 index 4a1390cb1955..000000000000 --- a/src/plugins/discover_legacy/public/application/components/sidebar/discover_field_search.tsx +++ /dev/null @@ -1,313 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React, { OptionHTMLAttributes, ReactNode, useState } from 'react'; -import { i18n } from '@osd/i18n'; -import { - EuiFacetButton, - EuiFieldSearch, - EuiFlexGroup, - EuiFlexItem, - EuiIcon, - EuiPopover, - EuiPopoverFooter, - EuiPopoverTitle, - EuiSelect, - EuiSwitch, - EuiSwitchEvent, - EuiForm, - EuiFormRow, - EuiButtonGroup, - EuiOutsideClickDetector, -} from '@elastic/eui'; -import { FormattedMessage } from '@osd/i18n/react'; - -export interface State { - searchable: string; - aggregatable: string; - type: string; - missing: boolean; - [index: string]: string | boolean; -} - -export interface Props { - /** - * triggered on input of user into search field - */ - onChange: (field: string, value: string | boolean | undefined) => void; - - /** - * the input value of the user - */ - value?: string; - - /** - * types for the type filter - */ - types: string[]; -} - -/** - * Component is Discover's side bar to search of available fields - * Additionally there's a button displayed that allows the user to show/hide more filter fields - */ -export function DiscoverFieldSearch({ onChange, value, types }: Props) { - const searchPlaceholder = i18n.translate('discover.fieldChooser.searchPlaceHolder', { - defaultMessage: 'Search field names', - }); - const aggregatableLabel = i18n.translate('discover.fieldChooser.filter.aggregatableLabel', { - defaultMessage: 'Aggregatable', - }); - const searchableLabel = i18n.translate('discover.fieldChooser.filter.searchableLabel', { - defaultMessage: 'Searchable', - }); - const typeLabel = i18n.translate('discover.fieldChooser.filter.typeLabel', { - defaultMessage: 'Type', - }); - const typeOptions = types - ? types.map((type) => { - return { value: type, text: type }; - }) - : [{ value: 'any', text: 'any' }]; - - const [activeFiltersCount, setActiveFiltersCount] = useState(0); - const [isPopoverOpen, setPopoverOpen] = useState(false); - const [values, setValues] = useState({ - searchable: 'any', - aggregatable: 'any', - type: 'any', - missing: true, - }); - - if (typeof value !== 'string') { - // at initial rendering value is undefined (angular related), this catches the warning - // should be removed once all is react - return null; - } - - const filterBtnAriaLabel = isPopoverOpen - ? i18n.translate('discover.fieldChooser.toggleFieldFilterButtonHideAriaLabel', { - defaultMessage: 'Hide field filter settings', - }) - : i18n.translate('discover.fieldChooser.toggleFieldFilterButtonShowAriaLabel', { - defaultMessage: 'Show field filter settings', - }); - - const handleFacetButtonClicked = () => { - setPopoverOpen(!isPopoverOpen); - }; - - const applyFilterValue = (id: string, filterValue: string | boolean) => { - switch (filterValue) { - case 'any': - if (id !== 'type') { - onChange(id, undefined); - } else { - onChange(id, filterValue); - } - break; - case 'true': - onChange(id, true); - break; - case 'false': - onChange(id, false); - break; - default: - onChange(id, filterValue); - } - }; - - const isFilterActive = (name: string, filterValue: string | boolean) => { - return name !== 'missing' && filterValue !== 'any'; - }; - - const handleValueChange = (name: string, filterValue: string | boolean) => { - const previousValue = values[name]; - updateFilterCount(name, previousValue, filterValue); - const updatedValues = { ...values }; - updatedValues[name] = filterValue; - setValues(updatedValues); - applyFilterValue(name, filterValue); - }; - - const updateFilterCount = ( - name: string, - previousValue: string | boolean, - currentValue: string | boolean - ) => { - const previouslyFilterActive = isFilterActive(name, previousValue); - const filterActive = isFilterActive(name, currentValue); - const diff = Number(filterActive) - Number(previouslyFilterActive); - setActiveFiltersCount(activeFiltersCount + diff); - }; - - const handleMissingChange = (e: EuiSwitchEvent) => { - const missingValue = e.target.checked; - handleValueChange('missing', missingValue); - }; - - const buttonContent = ( - } - isSelected={activeFiltersCount > 0} - quantity={activeFiltersCount} - onClick={handleFacetButtonClicked} - > - - - ); - - const select = ( - id: string, - selectOptions: Array<{ text: ReactNode } & OptionHTMLAttributes>, - selectValue: string - ) => { - return ( - ) => - handleValueChange(id, e.target.value) - } - aria-label={i18n.translate('discover.fieldChooser.filter.fieldSelectorLabel', { - defaultMessage: 'Selection of {id} filter options', - values: { id }, - })} - data-test-subj={`${id}Select`} - compressed - /> - ); - }; - - const toggleButtons = (id: string) => { - return [ - { - id: `${id}-any`, - label: 'any', - }, - { - id: `${id}-true`, - label: 'yes', - }, - { - id: `${id}-false`, - label: 'no', - }, - ]; - }; - - const buttonGroup = (id: string, legend: string) => { - return ( - handleValueChange(id, optionId.replace(`${id}-`, ''))} - buttonSize="compressed" - isFullWidth - data-test-subj={`${id}ButtonGroup`} - /> - ); - }; - - const selectionPanel = ( -
- - - {buttonGroup('aggregatable', aggregatableLabel)} - - - {buttonGroup('searchable', searchableLabel)} - - - {select('type', typeOptions, values.type)} - - -
- ); - - return ( - - - - onChange('name', event.currentTarget.value)} - placeholder={searchPlaceholder} - value={value} - /> - - -
- {}} isDisabled={!isPopoverOpen}> - { - setPopoverOpen(false); - }} - button={buttonContent} - > - - {i18n.translate('discover.fieldChooser.filter.filterByTypeLabel', { - defaultMessage: 'Filter by type', - })} - - {selectionPanel} - - - - - -
-
- ); -} diff --git a/src/plugins/discover_legacy/public/application/components/sidebar/discover_index_pattern.test.tsx b/src/plugins/discover_legacy/public/application/components/sidebar/discover_index_pattern.test.tsx deleted file mode 100644 index 9298aef92cf0..000000000000 --- a/src/plugins/discover_legacy/public/application/components/sidebar/discover_index_pattern.test.tsx +++ /dev/null @@ -1,111 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { shallowWithIntl as shallow } from 'test_utils/enzyme_helpers'; - -// @ts-ignore -import { ShallowWrapper } from 'enzyme'; -import { ChangeIndexPattern } from './change_indexpattern'; -import { SavedObject } from 'opensearch-dashboards/server'; -import { DiscoverIndexPattern } from './discover_index_pattern'; -import { EuiSelectable } from '@elastic/eui'; -import { IIndexPattern } from 'src/plugins/data/public'; - -const indexPattern = { - id: 'test1', - title: 'test1 title', -} as IIndexPattern; - -const indexPattern1 = { - id: 'test1', - attributes: { - title: 'test1 titleToDisplay', - }, -} as SavedObject; - -const indexPattern2 = { - id: 'test2', - attributes: { - title: 'test2 titleToDisplay', - }, -} as SavedObject; - -const defaultProps = { - indexPatternList: [indexPattern1, indexPattern2], - selectedIndexPattern: indexPattern, - setIndexPattern: jest.fn(async () => {}), -}; - -function getIndexPatternPickerList(instance: ShallowWrapper) { - return instance.find(ChangeIndexPattern).first().dive().find(EuiSelectable); -} - -function getIndexPatternPickerOptions(instance: ShallowWrapper) { - return getIndexPatternPickerList(instance).prop('options'); -} - -function selectIndexPatternPickerOption(instance: ShallowWrapper, selectedLabel: string) { - const options: Array<{ label: string; checked?: 'on' | 'off' }> = getIndexPatternPickerOptions( - instance - ).map((option: any) => - option.label === selectedLabel - ? { ...option, checked: 'on' } - : { ...option, checked: undefined } - ); - return getIndexPatternPickerList(instance).prop('onChange')!(options); -} - -describe('DiscoverIndexPattern', () => { - test('Invalid props dont cause an exception', () => { - const props = { - indexPatternList: null, - selectedIndexPattern: null, - setIndexPattern: jest.fn(), - } as any; - - expect(shallow()).toMatchSnapshot(`""`); - }); - test('should list all index patterns', () => { - const instance = shallow(); - - expect(getIndexPatternPickerOptions(instance)!.map((option: any) => option.label)).toEqual([ - 'test1 titleToDisplay', - 'test2 titleToDisplay', - ]); - }); - - test('should switch data panel to target index pattern', () => { - const instance = shallow(); - - selectIndexPatternPickerOption(instance, 'test2 titleToDisplay'); - expect(defaultProps.setIndexPattern).toHaveBeenCalledWith('test2'); - }); -}); diff --git a/src/plugins/discover_legacy/public/application/components/sidebar/discover_index_pattern.tsx b/src/plugins/discover_legacy/public/application/components/sidebar/discover_index_pattern.tsx deleted file mode 100644 index 95154bec1939..000000000000 --- a/src/plugins/discover_legacy/public/application/components/sidebar/discover_index_pattern.tsx +++ /dev/null @@ -1,104 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React, { useState, useEffect } from 'react'; -import { SavedObject } from 'opensearch-dashboards/public'; -import { IIndexPattern, IndexPatternAttributes } from 'src/plugins/data/public'; -import { I18nProvider } from '@osd/i18n/react'; - -import { IndexPatternRef } from './types'; -import { ChangeIndexPattern } from './change_indexpattern'; -export interface DiscoverIndexPatternProps { - /** - * list of available index patterns, if length > 1, component offers a "change" link - */ - indexPatternList: Array>; - /** - * currently selected index pattern, due to angular issues it's undefined at first rendering - */ - selectedIndexPattern: IIndexPattern; - /** - * triggered when user selects a new index pattern - */ - setIndexPattern: (id: string) => void; -} - -/** - * Component allows you to select an index pattern in discovers side bar - */ -export function DiscoverIndexPattern({ - indexPatternList, - selectedIndexPattern, - setIndexPattern, -}: DiscoverIndexPatternProps) { - const options: IndexPatternRef[] = (indexPatternList || []).map((entity) => ({ - id: entity.id, - title: entity.attributes!.title, - })); - const { id: selectedId, title: selectedTitle } = selectedIndexPattern || {}; - - const [selected, setSelected] = useState({ - id: selectedId, - title: selectedTitle || '', - }); - useEffect(() => { - const { id, title } = selectedIndexPattern; - const indexPattern = indexPatternList.find((pattern) => pattern.id === id); - const titleToDisplay = indexPattern ? indexPattern.attributes!.title : title; - setSelected({ id, title: titleToDisplay }); - }, [indexPatternList, selectedIndexPattern]); - if (!selectedId) { - return null; - } - - return ( -
- - { - const indexPattern = options.find((pattern) => pattern.id === id); - if (indexPattern) { - setIndexPattern(id); - setSelected(indexPattern); - } - }} - /> - -
- ); -} diff --git a/src/plugins/discover_legacy/public/application/components/sidebar/discover_index_pattern_title.tsx b/src/plugins/discover_legacy/public/application/components/sidebar/discover_index_pattern_title.tsx deleted file mode 100644 index 30b50a9006c8..000000000000 --- a/src/plugins/discover_legacy/public/application/components/sidebar/discover_index_pattern_title.tsx +++ /dev/null @@ -1,95 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { EuiToolTip, EuiFlexItem, EuiFlexGroup, EuiTitle, EuiButtonEmpty } from '@elastic/eui'; - -import { FormattedMessage } from '@osd/i18n/react'; -import { i18n } from '@osd/i18n'; -export interface DiscoverIndexPatternTitleProps { - /** - * determines whether the change link is displayed - */ - isChangeable: boolean; - /** - * function triggered when the change link is clicked - */ - onChange: () => void; - /** - * title of the current index pattern - */ - title: string; -} - -/** - * Component displaying the title of the current selected index pattern - * and if changeable is true, a link is provided to change the index pattern - */ -export function DiscoverIndexPatternTitle({ - isChangeable, - onChange, - title, -}: DiscoverIndexPatternTitleProps) { - return ( - - - - -

{title}

-
-
-
- {isChangeable && ( - - - } - > - onChange()} - iconSide="right" - iconType="arrowDown" - color="text" - /> - - - )} -
- ); -} diff --git a/src/plugins/discover_legacy/public/application/components/sidebar/discover_sidebar.scss b/src/plugins/discover_legacy/public/application/components/sidebar/discover_sidebar.scss deleted file mode 100644 index 9c80e0afa600..000000000000 --- a/src/plugins/discover_legacy/public/application/components/sidebar/discover_sidebar.scss +++ /dev/null @@ -1,99 +0,0 @@ -.dscSidebar__container { - padding-left: 0 !important; - padding-right: 0 !important; - background-color: transparent; - border-right-color: transparent; - border-bottom-color: transparent; -} - -.dscIndexPattern__container { - display: flex; - align-items: center; - height: $euiSize * 3; - margin-top: -$euiSizeS; -} - -.dscIndexPattern__triggerButton { - @include euiTitle("xs"); - - line-height: $euiSizeXXL; -} - -.dscFieldList { - list-style: none; - margin-bottom: 0; -} - -.dscFieldListHeader { - padding: $euiSizeS $euiSizeS 0 $euiSizeS; - background-color: lightOrDarkTheme(tint($euiColorPrimary, 90%), $euiColorLightShade); -} - -.dscFieldList--popular { - background-color: lightOrDarkTheme(tint($euiColorPrimary, 90%), $euiColorLightShade); -} - -.dscFieldChooser { - padding-left: $euiSize; -} - -.dscFieldChooser__toggle { - color: $euiColorMediumShade; - margin-left: $euiSizeS !important; -} - -.dscSidebarItem { - &:hover, - &:focus-within, - &[class*="-isActive"] { - .dscSidebarItem__action { - opacity: 1; - } - } -} - -/** - * 1. Only visually hide the action, so that it's still accessible to screen readers. - * 2. When tabbed to, this element needs to be visible for keyboard accessibility. - */ -.dscSidebarItem__action { - opacity: 0; /* 1 */ - transition: none; - - &:focus { - opacity: 1; /* 2 */ - } - - font-size: $euiFontSizeXS; - padding: 2px 6px !important; - height: 22px !important; - min-width: auto !important; - - .euiButton__content { - padding: 0 4px; - } -} - -.dscFieldSearch { - padding: $euiSizeS; -} - -.dscFieldSearch__toggleButton { - width: calc(100% - #{$euiSizeS}); - color: $euiColorPrimary; - padding-left: $euiSizeXS; - margin-left: $euiSizeXS; -} - -.dscFieldSearch__filterWrapper { - flex-grow: 0; -} - -.dscFieldSearch__formWrapper { - padding: $euiSizeM; -} - -.dscFieldDetails { - color: $euiTextColor; - margin-bottom: $euiSizeS; -} diff --git a/src/plugins/discover_legacy/public/application/components/sidebar/discover_sidebar.test.tsx b/src/plugins/discover_legacy/public/application/components/sidebar/discover_sidebar.test.tsx deleted file mode 100644 index fa692ca22b5b..000000000000 --- a/src/plugins/discover_legacy/public/application/components/sidebar/discover_sidebar.test.tsx +++ /dev/null @@ -1,148 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import _ from 'lodash'; -import { ReactWrapper } from 'enzyme'; -import { findTestSubject } from 'test_utils/helpers'; -// @ts-ignore -import realHits from 'fixtures/real_hits.js'; -// @ts-ignore -import stubbedLogstashFields from 'fixtures/logstash_fields'; -import { mountWithIntl } from 'test_utils/enzyme_helpers'; -import React from 'react'; -import { DiscoverSidebar, DiscoverSidebarProps } from './discover_sidebar'; -import { coreMock } from '../../../../../../core/public/mocks'; -import { IndexPatternAttributes } from '../../../../../data/common'; -import { getStubIndexPattern } from '../../../../../data/public/test_utils'; -import { SavedObject } from '../../../../../../core/types'; - -jest.mock('../../../opensearch_dashboards_services', () => ({ - getServices: () => ({ - history: () => ({ - location: { - search: '', - }, - }), - capabilities: { - visualize: { - show: true, - }, - discover: { - save: false, - }, - }, - uiSettings: { - get: (key: string) => { - if (key === 'fields:popularLimit') { - return 5; - } else if (key === 'shortDots:enable') { - return false; - } - }, - }, - }), -})); - -jest.mock('./lib/get_index_pattern_field_list', () => ({ - getIndexPatternFieldList: jest.fn((indexPattern) => indexPattern.fields), -})); - -function getCompProps() { - const indexPattern = getStubIndexPattern( - 'logstash-*', - (cfg: any) => cfg, - 'time', - stubbedLogstashFields(), - coreMock.createSetup() - ); - - // @ts-expect-error _.each() is passing additional args to flattenHit - const hits = _.each(_.cloneDeep(realHits), indexPattern.flattenHit) as Array< - Record - >; - - const indexPatternList = [ - { id: '0', attributes: { title: 'b' } } as SavedObject, - { id: '1', attributes: { title: 'a' } } as SavedObject, - { id: '2', attributes: { title: 'c' } } as SavedObject, - ]; - - const fieldCounts: Record = {}; - - for (const hit of hits) { - for (const key of Object.keys(indexPattern.flattenHit(hit))) { - fieldCounts[key] = (fieldCounts[key] || 0) + 1; - } - } - return { - columns: ['extension'], - fieldCounts, - hits, - indexPatternList, - onAddFilter: jest.fn(), - onAddField: jest.fn(), - onRemoveField: jest.fn(), - selectedIndexPattern: indexPattern, - setIndexPattern: jest.fn(), - state: {}, - }; -} - -describe('discover sidebar', function () { - let props: DiscoverSidebarProps; - let comp: ReactWrapper; - - beforeAll(() => { - props = getCompProps(); - comp = mountWithIntl(); - }); - - it('should have Selected Fields and Available Fields with Popular Fields sections', function () { - const popular = findTestSubject(comp, 'fieldList-popular'); - const selected = findTestSubject(comp, 'fieldList-selected'); - const unpopular = findTestSubject(comp, 'fieldList-unpopular'); - expect(popular.children().length).toBe(1); - expect(unpopular.children().length).toBe(7); - expect(selected.children().length).toBe(1); - }); - it('should allow selecting fields', function () { - findTestSubject(comp, 'fieldToggle-bytes').simulate('click'); - expect(props.onAddField).toHaveBeenCalledWith('bytes'); - }); - it('should allow deselecting fields', function () { - findTestSubject(comp, 'fieldToggle-extension').simulate('click'); - expect(props.onRemoveField).toHaveBeenCalledWith('extension'); - }); - it('should allow adding filters', function () { - findTestSubject(comp, 'field-extension-showDetails').simulate('click'); - findTestSubject(comp, 'plus-extension-gif').simulate('click'); - expect(props.onAddFilter).toHaveBeenCalled(); - }); -}); diff --git a/src/plugins/discover_legacy/public/application/components/sidebar/discover_sidebar.tsx b/src/plugins/discover_legacy/public/application/components/sidebar/discover_sidebar.tsx deleted file mode 100644 index 865aff590286..000000000000 --- a/src/plugins/discover_legacy/public/application/components/sidebar/discover_sidebar.tsx +++ /dev/null @@ -1,326 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import './discover_sidebar.scss'; -import React, { useCallback, useEffect, useState, useMemo } from 'react'; -import { i18n } from '@osd/i18n'; -import { EuiButtonIcon, EuiTitle, EuiSpacer } from '@elastic/eui'; -import { sortBy } from 'lodash'; -import { FormattedMessage, I18nProvider } from '@osd/i18n/react'; -import { DiscoverField } from './discover_field'; -import { DiscoverIndexPattern } from './discover_index_pattern'; -import { DiscoverFieldSearch } from './discover_field_search'; -import { IndexPatternAttributes } from '../../../../../data/common'; -import { SavedObject } from '../../../../../../core/types'; -import { FIELDS_LIMIT_SETTING } from '../../../../common'; -import { groupFields } from './lib/group_fields'; -import { IndexPatternField, IndexPattern, UI_SETTINGS } from '../../../../../data/public'; -import { getDetails } from './lib/get_details'; -import { getDefaultFieldFilter, setFieldFilterProp } from './lib/field_filter'; -import { getIndexPatternFieldList } from './lib/get_index_pattern_field_list'; -import { getServices } from '../../../opensearch_dashboards_services'; - -export interface DiscoverSidebarProps { - /** - * the selected columns displayed in the doc table in discover - */ - columns: string[]; - /** - * a statistics of the distribution of fields in the given hits - */ - fieldCounts: Record; - /** - * hits fetched from OpenSearch, displayed in the doc table - */ - hits: Array>; - /** - * List of available index patterns - */ - indexPatternList: Array>; - /** - * Callback function when selecting a field - */ - onAddField: (fieldName: string) => void; - /** - * Callback function when adding a filter from sidebar - */ - onAddFilter: (field: IndexPatternField | string, value: string, type: '+' | '-') => void; - /** - * Callback function when removing a field - * @param fieldName - */ - onRemoveField: (fieldName: string) => void; - /** - * Currently selected index pattern - */ - selectedIndexPattern?: IndexPattern; - /** - * Callback function to select another index pattern - */ - setIndexPattern: (id: string) => void; -} - -export function DiscoverSidebar({ - columns, - fieldCounts, - hits, - indexPatternList, - onAddField, - onAddFilter, - onRemoveField, - selectedIndexPattern, - setIndexPattern, -}: DiscoverSidebarProps) { - const [showFields, setShowFields] = useState(false); - const [fields, setFields] = useState(null); - const [fieldFilterState, setFieldFilterState] = useState(getDefaultFieldFilter()); - const services = useMemo(() => getServices(), []); - - useEffect(() => { - const newFields = getIndexPatternFieldList(selectedIndexPattern, fieldCounts); - setFields(newFields); - }, [selectedIndexPattern, fieldCounts, hits, services]); - - const onChangeFieldSearch = useCallback( - (field: string, value: string | boolean | undefined) => { - const newState = setFieldFilterProp(fieldFilterState, field, value); - setFieldFilterState(newState); - }, - [fieldFilterState] - ); - - const getDetailsByField = useCallback( - (ipField: IndexPatternField) => getDetails(ipField, hits, selectedIndexPattern), - [hits, selectedIndexPattern] - ); - - const popularLimit = services.uiSettings.get(FIELDS_LIMIT_SETTING); - const useShortDots = services.uiSettings.get(UI_SETTINGS.SHORT_DOTS_ENABLE); - - const { - selected: selectedFields, - popular: popularFields, - unpopular: unpopularFields, - } = useMemo(() => groupFields(fields, columns, popularLimit, fieldCounts, fieldFilterState), [ - fields, - columns, - popularLimit, - fieldCounts, - fieldFilterState, - ]); - - const fieldTypes = useMemo(() => { - const result = ['any']; - if (Array.isArray(fields)) { - for (const field of fields) { - if (result.indexOf(field.type) === -1) { - result.push(field.type); - } - } - } - return result; - }, [fields]); - - if (!selectedIndexPattern || !fields) { - return null; - } - - return ( - -
- o.attributes.title)} - /> -
-
- - -
-
- {fields.length > 0 && ( - <> - -

- -

-
- -
    - {selectedFields.map((field: IndexPatternField) => { - return ( -
  • - -
  • - ); - })} -
-
- -

- -

-
-
- setShowFields(!showFields)} - aria-label={ - showFields - ? i18n.translate( - 'discover.fieldChooser.filter.indexAndFieldsSectionHideAriaLabel', - { - defaultMessage: 'Hide fields', - } - ) - : i18n.translate( - 'discover.fieldChooser.filter.indexAndFieldsSectionShowAriaLabel', - { - defaultMessage: 'Show fields', - } - ) - } - /> -
-
- - )} - {popularFields.length > 0 && ( -
- - - -
    - {popularFields.map((field: IndexPatternField) => { - return ( -
  • - -
  • - ); - })} -
-
- )} - -
    - {unpopularFields.map((field: IndexPatternField) => { - return ( -
  • - -
  • - ); - })} -
-
-
-
- ); -} diff --git a/src/plugins/discover_legacy/public/application/components/sidebar/index.ts b/src/plugins/discover_legacy/public/application/components/sidebar/index.ts deleted file mode 100644 index 2799d47da83f..000000000000 --- a/src/plugins/discover_legacy/public/application/components/sidebar/index.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export { DiscoverSidebar } from './discover_sidebar'; diff --git a/src/plugins/discover_legacy/public/application/components/sidebar/lib/field_calculator.test.ts b/src/plugins/discover_legacy/public/application/components/sidebar/lib/field_calculator.test.ts deleted file mode 100644 index d580f7ae228a..000000000000 --- a/src/plugins/discover_legacy/public/application/components/sidebar/lib/field_calculator.test.ts +++ /dev/null @@ -1,268 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import _ from 'lodash'; -// @ts-ignore -import realHits from 'fixtures/real_hits.js'; -// @ts-ignore -import stubbedLogstashFields from 'fixtures/logstash_fields'; -import { coreMock } from '../../../../../../../core/public/mocks'; -import { IndexPattern, IndexPatternField } from '../../../../../../data/public'; -import { getStubIndexPattern } from '../../../../../../data/public/test_utils'; -import { - groupValues, - getFieldValues, - getFieldValueCounts, - FieldValueCountsParams, -} from './field_calculator'; -import { Bucket } from '../types'; - -let indexPattern: IndexPattern; - -describe('field_calculator', function () { - beforeEach(function () { - indexPattern = getStubIndexPattern( - 'logstash-*', - (cfg: any) => cfg, - 'time', - stubbedLogstashFields(), - coreMock.createSetup() - ); - }); - - describe('groupValues', function () { - let groups: Record; - let grouped: boolean; - let values: any[]; - beforeEach(function () { - values = [ - ['foo', 'bar'], - 'foo', - 'foo', - undefined, - ['foo', 'bar'], - 'bar', - 'baz', - null, - null, - null, - 'foo', - undefined, - ]; - groups = groupValues(values, grouped); - }); - - it('should return an object values', function () { - expect(groups).toBeInstanceOf(Object); - }); - - it('should throw an error if any value is a plain object', function () { - expect(function () { - groupValues([{}, true, false], grouped); - }).toThrowError(); - }); - - it('should handle values with dots in them', function () { - values = ['0', '0.........', '0.......,.....']; - groups = groupValues(values, grouped); - expect(groups[values[0]].count).toBe(1); - expect(groups[values[1]].count).toBe(1); - expect(groups[values[2]].count).toBe(1); - }); - - it('should have a key for value in the array when not grouping array terms', function () { - expect(_.keys(groups).length).toBe(3); - expect(groups.foo).toBeInstanceOf(Object); - expect(groups.bar).toBeInstanceOf(Object); - expect(groups.baz).toBeInstanceOf(Object); - }); - - it('should count array terms independently', function () { - expect(groups['foo,bar']).toBeUndefined(); - expect(groups.foo.count).toBe(5); - expect(groups.bar.count).toBe(3); - expect(groups.baz.count).toBe(1); - }); - - describe('grouped array terms', function () { - beforeEach(function () { - grouped = true; - groups = groupValues(values, grouped); - }); - - it('should group array terms when grouped is true', function () { - expect(_.keys(groups).length).toBe(4); - expect(groups['foo,bar']).toBeInstanceOf(Object); - }); - - it('should contain the original array as the value', function () { - expect(groups['foo,bar'].value).toEqual(['foo', 'bar']); - }); - - it('should count the pairs separately from the values they contain', function () { - expect(groups['foo,bar'].count).toBe(2); - expect(groups.foo.count).toBe(3); - expect(groups.bar.count).toBe(1); - }); - }); - }); - - describe('getFieldValues', function () { - let hits: any; - - beforeEach(function () { - hits = _.each(_.cloneDeep(realHits), (hit) => indexPattern.flattenHit(hit)); - }); - - it('should return an array of values for _source fields', function () { - const extensions = getFieldValues({ - hits, - field: indexPattern.fields.getByName('extension') as IndexPatternField, - indexPattern, - }); - expect(extensions).toBeInstanceOf(Array); - expect( - _.filter(extensions, function (v) { - return v === 'html'; - }).length - ).toBe(8); - expect(_.uniq(_.clone(extensions)).sort()).toEqual(['gif', 'html', 'php', 'png']); - }); - - it('should return an array of values for core meta fields', function () { - const types = getFieldValues({ - hits, - field: indexPattern.fields.getByName('_type') as IndexPatternField, - indexPattern, - }); - expect(types).toBeInstanceOf(Array); - expect( - _.filter(types, function (v) { - return v === 'apache'; - }).length - ).toBe(18); - expect(_.uniq(_.clone(types)).sort()).toEqual(['apache', 'nginx']); - }); - }); - - describe('getFieldValueCounts', function () { - let params: FieldValueCountsParams; - beforeEach(function () { - params = { - hits: _.cloneDeep(realHits), - field: indexPattern.fields.getByName('extension') as IndexPatternField, - count: 3, - indexPattern, - }; - }); - - it('counts the top 5 values by default', function () { - params.hits = params.hits.map((hit: Record, i) => ({ - ...hit, - _source: { - extension: `${hit._source.extension}-${i}`, - }, - })); - params.count = undefined; - const extensions = getFieldValueCounts(params); - expect(extensions).toBeInstanceOf(Object); - expect(extensions.buckets).toBeInstanceOf(Array); - const buckets = extensions.buckets as Bucket[]; - expect(buckets.length).toBe(5); - expect(extensions.error).toBeUndefined(); - }); - - it('counts only distinct values if less than default', function () { - params.count = undefined; - const extensions = getFieldValueCounts(params); - expect(extensions).toBeInstanceOf(Object); - expect(extensions.buckets).toBeInstanceOf(Array); - const buckets = extensions.buckets as Bucket[]; - expect(buckets.length).toBe(4); - expect(extensions.error).toBeUndefined(); - }); - - it('counts only distinct values if less than specified count', function () { - params.count = 10; - const extensions = getFieldValueCounts(params); - expect(extensions).toBeInstanceOf(Object); - expect(extensions.buckets).toBeInstanceOf(Array); - const buckets = extensions.buckets as Bucket[]; - expect(buckets.length).toBe(4); - expect(extensions.error).toBeUndefined(); - }); - - it('counts the top 3 values', function () { - const extensions = getFieldValueCounts(params); - expect(extensions).toBeInstanceOf(Object); - expect(extensions.buckets).toBeInstanceOf(Array); - const buckets = extensions.buckets as Bucket[]; - expect(buckets.length).toBe(3); - expect(_.map(buckets, 'value')).toEqual(['html', 'gif', 'php']); - expect(extensions.error).toBeUndefined(); - }); - - it('fails to analyze geo and attachment types', function () { - params.field = indexPattern.fields.getByName('point') as IndexPatternField; - expect(getFieldValueCounts(params).error).not.toBeUndefined(); - - params.field = indexPattern.fields.getByName('area') as IndexPatternField; - expect(getFieldValueCounts(params).error).not.toBeUndefined(); - - params.field = indexPattern.fields.getByName('request_body') as IndexPatternField; - expect(getFieldValueCounts(params).error).not.toBeUndefined(); - }); - - it('fails to analyze fields that are in the mapping, but not the hits', function () { - params.field = indexPattern.fields.getByName('ip') as IndexPatternField; - expect(getFieldValueCounts(params).error).not.toBeUndefined(); - }); - - it('counts the total hits', function () { - expect(getFieldValueCounts(params).total).toBe(params.hits.length); - }); - - it('counts the hits the field exists in', function () { - params.field = indexPattern.fields.getByName('phpmemory') as IndexPatternField; - expect(getFieldValueCounts(params).exists).toBe(5); - }); - - it('catches and returns errors', function () { - params.hits = params.hits.map((hit: Record) => ({ - ...hit, - _source: { - extension: { foo: hit._source.extension }, - }, - })); - params.grouped = true; - expect(typeof getFieldValueCounts(params).error).toBe('string'); - }); - }); -}); diff --git a/src/plugins/discover_legacy/public/application/components/sidebar/lib/field_calculator.ts b/src/plugins/discover_legacy/public/application/components/sidebar/lib/field_calculator.ts deleted file mode 100644 index 54f8832fa1fc..000000000000 --- a/src/plugins/discover_legacy/public/application/components/sidebar/lib/field_calculator.ts +++ /dev/null @@ -1,148 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { IndexPattern, IndexPatternField } from 'src/plugins/data/public'; -import { FieldValueCounts } from '../types'; - -const NO_ANALYSIS_TYPES = ['geo_point', 'geo_shape', 'attachment']; - -interface FieldValuesParams { - hits: Array>; - field: IndexPatternField; - indexPattern: IndexPattern; -} - -interface FieldValueCountsParams extends FieldValuesParams { - count?: number; - grouped?: boolean; -} - -const getFieldValues = ({ hits, field, indexPattern }: FieldValuesParams) => { - const name = field.name; - const flattenHit = indexPattern.flattenHit; - return hits.map((hit) => flattenHit(hit)[name]); -}; - -const getFieldValueCounts = (params: FieldValueCountsParams): FieldValueCounts => { - const { hits, field, indexPattern, count = 5, grouped = false } = params; - const { type: fieldType } = field; - - if (NO_ANALYSIS_TYPES.includes(fieldType)) { - return { - error: i18n.translate( - 'discover.fieldChooser.fieldCalculator.analysisIsNotAvailableForGeoFieldsErrorMessage', - { - defaultMessage: 'Analysis is not available for {fieldType} fields.', - values: { - fieldType, - }, - } - ), - }; - } - - const allValues = getFieldValues({ hits, field, indexPattern }); - const missing = allValues.filter((v) => v === undefined || v === null).length; - - try { - const groups = groupValues(allValues, grouped); - const counts = Object.keys(groups) - .sort((a, b) => groups[b].count - groups[a].count) - .slice(0, count) - .map((key) => ({ - value: groups[key].value, - count: groups[key].count, - percent: (groups[key].count / (hits.length - missing)) * 100, - display: indexPattern.getFormatterForField(field).convert(groups[key].value), - })); - - if (hits.length === missing) { - return { - error: i18n.translate( - 'discover.fieldChooser.fieldCalculator.fieldIsNotPresentInDocumentsErrorMessage', - { - defaultMessage: - 'This field is present in your OpenSearch mapping but not in the {hitsLength} documents shown in the doc table. You may still be able to visualize or search on it.', - values: { - hitsLength: hits.length, - }, - } - ), - }; - } - - return { - total: hits.length, - exists: hits.length - missing, - missing, - buckets: counts, - }; - } catch (e) { - return { - error: e instanceof Error ? e.message : String(e), - }; - } -}; - -const groupValues = ( - allValues: any[], - grouped?: boolean -): Record => { - const values = grouped ? allValues : allValues.flat(); - - return values - .filter((v) => { - if (v instanceof Object && !Array.isArray(v)) { - throw new Error( - i18n.translate( - 'discover.fieldChooser.fieldCalculator.analysisIsNotAvailableForObjectFieldsErrorMessage', - { - defaultMessage: 'Analysis is not available for object fields.', - } - ) - ); - } - return v !== undefined && v !== null; - }) - .reduce((groups, value) => { - if (groups.hasOwnProperty(value)) { - groups[value].count++; - } else { - groups[value] = { - value, - count: 1, - }; - } - return groups; - }, {}); -}; - -export { FieldValueCountsParams, groupValues, getFieldValues, getFieldValueCounts }; diff --git a/src/plugins/discover_legacy/public/application/components/sidebar/lib/field_filter.test.ts b/src/plugins/discover_legacy/public/application/components/sidebar/lib/field_filter.test.ts deleted file mode 100644 index a21d93cb5bc4..000000000000 --- a/src/plugins/discover_legacy/public/application/components/sidebar/lib/field_filter.test.ts +++ /dev/null @@ -1,107 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { getDefaultFieldFilter, setFieldFilterProp, isFieldFiltered } from './field_filter'; -import { IndexPatternField } from '../../../../../../data/public'; - -describe('field_filter', function () { - it('getDefaultFieldFilter should return default filter state', function () { - expect(getDefaultFieldFilter()).toMatchInlineSnapshot(` - Object { - "aggregatable": null, - "missing": true, - "name": "", - "searchable": null, - "type": "any", - } - `); - }); - it('setFieldFilterProp should return allow filter changes', function () { - const state = getDefaultFieldFilter(); - const targetState = { - aggregatable: true, - missing: true, - name: 'test', - searchable: true, - type: 'string', - }; - const actualState = Object.entries(targetState).reduce((acc, kv) => { - return setFieldFilterProp(acc, kv[0], kv[1]); - }, state); - expect(actualState).toMatchInlineSnapshot(` - Object { - "aggregatable": true, - "missing": true, - "name": "test", - "searchable": true, - "type": "string", - } - `); - }); - it('filters a given list', () => { - const defaultState = getDefaultFieldFilter(); - const fieldList = [ - { - name: 'bytes', - type: 'number', - esTypes: ['long'], - count: 10, - scripted: false, - searchable: false, - aggregatable: false, - }, - { - name: 'extension', - type: 'string', - esTypes: ['text'], - count: 10, - scripted: true, - searchable: true, - aggregatable: true, - }, - ] as IndexPatternField[]; - - [ - { filter: {}, result: ['bytes', 'extension'] }, - { filter: { name: 'by' }, result: ['bytes'] }, - { filter: { aggregatable: true }, result: ['extension'] }, - { filter: { aggregatable: true, searchable: false }, result: [] }, - { filter: { type: 'string' }, result: ['extension'] }, - ].forEach((test) => { - const filtered = fieldList - .filter((field) => - isFieldFiltered(field, { ...defaultState, ...test.filter }, { bytes: 1, extension: 1 }) - ) - .map((field) => field.name); - - expect(filtered).toEqual(test.result); - }); - }); -}); diff --git a/src/plugins/discover_legacy/public/application/components/sidebar/lib/field_filter.ts b/src/plugins/discover_legacy/public/application/components/sidebar/lib/field_filter.ts deleted file mode 100644 index d72af29b43e0..000000000000 --- a/src/plugins/discover_legacy/public/application/components/sidebar/lib/field_filter.ts +++ /dev/null @@ -1,89 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { IndexPatternField } from '../../../../../../data/public'; - -export interface FieldFilterState { - missing: boolean; - type: string; - name: string; - aggregatable: null | boolean; - searchable: null | boolean; -} - -export function getDefaultFieldFilter(): FieldFilterState { - return { - missing: true, - type: 'any', - name: '', - aggregatable: null, - searchable: null, - }; -} - -export function setFieldFilterProp( - state: FieldFilterState, - name: string, - value: string | boolean | null | undefined -): FieldFilterState { - const newState = { ...state }; - if (name === 'missing') { - newState.missing = Boolean(value); - } else if (name === 'aggregatable') { - newState.aggregatable = typeof value !== 'boolean' ? null : value; - } else if (name === 'searchable') { - newState.searchable = typeof value !== 'boolean' ? null : value; - } else if (name === 'name') { - newState.name = String(value); - } else if (name === 'type') { - newState.type = String(value); - } - return newState; -} - -export function isFieldFiltered( - field: IndexPatternField, - filterState: FieldFilterState, - fieldCounts: Record -): boolean { - const matchFilter = filterState.type === 'any' || field.type === filterState.type; - const isAggregatable = - filterState.aggregatable === null || field.aggregatable === filterState.aggregatable; - const isSearchable = - filterState.searchable === null || field.searchable === filterState.searchable; - const scriptedOrMissing = - !filterState.missing || - field.type === '_source' || - field.scripted || - fieldCounts[field.name] > 0; - const matchName = !filterState.name || field.name.indexOf(filterState.name) !== -1; - - return matchFilter && isAggregatable && isSearchable && scriptedOrMissing && matchName; -} diff --git a/src/plugins/discover_legacy/public/application/components/sidebar/lib/get_details.ts b/src/plugins/discover_legacy/public/application/components/sidebar/lib/get_details.ts deleted file mode 100644 index 823cbde9ba72..000000000000 --- a/src/plugins/discover_legacy/public/application/components/sidebar/lib/get_details.ts +++ /dev/null @@ -1,71 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -// @ts-ignore -import { i18n } from '@osd/i18n'; -import { getFieldValueCounts } from './field_calculator'; -import { IndexPattern, IndexPatternField } from '../../../../../../data/public'; - -export function getDetails( - field: IndexPatternField, - hits: Array>, - indexPattern?: IndexPattern -) { - const defaultDetails = { - error: '', - exists: 0, - total: 0, - buckets: [], - }; - if (!indexPattern) { - return { - ...defaultDetails, - error: i18n.translate('discover.fieldChooser.noIndexPatternSelectedErrorMessage', { - defaultMessage: 'Index pattern not specified.', - }), - }; - } - const details = { - ...defaultDetails, - ...getFieldValueCounts({ - hits, - field, - indexPattern, - count: 5, - grouped: false, - }), - }; - if (details.buckets) { - for (const bucket of details.buckets) { - bucket.display = indexPattern.getFormatterForField(field).convert(bucket.value); - } - } - return details; -} diff --git a/src/plugins/discover_legacy/public/application/components/sidebar/lib/get_field_type_name.ts b/src/plugins/discover_legacy/public/application/components/sidebar/lib/get_field_type_name.ts deleted file mode 100644 index 38b18792d3e4..000000000000 --- a/src/plugins/discover_legacy/public/application/components/sidebar/lib/get_field_type_name.ts +++ /dev/null @@ -1,85 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; - -export function getFieldTypeName(type: string) { - switch (type) { - case 'boolean': - return i18n.translate('discover.fieldNameIcons.booleanAriaLabel', { - defaultMessage: 'Boolean field', - }); - case 'conflict': - return i18n.translate('discover.fieldNameIcons.conflictFieldAriaLabel', { - defaultMessage: 'Conflicting field', - }); - case 'date': - return i18n.translate('discover.fieldNameIcons.dateFieldAriaLabel', { - defaultMessage: 'Date field', - }); - case 'geo_point': - return i18n.translate('discover.fieldNameIcons.geoPointFieldAriaLabel', { - defaultMessage: 'Geo point field', - }); - case 'geo_shape': - return i18n.translate('discover.fieldNameIcons.geoShapeFieldAriaLabel', { - defaultMessage: 'Geo shape field', - }); - case 'ip': - return i18n.translate('discover.fieldNameIcons.ipAddressFieldAriaLabel', { - defaultMessage: 'IP address field', - }); - case 'murmur3': - return i18n.translate('discover.fieldNameIcons.murmur3FieldAriaLabel', { - defaultMessage: 'Murmur3 field', - }); - case 'number': - return i18n.translate('discover.fieldNameIcons.numberFieldAriaLabel', { - defaultMessage: 'Number field', - }); - case 'source': - // Note that this type is currently not provided, type for _source is undefined - return i18n.translate('discover.fieldNameIcons.sourceFieldAriaLabel', { - defaultMessage: 'Source field', - }); - case 'string': - return i18n.translate('discover.fieldNameIcons.stringFieldAriaLabel', { - defaultMessage: 'String field', - }); - case 'nested': - return i18n.translate('discover.fieldNameIcons.nestedFieldAriaLabel', { - defaultMessage: 'Nested field', - }); - default: - return i18n.translate('discover.fieldNameIcons.unknownFieldAriaLabel', { - defaultMessage: 'Unknown field', - }); - } -} diff --git a/src/plugins/discover_legacy/public/application/components/sidebar/lib/get_index_pattern_field_list.ts b/src/plugins/discover_legacy/public/application/components/sidebar/lib/get_index_pattern_field_list.ts deleted file mode 100644 index b3a8ff5cd8d9..000000000000 --- a/src/plugins/discover_legacy/public/application/components/sidebar/lib/get_index_pattern_field_list.ts +++ /dev/null @@ -1,53 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { difference } from 'lodash'; -import { IndexPattern, IndexPatternField } from 'src/plugins/data/public'; - -export function getIndexPatternFieldList( - indexPattern?: IndexPattern, - fieldCounts?: Record -) { - if (!indexPattern || !fieldCounts) return []; - - const fieldNamesInDocs = Object.keys(fieldCounts); - const fieldNamesInIndexPattern = indexPattern.fields.getAll().map((fld) => fld.name); - const unknownTypes: IndexPatternField[] = []; - - difference(fieldNamesInDocs, fieldNamesInIndexPattern).forEach((unknownFieldName) => { - unknownTypes.push({ - displayName: String(unknownFieldName), - name: String(unknownFieldName), - type: 'unknown', - } as IndexPatternField); - }); - - return [...indexPattern.fields.getAll(), ...unknownTypes]; -} diff --git a/src/plugins/discover_legacy/public/application/components/sidebar/lib/get_warnings.ts b/src/plugins/discover_legacy/public/application/components/sidebar/lib/get_warnings.ts deleted file mode 100644 index 770a0ce664e4..000000000000 --- a/src/plugins/discover_legacy/public/application/components/sidebar/lib/get_warnings.ts +++ /dev/null @@ -1,55 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { IndexPatternField } from '../../../../../../data/public'; - -export function getWarnings(field: IndexPatternField) { - let warnings = []; - - if (field.scripted) { - warnings.push( - i18n.translate( - 'discover.fieldChooser.discoverField.scriptedFieldsTakeLongExecuteDescription', - { - defaultMessage: 'Scripted fields can take a long time to execute.', - } - ) - ); - } - - if (warnings.length > 1) { - warnings = warnings.map(function (warning, i) { - return (i > 0 ? '\n' : '') + (i + 1) + ' - ' + warning; - }); - } - - return warnings; -} diff --git a/src/plugins/discover_legacy/public/application/components/sidebar/lib/group_fields.test.ts b/src/plugins/discover_legacy/public/application/components/sidebar/lib/group_fields.test.ts deleted file mode 100644 index 7301ce3a4c96..000000000000 --- a/src/plugins/discover_legacy/public/application/components/sidebar/lib/group_fields.test.ts +++ /dev/null @@ -1,125 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { groupFields } from './group_fields'; -import { getDefaultFieldFilter } from './field_filter'; - -describe('group_fields', function () { - it('should group fields in selected, popular, unpopular group', function () { - const fields = [ - { - name: 'category', - type: 'string', - esTypes: ['text'], - count: 1, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: true, - }, - { - name: 'currency', - type: 'string', - esTypes: ['keyword'], - count: 0, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: true, - }, - { - name: 'customer_birth_date', - type: 'date', - esTypes: ['date'], - count: 0, - scripted: false, - searchable: true, - aggregatable: true, - readFromDocValues: true, - }, - ]; - - const fieldCounts = { - category: 1, - currency: 1, - customer_birth_date: 1, - }; - - const fieldFilterState = getDefaultFieldFilter(); - - const actual = groupFields(fields as any, ['currency'], 5, fieldCounts, fieldFilterState); - expect(actual).toMatchInlineSnapshot(` - Object { - "popular": Array [ - Object { - "aggregatable": true, - "count": 1, - "esTypes": Array [ - "text", - ], - "name": "category", - "readFromDocValues": true, - "scripted": false, - "searchable": true, - "type": "string", - }, - ], - "selected": Array [ - Object { - "aggregatable": true, - "count": 0, - "esTypes": Array [ - "keyword", - ], - "name": "currency", - "readFromDocValues": true, - "scripted": false, - "searchable": true, - "type": "string", - }, - ], - "unpopular": Array [ - Object { - "aggregatable": true, - "count": 0, - "esTypes": Array [ - "date", - ], - "name": "customer_birth_date", - "readFromDocValues": true, - "scripted": false, - "searchable": true, - "type": "date", - }, - ], - } - `); - }); -}); diff --git a/src/plugins/discover_legacy/public/application/components/sidebar/lib/group_fields.tsx b/src/plugins/discover_legacy/public/application/components/sidebar/lib/group_fields.tsx deleted file mode 100644 index fad1db402467..000000000000 --- a/src/plugins/discover_legacy/public/application/components/sidebar/lib/group_fields.tsx +++ /dev/null @@ -1,87 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { IndexPatternField } from 'src/plugins/data/public'; -import { FieldFilterState, isFieldFiltered } from './field_filter'; - -interface GroupedFields { - selected: IndexPatternField[]; - popular: IndexPatternField[]; - unpopular: IndexPatternField[]; -} - -/** - * group the fields into selected, popular and unpopular, filter by fieldFilterState - */ -export function groupFields( - fields: IndexPatternField[] | null, - columns: string[], - popularLimit: number, - fieldCounts: Record, - fieldFilterState: FieldFilterState -): GroupedFields { - const result: GroupedFields = { - selected: [], - popular: [], - unpopular: [], - }; - if (!Array.isArray(fields) || !Array.isArray(columns) || typeof fieldCounts !== 'object') { - return result; - } - - const popular = fields - .filter((field) => !columns.includes(field.name) && field.count) - .sort((a: IndexPatternField, b: IndexPatternField) => (b.count || 0) - (a.count || 0)) - .map((field) => field.name) - .slice(0, popularLimit); - - const compareFn = (a: IndexPatternField, b: IndexPatternField) => { - if (!a.displayName) { - return 0; - } - return a.displayName.localeCompare(b.displayName || ''); - }; - const fieldsSorted = fields.sort(compareFn); - - for (const field of fieldsSorted) { - if (!isFieldFiltered(field, fieldFilterState, fieldCounts)) { - continue; - } - if (columns.includes(field.name)) { - result.selected.push(field); - } else if (popular.includes(field.name) && field.type !== '_source') { - result.popular.push(field); - } else if (field.type !== '_source') { - result.unpopular.push(field); - } - } - - return result; -} diff --git a/src/plugins/discover_legacy/public/application/components/sidebar/lib/visualize_trigger_utils.ts b/src/plugins/discover_legacy/public/application/components/sidebar/lib/visualize_trigger_utils.ts deleted file mode 100644 index 36a6bcf2e329..000000000000 --- a/src/plugins/discover_legacy/public/application/components/sidebar/lib/visualize_trigger_utils.ts +++ /dev/null @@ -1,122 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { - VISUALIZE_FIELD_TRIGGER, - VISUALIZE_GEO_FIELD_TRIGGER, - visualizeFieldTrigger, - visualizeGeoFieldTrigger, -} from '../../../../../../ui_actions/public'; -import { getUiActions } from '../../../../opensearch_dashboards_services'; -import { IndexPatternField, OSD_FIELD_TYPES } from '../../../../../../data/public'; - -function getTriggerConstant(type: string) { - return type === OSD_FIELD_TYPES.GEO_POINT || type === OSD_FIELD_TYPES.GEO_SHAPE - ? VISUALIZE_GEO_FIELD_TRIGGER - : VISUALIZE_FIELD_TRIGGER; -} - -function getTrigger(type: string) { - return type === OSD_FIELD_TYPES.GEO_POINT || type === OSD_FIELD_TYPES.GEO_SHAPE - ? visualizeGeoFieldTrigger - : visualizeFieldTrigger; -} - -async function getCompatibleActions( - fieldName: string, - indexPatternId: string, - contextualFields: string[], - trigger: typeof VISUALIZE_FIELD_TRIGGER | typeof VISUALIZE_GEO_FIELD_TRIGGER -) { - const compatibleActions = await getUiActions().getTriggerCompatibleActions(trigger, { - indexPatternId, - fieldName, - contextualFields, - }); - return compatibleActions; -} - -export async function getVisualizeHref( - field: IndexPatternField, - indexPatternId: string | undefined, - contextualFields: string[] -) { - if (!indexPatternId) return undefined; - const triggerOptions = { - indexPatternId, - fieldName: field.name, - contextualFields, - trigger: getTrigger(field.type), - }; - const compatibleActions = await getCompatibleActions( - field.name, - indexPatternId, - contextualFields, - getTriggerConstant(field.type) - ); - // enable the link only if only one action is registered - return compatibleActions.length === 1 - ? compatibleActions[0].getHref?.(triggerOptions) - : undefined; -} - -export function triggerVisualizeActions( - field: IndexPatternField, - indexPatternId: string | undefined, - contextualFields: string[] -) { - if (!indexPatternId) return; - const trigger = getTriggerConstant(field.type); - const triggerOptions = { - indexPatternId, - fieldName: field.name, - contextualFields, - }; - getUiActions().getTrigger(trigger).exec(triggerOptions); -} - -export async function isFieldVisualizable( - field: IndexPatternField, - indexPatternId: string | undefined, - contextualFields: string[] -) { - if (field.name === '_id' || !indexPatternId) { - // for first condition you'd get a 'Fielddata access on the _id field is disallowed' error on OpenSearch side. - return false; - } - const trigger = getTriggerConstant(field.type); - const compatibleActions = await getCompatibleActions( - field.name, - indexPatternId, - contextualFields, - trigger - ); - return compatibleActions.length > 0 && field.visualizable; -} diff --git a/src/plugins/discover_legacy/public/application/components/sidebar/string_progress_bar.tsx b/src/plugins/discover_legacy/public/application/components/sidebar/string_progress_bar.tsx deleted file mode 100644 index dba087d0f9ed..000000000000 --- a/src/plugins/discover_legacy/public/application/components/sidebar/string_progress_bar.tsx +++ /dev/null @@ -1,46 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { EuiProgress } from '@elastic/eui'; - -interface Props { - percent: number; - count: number; - value: string; -} - -export function StringFieldProgressBar({ value, percent, count }: Props) { - const ariaLabel = `${value}: ${count} (${percent}%)`; - - return ( - - ); -} diff --git a/src/plugins/discover_legacy/public/application/components/sidebar/types.ts b/src/plugins/discover_legacy/public/application/components/sidebar/types.ts deleted file mode 100644 index a43120b28e96..000000000000 --- a/src/plugins/discover_legacy/public/application/components/sidebar/types.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export interface IndexPatternRef { - id: string; - title: string; -} - -export interface FieldDetails { - error: string; - exists: number; - total: number; - buckets: Bucket[]; -} - -export interface FieldValueCounts extends Partial { - missing?: number; -} - -export interface Bucket { - display: string; - value: string; - percent: number; - count: number; -} diff --git a/src/plugins/discover_legacy/public/application/components/skip_bottom_button/index.ts b/src/plugins/discover_legacy/public/application/components/skip_bottom_button/index.ts deleted file mode 100644 index 094d8e286875..000000000000 --- a/src/plugins/discover_legacy/public/application/components/skip_bottom_button/index.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export { SkipBottomButton } from './skip_bottom_button'; diff --git a/src/plugins/discover_legacy/public/application/components/skip_bottom_button/skip_bottom_button.test.tsx b/src/plugins/discover_legacy/public/application/components/skip_bottom_button/skip_bottom_button.test.tsx deleted file mode 100644 index 28ffef9dae86..000000000000 --- a/src/plugins/discover_legacy/public/application/components/skip_bottom_button/skip_bottom_button.test.tsx +++ /dev/null @@ -1,51 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { mountWithIntl } from 'test_utils/enzyme_helpers'; -import { ReactWrapper } from 'enzyme'; -import { SkipBottomButton, SkipBottomButtonProps } from './skip_bottom_button'; - -describe('Skip to Bottom Button', function () { - let props: SkipBottomButtonProps; - let component: ReactWrapper; - - beforeAll(() => { - props = { - onClick: jest.fn(), - }; - }); - - it('should be clickable', function () { - component = mountWithIntl(); - component.simulate('click'); - expect(props.onClick).toHaveBeenCalled(); - }); -}); diff --git a/src/plugins/discover_legacy/public/application/components/skip_bottom_button/skip_bottom_button.tsx b/src/plugins/discover_legacy/public/application/components/skip_bottom_button/skip_bottom_button.tsx deleted file mode 100644 index a1e5754cb312..000000000000 --- a/src/plugins/discover_legacy/public/application/components/skip_bottom_button/skip_bottom_button.tsx +++ /dev/null @@ -1,66 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { EuiSkipLink } from '@elastic/eui'; -import { FormattedMessage, I18nProvider } from '@osd/i18n/react'; - -export interface SkipBottomButtonProps { - /** - * Action to perform on click - */ - onClick: () => void; -} - -export function SkipBottomButton({ onClick }: SkipBottomButtonProps) { - return ( - - { - // prevent the anchor to reload the page on click - event.preventDefault(); - // The destinationId prop cannot be leveraged here as the table needs - // to be updated first (angular logic) - onClick(); - }} - className="dscSkipButton" - destinationId="" - data-test-subj="discoverSkipTableButton" - > - - - - ); -} diff --git a/src/plugins/discover_legacy/public/application/components/table/table.test.tsx b/src/plugins/discover_legacy/public/application/components/table/table.test.tsx deleted file mode 100644 index 220ac57feae2..000000000000 --- a/src/plugins/discover_legacy/public/application/components/table/table.test.tsx +++ /dev/null @@ -1,279 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { mount } from 'enzyme'; -import { findTestSubject } from 'test_utils/helpers'; -import { DocViewTable } from './table'; -import { indexPatterns, IndexPattern } from '../../../../../data/public'; - -const indexPattern = ({ - fields: { - getAll: () => [ - { - name: '_index', - type: 'string', - scripted: false, - filterable: true, - }, - { - name: 'message', - type: 'string', - scripted: false, - filterable: false, - }, - { - name: 'extension', - type: 'string', - scripted: false, - filterable: true, - }, - { - name: 'bytes', - type: 'number', - scripted: false, - filterable: true, - }, - { - name: 'scripted', - type: 'number', - scripted: true, - filterable: false, - }, - ], - }, - metaFields: ['_index', '_score'], - flattenHit: undefined, - formatHit: jest.fn((hit) => hit._source), -} as unknown) as IndexPattern; - -indexPattern.fields.getByName = (name: string) => { - return indexPattern.fields.getAll().find((field) => field.name === name); -}; - -indexPattern.flattenHit = indexPatterns.flattenHitWrapper(indexPattern, indexPattern.metaFields); - -describe('DocViewTable at Discover', () => { - // At Discover's main view, all buttons are rendered - // check for existence of action buttons and warnings - - const hit = { - _index: 'logstash-2014.09.09', - _type: 'doc', - _id: 'id123', - _score: 1, - _source: { - message: - 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. \ - Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus \ - et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, \ - ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. \ - Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, \ - rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. \ - Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. \ - Phasellus ullamcorper ipsum rutrum nunc. Nunc nonummy metus. Vestibulum volutpat pretium libero. Cras id dui. Aenean ut', - extension: 'html', - not_mapped: 'yes', - bytes: 100, - objectArray: [{ foo: true }], - relatedContent: { - test: 1, - }, - scripted: 123, - _underscore: 123, - }, - }; - - const props = { - hit, - columns: ['extension'], - indexPattern, - filter: jest.fn(), - onAddColumn: jest.fn(), - onRemoveColumn: jest.fn(), - }; - const component = mount(); - [ - { - _property: '_index', - addInclusiveFilterButton: true, - collapseBtn: false, - noMappingWarning: false, - toggleColumnButton: true, - underscoreWarning: false, - }, - { - _property: 'message', - addInclusiveFilterButton: false, - collapseBtn: true, - noMappingWarning: false, - toggleColumnButton: true, - underscoreWarning: false, - }, - { - _property: '_underscore', - addInclusiveFilterButton: false, - collapseBtn: false, - noMappingWarning: false, - toggleColumnButton: true, - underScoreWarning: true, - }, - { - _property: 'scripted', - addInclusiveFilterButton: false, - collapseBtn: false, - noMappingWarning: false, - toggleColumnButton: true, - underScoreWarning: false, - }, - { - _property: 'not_mapped', - addInclusiveFilterButton: false, - collapseBtn: false, - noMappingWarning: true, - toggleColumnButton: true, - underScoreWarning: false, - }, - ].forEach((check) => { - const rowComponent = findTestSubject(component, `tableDocViewRow-${check._property}`); - - it(`renders row for ${check._property}`, () => { - expect(rowComponent.length).toBe(1); - }); - - ([ - 'addInclusiveFilterButton', - 'collapseBtn', - 'toggleColumnButton', - 'underscoreWarning', - ] as const).forEach((element) => { - const elementExist = check[element]; - - if (typeof elementExist === 'boolean') { - const btn = findTestSubject(rowComponent, element); - - it(`renders ${element} for '${check._property}' correctly`, () => { - const disabled = btn.length ? btn.props().disabled : true; - const clickAble = btn.length && !disabled ? true : false; - expect(clickAble).toBe(elementExist); - }); - } - }); - - (['noMappingWarning'] as const).forEach((element) => { - const elementExist = check[element]; - - if (typeof elementExist === 'boolean') { - const el = findTestSubject(rowComponent, element); - - it(`renders ${element} for '${check._property}' correctly`, () => { - expect(el.length).toBe(elementExist ? 1 : 0); - }); - } - }); - }); -}); - -describe('DocViewTable at Discover Doc', () => { - const hit = { - _index: 'logstash-2014.09.09', - _score: 1, - _type: 'doc', - _id: 'id123', - _source: { - extension: 'html', - not_mapped: 'yes', - }, - }; - // here no action buttons are rendered - const props = { - hit, - indexPattern, - }; - const component = mount(); - const foundLength = findTestSubject(component, 'addInclusiveFilterButton').length; - - it(`renders no action buttons`, () => { - expect(foundLength).toBe(0); - }); -}); - -describe('DocViewTable at Discover Context', () => { - // here no toggleColumnButtons are rendered - const hit = { - _index: 'logstash-2014.09.09', - _type: 'doc', - _id: 'id123', - _score: 1, - _source: { - message: - 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. \ - Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus \ - et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, \ - ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. \ - Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, \ - rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. \ - Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. \ - Phasellus ullamcorper ipsum rutrum nunc. Nunc nonummy metus. Vestibulum volutpat pretium libero. Cras id dui. Aenean ut', - }, - }; - const props = { - hit, - columns: ['extension'], - indexPattern, - filter: jest.fn(), - }; - - const component = mount(); - - it(`renders no toggleColumnButton`, () => { - const foundLength = findTestSubject(component, 'toggleColumnButtons').length; - expect(foundLength).toBe(0); - }); - - it(`renders addInclusiveFilterButton`, () => { - const row = findTestSubject(component, `tableDocViewRow-_index`); - const btn = findTestSubject(row, 'addInclusiveFilterButton'); - expect(btn.length).toBe(1); - btn.simulate('click'); - expect(props.filter).toBeCalled(); - }); - - it(`renders functional collapse button`, () => { - const btn = findTestSubject(component, `collapseBtn`); - const html = component.html(); - - expect(component.html()).toContain('truncate-by-height'); - - expect(btn.length).toBe(1); - btn.simulate('click'); - expect(component.html() !== html).toBeTruthy(); - }); -}); diff --git a/src/plugins/discover_legacy/public/application/components/table/table.tsx b/src/plugins/discover_legacy/public/application/components/table/table.tsx deleted file mode 100644 index 90167a515985..000000000000 --- a/src/plugins/discover_legacy/public/application/components/table/table.tsx +++ /dev/null @@ -1,149 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React, { useState } from 'react'; -import { escapeRegExp } from 'lodash'; -import { DocViewTableRow } from './table_row'; -import { arrayContainsObjects, trimAngularSpan } from './table_helper'; -import { DocViewRenderProps } from '../../doc_views/doc_views_types'; - -const COLLAPSE_LINE_LENGTH = 350; - -export function DocViewTable({ - hit, - indexPattern, - filter, - columns, - onAddColumn, - onRemoveColumn, -}: DocViewRenderProps) { - const mapping = indexPattern.fields.getByName; - const flattened = indexPattern.flattenHit(hit); - const formatted = indexPattern.formatHit(hit, 'html'); - const [fieldRowOpen, setFieldRowOpen] = useState({} as Record); - - function toggleValueCollapse(field: string) { - fieldRowOpen[field] = fieldRowOpen[field] !== true; - setFieldRowOpen({ ...fieldRowOpen }); - } - - return ( - - - {Object.keys(flattened) - .sort() - .map((field) => { - const valueRaw = flattened[field]; - const value = trimAngularSpan(String(formatted[field])); - - const isCollapsible = value.length > COLLAPSE_LINE_LENGTH; - const isCollapsed = isCollapsible && !fieldRowOpen[field]; - const toggleColumn = - onRemoveColumn && onAddColumn && Array.isArray(columns) - ? () => { - if (columns.includes(field)) { - onRemoveColumn(field); - } else { - onAddColumn(field); - } - } - : undefined; - const isArrayOfObjects = - Array.isArray(flattened[field]) && arrayContainsObjects(flattened[field]); - const displayUnderscoreWarning = !mapping(field) && field.indexOf('_') === 0; - const displayNoMappingWarning = - !mapping(field) && !displayUnderscoreWarning && !isArrayOfObjects; - - // Discover doesn't flatten arrays of objects, so for documents with an `object` or `nested` field that - // contains an array, Discover will only detect the top level root field. We want to detect when those - // root fields are `nested` so that we can display the proper icon and label. However, those root - // `nested` fields are not a part of the index pattern. Their children are though, and contain nested path - // info. So to detect nested fields we look through the index pattern for nested children - // whose path begins with the current field. There are edge cases where - // this could incorrectly identify a plain `object` field as `nested`. Say we had the following document - // where `foo` is a plain object field and `bar` is a nested field. - // { - // "foo": [ - // { - // "bar": [ - // { - // "baz": "qux" - // } - // ] - // }, - // { - // "bar": [ - // { - // "baz": "qux" - // } - // ] - // } - // ] - // } - // - // The following code will search for `foo`, find it at the beginning of the path to the nested child field - // `foo.bar.baz` and incorrectly mark `foo` as nested. Any time we're searching for the name of a plain object - // field that happens to match a segment of a nested path, we'll get a false positive. - // We're aware of this issue and we'll have to live with - // it in the short term. The long term fix will be to add info about the `nested` and `object` root fields - // to the index pattern, but that has its own complications which you can read more about in the following - // issue: https://github.com/elastic/kibana/issues/54957 - const isNestedField = - !indexPattern.fields.getByName(field) && - !!indexPattern.fields.getAll().find((patternField) => { - // We only want to match a full path segment - const nestedRootRegex = new RegExp(escapeRegExp(field) + '(\\.|$)'); - return nestedRootRegex.test(patternField.subType?.nested?.path ?? ''); - }); - const fieldType = isNestedField ? 'nested' : indexPattern.fields.getByName(field)?.type; - - return ( - toggleValueCollapse(field)} - onToggleColumn={toggleColumn} - value={value} - valueRaw={valueRaw} - /> - ); - })} - -
- ); -} diff --git a/src/plugins/discover_legacy/public/application/components/table/table_helper.test.ts b/src/plugins/discover_legacy/public/application/components/table/table_helper.test.ts deleted file mode 100644 index 20c1092ef86d..000000000000 --- a/src/plugins/discover_legacy/public/application/components/table/table_helper.test.ts +++ /dev/null @@ -1,58 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { arrayContainsObjects } from './table_helper'; - -describe('arrayContainsObjects', () => { - it(`returns false for an array of primitives`, () => { - const actual = arrayContainsObjects(['test', 'test']); - expect(actual).toBeFalsy(); - }); - - it(`returns true for an array of objects`, () => { - const actual = arrayContainsObjects([{}, {}]); - expect(actual).toBeTruthy(); - }); - - it(`returns true for an array of objects and primitves`, () => { - const actual = arrayContainsObjects([{}, 'sdf']); - expect(actual).toBeTruthy(); - }); - - it(`returns false for an array of null values`, () => { - const actual = arrayContainsObjects([null, null]); - expect(actual).toBeFalsy(); - }); - - it(`returns false if no array is given`, () => { - const actual = arrayContainsObjects([null, null]); - expect(actual).toBeFalsy(); - }); -}); diff --git a/src/plugins/discover_legacy/public/application/components/table/table_helper.tsx b/src/plugins/discover_legacy/public/application/components/table/table_helper.tsx deleted file mode 100644 index 2e63b43b8310..000000000000 --- a/src/plugins/discover_legacy/public/application/components/table/table_helper.tsx +++ /dev/null @@ -1,43 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/** - * Returns true if the given array contains at least 1 object - */ -export function arrayContainsObjects(value: unknown[]): boolean { - return Array.isArray(value) && value.some((v) => typeof v === 'object' && v !== null); -} - -/** - * Removes markup added by OpenSearch Dashboards fields html formatter - */ -export function trimAngularSpan(text: string): string { - return text.replace(/^/, '').replace(/<\/span>$/, ''); -} diff --git a/src/plugins/discover_legacy/public/application/components/table/table_row.tsx b/src/plugins/discover_legacy/public/application/components/table/table_row.tsx deleted file mode 100644 index 95ba38106e3e..000000000000 --- a/src/plugins/discover_legacy/public/application/components/table/table_row.tsx +++ /dev/null @@ -1,129 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import classNames from 'classnames'; -import React, { ReactNode } from 'react'; -import { FieldMapping, DocViewFilterFn } from '../../doc_views/doc_views_types'; -import { DocViewTableRowBtnFilterAdd } from './table_row_btn_filter_add'; -import { DocViewTableRowBtnFilterRemove } from './table_row_btn_filter_remove'; -import { DocViewTableRowBtnToggleColumn } from './table_row_btn_toggle_column'; -import { DocViewTableRowBtnCollapse } from './table_row_btn_collapse'; -import { DocViewTableRowBtnFilterExists } from './table_row_btn_filter_exists'; -import { DocViewTableRowIconNoMapping } from './table_row_icon_no_mapping'; -import { DocViewTableRowIconUnderscore } from './table_row_icon_underscore'; -import { FieldName } from '../field_name/field_name'; - -export interface Props { - field: string; - fieldMapping?: FieldMapping; - fieldType: string; - displayNoMappingWarning: boolean; - displayUnderscoreWarning: boolean; - isCollapsible: boolean; - isColumnActive: boolean; - isCollapsed: boolean; - onToggleCollapse: () => void; - onFilter?: DocViewFilterFn; - onToggleColumn?: () => void; - value: string | ReactNode; - valueRaw: unknown; -} - -export function DocViewTableRow({ - field, - fieldMapping, - fieldType, - displayNoMappingWarning, - displayUnderscoreWarning, - isCollapsible, - isCollapsed, - isColumnActive, - onFilter, - onToggleCollapse, - onToggleColumn, - value, - valueRaw, -}: Props) { - const valueClassName = classNames({ - // eslint-disable-next-line @typescript-eslint/naming-convention - osdDocViewer__value: true, - 'truncate-by-height': isCollapsible && isCollapsed, - }); - - return ( - - {typeof onFilter === 'function' && ( - - onFilter(fieldMapping, valueRaw, '+')} - /> - onFilter(fieldMapping, valueRaw, '-')} - /> - {typeof onToggleColumn === 'function' && ( - - )} - onFilter('_exists_', field, '+')} - scripted={fieldMapping && fieldMapping.scripted} - /> - - )} - - - - - {isCollapsible && ( - - )} - {displayUnderscoreWarning && } - {displayNoMappingWarning && } -
- - - ); -} diff --git a/src/plugins/discover_legacy/public/application/components/table/table_row_btn_collapse.tsx b/src/plugins/discover_legacy/public/application/components/table/table_row_btn_collapse.tsx deleted file mode 100644 index de25c73e9c95..000000000000 --- a/src/plugins/discover_legacy/public/application/components/table/table_row_btn_collapse.tsx +++ /dev/null @@ -1,56 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { i18n } from '@osd/i18n'; -import { EuiToolTip, EuiButtonIcon } from '@elastic/eui'; - -export interface Props { - onClick: () => void; - isCollapsed: boolean; -} - -export function DocViewTableRowBtnCollapse({ onClick, isCollapsed }: Props) { - const label = i18n.translate('discover.docViews.table.toggleFieldDetails', { - defaultMessage: 'Toggle field details', - }); - return ( - - onClick()} - iconType={isCollapsed ? 'arrowRight' : 'arrowDown'} - iconSize={'s'} - /> - - ); -} diff --git a/src/plugins/discover_legacy/public/application/components/table/table_row_btn_filter_add.tsx b/src/plugins/discover_legacy/public/application/components/table/table_row_btn_filter_add.tsx deleted file mode 100644 index 1707861faf28..000000000000 --- a/src/plugins/discover_legacy/public/application/components/table/table_row_btn_filter_add.tsx +++ /dev/null @@ -1,69 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { FormattedMessage } from '@osd/i18n/react'; -import { EuiToolTip, EuiButtonIcon } from '@elastic/eui'; -import { i18n } from '@osd/i18n'; - -export interface Props { - onClick: () => void; - disabled: boolean; -} - -export function DocViewTableRowBtnFilterAdd({ onClick, disabled = false }: Props) { - const tooltipContent = disabled ? ( - - ) : ( - - ); - - return ( - - - - ); -} diff --git a/src/plugins/discover_legacy/public/application/components/table/table_row_btn_filter_exists.tsx b/src/plugins/discover_legacy/public/application/components/table/table_row_btn_filter_exists.tsx deleted file mode 100644 index d4f401282e14..000000000000 --- a/src/plugins/discover_legacy/public/application/components/table/table_row_btn_filter_exists.tsx +++ /dev/null @@ -1,81 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { FormattedMessage } from '@osd/i18n/react'; -import { EuiToolTip, EuiButtonIcon } from '@elastic/eui'; -import { i18n } from '@osd/i18n'; - -export interface Props { - onClick: () => void; - disabled?: boolean; - scripted?: boolean; -} - -export function DocViewTableRowBtnFilterExists({ - onClick, - disabled = false, - scripted = false, -}: Props) { - const tooltipContent = disabled ? ( - scripted ? ( - - ) : ( - - ) - ) : ( - - ); - - return ( - - - - ); -} diff --git a/src/plugins/discover_legacy/public/application/components/table/table_row_btn_filter_remove.tsx b/src/plugins/discover_legacy/public/application/components/table/table_row_btn_filter_remove.tsx deleted file mode 100644 index 3b58fbfdc282..000000000000 --- a/src/plugins/discover_legacy/public/application/components/table/table_row_btn_filter_remove.tsx +++ /dev/null @@ -1,69 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { FormattedMessage } from '@osd/i18n/react'; -import { EuiToolTip, EuiButtonIcon } from '@elastic/eui'; -import { i18n } from '@osd/i18n'; - -export interface Props { - onClick: () => void; - disabled?: boolean; -} - -export function DocViewTableRowBtnFilterRemove({ onClick, disabled = false }: Props) { - const tooltipContent = disabled ? ( - - ) : ( - - ); - - return ( - - - - ); -} diff --git a/src/plugins/discover_legacy/public/application/components/table/table_row_btn_toggle_column.tsx b/src/plugins/discover_legacy/public/application/components/table/table_row_btn_toggle_column.tsx deleted file mode 100644 index 74f0972fa0ee..000000000000 --- a/src/plugins/discover_legacy/public/application/components/table/table_row_btn_toggle_column.tsx +++ /dev/null @@ -1,79 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { FormattedMessage } from '@osd/i18n/react'; -import { EuiToolTip, EuiButtonIcon } from '@elastic/eui'; -import { i18n } from '@osd/i18n'; - -export interface Props { - active: boolean; - disabled?: boolean; - onClick: () => void; -} - -export function DocViewTableRowBtnToggleColumn({ onClick, active, disabled = false }: Props) { - if (disabled) { - return ( - - ); - } - return ( - - } - > - - - ); -} diff --git a/src/plugins/discover_legacy/public/application/components/table/table_row_icon_no_mapping.tsx b/src/plugins/discover_legacy/public/application/components/table/table_row_icon_no_mapping.tsx deleted file mode 100644 index edc4bea91bd8..000000000000 --- a/src/plugins/discover_legacy/public/application/components/table/table_row_icon_no_mapping.tsx +++ /dev/null @@ -1,59 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { EuiIconTip } from '@elastic/eui'; -import { i18n } from '@osd/i18n'; - -export function DocViewTableRowIconNoMapping() { - const ariaLabel = i18n.translate('discover.docViews.table.noCachedMappingForThisFieldAriaLabel', { - defaultMessage: 'Warning', - }); - const tooltipContent = i18n.translate( - 'discover.docViews.table.noCachedMappingForThisFieldTooltip', - { - defaultMessage: - 'No cached mapping for this field. Refresh field list from the Management > Index Patterns page', - } - ); - return ( - - ); -} diff --git a/src/plugins/discover_legacy/public/application/components/table/table_row_icon_underscore.tsx b/src/plugins/discover_legacy/public/application/components/table/table_row_icon_underscore.tsx deleted file mode 100644 index f1d09e2c8d44..000000000000 --- a/src/plugins/discover_legacy/public/application/components/table/table_row_icon_underscore.tsx +++ /dev/null @@ -1,63 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { EuiIconTip } from '@elastic/eui'; -import { i18n } from '@osd/i18n'; - -export function DocViewTableRowIconUnderscore() { - const ariaLabel = i18n.translate( - 'discover.docViews.table.fieldNamesBeginningWithUnderscoreUnsupportedAriaLabel', - { - defaultMessage: 'Warning', - } - ); - const tooltipContent = i18n.translate( - 'discover.docViews.table.fieldNamesBeginningWithUnderscoreUnsupportedTooltip', - { - defaultMessage: 'Field names beginning with {underscoreSign} are not supported', - values: { underscoreSign: '_' }, - } - ); - - return ( - - ); -} diff --git a/src/plugins/discover_legacy/public/application/components/timechart_header/index.ts b/src/plugins/discover_legacy/public/application/components/timechart_header/index.ts deleted file mode 100644 index 880610ac57e9..000000000000 --- a/src/plugins/discover_legacy/public/application/components/timechart_header/index.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export { TimechartHeader } from './timechart_header'; diff --git a/src/plugins/discover_legacy/public/application/components/timechart_header/timechart_header.test.tsx b/src/plugins/discover_legacy/public/application/components/timechart_header/timechart_header.test.tsx deleted file mode 100644 index 9011c38a6acb..000000000000 --- a/src/plugins/discover_legacy/public/application/components/timechart_header/timechart_header.test.tsx +++ /dev/null @@ -1,110 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { mountWithIntl } from 'test_utils/enzyme_helpers'; -import { ReactWrapper } from 'enzyme'; -import { TimechartHeader, TimechartHeaderProps } from './timechart_header'; -import { EuiIconTip } from '@elastic/eui'; -import { findTestSubject } from 'test_utils/helpers'; - -describe('timechart header', function () { - let props: TimechartHeaderProps; - let component: ReactWrapper; - - beforeAll(() => { - props = { - timeRange: { - from: 'May 14, 2020 @ 11:05:13.590', - to: 'May 14, 2020 @ 11:20:13.590', - }, - stateInterval: 's', - options: [ - { - display: 'Auto', - val: 'auto', - }, - { - display: 'Millisecond', - val: 'ms', - }, - { - display: 'Second', - val: 's', - }, - ], - onChangeInterval: jest.fn(), - bucketInterval: { - scaled: undefined, - description: 'second', - scale: undefined, - }, - }; - }); - - it('TimechartHeader not renders an info text when the showScaledInfo property is not provided', () => { - component = mountWithIntl(); - expect(component.find(EuiIconTip).length).toBe(0); - }); - - it('TimechartHeader renders an info when bucketInterval.scale is set to true', () => { - props.bucketInterval!.scaled = true; - component = mountWithIntl(); - expect(component.find(EuiIconTip).length).toBe(1); - }); - - it('expect to render the date range', function () { - component = mountWithIntl(); - const datetimeRangeText = findTestSubject(component, 'discoverIntervalDateRange'); - expect(datetimeRangeText.text()).toBe( - 'May 14, 2020 @ 11:05:13.590 - May 14, 2020 @ 11:20:13.590 per' - ); - }); - - it('expects to render a dropdown with the interval options', () => { - component = mountWithIntl(); - const dropdown = findTestSubject(component, 'discoverIntervalSelect'); - expect(dropdown.length).toBe(1); - // @ts-ignore - const values = dropdown.find('option').map((option) => option.prop('value')); - expect(values).toEqual(['auto', 'ms', 's']); - // @ts-ignore - const labels = dropdown.find('option').map((option) => option.text()); - expect(labels).toEqual(['Auto', 'Millisecond', 'Second']); - }); - - it('should change the interval', function () { - component = mountWithIntl(); - findTestSubject(component, 'discoverIntervalSelect').simulate('change', { - target: { value: 'ms' }, - }); - expect(props.onChangeInterval).toHaveBeenCalled(); - }); -}); diff --git a/src/plugins/discover_legacy/public/application/components/timechart_header/timechart_header.tsx b/src/plugins/discover_legacy/public/application/components/timechart_header/timechart_header.tsx deleted file mode 100644 index b73e8b162570..000000000000 --- a/src/plugins/discover_legacy/public/application/components/timechart_header/timechart_header.tsx +++ /dev/null @@ -1,183 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React, { useState, useEffect, useCallback } from 'react'; -import { - EuiFlexGroup, - EuiFlexItem, - EuiToolTip, - EuiText, - EuiSelect, - EuiIconTip, -} from '@elastic/eui'; -import { I18nProvider } from '@osd/i18n/react'; -import { i18n } from '@osd/i18n'; -import moment from 'moment'; - -export interface TimechartHeaderProps { - /** - * Format of date to be displayed - */ - dateFormat?: string; - /** - * Interval for the buckets of the recent request - */ - bucketInterval?: { - scaled?: boolean; - description?: string; - scale?: number; - }; - /** - * Range of dates to be displayed - */ - timeRange?: { - from: string; - to: string; - }; - /** - * Interval Options - */ - options: Array<{ display: string; val: string }>; - /** - * changes the interval - */ - onChangeInterval: (interval: string) => void; - /** - * selected interval - */ - stateInterval: string; -} - -export function TimechartHeader({ - bucketInterval, - dateFormat, - timeRange, - options, - onChangeInterval, - stateInterval, -}: TimechartHeaderProps) { - const [interval, setInterval] = useState(stateInterval); - const toMoment = useCallback( - (datetime: string) => { - if (!datetime) { - return ''; - } - if (!dateFormat) { - return datetime; - } - return moment(datetime).format(dateFormat); - }, - [dateFormat] - ); - - useEffect(() => { - setInterval(stateInterval); - }, [stateInterval]); - - const handleIntervalChange = (e: React.ChangeEvent) => { - setInterval(e.target.value); - onChangeInterval(e.target.value); - }; - - if (!timeRange || !bucketInterval) { - return null; - } - - return ( - - - - - - {`${toMoment(timeRange.from)} - ${toMoment(timeRange.to)} ${ - interval !== 'auto' - ? i18n.translate('discover.timechartHeader.timeIntervalSelect.per', { - defaultMessage: 'per', - }) - : '' - }`} - - - - - val !== 'custom') - .map(({ display, val }) => { - return { - text: display, - value: val, - label: display, - }; - })} - value={interval} - onChange={handleIntervalChange} - append={ - bucketInterval.scaled ? ( - 1 - ? i18n.translate('discover.bucketIntervalTooltip.tooLargeBucketsText', { - defaultMessage: 'buckets that are too large', - }) - : i18n.translate('discover.bucketIntervalTooltip.tooManyBucketsText', { - defaultMessage: 'too many buckets', - }), - bucketIntervalDescription: bucketInterval.description, - }, - })} - color="warning" - size="s" - type="alert" - /> - ) : undefined - } - /> - - - - ); -} diff --git a/src/plugins/discover_legacy/public/application/components/top_nav/open_search_panel.test.js b/src/plugins/discover_legacy/public/application/components/top_nav/open_search_panel.test.js deleted file mode 100644 index 6316471e9a40..000000000000 --- a/src/plugins/discover_legacy/public/application/components/top_nav/open_search_panel.test.js +++ /dev/null @@ -1,48 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { shallow } from 'enzyme'; - -jest.mock('../../../opensearch_dashboards_services', () => { - return { - getServices: () => ({ - core: { uiSettings: {}, savedObjects: {} }, - addBasePath: (path) => path, - }), - }; -}); - -import { OpenSearchPanel } from './open_search_panel'; - -test('render', () => { - const component = shallow( {}} makeUrl={() => {}} />); - expect(component).toMatchSnapshot(); -}); diff --git a/src/plugins/discover_legacy/public/application/components/top_nav/show_open_search_panel.js b/src/plugins/discover_legacy/public/application/components/top_nav/show_open_search_panel.js deleted file mode 100644 index 8cb550f49f16..000000000000 --- a/src/plugins/discover_legacy/public/application/components/top_nav/show_open_search_panel.js +++ /dev/null @@ -1,57 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import ReactDOM from 'react-dom'; -import { OpenSearchPanel } from './open_search_panel'; - -let isOpen = false; - -export function showOpenSearchPanel({ makeUrl, I18nContext }) { - if (isOpen) { - return; - } - - isOpen = true; - const container = document.createElement('div'); - const onClose = () => { - ReactDOM.unmountComponentAtNode(container); - document.body.removeChild(container); - isOpen = false; - }; - - document.body.appendChild(container); - const element = ( - - - - ); - ReactDOM.render(element, container); -} diff --git a/src/plugins/discover_legacy/public/application/doc_views/doc_views_helpers.tsx b/src/plugins/discover_legacy/public/application/doc_views/doc_views_helpers.tsx deleted file mode 100644 index e113f6961fc3..000000000000 --- a/src/plugins/discover_legacy/public/application/doc_views/doc_views_helpers.tsx +++ /dev/null @@ -1,105 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { auto, IController } from 'angular'; -import React from 'react'; -import { render } from 'react-dom'; -import angular, { ICompileService } from 'angular'; -import { DocViewRenderProps, AngularScope, AngularDirective } from './doc_views_types'; -import { DocViewerError } from '../components/doc_viewer/doc_viewer_render_error'; - -/** - * Compiles and injects the give angular template into the given dom node - * returns a function to cleanup the injected angular element - */ -export async function injectAngularElement( - domNode: Element, - template: string, - scopeProps: DocViewRenderProps, - Controller: IController, - getInjector: () => Promise -): Promise<() => void> { - const $injector = await getInjector(); - const rootScope: AngularScope = $injector.get('$rootScope'); - const $compile: ICompileService = $injector.get('$compile'); - const newScope = Object.assign(rootScope.$new(), scopeProps); - - if (typeof Controller === 'function') { - // when a controller is defined, expose the value it produces to the view as `$ctrl` - // see: https://docs.angularjs.org/api/ng/provider/$compileProvider#component - (newScope as any).$ctrl = $injector.instantiate(Controller, { - $scope: newScope, - }); - } - - const $target = angular.element(domNode); - const $element = angular.element(template); - - newScope.$apply(() => { - const linkFn = $compile($element); - $target.empty().append($element); - linkFn(newScope); - }); - - return () => { - newScope.$destroy(); - }; -} -/** - * Converts a given legacy angular directive to a render function - * for usage in a react component. Note that the rendering is async - */ -export function convertDirectiveToRenderFn( - directive: AngularDirective, - getInjector: () => Promise -) { - return (domNode: Element, props: DocViewRenderProps) => { - let rejected = false; - - const cleanupFnPromise = injectAngularElement( - domNode, - directive.template, - props, - directive.controller, - getInjector - ); - cleanupFnPromise.catch((e) => { - rejected = true; - render(, domNode); - }); - - return () => { - if (!rejected) { - // for cleanup - cleanupFnPromise.then((cleanup) => cleanup()); - } - }; - }; -} diff --git a/src/plugins/discover_legacy/public/application/doc_views/doc_views_registry.ts b/src/plugins/discover_legacy/public/application/doc_views/doc_views_registry.ts deleted file mode 100644 index 56f167b5f2cc..000000000000 --- a/src/plugins/discover_legacy/public/application/doc_views/doc_views_registry.ts +++ /dev/null @@ -1,70 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { auto } from 'angular'; -import { convertDirectiveToRenderFn } from './doc_views_helpers'; -import { DocView, DocViewInput, OpenSearchSearchHit, DocViewInputFn } from './doc_views_types'; - -export class DocViewsRegistry { - private docViews: DocView[] = []; - private angularInjectorGetter: (() => Promise) | null = null; - - setAngularInjectorGetter = (injectorGetter: () => Promise) => { - this.angularInjectorGetter = injectorGetter; - }; - - /** - * Extends and adds the given doc view to the registry array - */ - addDocView(docViewRaw: DocViewInput | DocViewInputFn) { - const docView = typeof docViewRaw === 'function' ? docViewRaw() : docViewRaw; - if (docView.directive) { - // convert angular directive to render function for backwards compatibility - docView.render = convertDirectiveToRenderFn(docView.directive, () => { - if (!this.angularInjectorGetter) { - throw new Error('Angular was not initialized'); - } - return this.angularInjectorGetter(); - }); - } - if (typeof docView.shouldShow !== 'function') { - docView.shouldShow = () => true; - } - this.docViews.push(docView as DocView); - } - /** - * Returns a sorted array of doc_views for rendering tabs - */ - getDocViewsSorted(hit: OpenSearchSearchHit) { - return this.docViews - .filter((docView) => docView.shouldShow(hit)) - .sort((a, b) => (Number(a.order) > Number(b.order) ? 1 : -1)); - } -} diff --git a/src/plugins/discover_legacy/public/application/doc_views/doc_views_types.ts b/src/plugins/discover_legacy/public/application/doc_views/doc_views_types.ts deleted file mode 100644 index 961fc98516f6..000000000000 --- a/src/plugins/discover_legacy/public/application/doc_views/doc_views_types.ts +++ /dev/null @@ -1,86 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { ComponentType } from 'react'; -import { IScope } from 'angular'; -import { SearchResponse } from 'elasticsearch'; -import { IndexPattern } from '../../../../data/public'; - -export interface AngularDirective { - controller: (...injectedServices: any[]) => void; - template: string; -} - -export type AngularScope = IScope; - -export type OpenSearchSearchHit = SearchResponse['hits']['hits'][number]; - -export interface FieldMapping { - filterable?: boolean; - scripted?: boolean; - rowCount?: number; - type: string; - name: string; -} - -export type DocViewFilterFn = ( - mapping: FieldMapping | string | undefined, - value: unknown, - mode: '+' | '-' -) => void; - -export interface DocViewRenderProps { - columns?: string[]; - filter?: DocViewFilterFn; - hit: OpenSearchSearchHit; - indexPattern: IndexPattern; - onAddColumn?: (columnName: string) => void; - onRemoveColumn?: (columnName: string) => void; -} -export type DocViewerComponent = ComponentType; -export type DocViewRenderFn = ( - domeNode: HTMLDivElement, - renderProps: DocViewRenderProps -) => () => void; - -export interface DocViewInput { - component?: DocViewerComponent; - directive?: AngularDirective; - order: number; - render?: DocViewRenderFn; - shouldShow?: (hit: OpenSearchSearchHit) => boolean; - title: string; -} - -export interface DocView extends DocViewInput { - shouldShow: (hit: OpenSearchSearchHit) => boolean; -} - -export type DocViewInputFn = () => DocViewInput; diff --git a/src/plugins/discover_legacy/public/application/doc_views_links/doc_views_links_registry.ts b/src/plugins/discover_legacy/public/application/doc_views_links/doc_views_links_registry.ts deleted file mode 100644 index 16653f5d5377..000000000000 --- a/src/plugins/discover_legacy/public/application/doc_views_links/doc_views_links_registry.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import { DocViewLink } from './doc_views_links_types'; - -export class DocViewsLinksRegistry { - private docViewsLinks: DocViewLink[] = []; - - addDocViewLink(docViewLink: DocViewLink) { - this.docViewsLinks.push(docViewLink); - } - - getDocViewsLinksSorted() { - return this.docViewsLinks.sort((a, b) => (Number(a.order) > Number(b.order) ? 1 : -1)); - } -} diff --git a/src/plugins/discover_legacy/public/application/doc_views_links/doc_views_links_types.ts b/src/plugins/discover_legacy/public/application/doc_views_links/doc_views_links_types.ts deleted file mode 100644 index bbc5caadafcd..000000000000 --- a/src/plugins/discover_legacy/public/application/doc_views_links/doc_views_links_types.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import { EuiListGroupItemProps } from '@elastic/eui'; -import { OpenSearchSearchHit } from '../doc_views/doc_views_types'; -import { IndexPattern } from '../../../../data/public'; - -export interface DocViewLink extends EuiListGroupItemProps { - href?: string; - order: number; - generateCb?( - renderProps: any - ): { - url: string; - hide?: boolean; - }; -} - -export interface DocViewLinkRenderProps { - columns?: string[]; - hit: OpenSearchSearchHit; - indexPattern: IndexPattern; -} diff --git a/src/plugins/discover_legacy/public/application/embeddable/search_template.html b/src/plugins/discover_legacy/public/application/embeddable/search_template.html deleted file mode 100644 index e188d230ea30..000000000000 --- a/src/plugins/discover_legacy/public/application/embeddable/search_template.html +++ /dev/null @@ -1,20 +0,0 @@ - - diff --git a/src/plugins/discover_legacy/public/application/helpers/breadcrumbs.ts b/src/plugins/discover_legacy/public/application/helpers/breadcrumbs.ts deleted file mode 100644 index e30f50206aef..000000000000 --- a/src/plugins/discover_legacy/public/application/helpers/breadcrumbs.ts +++ /dev/null @@ -1,51 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; - -export function getRootBreadcrumbs() { - return [ - { - text: i18n.translate('discover.rootBreadcrumb', { - defaultMessage: 'Discover', - }), - href: '#/', - }, - ]; -} - -export function getSavedSearchBreadcrumbs($route: any) { - return [ - ...getRootBreadcrumbs(), - { - text: $route.current.locals.savedObjects.savedSearch.id, - }, - ]; -} diff --git a/src/plugins/discover_legacy/public/application/helpers/format_number_with_commas.ts b/src/plugins/discover_legacy/public/application/helpers/format_number_with_commas.ts deleted file mode 100644 index b1b3c96e0958..000000000000 --- a/src/plugins/discover_legacy/public/application/helpers/format_number_with_commas.ts +++ /dev/null @@ -1,38 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -const COMMA_SEPARATOR_RE = /(\d)(?=(\d{3})+(?!\d))/g; - -/** - * Converts a number to a string and adds commas - * as thousands separators - */ -export const formatNumWithCommas = (input: number) => - String(input).replace(COMMA_SEPARATOR_RE, '$1,'); diff --git a/src/plugins/discover_legacy/public/application/helpers/get_index_pattern_id.ts b/src/plugins/discover_legacy/public/application/helpers/get_index_pattern_id.ts deleted file mode 100644 index dfb02c0b0740..000000000000 --- a/src/plugins/discover_legacy/public/application/helpers/get_index_pattern_id.ts +++ /dev/null @@ -1,71 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { IIndexPattern } from '../../../../data/common/index_patterns'; - -export function findIndexPatternById( - indexPatterns: IIndexPattern[], - id: string -): IIndexPattern | undefined { - if (!Array.isArray(indexPatterns) || !id) { - return; - } - return indexPatterns.find((o) => o.id === id); -} - -/** - * Checks if the given defaultIndex exists and returns - * the first available index pattern id if not - */ -export function getFallbackIndexPatternId( - indexPatterns: IIndexPattern[], - defaultIndex: string = '' -): string { - if (defaultIndex && findIndexPatternById(indexPatterns, defaultIndex)) { - return defaultIndex; - } - return !indexPatterns || !indexPatterns.length || !indexPatterns[0].id ? '' : indexPatterns[0].id; -} - -/** - * A given index pattern id is checked for existence and a fallback is provided if it doesn't exist - * The provided defaultIndex is usually configured in Advanced Settings, if it's also invalid - * the first entry of the given list of Indexpatterns is used - */ -export function getIndexPatternId( - id: string = '', - indexPatterns: IIndexPattern[], - defaultIndex: string = '' -): string { - if (!id || !findIndexPatternById(indexPatterns, id)) { - return getFallbackIndexPatternId(indexPatterns, defaultIndex); - } - return id; -} diff --git a/src/plugins/discover_legacy/public/application/helpers/get_switch_index_pattern_app_state.test.ts b/src/plugins/discover_legacy/public/application/helpers/get_switch_index_pattern_app_state.test.ts deleted file mode 100644 index c1a8acb04ac3..000000000000 --- a/src/plugins/discover_legacy/public/application/helpers/get_switch_index_pattern_app_state.test.ts +++ /dev/null @@ -1,101 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { getSwitchIndexPatternAppState } from './get_switch_index_pattern_app_state'; -import { IIndexPatternFieldList, IndexPattern } from '../../../../data/common/index_patterns'; - -const currentIndexPattern: IndexPattern = { - id: 'prev', - getFieldByName(name) { - return this.fields.getByName(name); - }, - fields: { - getByName: (name: string) => { - const fields = [ - { name: 'category', sortable: true }, - { name: 'name', sortable: true }, - ] as IIndexPatternFieldList; - return fields.find((field) => field.name === name); - }, - }, -} as IndexPattern; - -const nextIndexPattern = { - id: 'next', - getFieldByName(name) { - return this.fields.getByName(name); - }, - fields: { - getByName: (name: string) => { - const fields = [{ name: 'category', sortable: true }] as IIndexPatternFieldList; - return fields.find((field) => field.name === name); - }, - }, -} as IndexPattern; - -describe('Discover getSwitchIndexPatternAppState', () => { - test('removing fields that are not part of the next index pattern, keeping unknown fields ', async () => { - const result = getSwitchIndexPatternAppState( - currentIndexPattern, - nextIndexPattern, - ['category', 'name', 'unknown'], - [['category', 'desc']] - ); - expect(result.columns).toEqual(['category', 'unknown']); - expect(result.sort).toEqual([['category', 'desc']]); - }); - test('removing sorted by fields that are not part of the next index pattern', async () => { - const result = getSwitchIndexPatternAppState( - currentIndexPattern, - nextIndexPattern, - ['name'], - [ - ['category', 'desc'], - ['name', 'asc'], - ] - ); - expect(result.columns).toEqual(['_source']); - expect(result.sort).toEqual([['category', 'desc']]); - }); - test('removing sorted by fields that without modifying columns', async () => { - const result = getSwitchIndexPatternAppState( - currentIndexPattern, - nextIndexPattern, - ['name'], - [ - ['category', 'desc'], - ['name', 'asc'], - ], - false - ); - expect(result.columns).toEqual(['name']); - expect(result.sort).toEqual([['category', 'desc']]); - }); -}); diff --git a/src/plugins/discover_legacy/public/application/helpers/get_switch_index_pattern_app_state.ts b/src/plugins/discover_legacy/public/application/helpers/get_switch_index_pattern_app_state.ts deleted file mode 100644 index 51835910a402..000000000000 --- a/src/plugins/discover_legacy/public/application/helpers/get_switch_index_pattern_app_state.ts +++ /dev/null @@ -1,58 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { getSortArray } from '../angular/doc_table'; -import { SortPairArr } from '../angular/doc_table/lib/get_sort'; -import { IndexPattern } from '../../opensearch_dashboards_services'; - -/** - * Helper function to remove or adapt the currently selected columns/sort to be valid with the next - * index pattern, returns a new state object - */ -export function getSwitchIndexPatternAppState( - currentIndexPattern: IndexPattern, - nextIndexPattern: IndexPattern, - currentColumns: string[], - currentSort: SortPairArr[], - modifyColumns: boolean = true -) { - const nextColumns = modifyColumns - ? currentColumns.filter( - (column) => - nextIndexPattern.fields.getByName(column) || !currentIndexPattern.fields.getByName(column) - ) - : currentColumns; - const nextSort = getSortArray(currentSort, nextIndexPattern); - return { - index: nextIndexPattern.id, - columns: nextColumns.length ? nextColumns : ['_source'], - sort: nextSort, - }; -} diff --git a/src/plugins/discover_legacy/public/application/helpers/index.ts b/src/plugins/discover_legacy/public/application/helpers/index.ts deleted file mode 100644 index d765fdf60cee..000000000000 --- a/src/plugins/discover_legacy/public/application/helpers/index.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export { shortenDottedString } from './shorten_dotted_string'; -export { formatNumWithCommas } from './format_number_with_commas'; diff --git a/src/plugins/discover_legacy/public/application/helpers/migrate_legacy_query.ts b/src/plugins/discover_legacy/public/application/helpers/migrate_legacy_query.ts deleted file mode 100644 index 90458c135b98..000000000000 --- a/src/plugins/discover_legacy/public/application/helpers/migrate_legacy_query.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { has } from 'lodash'; -import { Query } from 'src/plugins/data/public'; - -/** - * Creates a standardized query object from old queries that were either strings or pure OpenSearch query DSL - * - * @param query - a legacy query, what used to be stored in SearchSource's query property - * @return Object - */ - -export function migrateLegacyQuery(query: Query | { [key: string]: any } | string): Query { - // Lucene was the only option before, so language-less queries are all lucene - if (!has(query, 'language')) { - return { query, language: 'lucene' }; - } - - return query as Query; -} diff --git a/src/plugins/discover_legacy/public/application/helpers/popularize_field.test.ts b/src/plugins/discover_legacy/public/application/helpers/popularize_field.test.ts deleted file mode 100644 index cdd49c0f77f1..000000000000 --- a/src/plugins/discover_legacy/public/application/helpers/popularize_field.test.ts +++ /dev/null @@ -1,93 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { IndexPattern, IndexPatternsService } from '../../../../data/public'; -import { popularizeField } from './popularize_field'; - -describe('Popularize field', () => { - test('returns undefined if index pattern lacks id', async () => { - const indexPattern = ({} as unknown) as IndexPattern; - const fieldName = '@timestamp'; - const indexPatternsService = ({} as unknown) as IndexPatternsService; - const result = await popularizeField(indexPattern, fieldName, indexPatternsService); - expect(result).toBeUndefined(); - }); - - test('returns undefined if field not found', async () => { - const indexPattern = ({ - fields: { - getByName: () => {}, - }, - } as unknown) as IndexPattern; - const fieldName = '@timestamp'; - const indexPatternsService = ({} as unknown) as IndexPatternsService; - const result = await popularizeField(indexPattern, fieldName, indexPatternsService); - expect(result).toBeUndefined(); - }); - - test('returns undefined if successful', async () => { - const field = { - count: 0, - }; - const indexPattern = ({ - id: 'id', - fields: { - getByName: () => field, - }, - } as unknown) as IndexPattern; - const fieldName = '@timestamp'; - const indexPatternsService = ({ - updateSavedObject: async () => {}, - } as unknown) as IndexPatternsService; - const result = await popularizeField(indexPattern, fieldName, indexPatternsService); - expect(result).toBeUndefined(); - expect(field.count).toEqual(1); - }); - - test('hides errors', async () => { - const field = { - count: 0, - }; - const indexPattern = ({ - id: 'id', - fields: { - getByName: () => field, - }, - } as unknown) as IndexPattern; - const fieldName = '@timestamp'; - const indexPatternsService = ({ - updateSavedObject: async () => { - throw new Error('unknown error'); - }, - } as unknown) as IndexPatternsService; - const result = await popularizeField(indexPattern, fieldName, indexPatternsService); - expect(result).toBeUndefined(); - }); -}); diff --git a/src/plugins/discover_legacy/public/application/helpers/popularize_field.ts b/src/plugins/discover_legacy/public/application/helpers/popularize_field.ts deleted file mode 100644 index e7c4b900fa19..000000000000 --- a/src/plugins/discover_legacy/public/application/helpers/popularize_field.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { IndexPattern, IndexPatternsService } from '../../../../data/public'; - -async function popularizeField( - indexPattern: IndexPattern, - fieldName: string, - indexPatternsService: IndexPatternsService -) { - if (!indexPattern.id) return; - const field = indexPattern.fields.getByName(fieldName); - if (!field) { - return; - } - - field.count++; - // Catch 409 errors caused by user adding columns in a higher frequency that the changes can be persisted to OpenSearch - try { - await indexPatternsService.updateSavedObject(indexPattern, 0, true); - // eslint-disable-next-line no-empty - } catch {} -} - -export { popularizeField }; diff --git a/src/plugins/discover_legacy/public/application/helpers/shorten_dotted_string.ts b/src/plugins/discover_legacy/public/application/helpers/shorten_dotted_string.ts deleted file mode 100644 index 39450f8c82c0..000000000000 --- a/src/plugins/discover_legacy/public/application/helpers/shorten_dotted_string.ts +++ /dev/null @@ -1,37 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -const DOT_PREFIX_RE = /(.).+?\./g; - -/** - * Convert a dot.notated.string into a short - * version (d.n.string) - */ -export const shortenDottedString = (input: string) => input.replace(DOT_PREFIX_RE, '$1.'); diff --git a/src/plugins/discover_legacy/public/application/helpers/validate_time_range.test.ts b/src/plugins/discover_legacy/public/application/helpers/validate_time_range.test.ts deleted file mode 100644 index 902f3d8a4b62..000000000000 --- a/src/plugins/discover_legacy/public/application/helpers/validate_time_range.test.ts +++ /dev/null @@ -1,58 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { validateTimeRange } from './validate_time_range'; -import { notificationServiceMock } from '../../../../../core/public/mocks'; - -describe('Discover validateTimeRange', () => { - test('validates given time ranges correctly', async () => { - const { toasts } = notificationServiceMock.createStartContract(); - [ - { from: '', to: '', result: false }, - { from: 'now', to: 'now+1h', result: true }, - { from: 'now', to: 'lala+1h', result: false }, - { from: '', to: 'now', result: false }, - { from: 'now', to: '', result: false }, - { from: ' 2020-06-02T13:36:13.689Z', to: 'now', result: true }, - { from: ' 2020-06-02T13:36:13.689Z', to: '2020-06-02T13:36:13.690Z', result: true }, - ].map((test) => { - expect(validateTimeRange({ from: test.from, to: test.to }, toasts)).toEqual(test.result); - }); - }); - - test('displays a toast when invalid data is entered', async () => { - const { toasts } = notificationServiceMock.createStartContract(); - expect(validateTimeRange({ from: 'now', to: 'null' }, toasts)).toEqual(false); - expect(toasts.addDanger).toHaveBeenCalledWith({ - title: 'Invalid time range', - text: "The provided time range is invalid. (from: 'now', to: 'null')", - }); - }); -}); diff --git a/src/plugins/discover_legacy/public/application/helpers/validate_time_range.ts b/src/plugins/discover_legacy/public/application/helpers/validate_time_range.ts deleted file mode 100644 index d23a84aabb14..000000000000 --- a/src/plugins/discover_legacy/public/application/helpers/validate_time_range.ts +++ /dev/null @@ -1,61 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import dateMath from '@elastic/datemath'; -import { i18n } from '@osd/i18n'; -import { ToastsStart } from 'opensearch-dashboards/public'; - -/** - * Validates a given time filter range, provided by URL or UI - * Unless valid, it returns false and displays a notification - */ -export function validateTimeRange( - { from, to }: { from: string; to: string }, - toastNotifications: ToastsStart -): boolean { - const fromMoment = dateMath.parse(from); - const toMoment = dateMath.parse(to); - if (!fromMoment || !toMoment || !fromMoment.isValid() || !toMoment.isValid()) { - toastNotifications.addDanger({ - title: i18n.translate('discover.notifications.invalidTimeRangeTitle', { - defaultMessage: `Invalid time range`, - }), - text: i18n.translate('discover.notifications.invalidTimeRangeText', { - defaultMessage: `The provided time range is invalid. (from: '{from}', to: '{to}')`, - values: { - from, - to, - }, - }), - }); - return false; - } - return true; -} diff --git a/src/plugins/discover_legacy/public/application/index.scss b/src/plugins/discover_legacy/public/application/index.scss deleted file mode 100644 index b9f191ac6d69..000000000000 --- a/src/plugins/discover_legacy/public/application/index.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import "angular/index"; -@import "discover"; diff --git a/src/plugins/discover_legacy/public/build_services.ts b/src/plugins/discover_legacy/public/build_services.ts deleted file mode 100644 index 3fdafcff0c40..000000000000 --- a/src/plugins/discover_legacy/public/build_services.ts +++ /dev/null @@ -1,129 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { History } from 'history'; - -import { - Capabilities, - ChromeStart, - CoreStart, - DocLinksStart, - ToastsStart, - IUiSettingsClient, - PluginInitializerContext, -} from 'opensearch-dashboards/public'; -import { - FilterManager, - TimefilterContract, - IndexPatternsContract, - DataPublicPluginStart, -} from 'src/plugins/data/public'; -import { Start as InspectorPublicPluginStart } from 'src/plugins/inspector/public'; -import { SharePluginStart } from 'src/plugins/share/public'; -import { ChartsPluginStart } from 'src/plugins/charts/public'; -import { VisualizationsStart } from 'src/plugins/visualizations/public'; -import { SavedObjectOpenSearchDashboardsServices } from 'src/plugins/saved_objects/public'; - -import { DiscoverStartPlugins } from './plugin'; -import { createSavedSearchesLoader, SavedSearch } from './saved_searches'; -import { getHistory } from './opensearch_dashboards_services'; -import { OpenSearchDashboardsLegacyStart } from '../../opensearch_dashboards_legacy/public'; -import { UrlForwardingStart } from '../../url_forwarding/public'; -import { NavigationPublicPluginStart } from '../../navigation/public'; - -export interface DiscoverServices { - addBasePath: (path: string) => string; - capabilities: Capabilities; - chrome: ChromeStart; - core: CoreStart; - data: DataPublicPluginStart; - docLinks: DocLinksStart; - history: () => History; - theme: ChartsPluginStart['theme']; - filterManager: FilterManager; - indexPatterns: IndexPatternsContract; - inspector: InspectorPublicPluginStart; - metadata: { branch: string }; - navigation: NavigationPublicPluginStart; - share?: SharePluginStart; - opensearchDashboardsLegacy: OpenSearchDashboardsLegacyStart; - urlForwarding: UrlForwardingStart; - timefilter: TimefilterContract; - toastNotifications: ToastsStart; - getSavedSearchById: (id: string) => Promise; - getSavedSearchUrlById: (id: string) => Promise; - getEmbeddableInjector: any; - uiSettings: IUiSettingsClient; - visualizations: VisualizationsStart; -} - -export async function buildServices( - core: CoreStart, - plugins: DiscoverStartPlugins, - context: PluginInitializerContext, - getEmbeddableInjector: any -): Promise { - const services: SavedObjectOpenSearchDashboardsServices = { - savedObjectsClient: core.savedObjects.client, - indexPatterns: plugins.data.indexPatterns, - search: plugins.data.search, - chrome: core.chrome, - overlays: core.overlays, - }; - const savedObjectService = createSavedSearchesLoader(services); - - return { - addBasePath: core.http.basePath.prepend, - capabilities: core.application.capabilities, - chrome: core.chrome, - core, - data: plugins.data, - docLinks: core.docLinks, - theme: plugins.charts.theme, - filterManager: plugins.data.query.filterManager, - getEmbeddableInjector, - getSavedSearchById: async (id: string) => savedObjectService.get(id), - getSavedSearchUrlById: async (id: string) => savedObjectService.urlFor(id), - history: getHistory, - indexPatterns: plugins.data.indexPatterns, - inspector: plugins.inspector, - metadata: { - branch: context.env.packageInfo.branch, - }, - navigation: plugins.navigation, - share: plugins.share, - opensearchDashboardsLegacy: plugins.opensearchDashboardsLegacy, - urlForwarding: plugins.urlForwarding, - timefilter: plugins.data.query.timefilter.timefilter, - toastNotifications: core.notifications.toasts, - uiSettings: core.uiSettings, - visualizations: plugins.visualizations, - }; -} diff --git a/src/plugins/discover_legacy/public/get_inner_angular.ts b/src/plugins/discover_legacy/public/get_inner_angular.ts deleted file mode 100644 index b4a7a17357ab..000000000000 --- a/src/plugins/discover_legacy/public/get_inner_angular.ts +++ /dev/null @@ -1,208 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -// inner angular imports -// these are necessary to bootstrap the local angular. -// They can stay even after NP cutover -import './application/index.scss'; -import angular from 'angular'; -// required for `ngSanitize` angular module -import 'angular-sanitize'; -import { EuiIcon } from '@elastic/eui'; -import { i18nDirective, i18nFilter, I18nProvider } from '@osd/i18n/angular'; -import { CoreStart, PluginInitializerContext } from 'opensearch-dashboards/public'; -import { DataPublicPluginStart } from '../../data/public'; -import { Storage } from '../../opensearch_dashboards_utils/public'; -import { NavigationPublicPluginStart as NavigationStart } from '../../navigation/public'; -import { createDocTableDirective } from './application/angular/doc_table'; -import { createTableHeaderDirective } from './application/angular/doc_table/components/table_header'; -import { - createToolBarPagerButtonsDirective, - createToolBarPagerTextDirective, -} from './application/angular/doc_table/components/pager'; -import { createTableRowDirective } from './application/angular/doc_table/components/table_row'; -import { createPagerFactory } from './application/angular/doc_table/lib/pager/pager_factory'; -import { createInfiniteScrollDirective } from './application/angular/doc_table/infinite_scroll'; -import { createDocViewerDirective } from './application/angular/doc_viewer'; -import { createDocViewerLinksDirective } from './application/angular/doc_viewer_links'; -import { createRenderCompleteDirective } from './application/angular/directives/render_complete'; -import { - initAngularBootstrap, - configureAppAngularModule, - PrivateProvider, - PromiseServiceCreator, - registerListenEventListener, - watchMultiDecorator, - createTopNavDirective, - createTopNavHelper, -} from '../../opensearch_dashboards_legacy/public'; -import { createContextErrorMessageDirective } from './application/components/context_error_message'; -import { DiscoverStartPlugins } from './plugin'; -import { getScopedHistory } from './opensearch_dashboards_services'; -import { createDiscoverLegacyDirective } from './application/components/create_discover_legacy_directive'; - -/** - * returns the main inner angular module, it contains all the parts of Angular Discover - * needs to render, so in the end the current 'opensearchDashboards' angular module is no longer necessary - */ -export function getInnerAngularModule( - name: string, - core: CoreStart, - deps: DiscoverStartPlugins, - context: PluginInitializerContext -) { - initAngularBootstrap(); - const module = initializeInnerAngularModule(name, core, deps.navigation, deps.data); - configureAppAngularModule(module, { core, env: context.env }, true, getScopedHistory); - return module; -} - -/** - * returns a slimmer inner angular module for embeddable rendering - */ -export function getInnerAngularModuleEmbeddable( - name: string, - core: CoreStart, - deps: DiscoverStartPlugins -) { - return initializeInnerAngularModule(name, core, deps.navigation, deps.data, true); -} - -let initialized = false; - -export function initializeInnerAngularModule( - name = 'app/discover', - core: CoreStart, - navigation: NavigationStart, - data: DataPublicPluginStart, - embeddable = false -) { - if (!initialized) { - createLocalI18nModule(); - createLocalPrivateModule(); - createLocalPromiseModule(); - createLocalTopNavModule(navigation); - createLocalStorageModule(); - createPagerFactoryModule(); - createDocTableModule(); - initialized = true; - } - - if (embeddable) { - return angular - .module(name, [ - 'ngSanitize', - 'react', - 'ui.bootstrap', - 'discoverI18n', - 'discoverPrivate', - 'discoverDocTable', - 'discoverPagerFactory', - 'discoverPromise', - ]) - .config(watchMultiDecorator) - .directive('icon', (reactDirective) => reactDirective(EuiIcon)) - .directive('renderComplete', createRenderCompleteDirective); - } - - return angular - .module(name, [ - 'ngSanitize', - 'ngRoute', - 'react', - 'ui.bootstrap', - 'discoverI18n', - 'discoverPrivate', - 'discoverPromise', - 'discoverTopNav', - 'discoverLocalStorageProvider', - 'discoverDocTable', - 'discoverPagerFactory', - ]) - .config(watchMultiDecorator) - .run(registerListenEventListener) - .directive('icon', (reactDirective) => reactDirective(EuiIcon)) - .directive('renderComplete', createRenderCompleteDirective) - .directive('discoverLegacy', createDiscoverLegacyDirective) - .directive('contextErrorMessage', createContextErrorMessageDirective); -} - -function createLocalPromiseModule() { - angular.module('discoverPromise', []).service('Promise', PromiseServiceCreator); -} - -function createLocalPrivateModule() { - angular.module('discoverPrivate', []).provider('Private', PrivateProvider); -} - -function createLocalTopNavModule(navigation: NavigationStart) { - angular - .module('discoverTopNav', ['react']) - .directive('osdTopNav', createTopNavDirective) - .directive('osdTopNavHelper', createTopNavHelper(navigation.ui)); -} - -function createLocalI18nModule() { - angular - .module('discoverI18n', []) - .provider('i18n', I18nProvider) - .filter('i18n', i18nFilter) - .directive('i18nId', i18nDirective); -} - -function createLocalStorageModule() { - angular - .module('discoverLocalStorageProvider', ['discoverPrivate']) - .service('localStorage', createLocalStorageService('localStorage')) - .service('sessionStorage', createLocalStorageService('sessionStorage')); -} - -const createLocalStorageService = function (type: string) { - return function ($window: any) { - return new Storage($window[type]); - }; -}; - -function createPagerFactoryModule() { - angular.module('discoverPagerFactory', []).factory('pagerFactory', createPagerFactory); -} - -function createDocTableModule() { - angular - .module('discoverDocTable', ['discoverPagerFactory', 'react']) - .directive('docTable', createDocTableDirective) - .directive('osdTableHeader', createTableHeaderDirective) - .directive('toolBarPagerText', createToolBarPagerTextDirective) - .directive('osdTableRow', createTableRowDirective) - .directive('toolBarPagerButtons', createToolBarPagerButtonsDirective) - .directive('osdInfiniteScroll', createInfiniteScrollDirective) - .directive('docViewer', createDocViewerDirective) - .directive('docViewerLinks', createDocViewerLinksDirective); -} diff --git a/src/plugins/discover_legacy/public/index.ts b/src/plugins/discover_legacy/public/index.ts deleted file mode 100644 index 6c9ab46b656e..000000000000 --- a/src/plugins/discover_legacy/public/index.ts +++ /dev/null @@ -1,41 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { PluginInitializerContext } from 'opensearch-dashboards/public'; -import { DiscoverPlugin } from './plugin'; - -export { DiscoverSetup, DiscoverStart } from './plugin'; -export function plugin(initializerContext: PluginInitializerContext) { - return new DiscoverPlugin(initializerContext); -} - -export { SavedSearch, SavedSearchLoader, createSavedSearchesLoader } from './saved_searches'; -export { ISearchEmbeddable, SEARCH_EMBEDDABLE_TYPE, SearchInput } from './application/embeddable'; -export { DISCOVER_APP_URL_GENERATOR, DiscoverUrlGeneratorState } from './url_generator'; diff --git a/src/plugins/discover_legacy/public/mocks.ts b/src/plugins/discover_legacy/public/mocks.ts deleted file mode 100644 index 4724ced290ff..000000000000 --- a/src/plugins/discover_legacy/public/mocks.ts +++ /dev/null @@ -1,61 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { DiscoverSetup, DiscoverStart } from '.'; - -export type Setup = jest.Mocked; -export type Start = jest.Mocked; - -const createSetupContract = (): Setup => { - const setupContract: Setup = { - docViews: { - addDocView: jest.fn(), - }, - docViewsLinks: { - addDocViewLink: jest.fn(), - }, - }; - return setupContract; -}; - -const createStartContract = (): Start => { - const startContract: Start = { - savedSearchLoader: {} as any, - urlGenerator: { - createUrl: jest.fn(), - } as any, - }; - return startContract; -}; - -export const discoverPluginMock = { - createSetupContract, - createStartContract, -}; diff --git a/src/plugins/discover_legacy/public/opensearch_dashboards_services.ts b/src/plugins/discover_legacy/public/opensearch_dashboards_services.ts deleted file mode 100644 index 8531564e0cc7..000000000000 --- a/src/plugins/discover_legacy/public/opensearch_dashboards_services.ts +++ /dev/null @@ -1,129 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import _ from 'lodash'; -import { createHashHistory } from 'history'; -import { ScopedHistory, AppMountParameters } from 'opensearch-dashboards/public'; -import { UiActionsStart } from 'src/plugins/ui_actions/public'; -import { DiscoverServices } from './build_services'; -import { createGetterSetter } from '../../opensearch_dashboards_utils/public'; -import { search } from '../../data/public'; -import { DocViewsRegistry } from './application/doc_views/doc_views_registry'; -import { DocViewsLinksRegistry } from './application/doc_views_links/doc_views_links_registry'; - -let angularModule: any = null; -let services: DiscoverServices | null = null; -let uiActions: UiActionsStart; - -/** - * set bootstrapped inner angular module - */ -export function setAngularModule(module: any) { - angularModule = module; -} - -/** - * get boostrapped inner angular module - */ -export function getAngularModule() { - return angularModule; -} - -export function getServices(): DiscoverServices { - if (!services) { - throw new Error('Discover services are not yet available'); - } - return services; -} - -export function setServices(newServices: any) { - services = newServices; -} - -export const setUiActions = (pluginUiActions: UiActionsStart) => (uiActions = pluginUiActions); -export const getUiActions = () => uiActions; - -export const [getHeaderActionMenuMounter, setHeaderActionMenuMounter] = createGetterSetter< - AppMountParameters['setHeaderActionMenu'] ->('headerActionMenuMounter'); - -export const [getUrlTracker, setUrlTracker] = createGetterSetter<{ - setTrackedUrl: (url: string) => void; - restorePreviousUrl: () => void; -}>('urlTracker'); - -export const [getDocViewsRegistry, setDocViewsRegistry] = createGetterSetter( - 'DocViewsRegistry' -); - -export const [getDocViewsLinksRegistry, setDocViewsLinksRegistry] = createGetterSetter< - DocViewsLinksRegistry ->('DocViewsLinksRegistry'); -/** - * Makes sure discover and context are using one instance of history. - */ -export const getHistory = _.once(() => createHashHistory()); - -/** - * Discover currently uses two `history` instances: one from OpenSearch Dashboards Platform and - * another from `history` package. Below function is used every time Discover - * app is loaded to synchronize both instances. - * - * This helper is temporary until https://github.com/elastic/kibana/issues/65161 is resolved. - */ -export const syncHistoryLocations = () => { - const h = getHistory(); - Object.assign(h.location, createHashHistory().location); - return h; -}; - -export const [getScopedHistory, setScopedHistory] = createGetterSetter( - 'scopedHistory' -); - -export const { getRequestInspectorStats, getResponseInspectorStats, tabifyAggResponse } = search; -export { unhashUrl, redirectWhenMissing } from '../../opensearch_dashboards_utils/public'; -export { - formatMsg, - formatStack, - subscribeWithScope, -} from '../../opensearch_dashboards_legacy/public'; - -// EXPORT types -export { - IndexPatternsContract, - IIndexPattern, - IndexPattern, - indexPatterns, - IFieldType, - ISearchSource, - OpenSearchQuerySortValue, - SortDirection, -} from '../../data/public'; diff --git a/src/plugins/discover_legacy/public/plugin.ts b/src/plugins/discover_legacy/public/plugin.ts deleted file mode 100644 index 7e855b707891..000000000000 --- a/src/plugins/discover_legacy/public/plugin.ts +++ /dev/null @@ -1,487 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import angular, { auto } from 'angular'; -import { BehaviorSubject } from 'rxjs'; -import { filter, map } from 'rxjs/operators'; - -import { - AppMountParameters, - AppUpdater, - CoreSetup, - CoreStart, - Plugin, - PluginInitializerContext, -} from 'opensearch-dashboards/public'; -import { UiActionsStart, UiActionsSetup } from 'src/plugins/ui_actions/public'; -import { EmbeddableStart, EmbeddableSetup } from 'src/plugins/embeddable/public'; -import { ChartsPluginStart } from 'src/plugins/charts/public'; -import { NavigationPublicPluginStart as NavigationStart } from 'src/plugins/navigation/public'; -import { SharePluginStart, SharePluginSetup, UrlGeneratorContract } from 'src/plugins/share/public'; -import { VisualizationsStart, VisualizationsSetup } from 'src/plugins/visualizations/public'; -import { - OpenSearchDashboardsLegacySetup, - OpenSearchDashboardsLegacyStart, -} from 'src/plugins/opensearch_dashboards_legacy/public'; -import { UrlForwardingSetup, UrlForwardingStart } from 'src/plugins/url_forwarding/public'; -import { HomePublicPluginSetup } from 'src/plugins/home/public'; -import { Start as InspectorPublicPluginStart } from 'src/plugins/inspector/public'; -import { stringify } from 'query-string'; -import rison from 'rison-node'; -import { NEW_DISCOVER_APP } from '../../discover/public'; -import { DataPublicPluginStart, DataPublicPluginSetup, opensearchFilters } from '../../data/public'; -import { SavedObjectLoader } from '../../saved_objects/public'; -import { createOsdUrlTracker, url } from '../../opensearch_dashboards_utils/public'; -import { UrlGeneratorState } from '../../share/public'; -import { DocViewInput, DocViewInputFn } from './application/doc_views/doc_views_types'; -import { DocViewLink } from './application/doc_views_links/doc_views_links_types'; -import { DocViewsRegistry } from './application/doc_views/doc_views_registry'; -import { DocViewsLinksRegistry } from './application/doc_views_links/doc_views_links_registry'; -import { DocViewTable } from './application/components/table/table'; -import { JsonCodeBlock } from './application/components/json_code_block/json_code_block'; -import { - setDocViewsRegistry, - setDocViewsLinksRegistry, - setUrlTracker, - setAngularModule, - setServices, - setHeaderActionMenuMounter, - setUiActions, - setScopedHistory, - getScopedHistory, - syncHistoryLocations, - getServices, -} from './opensearch_dashboards_services'; -import { createSavedSearchesLoader } from './saved_searches'; -import { buildServices } from './build_services'; -import { - DiscoverUrlGeneratorState, - DISCOVER_APP_URL_GENERATOR, - DiscoverUrlGenerator, -} from './url_generator'; -import { SearchEmbeddableFactory } from './application/embeddable'; -import { AppNavLinkStatus } from '../../../core/public'; -import { ViewRedirectParams } from '../../data_explorer/public'; - -declare module '../../share/public' { - export interface UrlGeneratorStateMapping { - [DISCOVER_APP_URL_GENERATOR]: UrlGeneratorState; - } -} - -/** - * @public - */ -export interface DiscoverSetup { - docViews: { - /** - * Add new doc view shown along with table view and json view in the details of each document in Discover. - * Both react and angular doc views are supported. - * @param docViewRaw - */ - addDocView(docViewRaw: DocViewInput | DocViewInputFn): void; - }; - - docViewsLinks: { - addDocViewLink(docViewLinkRaw: DocViewLink): void; - }; -} - -export interface DiscoverStart { - savedSearchLoader: SavedObjectLoader; - - /** - * `share` plugin URL generator for Discover app. Use it to generate links into - * Discover application, example: - * - * ```ts - * const url = await plugins.discover.urlGenerator.createUrl({ - * savedSearchId: '571aaf70-4c88-11e8-b3d7-01146121b73d', - * indexPatternId: 'c367b774-a4c2-11ea-bb37-0242ac130002', - * timeRange: { - * to: 'now', - * from: 'now-15m', - * mode: 'relative', - * }, - * }); - * ``` - */ - readonly urlGenerator: undefined | UrlGeneratorContract<'DISCOVER_APP_URL_GENERATOR'>; -} - -/** - * @internal - */ -export interface DiscoverSetupPlugins { - share?: SharePluginSetup; - uiActions: UiActionsSetup; - embeddable: EmbeddableSetup; - opensearchDashboardsLegacy: OpenSearchDashboardsLegacySetup; - urlForwarding: UrlForwardingSetup; - home?: HomePublicPluginSetup; - visualizations: VisualizationsSetup; - data: DataPublicPluginSetup; -} - -/** - * @internal - */ -export interface DiscoverStartPlugins { - uiActions: UiActionsStart; - embeddable: EmbeddableStart; - navigation: NavigationStart; - charts: ChartsPluginStart; - data: DataPublicPluginStart; - share?: SharePluginStart; - opensearchDashboardsLegacy: OpenSearchDashboardsLegacyStart; - urlForwarding: UrlForwardingStart; - inspector: InspectorPublicPluginStart; - visualizations: VisualizationsStart; -} - -const innerAngularName = 'app/discover'; -const embeddableAngularName = 'app/discoverEmbeddable'; - -/** - * Contains Discover, one of the oldest parts of OpenSearch Dashboards - * There are 2 kinds of Angular bootstrapped for rendering, additionally to the main Angular - * Discover provides embeddables, those contain a slimmer Angular - */ -export class DiscoverPlugin - implements Plugin { - constructor(private readonly initializerContext: PluginInitializerContext) {} - - private appStateUpdater = new BehaviorSubject(() => ({})); - private docViewsRegistry: DocViewsRegistry | null = null; - private docViewsLinksRegistry: DocViewsLinksRegistry | null = null; - private embeddableInjector: auto.IInjectorService | null = null; - private stopUrlTracking: (() => void) | undefined = undefined; - private servicesInitialized: boolean = false; - private innerAngularInitialized: boolean = false; - private urlGenerator?: DiscoverStart['urlGenerator']; - - /** - * why are those functions public? they are needed for some mocha tests - * can be removed once all is Jest - */ - public initializeInnerAngular?: () => void; - public initializeServices?: () => Promise<{ core: CoreStart; plugins: DiscoverStartPlugins }>; - - setup(core: CoreSetup, plugins: DiscoverSetupPlugins) { - const baseUrl = core.http.basePath.prepend('/app/discover'); - - if (plugins.share) { - this.urlGenerator = plugins.share.urlGenerators.registerUrlGenerator( - new DiscoverUrlGenerator({ - appBasePath: baseUrl, - useHash: core.uiSettings.get('state:storeInSessionStorage'), - }) - ); - } - - this.docViewsRegistry = new DocViewsRegistry(); - setDocViewsRegistry(this.docViewsRegistry); - this.docViewsRegistry.addDocView({ - title: i18n.translate('discover.docViews.table.tableTitle', { - defaultMessage: 'Table', - }), - order: 10, - component: DocViewTable, - }); - - this.docViewsRegistry.addDocView({ - title: i18n.translate('discover.docViews.json.jsonTitle', { - defaultMessage: 'JSON', - }), - order: 20, - component: JsonCodeBlock, - }); - - this.docViewsLinksRegistry = new DocViewsLinksRegistry(); - setDocViewsLinksRegistry(this.docViewsLinksRegistry); - - this.docViewsLinksRegistry.addDocViewLink({ - label: i18n.translate('discover.docTable.tableRow.viewSurroundingDocumentsLinkText', { - defaultMessage: 'View surrounding documents', - }), - generateCb: (renderProps: any) => { - const globalFilters: any = getServices().filterManager.getGlobalFilters(); - const appFilters: any = getServices().filterManager.getAppFilters(); - - const hash = stringify( - url.encodeQuery({ - _g: rison.encode({ - filters: globalFilters || [], - }), - _a: rison.encode({ - columns: renderProps.columns, - filters: (appFilters || []).map(opensearchFilters.disableFilter), - }), - }), - { encode: false, sort: false } - ); - - return { - url: `#/context/${encodeURIComponent(renderProps.indexPattern.id)}/${encodeURIComponent( - renderProps.hit._id - )}?${hash}`, - hide: !renderProps.indexPattern.isTimeBased(), - }; - }, - order: 1, - }); - - this.docViewsLinksRegistry.addDocViewLink({ - label: i18n.translate('discover.docTable.tableRow.viewSingleDocumentLinkText', { - defaultMessage: 'View single document', - }), - generateCb: (renderProps) => ({ - url: `#/doc/${renderProps.indexPattern.id}/${ - renderProps.hit._index - }?id=${encodeURIComponent(renderProps.hit._id)}`, - }), - order: 2, - }); - - const { - appMounted, - appUnMounted, - stop: stopUrlTracker, - setActiveUrl: setTrackedUrl, - restorePreviousUrl, - } = createOsdUrlTracker({ - // we pass getter here instead of plain `history`, - // so history is lazily created (when app is mounted) - // this prevents redundant `#` when not in discover app - getHistory: getScopedHistory, - baseUrl, - defaultSubUrl: '#/', - storageKey: `lastUrl:${core.http.basePath.get()}:discover_legacy`, - navLinkUpdater$: this.appStateUpdater, - toastNotifications: core.notifications.toasts, - stateParams: [ - { - osdUrlKey: '_g', - stateUpdate$: plugins.data.query.state$.pipe( - filter( - ({ changes }) => !!(changes.globalFilters || changes.time || changes.refreshInterval) - ), - map(({ state }) => ({ - ...state, - filters: state.filters?.filter(opensearchFilters.isFilterPinned), - })) - ), - }, - ], - }); - setUrlTracker({ setTrackedUrl, restorePreviousUrl }); - this.stopUrlTracking = () => { - stopUrlTracker(); - }; - - this.docViewsRegistry.setAngularInjectorGetter(this.getEmbeddableInjector); - core.application.register({ - id: 'discoverLegacy', - title: 'Discover Legacy', - defaultPath: '#/', - navLinkStatus: AppNavLinkStatus.hidden, - mount: async (params: AppMountParameters) => { - if (!this.initializeServices) { - throw Error('Discover plugin method initializeServices is undefined'); - } - if (!this.initializeInnerAngular) { - throw Error('Discover plugin method initializeInnerAngular is undefined'); - } - - // If a user explicitly tries to access the legacy app URL - const { - core: { - application: { navigateToApp }, - }, - } = await this.initializeServices(); - const path = window.location.hash; - - const v2Enabled = core.uiSettings.get(NEW_DISCOVER_APP); - if (v2Enabled) { - navigateToApp('discover', { - replace: true, - path, - }); - } - setScopedHistory(params.history); - setHeaderActionMenuMounter(params.setHeaderActionMenu); - syncHistoryLocations(); - appMounted(); - const { - plugins: { data: dataStart }, - } = await this.initializeServices(); - await this.initializeInnerAngular(); - - // make sure the index pattern list is up to date - await dataStart.indexPatterns.clearCache(); - const { renderApp } = await import('./application/application'); - params.element.classList.add('dscAppWrapper'); - const unmount = await renderApp(innerAngularName, params.element); - return () => { - params.element.classList.remove('dscAppWrapper'); - unmount(); - appUnMounted(); - }; - }, - }); - - plugins.urlForwarding.forwardApp('doc', 'discoverLegacy', (path) => { - return `#${path}`; - }); - plugins.urlForwarding.forwardApp('context', 'discoverLegacy', (path) => { - const urlParts = path.split('/'); - // take care of urls containing legacy url, those split in the following way - // ["", "context", indexPatternId, _type, id + params] - if (urlParts[4]) { - // remove _type part - const newPath = [...urlParts.slice(0, 3), ...urlParts.slice(4)].join('/'); - return `#${newPath}`; - } - return `#${path}`; - }); - plugins.urlForwarding.forwardApp('discover', 'discoverLegacy', (path) => { - const [, id, tail] = /discover\/([^\?]+)(.*)/.exec(path) || []; - if (!id) { - return `#${path.replace('/discover', '') || '/'}`; - } - return `#/view/${id}${tail || ''}`; - }); - - this.registerEmbeddable(core, plugins); - - return { - docViews: { - addDocView: this.docViewsRegistry.addDocView.bind(this.docViewsRegistry), - }, - docViewsLinks: { - addDocViewLink: this.docViewsLinksRegistry.addDocViewLink.bind(this.docViewsLinksRegistry), - }, - }; - } - - start(core: CoreStart, plugins: DiscoverStartPlugins) { - // we need to register the application service at setup, but to render it - // there are some start dependencies necessary, for this reason - // initializeInnerAngular + initializeServices are assigned at start and used - // when the application/embeddable is mounted - this.initializeInnerAngular = async () => { - if (this.innerAngularInitialized) { - return; - } - // this is used by application mount and tests - const { getInnerAngularModule } = await import('./get_inner_angular'); - const module = getInnerAngularModule( - innerAngularName, - core, - plugins, - this.initializerContext - ); - setAngularModule(module); - this.innerAngularInitialized = true; - }; - - setUiActions(plugins.uiActions); - - this.initializeServices = async () => { - if (this.servicesInitialized) { - return { core, plugins }; - } - const services = await buildServices( - core, - plugins, - this.initializerContext, - this.getEmbeddableInjector - ); - setServices(services); - this.servicesInitialized = true; - - return { core, plugins }; - }; - - return { - urlGenerator: this.urlGenerator, - savedSearchLoader: createSavedSearchesLoader({ - savedObjectsClient: core.savedObjects.client, - indexPatterns: plugins.data.indexPatterns, - search: plugins.data.search, - chrome: core.chrome, - overlays: core.overlays, - }), - }; - } - - stop() { - if (this.stopUrlTracking) { - this.stopUrlTracking(); - } - } - - /** - * register embeddable with a slimmer embeddable version of inner angular - */ - private registerEmbeddable(core: CoreSetup, plugins: DiscoverSetupPlugins) { - if (!this.getEmbeddableInjector) { - throw Error('Discover plugin method getEmbeddableInjector is undefined'); - } - - const getStartServices = async () => { - const [coreStart, deps] = await core.getStartServices(); - return { - executeTriggerActions: deps.uiActions.executeTriggerActions, - isEditable: () => coreStart.application.capabilities.discover.save as boolean, - }; - }; - - const factory = new SearchEmbeddableFactory(getStartServices, this.getEmbeddableInjector); - plugins.embeddable.registerEmbeddableFactory(factory.type, factory); - } - - private getEmbeddableInjector = async () => { - if (!this.embeddableInjector) { - if (!this.initializeServices) { - throw Error('Discover plugin getEmbeddableInjector: initializeServices is undefined'); - } - const { core, plugins } = await this.initializeServices(); - getServices().opensearchDashboardsLegacy.loadFontAwesome(); - const { getInnerAngularModuleEmbeddable } = await import('./get_inner_angular'); - getInnerAngularModuleEmbeddable(embeddableAngularName, core, plugins); - const mountpoint = document.createElement('div'); - this.embeddableInjector = angular.bootstrap(mountpoint, [embeddableAngularName]); - } - - return this.embeddableInjector; - }; -} diff --git a/src/plugins/discover_legacy/public/saved_searches/_saved_search.ts b/src/plugins/discover_legacy/public/saved_searches/_saved_search.ts deleted file mode 100644 index 55cd59104ecb..000000000000 --- a/src/plugins/discover_legacy/public/saved_searches/_saved_search.ts +++ /dev/null @@ -1,86 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { - createSavedObjectClass, - SavedObject, - SavedObjectOpenSearchDashboardsServices, -} from '../../../saved_objects/public'; - -export function createSavedSearchClass(services: SavedObjectOpenSearchDashboardsServices) { - const SavedObjectClass = createSavedObjectClass(services); - - class SavedSearch extends SavedObjectClass { - public static type: string = 'search'; - public static mapping = { - title: 'text', - description: 'text', - hits: 'integer', - columns: 'keyword', - sort: 'keyword', - version: 'integer', - }; - // Order these fields to the top, the rest are alphabetical - public static fieldOrder = ['title', 'description']; - public static searchSource = true; - - public id: string; - public showInRecentlyAccessed: boolean; - - constructor(id: string) { - super({ - id, - type: 'search', - mapping: { - title: 'text', - description: 'text', - hits: 'integer', - columns: 'keyword', - sort: 'keyword', - version: 'integer', - }, - searchSource: true, - defaults: { - title: '', - description: '', - columns: [], - hits: 0, - sort: [], - version: 1, - }, - }); - this.showInRecentlyAccessed = true; - this.id = id; - this.getFullPath = () => `/app/discover#/view/${String(id)}`; - } - } - - return SavedSearch as new (id: string) => SavedObject; -} diff --git a/src/plugins/discover_legacy/public/saved_searches/index.ts b/src/plugins/discover_legacy/public/saved_searches/index.ts deleted file mode 100644 index f576a9a9377a..000000000000 --- a/src/plugins/discover_legacy/public/saved_searches/index.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export { createSavedSearchesLoader } from './saved_searches'; -export { SavedSearch, SavedSearchLoader } from './types'; diff --git a/src/plugins/discover_legacy/public/saved_searches/saved_searches.ts b/src/plugins/discover_legacy/public/saved_searches/saved_searches.ts deleted file mode 100644 index dd3243568159..000000000000 --- a/src/plugins/discover_legacy/public/saved_searches/saved_searches.ts +++ /dev/null @@ -1,50 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { - SavedObjectLoader, - SavedObjectOpenSearchDashboardsServices, -} from '../../../saved_objects/public'; -import { createSavedSearchClass } from './_saved_search'; - -export function createSavedSearchesLoader(services: SavedObjectOpenSearchDashboardsServices) { - const SavedSearchClass = createSavedSearchClass(services); - const savedSearchLoader = new SavedObjectLoader(SavedSearchClass, services.savedObjectsClient); - // Customize loader properties since adding an 's' on type doesn't work for type 'search' . - savedSearchLoader.loaderProperties = { - name: 'searches', - noun: 'Saved Search', - nouns: 'saved searches', - }; - - savedSearchLoader.urlFor = (id: string) => (id ? `#/view/${encodeURIComponent(id)}` : '#/'); - - return savedSearchLoader; -} diff --git a/src/plugins/discover_legacy/public/saved_searches/types.ts b/src/plugins/discover_legacy/public/saved_searches/types.ts deleted file mode 100644 index e02fd65e6899..000000000000 --- a/src/plugins/discover_legacy/public/saved_searches/types.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { ISearchSource } from '../../../data/public'; - -export type SortOrder = [string, string]; -export interface SavedSearch { - readonly id: string; - title: string; - searchSource: ISearchSource; - description?: string; - columns: string[]; - sort: SortOrder[]; - destroy: () => void; - lastSavedTitle?: string; -} -export interface SavedSearchLoader { - get: (id: string) => Promise; - urlFor: (id: string) => string; -} diff --git a/src/plugins/discover_legacy/public/url_generator.test.ts b/src/plugins/discover_legacy/public/url_generator.test.ts deleted file mode 100644 index c352dd5133a4..000000000000 --- a/src/plugins/discover_legacy/public/url_generator.test.ts +++ /dev/null @@ -1,269 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { DiscoverUrlGenerator } from './url_generator'; -import { hashedItemStore, getStatesFromOsdUrl } from '../../opensearch_dashboards_utils/public'; -import { mockStorage } from '../../opensearch_dashboards_utils/public/storage/hashed_item_store/mock'; -import { FilterStateStore } from '../../data/common'; - -const appBasePath: string = 'xyz/app/discover'; -const indexPatternId: string = 'c367b774-a4c2-11ea-bb37-0242ac130002'; -const savedSearchId: string = '571aaf70-4c88-11e8-b3d7-01146121b73d'; - -interface SetupParams { - useHash?: boolean; -} - -const setup = async ({ useHash = false }: SetupParams = {}) => { - const generator = new DiscoverUrlGenerator({ - appBasePath, - useHash, - }); - - return { - generator, - }; -}; - -beforeEach(() => { - // @ts-ignore - hashedItemStore.storage = mockStorage; -}); - -describe('Discover url generator', () => { - test('can create a link to Discover with no state and no saved search', async () => { - const { generator } = await setup(); - const url = await generator.createUrl({}); - const { _a, _g } = getStatesFromOsdUrl(url, ['_a', '_g']); - - expect(url.startsWith(appBasePath)).toBe(true); - expect(_a).toEqual({}); - expect(_g).toEqual({}); - }); - - test('can create a link to a saved search in Discover', async () => { - const { generator } = await setup(); - const url = await generator.createUrl({ savedSearchId }); - const { _a, _g } = getStatesFromOsdUrl(url, ['_a', '_g']); - - expect(url.startsWith(`${appBasePath}#/${savedSearchId}`)).toBe(true); - expect(_a).toEqual({}); - expect(_g).toEqual({}); - }); - - test('can specify specific index pattern', async () => { - const { generator } = await setup(); - const url = await generator.createUrl({ - indexPatternId, - }); - const { _a, _g } = getStatesFromOsdUrl(url, ['_a', '_g']); - - expect(_a).toEqual({ - index: indexPatternId, - }); - expect(_g).toEqual({}); - }); - - test('can specify specific time range', async () => { - const { generator } = await setup(); - const url = await generator.createUrl({ - timeRange: { to: 'now', from: 'now-15m', mode: 'relative' }, - }); - const { _a, _g } = getStatesFromOsdUrl(url, ['_a', '_g']); - - expect(_a).toEqual({}); - expect(_g).toEqual({ - time: { - from: 'now-15m', - mode: 'relative', - to: 'now', - }, - }); - }); - - test('can specify query', async () => { - const { generator } = await setup(); - const url = await generator.createUrl({ - query: { - language: 'kuery', - query: 'foo', - }, - }); - const { _a, _g } = getStatesFromOsdUrl(url, ['_a', '_g']); - - expect(_a).toEqual({ - query: { - language: 'kuery', - query: 'foo', - }, - }); - expect(_g).toEqual({}); - }); - - test('can specify local and global filters', async () => { - const { generator } = await setup(); - const url = await generator.createUrl({ - filters: [ - { - meta: { - alias: 'foo', - disabled: false, - negate: false, - }, - $state: { - store: FilterStateStore.APP_STATE, - }, - }, - { - meta: { - alias: 'bar', - disabled: false, - negate: false, - }, - $state: { - store: FilterStateStore.GLOBAL_STATE, - }, - }, - ], - }); - const { _a, _g } = getStatesFromOsdUrl(url, ['_a', '_g']); - - expect(_a).toEqual({ - filters: [ - { - $state: { - store: 'appState', - }, - meta: { - alias: 'foo', - disabled: false, - negate: false, - }, - }, - ], - }); - expect(_g).toEqual({ - filters: [ - { - $state: { - store: 'globalState', - }, - meta: { - alias: 'bar', - disabled: false, - negate: false, - }, - }, - ], - }); - }); - - test('can set refresh interval', async () => { - const { generator } = await setup(); - const url = await generator.createUrl({ - refreshInterval: { - pause: false, - value: 666, - }, - }); - const { _a, _g } = getStatesFromOsdUrl(url, ['_a', '_g']); - - expect(_a).toEqual({}); - expect(_g).toEqual({ - refreshInterval: { - pause: false, - value: 666, - }, - }); - }); - - test('can set time range', async () => { - const { generator } = await setup(); - const url = await generator.createUrl({ - timeRange: { - from: 'now-3h', - to: 'now', - }, - }); - const { _a, _g } = getStatesFromOsdUrl(url, ['_a', '_g']); - - expect(_a).toEqual({}); - expect(_g).toEqual({ - time: { - from: 'now-3h', - to: 'now', - }, - }); - }); - - describe('useHash property', () => { - describe('when default useHash is set to false', () => { - test('when using default, sets index pattern ID in the generated URL', async () => { - const { generator } = await setup(); - const url = await generator.createUrl({ - indexPatternId, - }); - - expect(url.indexOf(indexPatternId) > -1).toBe(true); - }); - - test('when enabling useHash, does not set index pattern ID in the generated URL', async () => { - const { generator } = await setup(); - const url = await generator.createUrl({ - useHash: true, - indexPatternId, - }); - - expect(url.indexOf(indexPatternId) > -1).toBe(false); - }); - }); - - describe('when default useHash is set to true', () => { - test('when using default, does not set index pattern ID in the generated URL', async () => { - const { generator } = await setup({ useHash: true }); - const url = await generator.createUrl({ - indexPatternId, - }); - - expect(url.indexOf(indexPatternId) > -1).toBe(false); - }); - - test('when disabling useHash, sets index pattern ID in the generated URL', async () => { - const { generator } = await setup(); - const url = await generator.createUrl({ - useHash: false, - indexPatternId, - }); - - expect(url.indexOf(indexPatternId) > -1).toBe(true); - }); - }); - }); -}); diff --git a/src/plugins/discover_legacy/public/url_generator.ts b/src/plugins/discover_legacy/public/url_generator.ts deleted file mode 100644 index 25e8517c8c9d..000000000000 --- a/src/plugins/discover_legacy/public/url_generator.ts +++ /dev/null @@ -1,127 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { - TimeRange, - Filter, - Query, - opensearchFilters, - QueryState, - RefreshInterval, -} from '../../data/public'; -import { setStateToOsdUrl } from '../../opensearch_dashboards_utils/public'; -import { UrlGeneratorsDefinition } from '../../share/public'; - -export const DISCOVER_APP_URL_GENERATOR = 'DISCOVER_APP_URL_GENERATOR'; - -export interface DiscoverUrlGeneratorState { - /** - * Optionally set saved search ID. - */ - savedSearchId?: string; - - /** - * Optionally set index pattern ID. - */ - indexPatternId?: string; - - /** - * Optionally set the time range in the time picker. - */ - timeRange?: TimeRange; - - /** - * Optionally set the refresh interval. - */ - refreshInterval?: RefreshInterval; - - /** - * Optionally apply filers. - */ - filters?: Filter[]; - - /** - * Optionally set a query. NOTE: if given and used in conjunction with `dashboardId`, and the - * saved dashboard has a query saved with it, this will _replace_ that query. - */ - query?: Query; - - /** - * If not given, will use the uiSettings configuration for `storeInSessionStorage`. useHash determines - * whether to hash the data in the url to avoid url length issues. - */ - useHash?: boolean; -} - -interface Params { - appBasePath: string; - useHash: boolean; -} - -export class DiscoverUrlGenerator - implements UrlGeneratorsDefinition { - constructor(private readonly params: Params) {} - - public readonly id = DISCOVER_APP_URL_GENERATOR; - - public readonly createUrl = async ({ - filters, - indexPatternId, - query, - refreshInterval, - savedSearchId, - timeRange, - useHash = this.params.useHash, - }: DiscoverUrlGeneratorState): Promise => { - const savedSearchPath = savedSearchId ? encodeURIComponent(savedSearchId) : ''; - const appState: { - query?: Query; - filters?: Filter[]; - index?: string; - } = {}; - const queryState: QueryState = {}; - - if (query) appState.query = query; - if (filters && filters.length) - appState.filters = filters?.filter((f) => !opensearchFilters.isFilterPinned(f)); - if (indexPatternId) appState.index = indexPatternId; - - if (timeRange) queryState.time = timeRange; - if (filters && filters.length) - queryState.filters = filters?.filter((f) => opensearchFilters.isFilterPinned(f)); - if (refreshInterval) queryState.refreshInterval = refreshInterval; - - let url = `${this.params.appBasePath}#/${savedSearchPath}`; - url = setStateToOsdUrl('_g', queryState, { useHash }, url); - url = setStateToOsdUrl('_a', appState, { useHash }, url); - - return url; - }; -} diff --git a/src/plugins/expressions/public/react_expression_renderer.tsx b/src/plugins/expressions/public/react_expression_renderer.tsx index 838fa703a750..dd2d1bdccc42 100644 --- a/src/plugins/expressions/public/react_expression_renderer.tsx +++ b/src/plugins/expressions/public/react_expression_renderer.tsx @@ -34,7 +34,7 @@ import { Observable, Subscription } from 'rxjs'; import { filter } from 'rxjs/operators'; import useShallowCompareEffect from 'react-use/lib/useShallowCompareEffect'; import { EuiLoadingChart, EuiProgress } from '@elastic/eui'; -import theme from '@elastic/eui/dist/eui_theme_light.json'; +import { euiThemeVars } from '@osd/ui-shared-deps/theme'; import { IExpressionLoaderParams, ExpressionRenderError } from './types'; import { ExpressionAstExpression, IInterpreterRenderHandlers } from '../common'; import { ExpressionLoader } from './loader'; @@ -191,8 +191,9 @@ export const ReactExpressionRenderer = ({ const expressionStyles: React.CSSProperties = {}; + // TODO: refactor to SCSS instead of getting values from theme: https://github.com/opensearch-project/OpenSearch-Dashboards/issues/5661 if (padding) { - expressionStyles.padding = theme.paddingSizes[padding]; + expressionStyles.padding = euiThemeVars.paddingSizes[padding]; } return ( diff --git a/src/plugins/home/public/application/components/sample_data_set_card.js b/src/plugins/home/public/application/components/sample_data_set_card.js index 7d8b97a1c982..086484fa12c3 100644 --- a/src/plugins/home/public/application/components/sample_data_set_card.js +++ b/src/plugins/home/public/application/components/sample_data_set_card.js @@ -65,6 +65,15 @@ export class SampleDataSetCard extends React.Component { }; renderBtn = () => { + const dataSourceEnabled = this.props.isDataSourceEnabled; + const hideLocalCluster = this.props.isLocalClusterHidden; + const dataSourceId = this.props.dataSourceId; + + let buttonDisabled = false; + if (dataSourceEnabled && hideLocalCluster) { + buttonDisabled = dataSourceId === undefined; + } + switch (this.props.status) { case INSTALLED_STATUS: return ( @@ -121,6 +130,7 @@ export class SampleDataSetCard extends React.Component { ); diff --git a/src/plugins/home/public/application/components/tutorial_directory.js b/src/plugins/home/public/application/components/tutorial_directory.js index 0dcdc3ec775f..45da6b07fd9e 100644 --- a/src/plugins/home/public/application/components/tutorial_directory.js +++ b/src/plugins/home/public/application/components/tutorial_directory.js @@ -34,8 +34,6 @@ import PropTypes from 'prop-types'; import { Synopsis } from './synopsis'; import { SampleDataSetCards } from './sample_data_set_cards'; import { getServices } from '../opensearch_dashboards_services'; -// eslint-disable-next-line @osd/eslint/no-restricted-paths -import { getDataSources } from '../../../../data_source_management/public/components/utils'; import { EuiPage, @@ -48,12 +46,12 @@ import { EuiSpacer, EuiTitle, EuiPageBody, - EuiComboBox, } from '@elastic/eui'; import { getTutorials } from '../load_tutorials'; import { injectI18n, FormattedMessage } from '@osd/i18n/react'; import { i18n } from '@osd/i18n'; +import { ClusterSelector } from '../../../../data_source_management/public'; const ALL_TAB_ID = 'all'; const SAMPLE_DATA_TAB_ID = 'sampleData'; @@ -62,9 +60,6 @@ const homeTitle = i18n.translate('home.breadcrumbs.homeTitle', { defaultMessage: const addDataTitle = i18n.translate('home.breadcrumbs.addDataTitle', { defaultMessage: 'Add data', }); -const localCluster = i18n.translate('home.dataSource.localCluster', { - defaultMessage: 'Local Cluster', -}); class TutorialDirectoryUi extends React.Component { constructor(props) { @@ -88,7 +83,7 @@ class TutorialDirectoryUi extends React.Component { tutorialCards: [], notices: getServices().tutorialService.getDirectoryNotices(), isDataSourceEnabled: !!getServices().dataSource, - selectedOption: [{ label: localCluster }], + isLocalClusterHidden: getServices().dataSource?.hideLocalCluster ?? false, }; } @@ -160,31 +155,6 @@ class TutorialDirectoryUi extends React.Component { return a.name.toLowerCase().localeCompare(b.name.toLowerCase()); }); - if (this.state.isDataSourceEnabled) { - getDataSources(getServices().savedObjectsClient) - .then((fetchedDataSources) => { - if (fetchedDataSources?.length) { - const dataSourceOptions = fetchedDataSources.map((dataSource) => ({ - id: dataSource.id, - label: dataSource.title, - })); - - dataSourceOptions.push({ label: localCluster }); - this.setState({ - // eslint-disable-line react/no-did-mount-set-state - dataSources: dataSourceOptions, - }); - } - }) - .catch(() => { - getServices().toastNotifications.addWarning( - i18n.translate('home.dataSource.fetchDataSourceError', { - defaultMessage: 'Unable to fetch existing data sources', - }) - ); - }); - } - this.setState({ // eslint-disable-line react/no-did-mount-set-state tutorialCards: tutorialCards, @@ -197,12 +167,6 @@ class TutorialDirectoryUi extends React.Component { }); }; - onSelectedDataSourceChange = (e) => { - this.setState({ selectedOption: e }); - const dataSourceId = e[0] ? e[0].id : undefined; - this.setState({ selectedDataSourceId: dataSourceId }); - }; - renderTabs = () => { return this.tabs.map((tab, index) => ( ); } @@ -255,25 +220,22 @@ class TutorialDirectoryUi extends React.Component { ); }; + onSelectedDataSourceChange = (e) => { + const dataSourceId = e[0] ? e[0].id : undefined; + this.setState({ selectedDataSourceId: dataSourceId }); + }; + renderDataSourceSelector = () => { - const { isDataSourceEnabled, dataSources, selectedOption } = this.state; + const { isDataSourceEnabled, isLocalClusterHidden } = this.state; return isDataSourceEnabled ? ( -
- +
) : null; diff --git a/src/plugins/home/public/assets/tutorials/logos/activemq.svg b/src/plugins/home/public/assets/tutorials/logos/activemq.svg deleted file mode 100644 index 20694ba6e62c..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/activemq.svg +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/plugins/home/public/assets/tutorials/logos/barracuda.svg b/src/plugins/home/public/assets/tutorials/logos/barracuda.svg deleted file mode 100644 index 555cdd6f8a32..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/barracuda.svg +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - - - - diff --git a/src/plugins/home/public/assets/tutorials/logos/checkpoint.svg b/src/plugins/home/public/assets/tutorials/logos/checkpoint.svg deleted file mode 100644 index e71866e78c29..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/checkpoint.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/plugins/home/public/assets/tutorials/logos/cisco.svg b/src/plugins/home/public/assets/tutorials/logos/cisco.svg deleted file mode 100644 index 20ebebf19741..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/cisco.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/plugins/home/public/assets/tutorials/logos/cockroachdb.svg b/src/plugins/home/public/assets/tutorials/logos/cockroachdb.svg deleted file mode 100644 index 72f0958f5282..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/cockroachdb.svg +++ /dev/null @@ -1,666 +0,0 @@ - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - diff --git a/src/plugins/home/public/assets/tutorials/logos/consul.svg b/src/plugins/home/public/assets/tutorials/logos/consul.svg deleted file mode 100644 index 28bbadd24c8a..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/consul.svg +++ /dev/null @@ -1 +0,0 @@ -Asset 1 \ No newline at end of file diff --git a/src/plugins/home/public/assets/tutorials/logos/coredns.svg b/src/plugins/home/public/assets/tutorials/logos/coredns.svg deleted file mode 100644 index 863a81e0f40e..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/coredns.svg +++ /dev/null @@ -1 +0,0 @@ -CoreDNS_Colour_Icon diff --git a/src/plugins/home/public/assets/tutorials/logos/couchdb.svg b/src/plugins/home/public/assets/tutorials/logos/couchdb.svg deleted file mode 100644 index 2512c2eb2a01..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/couchdb.svg +++ /dev/null @@ -1,86 +0,0 @@ - - - -image/svg+xml \ No newline at end of file diff --git a/src/plugins/home/public/assets/tutorials/logos/crowdstrike.svg b/src/plugins/home/public/assets/tutorials/logos/crowdstrike.svg deleted file mode 100644 index 1b2195a2244f..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/crowdstrike.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/plugins/home/public/assets/tutorials/logos/cylance.svg b/src/plugins/home/public/assets/tutorials/logos/cylance.svg deleted file mode 100644 index ccd6004d19e7..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/cylance.svg +++ /dev/null @@ -1,82 +0,0 @@ - - - - -Cylance_BB_Logo_RGB_Vert_Black - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/plugins/home/public/assets/tutorials/logos/envoyproxy.svg b/src/plugins/home/public/assets/tutorials/logos/envoyproxy.svg deleted file mode 100644 index 45c995b19603..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/envoyproxy.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/plugins/home/public/assets/tutorials/logos/f5.svg b/src/plugins/home/public/assets/tutorials/logos/f5.svg deleted file mode 100644 index d985bde96291..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/f5.svg +++ /dev/null @@ -1 +0,0 @@ -Asset 1 \ No newline at end of file diff --git a/src/plugins/home/public/assets/tutorials/logos/fortinet.svg b/src/plugins/home/public/assets/tutorials/logos/fortinet.svg deleted file mode 100644 index d6a8448f320b..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/fortinet.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/src/plugins/home/public/assets/tutorials/logos/ibmmq.svg b/src/plugins/home/public/assets/tutorials/logos/ibmmq.svg deleted file mode 100644 index e474d93359be..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/ibmmq.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/plugins/home/public/assets/tutorials/logos/icinga.svg b/src/plugins/home/public/assets/tutorials/logos/icinga.svg deleted file mode 100644 index 88161d61ca75..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/icinga.svg +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/plugins/home/public/assets/tutorials/logos/iis.svg b/src/plugins/home/public/assets/tutorials/logos/iis.svg deleted file mode 100644 index 99964448d33c..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/iis.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/plugins/home/public/assets/tutorials/logos/infoblox.svg b/src/plugins/home/public/assets/tutorials/logos/infoblox.svg deleted file mode 100644 index 57b4d23b1681..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/infoblox.svg +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/plugins/home/public/assets/tutorials/logos/juniper.svg b/src/plugins/home/public/assets/tutorials/logos/juniper.svg deleted file mode 100644 index 8802414a5aaf..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/juniper.svg +++ /dev/null @@ -1,72 +0,0 @@ - -image/svg+xml \ No newline at end of file diff --git a/src/plugins/home/public/assets/tutorials/logos/linux.svg b/src/plugins/home/public/assets/tutorials/logos/linux.svg deleted file mode 100644 index c0a92e0c0f40..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/linux.svg +++ /dev/null @@ -1,1532 +0,0 @@ - - - - Tux - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - Tux - 20 June 2012 - - - Garrett LeSage - - - - - - Larry Ewing, the creator of the original Tux graphic - - - - - tux - Linux - penguin - logo - - - - - Larry Ewing, Garrett LeSage - - - https://github.com/garrett/Tux - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/plugins/home/public/assets/tutorials/logos/microsoft.svg b/src/plugins/home/public/assets/tutorials/logos/microsoft.svg deleted file mode 100644 index 5334aa7ca686..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/microsoft.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/plugins/home/public/assets/tutorials/logos/misp.svg b/src/plugins/home/public/assets/tutorials/logos/misp.svg deleted file mode 100644 index 1cc61eda0b60..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/misp.svg +++ /dev/null @@ -1,167 +0,0 @@ - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - Threat Sharing - - - - - - - - - - - - diff --git a/src/plugins/home/public/assets/tutorials/logos/mssql.svg b/src/plugins/home/public/assets/tutorials/logos/mssql.svg deleted file mode 100644 index 738507c5809c..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/mssql.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/plugins/home/public/assets/tutorials/logos/munin.svg b/src/plugins/home/public/assets/tutorials/logos/munin.svg deleted file mode 100644 index b35a39498c9e..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/munin.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/plugins/home/public/assets/tutorials/logos/nats.svg b/src/plugins/home/public/assets/tutorials/logos/nats.svg deleted file mode 100644 index 5a1d6e9a52f1..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/nats.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/plugins/home/public/assets/tutorials/logos/netscout.svg b/src/plugins/home/public/assets/tutorials/logos/netscout.svg deleted file mode 100644 index cbd25cd92594..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/netscout.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/src/plugins/home/public/assets/tutorials/logos/o365.svg b/src/plugins/home/public/assets/tutorials/logos/o365.svg deleted file mode 100644 index 3763f267ffc7..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/o365.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - logo-integrations-Desktop HD Copy 2 - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/plugins/home/public/assets/tutorials/logos/okta.svg b/src/plugins/home/public/assets/tutorials/logos/okta.svg deleted file mode 100644 index d806cb7dc645..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/okta.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/plugins/home/public/assets/tutorials/logos/openmetrics.svg b/src/plugins/home/public/assets/tutorials/logos/openmetrics.svg deleted file mode 100644 index feccb88a3f34..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/openmetrics.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/plugins/home/public/assets/tutorials/logos/oracle.svg b/src/plugins/home/public/assets/tutorials/logos/oracle.svg deleted file mode 100644 index 78db57f91481..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/oracle.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/plugins/home/public/assets/tutorials/logos/osquery.svg b/src/plugins/home/public/assets/tutorials/logos/osquery.svg deleted file mode 100755 index c2bf733d3593..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/osquery.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/plugins/home/public/assets/tutorials/logos/paloalto.svg b/src/plugins/home/public/assets/tutorials/logos/paloalto.svg deleted file mode 100644 index 8c8e71ae0d9f..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/paloalto.svg +++ /dev/null @@ -1,29 +0,0 @@ - - - logo-integrations-Desktop HD - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/plugins/home/public/assets/tutorials/logos/rabbitmq.svg b/src/plugins/home/public/assets/tutorials/logos/rabbitmq.svg deleted file mode 100644 index dabd2a5744cb..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/rabbitmq.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/plugins/home/public/assets/tutorials/logos/radware.svg b/src/plugins/home/public/assets/tutorials/logos/radware.svg deleted file mode 100644 index 6252efef7762..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/radware.svg +++ /dev/null @@ -1,66 +0,0 @@ - -image/svg+xml \ No newline at end of file diff --git a/src/plugins/home/public/assets/tutorials/logos/sonicwall.svg b/src/plugins/home/public/assets/tutorials/logos/sonicwall.svg deleted file mode 100644 index fb1aded68a29..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/sonicwall.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/plugins/home/public/assets/tutorials/logos/sophos.svg b/src/plugins/home/public/assets/tutorials/logos/sophos.svg deleted file mode 100644 index 1f2cfc3a7f03..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/sophos.svg +++ /dev/null @@ -1,69 +0,0 @@ - - - -image/svg+xml \ No newline at end of file diff --git a/src/plugins/home/public/assets/tutorials/logos/stan.svg b/src/plugins/home/public/assets/tutorials/logos/stan.svg deleted file mode 100644 index 5a1d6e9a52f1..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/stan.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/plugins/home/public/assets/tutorials/logos/statsd.svg b/src/plugins/home/public/assets/tutorials/logos/statsd.svg deleted file mode 100644 index f4458439fceb..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/statsd.svg +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - diff --git a/src/plugins/home/public/assets/tutorials/logos/suricata.svg b/src/plugins/home/public/assets/tutorials/logos/suricata.svg deleted file mode 100644 index 06e627a7e4ba..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/suricata.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/plugins/home/public/assets/tutorials/logos/system.svg b/src/plugins/home/public/assets/tutorials/logos/system.svg deleted file mode 100644 index 0aba96275e24..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/system.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/plugins/home/public/assets/tutorials/logos/tomcat.svg b/src/plugins/home/public/assets/tutorials/logos/tomcat.svg deleted file mode 100644 index 26ad7b6d33ad..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/tomcat.svg +++ /dev/null @@ -1,107 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/plugins/home/public/assets/tutorials/logos/traefik.svg b/src/plugins/home/public/assets/tutorials/logos/traefik.svg deleted file mode 100644 index 8ee3448f5626..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/traefik.svg +++ /dev/null @@ -1,342 +0,0 @@ - - - -image/svg+xml \ No newline at end of file diff --git a/src/plugins/home/public/assets/tutorials/logos/ubiquiti.svg b/src/plugins/home/public/assets/tutorials/logos/ubiquiti.svg deleted file mode 100644 index 99a911e32863..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/ubiquiti.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/plugins/home/public/assets/tutorials/logos/uwsgi.svg b/src/plugins/home/public/assets/tutorials/logos/uwsgi.svg deleted file mode 100644 index 43818ab211ee..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/uwsgi.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/plugins/home/public/assets/tutorials/logos/vsphere.svg b/src/plugins/home/public/assets/tutorials/logos/vsphere.svg deleted file mode 100644 index 999fabc613c4..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/vsphere.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/plugins/home/public/assets/tutorials/logos/zeek.svg b/src/plugins/home/public/assets/tutorials/logos/zeek.svg deleted file mode 100644 index 0e346f98c723..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/zeek.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/plugins/home/public/assets/tutorials/logos/zookeeper.svg b/src/plugins/home/public/assets/tutorials/logos/zookeeper.svg deleted file mode 100644 index 80db65f670d1..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/zookeeper.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/plugins/home/public/assets/tutorials/logos/zscaler.svg b/src/plugins/home/public/assets/tutorials/logos/zscaler.svg deleted file mode 100644 index b8a21a2fa6ee..000000000000 --- a/src/plugins/home/public/assets/tutorials/logos/zscaler.svg +++ /dev/null @@ -1 +0,0 @@ -Zscaler-Logo-TM-Blue-RGB-May2019 \ No newline at end of file diff --git a/src/plugins/home/server/services/sample_data/data_sets/logs/saved_objects.ts b/src/plugins/home/server/services/sample_data/data_sets/logs/saved_objects.ts index ae881354e333..943c3599a2cb 100644 --- a/src/plugins/home/server/services/sample_data/data_sets/logs/saved_objects.ts +++ b/src/plugins/home/server/services/sample_data/data_sets/logs/saved_objects.ts @@ -269,21 +269,6 @@ export const getSavedObjects = (): SavedObject[] => [ }, references: [], }, - { - id: '90943e30-9a47-11e8-b64d-95841ca0b247', - type: 'index-pattern', - updated_at: '2018-08-29T13:22:17.617Z', - version: '1', - migrationVersion: {}, - attributes: { - title: 'opensearch_dashboards_sample_data_logs', - timeFieldName: 'timestamp', - fields: - '[{"name":"@timestamp","type":"date","esTypes":["date"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"_id","type":"string","esTypes":["_id"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":false},{"name":"_index","type":"string","esTypes":["_index"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":false},{"name":"_score","type":"number","count":0,"scripted":false,"searchable":false,"aggregatable":false,"readFromDocValues":false},{"name":"_source","type":"_source","esTypes":["_source"],"count":0,"scripted":false,"searchable":false,"aggregatable":false,"readFromDocValues":false},{"name":"_type","type":"string","esTypes":["_type"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":false},{"name":"agent","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"agent.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent": "agent"}}},{"name":"bytes","type":"number","esTypes":["long"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"clientip","type":"ip","esTypes":["ip"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"event.dataset","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"extension","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"extension.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent": "extension"}}},{"name":"geo.coordinates","type":"geo_point","esTypes":["geo_point"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"geo.dest","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"geo.src","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"geo.srcdest","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"host","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"host.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent": "host"}}},{"name":"index","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"index.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent": "index"}}},{"name":"ip","type":"ip","esTypes":["ip"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"machine.os","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"machine.os.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent": "machine.os"}}},{"name":"machine.ram","type":"number","esTypes":["long"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"memory","type":"number","esTypes":["double"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"message","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"message.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent": "message"}}},{"name":"phpmemory","type":"number","esTypes":["long"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"referer","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"request","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"request.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent": "request"}}},{"name":"response","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"response.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent": "response"}}},{"name":"tags","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"tags.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent": "tags"}}},{"name":"timestamp","type":"date","esTypes":["date"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"url","type":"string","esTypes":["text"],"count":0,"scripted":false,"searchable":true,"aggregatable":false,"readFromDocValues":false},{"name":"url.keyword","type":"string","esTypes":["keyword"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true,"subType":{"multi":{"parent": "url"}}},{"name":"utc_time","type":"date","esTypes":["date"],"count":0,"scripted":false,"searchable":true,"aggregatable":true,"readFromDocValues":true},{"name":"hour_of_day","type":"number","count":0,"scripted":true,"script":"doc[\'timestamp\'].value.getHour()","lang":"painless","searchable":true,"aggregatable":true,"readFromDocValues":false}]', - fieldFormatMap: '{"hour_of_day":{}}', - }, - references: [], - }, { id: 'edf84fe0-e1a0-11e7-b6d5-4dc382ef7f5b', type: 'dashboard', diff --git a/src/plugins/home/server/services/tutorials/lib/tutorial_schema.ts b/src/plugins/home/server/services/tutorials/lib/tutorial_schema.ts index c8be95ca5a10..a0af70efd135 100644 --- a/src/plugins/home/server/services/tutorials/lib/tutorial_schema.ts +++ b/src/plugins/home/server/services/tutorials/lib/tutorial_schema.ts @@ -124,7 +124,7 @@ export const tutorialSchema = { moduleName: Joi.string(), isBeta: Joi.boolean().default(false), shortDescription: Joi.string().required(), - euiIconType: Joi.string(), // EUI icon type string, one of https://elastic.github.io/eui/#/icons + euiIconType: Joi.string(), // OUI icon type string, one of https://oui.opensearch.org/#/display/icons longDescription: Joi.string().required(), completionTimeMinutes: Joi.number().integer(), previewImagePath: Joi.string(), diff --git a/src/plugins/home/server/services/tutorials/lib/tutorials_registry_types.ts b/src/plugins/home/server/services/tutorials/lib/tutorials_registry_types.ts index 508025202c52..4bde63a42f68 100644 --- a/src/plugins/home/server/services/tutorials/lib/tutorials_registry_types.ts +++ b/src/plugins/home/server/services/tutorials/lib/tutorials_registry_types.ts @@ -95,7 +95,7 @@ export interface TutorialSchema { moduleName?: string; isBeta?: boolean; shortDescription: string; - euiIconType?: IconType; // EUI icon type string, one of https://elastic.github.io/eui/#/display/icons; + euiIconType?: IconType; // OUI icon type string, one of https://oui.opensearch.org/#/display/icons; longDescription: string; completionTimeMinutes?: number; previewImagePath?: string; diff --git a/src/plugins/home/server/services/tutorials/tutorials_registry.ts b/src/plugins/home/server/services/tutorials/tutorials_registry.ts index 39c14060e30a..11bb460b9012 100644 --- a/src/plugins/home/server/services/tutorials/tutorials_registry.ts +++ b/src/plugins/home/server/services/tutorials/tutorials_registry.ts @@ -36,7 +36,6 @@ import { ScopedTutorialContextFactory, } from './lib/tutorials_registry_types'; import { tutorialSchema } from './lib/tutorial_schema'; -import { builtInTutorials } from '../../tutorials/register'; export class TutorialsRegistry { private tutorialProviders: TutorialProvider[] = []; // pre-register all the tutorials we know we want in here diff --git a/src/plugins/home/server/tutorials/activemq_logs/index.ts b/src/plugins/home/server/tutorials/activemq_logs/index.ts deleted file mode 100644 index 151ea6d5180c..000000000000 --- a/src/plugins/home/server/tutorials/activemq_logs/index.ts +++ /dev/null @@ -1,77 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function activemqLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'activemq'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'activemqLogs', - name: i18n.translate('home.tutorials.activemqLogs.nameTitle', { - defaultMessage: 'ActiveMQ logs', - }), - moduleName, - category: TutorialsCategory.LOGGING, - shortDescription: i18n.translate('home.tutorials.activemqLogs.shortDescription', { - defaultMessage: 'Collect ActiveMQ logs with Filebeat.', - }), - longDescription: i18n.translate('home.tutorials.activemqLogs.longDescription', { - defaultMessage: 'Collect ActiveMQ logs with Filebeat. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-activemq.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/activemq.svg', - artifacts: { - dashboards: [ - { - id: 'ffe86390-145f-11ea-8fd8-030a13064883', - linkLabel: i18n.translate('home.tutorials.activemqLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'ActiveMQ Audit Events', - }), - isOverview: false, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-activemq.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/activemq_metrics/index.ts b/src/plugins/home/server/tutorials/activemq_metrics/index.ts deleted file mode 100644 index bf0551b9594b..000000000000 --- a/src/plugins/home/server/tutorials/activemq_metrics/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialsCategory, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function activemqMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'activemq'; - return { - id: 'activemqMetrics', - name: i18n.translate('home.tutorials.activemqMetrics.nameTitle', { - defaultMessage: 'ActiveMQ metrics', - }), - moduleName, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.activemqMetrics.shortDescription', { - defaultMessage: 'Fetch monitoring metrics from ActiveMQ instances.', - }), - longDescription: i18n.translate('home.tutorials.activemqMetrics.longDescription', { - defaultMessage: - 'The `activemq` Metricbeat module fetches monitoring metrics from ActiveMQ instances \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-activemq.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/activemq.svg', - isBeta: true, - artifacts: { - application: { - label: i18n.translate('home.tutorials.activemqMetrics.artifacts.application.label', { - defaultMessage: 'Discover', - }), - path: '/app/discover#/', - }, - dashboards: [], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-activemq.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/aerospike_metrics/index.ts b/src/plugins/home/server/tutorials/aerospike_metrics/index.ts deleted file mode 100644 index bad23346c7d6..000000000000 --- a/src/plugins/home/server/tutorials/aerospike_metrics/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialsCategory, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function aerospikeMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'aerospike'; - return { - id: 'aerospikeMetrics', - name: i18n.translate('home.tutorials.aerospikeMetrics.nameTitle', { - defaultMessage: 'Aerospike metrics', - }), - moduleName, - isBeta: false, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.aerospikeMetrics.shortDescription', { - defaultMessage: 'Fetch internal metrics from the Aerospike server.', - }), - longDescription: i18n.translate('home.tutorials.aerospikeMetrics.longDescription', { - defaultMessage: - 'The `aerospike` Metricbeat module fetches internal metrics from Aerospike. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-aerospike.html', - }, - }), - euiIconType: 'logoAerospike', - artifacts: { - application: { - label: i18n.translate('home.tutorials.aerospikeMetrics.artifacts.application.label', { - defaultMessage: 'Discover', - }), - path: '/app/discover#/', - }, - dashboards: [], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-aerospike.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/apache_logs/index.ts b/src/plugins/home/server/tutorials/apache_logs/index.ts deleted file mode 100644 index b9e6f435ff61..000000000000 --- a/src/plugins/home/server/tutorials/apache_logs/index.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function apacheLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'apache'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'apacheLogs', - name: i18n.translate('home.tutorials.apacheLogs.nameTitle', { - defaultMessage: 'Apache logs', - }), - moduleName, - category: TutorialsCategory.LOGGING, - shortDescription: i18n.translate('home.tutorials.apacheLogs.shortDescription', { - defaultMessage: 'Collect and parse access and error logs created by the Apache HTTP server.', - }), - longDescription: i18n.translate('home.tutorials.apacheLogs.longDescription', { - defaultMessage: - 'The apache Filebeat module parses access and error logs created by the Apache HTTP server. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-apache.html', - }, - }), - euiIconType: 'logoApache', - artifacts: { - dashboards: [ - { - id: 'Filebeat-Apache-Dashboard-ecs', - linkLabel: i18n.translate('home.tutorials.apacheLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'Apache logs dashboard', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-apache.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/apache_metrics/index.ts b/src/plugins/home/server/tutorials/apache_metrics/index.ts deleted file mode 100644 index c384777debb9..000000000000 --- a/src/plugins/home/server/tutorials/apache_metrics/index.ts +++ /dev/null @@ -1,77 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function apacheMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'apache'; - return { - id: 'apacheMetrics', - name: i18n.translate('home.tutorials.apacheMetrics.nameTitle', { - defaultMessage: 'Apache metrics', - }), - moduleName, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.apacheMetrics.shortDescription', { - defaultMessage: 'Fetch internal metrics from the Apache 2 HTTP server.', - }), - longDescription: i18n.translate('home.tutorials.apacheMetrics.longDescription', { - defaultMessage: - 'The `apache` Metricbeat module fetches internal metrics from the Apache 2 HTTP server. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-apache.html', - }, - }), - euiIconType: 'logoApache', - artifacts: { - dashboards: [ - { - id: 'Metricbeat-Apache-HTTPD-server-status-ecs', - linkLabel: i18n.translate('home.tutorials.apacheMetrics.artifacts.dashboards.linkLabel', { - defaultMessage: 'Apache metrics dashboard', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-apache.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/auditbeat/index.ts b/src/plugins/home/server/tutorials/auditbeat/index.ts deleted file mode 100644 index bc52bc14ece6..000000000000 --- a/src/plugins/home/server/tutorials/auditbeat/index.ts +++ /dev/null @@ -1,77 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/auditbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function auditbeatSpecProvider(context: TutorialContext): TutorialSchema { - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - const moduleName = 'auditbeat'; - return { - id: 'auditbeat', - name: i18n.translate('home.tutorials.auditbeat.nameTitle', { - defaultMessage: 'Auditbeat', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.auditbeat.shortDescription', { - defaultMessage: 'Collect audit data from your hosts.', - }), - longDescription: i18n.translate('home.tutorials.auditbeat.longDescription', { - defaultMessage: - 'Use Auditbeat to collect auditing data from your hosts. These include \ -processes, users, logins, sockets information, file accesses, and more. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.auditbeat}/auditbeat-overview.html', - }, - }), - euiIconType: 'securityAnalyticsApp', - artifacts: { - dashboards: [], - application: { - path: '/app/security', - label: i18n.translate('home.tutorials.auditbeat.artifacts.dashboards.linkLabel', { - defaultMessage: 'Security App', - }), - }, - exportedFields: { - documentationUrl: '{config.docs.beats.auditbeat}/exported-fields.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/auditd_logs/index.ts b/src/plugins/home/server/tutorials/auditd_logs/index.ts deleted file mode 100644 index d262aee0106f..000000000000 --- a/src/plugins/home/server/tutorials/auditd_logs/index.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function auditdLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'auditd'; - const platforms = ['DEB', 'RPM'] as const; - return { - id: 'auditdLogs', - name: i18n.translate('home.tutorials.auditdLogs.nameTitle', { - defaultMessage: 'Auditd logs', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.auditdLogs.shortDescription', { - defaultMessage: 'Collect logs from the Linux auditd daemon.', - }), - longDescription: i18n.translate('home.tutorials.auditdLogs.longDescription', { - defaultMessage: - 'The module collects and parses logs from the audit daemon ( `auditd`). \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-auditd.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/linux.svg', - artifacts: { - dashboards: [ - { - id: 'dfbb49f0-0a0f-11e7-8a62-2d05eaaac5cb-ecs', - linkLabel: i18n.translate('home.tutorials.auditdLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'Audit Events', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-auditd.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/aws_logs/index.ts b/src/plugins/home/server/tutorials/aws_logs/index.ts deleted file mode 100644 index 2d987bf98290..000000000000 --- a/src/plugins/home/server/tutorials/aws_logs/index.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function awsLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'aws'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'awsLogs', - name: i18n.translate('home.tutorials.awsLogs.nameTitle', { - defaultMessage: 'AWS S3 based logs', - }), - moduleName, - category: TutorialsCategory.LOGGING, - shortDescription: i18n.translate('home.tutorials.awsLogs.shortDescription', { - defaultMessage: 'Collect AWS logs from S3 bucket with Filebeat.', - }), - longDescription: i18n.translate('home.tutorials.awsLogs.longDescription', { - defaultMessage: - 'Collect AWS logs by exporting them to an S3 bucket which is configured with SQS notification. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-aws.html', - }, - }), - euiIconType: 'logoAWS', - artifacts: { - dashboards: [ - { - id: '4746e000-bacd-11e9-9f70-1f7bda85a5eb', - linkLabel: i18n.translate('home.tutorials.awsLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'AWS S3 server access log dashboard', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-aws.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/aws_metrics/index.ts b/src/plugins/home/server/tutorials/aws_metrics/index.ts deleted file mode 100644 index f239f224eb5a..000000000000 --- a/src/plugins/home/server/tutorials/aws_metrics/index.ts +++ /dev/null @@ -1,79 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function awsMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'aws'; - return { - id: 'awsMetrics', - name: i18n.translate('home.tutorials.awsMetrics.nameTitle', { - defaultMessage: 'AWS metrics', - }), - moduleName, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.awsMetrics.shortDescription', { - defaultMessage: - 'Fetch monitoring metrics for EC2 instances from the AWS APIs and Cloudwatch.', - }), - longDescription: i18n.translate('home.tutorials.awsMetrics.longDescription', { - defaultMessage: - 'The `aws` Metricbeat module fetches monitoring metrics from the AWS APIs and Cloudwatch. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-aws.html', - }, - }), - euiIconType: 'logoAWS', - isBeta: false, - artifacts: { - dashboards: [ - { - id: 'c5846400-f7fb-11e8-af03-c999c9dea608-ecs', - linkLabel: i18n.translate('home.tutorials.awsMetrics.artifacts.dashboards.linkLabel', { - defaultMessage: 'AWS metrics dashboard', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-aws.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/azure_logs/index.ts b/src/plugins/home/server/tutorials/azure_logs/index.ts deleted file mode 100644 index 08d089aa6422..000000000000 --- a/src/plugins/home/server/tutorials/azure_logs/index.ts +++ /dev/null @@ -1,79 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function azureLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'azure'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'azureLogs', - name: i18n.translate('home.tutorials.azureLogs.nameTitle', { - defaultMessage: 'Azure logs', - }), - moduleName, - isBeta: true, - category: TutorialsCategory.LOGGING, - shortDescription: i18n.translate('home.tutorials.azureLogs.shortDescription', { - defaultMessage: 'Collects Azure activity and audit related logs.', - }), - longDescription: i18n.translate('home.tutorials.azureLogs.longDescription', { - defaultMessage: - 'The `azure` Filebeat module collects Azure activity and audit related logs. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-azure.html', - }, - }), - euiIconType: 'logoAzure', - artifacts: { - dashboards: [ - { - id: '41e84340-ec20-11e9-90ec-112a988266d5', - linkLabel: i18n.translate('home.tutorials.azureLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'Azure logs dashboard', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-azure.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/azure_metrics/index.ts b/src/plugins/home/server/tutorials/azure_metrics/index.ts deleted file mode 100644 index b93f697a331a..000000000000 --- a/src/plugins/home/server/tutorials/azure_metrics/index.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function azureMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'azure'; - return { - id: 'azureMetrics', - name: i18n.translate('home.tutorials.azureMetrics.nameTitle', { - defaultMessage: 'Azure metrics', - }), - moduleName, - isBeta: false, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.azureMetrics.shortDescription', { - defaultMessage: 'Fetch Azure Monitor metrics.', - }), - longDescription: i18n.translate('home.tutorials.azureMetrics.longDescription', { - defaultMessage: - 'The `azure` Metricbeat module fetches Azure Monitor metrics. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-azure.html', - }, - }), - euiIconType: 'logoAzure', - artifacts: { - dashboards: [ - { - id: 'eb3f05f0-ea9a-11e9-90ec-112a988266d5', - linkLabel: i18n.translate('home.tutorials.azureMetrics.artifacts.dashboards.linkLabel', { - defaultMessage: 'Azure metrics dashboard', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-azure.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/barracuda_logs/index.ts b/src/plugins/home/server/tutorials/barracuda_logs/index.ts deleted file mode 100644 index 5abdc41023ce..000000000000 --- a/src/plugins/home/server/tutorials/barracuda_logs/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function barracudaLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'barracuda'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'barracudaLogs', - name: i18n.translate('home.tutorials.barracudaLogs.nameTitle', { - defaultMessage: 'Barracuda logs', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.barracudaLogs.shortDescription', { - defaultMessage: 'Collect Barracuda Web Application Firewall logs over syslog or from a file.', - }), - longDescription: i18n.translate('home.tutorials.barracudaLogs.longDescription', { - defaultMessage: - 'This is a module for receiving Barracuda Web Application Firewall logs over Syslog or a file. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-barracuda.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/barracuda.svg', - artifacts: { - dashboards: [], - application: { - path: '/app/security', - label: i18n.translate('home.tutorials.barracudaLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'Security App', - }), - }, - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-barracuda.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/bluecoat_logs/index.ts b/src/plugins/home/server/tutorials/bluecoat_logs/index.ts deleted file mode 100644 index 44e2bcc8a759..000000000000 --- a/src/plugins/home/server/tutorials/bluecoat_logs/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function bluecoatLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'bluecoat'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'bluecoatLogs', - name: i18n.translate('home.tutorials.bluecoatLogs.nameTitle', { - defaultMessage: 'Bluecoat logs', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.bluecoatLogs.shortDescription', { - defaultMessage: 'Collect Blue Coat Director logs over syslog or from a file.', - }), - longDescription: i18n.translate('home.tutorials.bluecoatLogs.longDescription', { - defaultMessage: - 'This is a module for receiving Blue Coat Director logs over Syslog or a file. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-bluecoat.html', - }, - }), - euiIconType: 'logoLogging', - artifacts: { - dashboards: [], - application: { - path: '/app/security', - label: i18n.translate('home.tutorials.bluecoatLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'Security App', - }), - }, - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-bluecoat.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/cef_logs/index.ts b/src/plugins/home/server/tutorials/cef_logs/index.ts deleted file mode 100644 index 4972b3a5ccb2..000000000000 --- a/src/plugins/home/server/tutorials/cef_logs/index.ts +++ /dev/null @@ -1,83 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function cefLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'cef'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'cefLogs', - name: i18n.translate('home.tutorials.cefLogs.nameTitle', { - defaultMessage: 'CEF logs', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.cefLogs.shortDescription', { - defaultMessage: 'Collect Common Event Format (CEF) log data over syslog.', - }), - longDescription: i18n.translate('home.tutorials.cefLogs.longDescription', { - defaultMessage: - 'This is a module for receiving Common Event Format (CEF) data over \ - Syslog. When messages are received over the syslog protocol the syslog \ - input will parse the header and set the timestamp value. Then the \ - processor is applied to parse the CEF encoded data. The decoded data \ - is written into a `cef` object field. Lastly any OpenSearch Common Schema \ - (OCS) fields that can be populated with the CEF data are populated. \ - [Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-cef.html', - }, - }), - euiIconType: 'logoLogging', - artifacts: { - dashboards: [ - { - id: 'dd0bc9af-2e89-4150-9b42-62517ea56b71', - linkLabel: i18n.translate('home.tutorials.cefLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'CEF Network Overview Dashboard', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-cef.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/ceph_metrics/index.ts b/src/plugins/home/server/tutorials/ceph_metrics/index.ts deleted file mode 100644 index c23e03196c2c..000000000000 --- a/src/plugins/home/server/tutorials/ceph_metrics/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function cephMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'ceph'; - return { - id: 'cephMetrics', - name: i18n.translate('home.tutorials.cephMetrics.nameTitle', { - defaultMessage: 'Ceph metrics', - }), - moduleName, - isBeta: false, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.cephMetrics.shortDescription', { - defaultMessage: 'Fetch internal metrics from the Ceph server.', - }), - longDescription: i18n.translate('home.tutorials.cephMetrics.longDescription', { - defaultMessage: - 'The `ceph` Metricbeat module fetches internal metrics from Ceph. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-ceph.html', - }, - }), - euiIconType: 'logoCeph', - artifacts: { - application: { - label: i18n.translate('home.tutorials.cephMetrics.artifacts.application.label', { - defaultMessage: 'Discover', - }), - path: '/app/discover#/', - }, - dashboards: [], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-ceph.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/checkpoint_logs/index.ts b/src/plugins/home/server/tutorials/checkpoint_logs/index.ts deleted file mode 100644 index 72198e709502..000000000000 --- a/src/plugins/home/server/tutorials/checkpoint_logs/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function checkpointLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'checkpoint'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'checkpointLogs', - name: i18n.translate('home.tutorials.checkpointLogs.nameTitle', { - defaultMessage: 'Check Point logs', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.checkpointLogs.shortDescription', { - defaultMessage: 'Collect Check Point firewall logs.', - }), - longDescription: i18n.translate('home.tutorials.checkpointLogs.longDescription', { - defaultMessage: - 'This is a module for Check Point firewall logs. It supports logs from the Log Exporter in the Syslog format. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-checkpoint.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/checkpoint.svg', - artifacts: { - dashboards: [], - application: { - path: '/app/security', - label: i18n.translate('home.tutorials.checkpointLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'Security App', - }), - }, - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-checkpoint.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/cisco_logs/index.ts b/src/plugins/home/server/tutorials/cisco_logs/index.ts deleted file mode 100644 index aa0796a1ffbc..000000000000 --- a/src/plugins/home/server/tutorials/cisco_logs/index.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function ciscoLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'cisco'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'ciscoLogs', - name: i18n.translate('home.tutorials.ciscoLogs.nameTitle', { - defaultMessage: 'Cisco logs', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.ciscoLogs.shortDescription', { - defaultMessage: 'Collect Cisco network device logs over syslog or from a file.', - }), - longDescription: i18n.translate('home.tutorials.ciscoLogs.longDescription', { - defaultMessage: - 'This is a module for Cisco network devices logs (ASA, FTD, IOS, Nexus). It includes the following filesets for receiving logs over syslog or read from a file: \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-cisco.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/cisco.svg', - artifacts: { - dashboards: [ - { - id: 'a555b160-4987-11e9-b8ce-ed898b5ef295', - linkLabel: i18n.translate('home.tutorials.ciscoLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'ASA Firewall Dashboard', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-cisco.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/cloudwatch_logs/index.ts b/src/plugins/home/server/tutorials/cloudwatch_logs/index.ts deleted file mode 100644 index 744943fbb376..000000000000 --- a/src/plugins/home/server/tutorials/cloudwatch_logs/index.ts +++ /dev/null @@ -1,73 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/functionbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function cloudwatchLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'aws'; - return { - id: 'cloudwatchLogs', - name: i18n.translate('home.tutorials.cloudwatchLogs.nameTitle', { - defaultMessage: 'AWS Cloudwatch logs', - }), - moduleName, - category: TutorialsCategory.LOGGING, - shortDescription: i18n.translate('home.tutorials.cloudwatchLogs.shortDescription', { - defaultMessage: 'Collect Cloudwatch logs with Functionbeat.', - }), - longDescription: i18n.translate('home.tutorials.cloudwatchLogs.longDescription', { - defaultMessage: - 'Collect Cloudwatch logs by deploying Functionbeat to run as \ - an AWS Lambda function. \ - [Learn more]({learnMoreLink}).', - values: { - learnMoreLink: - '{config.docs.beats.functionbeat}/functionbeat-installation-configuration.html', - }, - }), - euiIconType: 'logoAWS', - artifacts: { - dashboards: [ - // TODO - ], - exportedFields: { - documentationUrl: '{config.docs.beats.functionbeat}/exported-fields.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions([], context), - }; -} diff --git a/src/plugins/home/server/tutorials/cockroachdb_metrics/index.ts b/src/plugins/home/server/tutorials/cockroachdb_metrics/index.ts deleted file mode 100644 index 882fbf83091c..000000000000 --- a/src/plugins/home/server/tutorials/cockroachdb_metrics/index.ts +++ /dev/null @@ -1,80 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function cockroachdbMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'cockroachdb'; - return { - id: 'cockroachdbMetrics', - name: i18n.translate('home.tutorials.cockroachdbMetrics.nameTitle', { - defaultMessage: 'CockroachDB metrics', - }), - moduleName, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.cockroachdbMetrics.shortDescription', { - defaultMessage: 'Fetch monitoring metrics from the CockroachDB server.', - }), - longDescription: i18n.translate('home.tutorials.cockroachdbMetrics.longDescription', { - defaultMessage: - 'The `cockroachdb` Metricbeat module fetches monitoring metrics from CockroachDB. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-cockroachdb.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/cockroachdb.svg', - artifacts: { - dashboards: [ - { - id: 'e3ba0c30-9766-11e9-9eea-6f554992ec1f', - linkLabel: i18n.translate( - 'home.tutorials.cockroachdbMetrics.artifacts.dashboards.linkLabel', - { - defaultMessage: 'CockroachDB metrics dashboard', - } - ), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-cockroachdb.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/consul_metrics/index.ts b/src/plugins/home/server/tutorials/consul_metrics/index.ts deleted file mode 100644 index 5b7b57fea5b1..000000000000 --- a/src/plugins/home/server/tutorials/consul_metrics/index.ts +++ /dev/null @@ -1,77 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function consulMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'consul'; - return { - id: 'consulMetrics', - name: i18n.translate('home.tutorials.consulMetrics.nameTitle', { - defaultMessage: 'Consul metrics', - }), - moduleName, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.consulMetrics.shortDescription', { - defaultMessage: 'Fetch monitoring metrics from the Consul server.', - }), - longDescription: i18n.translate('home.tutorials.consulMetrics.longDescription', { - defaultMessage: - 'The `consul` Metricbeat module fetches monitoring metrics from Consul. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-consul.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/consul.svg', - artifacts: { - dashboards: [ - { - id: '496910f0-b952-11e9-a579-f5c0a5d81340', - linkLabel: i18n.translate('home.tutorials.consulMetrics.artifacts.dashboards.linkLabel', { - defaultMessage: 'Consul metrics dashboard', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-consul.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/coredns_logs/index.ts b/src/plugins/home/server/tutorials/coredns_logs/index.ts deleted file mode 100644 index 63ec23201d88..000000000000 --- a/src/plugins/home/server/tutorials/coredns_logs/index.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function corednsLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'coredns'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'corednsLogs', - name: i18n.translate('home.tutorials.corednsLogs.nameTitle', { - defaultMessage: 'CoreDNS logs', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.corednsLogs.shortDescription', { - defaultMessage: 'Collect CoreDNS logs.', - }), - longDescription: i18n.translate('home.tutorials.corednsLogs.longDescription', { - defaultMessage: - 'This is a filebeat module for CoreDNS. It supports both standalone CoreDNS deployment and CoreDNS deployment in Kubernetes. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-coredns.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/coredns.svg', - artifacts: { - dashboards: [ - { - id: '53aa1f70-443e-11e9-8548-ab7fbe04f038', - linkLabel: i18n.translate('home.tutorials.corednsLogs.artifacts.dashboards.linkLabel', { - defaultMessage: '[Filebeat CoreDNS] Overview', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-coredns.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/coredns_metrics/index.ts b/src/plugins/home/server/tutorials/coredns_metrics/index.ts deleted file mode 100644 index 4fc64a172bc0..000000000000 --- a/src/plugins/home/server/tutorials/coredns_metrics/index.ts +++ /dev/null @@ -1,75 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function corednsMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'coredns'; - return { - id: 'corednsMetrics', - name: i18n.translate('home.tutorials.corednsMetrics.nameTitle', { - defaultMessage: 'CoreDNS metrics', - }), - moduleName, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.corednsMetrics.shortDescription', { - defaultMessage: 'Fetch monitoring metrics from the CoreDNS server.', - }), - longDescription: i18n.translate('home.tutorials.corednsMetrics.longDescription', { - defaultMessage: - 'The `coredns` Metricbeat module fetches monitoring metrics from CoreDNS. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-coredns.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/coredns.svg', - artifacts: { - application: { - label: i18n.translate('home.tutorials.corednsMetrics.artifacts.application.label', { - defaultMessage: 'Discover', - }), - path: '/app/discover#/', - }, - dashboards: [], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-coredns.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/couchbase_metrics/index.ts b/src/plugins/home/server/tutorials/couchbase_metrics/index.ts deleted file mode 100644 index cdbd82e0d498..000000000000 --- a/src/plugins/home/server/tutorials/couchbase_metrics/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function couchbaseMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'couchbase'; - return { - id: 'couchbaseMetrics', - name: i18n.translate('home.tutorials.couchbaseMetrics.nameTitle', { - defaultMessage: 'Couchbase metrics', - }), - moduleName, - isBeta: false, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.couchbaseMetrics.shortDescription', { - defaultMessage: 'Fetch internal metrics from Couchbase.', - }), - longDescription: i18n.translate('home.tutorials.couchbaseMetrics.longDescription', { - defaultMessage: - 'The `couchbase` Metricbeat module fetches internal metrics from Couchbase. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-couchbase.html', - }, - }), - euiIconType: 'logoCouchbase', - artifacts: { - application: { - label: i18n.translate('home.tutorials.couchbaseMetrics.artifacts.application.label', { - defaultMessage: 'Discover', - }), - path: '/app/discover#/', - }, - dashboards: [], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-couchbase.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/couchdb_metrics/index.ts b/src/plugins/home/server/tutorials/couchdb_metrics/index.ts deleted file mode 100644 index cb5aca214228..000000000000 --- a/src/plugins/home/server/tutorials/couchdb_metrics/index.ts +++ /dev/null @@ -1,80 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function couchdbMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'couchdb'; - return { - id: 'couchdbMetrics', - name: i18n.translate('home.tutorials.couchdbMetrics.nameTitle', { - defaultMessage: 'CouchDB metrics', - }), - moduleName, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.couchdbMetrics.shortDescription', { - defaultMessage: 'Fetch monitoring metrics from the CouchdB server.', - }), - longDescription: i18n.translate('home.tutorials.couchdbMetrics.longDescription', { - defaultMessage: - 'The `couchdb` Metricbeat module fetches monitoring metrics from CouchDB. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-couchdb.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/couchdb.svg', - artifacts: { - dashboards: [ - { - id: '496910f0-b952-11e9-a579-f5c0a5d81340', - linkLabel: i18n.translate( - 'home.tutorials.couchdbMetrics.artifacts.dashboards.linkLabel', - { - defaultMessage: 'CouchDB metrics dashboard', - } - ), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-couchdb.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/crowdstrike_logs/index.ts b/src/plugins/home/server/tutorials/crowdstrike_logs/index.ts deleted file mode 100644 index 949c2e1283dd..000000000000 --- a/src/plugins/home/server/tutorials/crowdstrike_logs/index.ts +++ /dev/null @@ -1,79 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function crowdstrikeLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'crowdstrike'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'crowdstrikeLogs', - name: i18n.translate('home.tutorials.crowdstrikeLogs.nameTitle', { - defaultMessage: 'CrowdStrike logs', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.crowdstrikeLogs.shortDescription', { - defaultMessage: 'Collect CrowdStrike Falcon logs using the Falcon SIEM Connector.', - }), - longDescription: i18n.translate('home.tutorials.crowdstrikeLogs.longDescription', { - defaultMessage: - 'This is the Filebeat module for CrowdStrike Falcon using the Falcon \ - [SIEM Connector](https://www.crowdstrike.com/blog/tech-center/integrate-with-your-siem). \ - This module collects this data, converts it to OCS, and ingests it to view in the SIEM. \ - By default, the Falcon SIEM connector outputs JSON formatted Falcon Streaming API event data. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-crowdstrike.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/crowdstrike.svg', - artifacts: { - dashboards: [], - application: { - path: '/app/security', - label: i18n.translate('home.tutorials.crowdstrikeLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'Security App', - }), - }, - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-crowdstrike.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/cylance_logs/index.ts b/src/plugins/home/server/tutorials/cylance_logs/index.ts deleted file mode 100644 index 20f271253918..000000000000 --- a/src/plugins/home/server/tutorials/cylance_logs/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function cylanceLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'cylance'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'cylanceLogs', - name: i18n.translate('home.tutorials.cylanceLogs.nameTitle', { - defaultMessage: 'CylancePROTECT logs', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.cylanceLogs.shortDescription', { - defaultMessage: 'Collect CylancePROTECT logs over syslog or from a file.', - }), - longDescription: i18n.translate('home.tutorials.cylanceLogs.longDescription', { - defaultMessage: - 'This is a module for receiving CylancePROTECT logs over Syslog or a file. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-cylance.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/cylance.svg', - artifacts: { - dashboards: [], - application: { - path: '/app/security', - label: i18n.translate('home.tutorials.cylanceLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'Security App', - }), - }, - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-cylance.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/docker_metrics/index.ts b/src/plugins/home/server/tutorials/docker_metrics/index.ts deleted file mode 100644 index 9a396b81e34f..000000000000 --- a/src/plugins/home/server/tutorials/docker_metrics/index.ts +++ /dev/null @@ -1,77 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function dockerMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'docker'; - return { - id: 'dockerMetrics', - name: i18n.translate('home.tutorials.dockerMetrics.nameTitle', { - defaultMessage: 'Docker metrics', - }), - moduleName, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.dockerMetrics.shortDescription', { - defaultMessage: 'Fetch metrics about your Docker containers.', - }), - longDescription: i18n.translate('home.tutorials.dockerMetrics.longDescription', { - defaultMessage: - 'The `docker` Metricbeat module fetches metrics from the Docker server. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-docker.html', - }, - }), - euiIconType: 'logoDocker', - artifacts: { - dashboards: [ - { - id: 'AV4REOpp5NkDleZmzKkE-ecs', - linkLabel: i18n.translate('home.tutorials.dockerMetrics.artifacts.dashboards.linkLabel', { - defaultMessage: 'Docker metrics dashboard', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-docker.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/dropwizard_metrics/index.ts b/src/plugins/home/server/tutorials/dropwizard_metrics/index.ts deleted file mode 100644 index e279176e8ff9..000000000000 --- a/src/plugins/home/server/tutorials/dropwizard_metrics/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function dropwizardMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'dropwizard'; - return { - id: 'dropwizardMetrics', - name: i18n.translate('home.tutorials.dropwizardMetrics.nameTitle', { - defaultMessage: 'Dropwizard metrics', - }), - moduleName, - isBeta: false, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.dropwizardMetrics.shortDescription', { - defaultMessage: 'Fetch internal metrics from Dropwizard Java application.', - }), - longDescription: i18n.translate('home.tutorials.dropwizardMetrics.longDescription', { - defaultMessage: - 'The `dropwizard` Metricbeat module fetches internal metrics from Dropwizard Java Application. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-dropwizard.html', - }, - }), - euiIconType: 'logoDropwizard', - artifacts: { - application: { - label: i18n.translate('home.tutorials.dropwizardMetrics.artifacts.application.label', { - defaultMessage: 'Discover', - }), - path: '/app/discover#/', - }, - dashboards: [], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-dropwizard.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/envoyproxy_logs/index.ts b/src/plugins/home/server/tutorials/envoyproxy_logs/index.ts deleted file mode 100644 index d18cb1425116..000000000000 --- a/src/plugins/home/server/tutorials/envoyproxy_logs/index.ts +++ /dev/null @@ -1,81 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function envoyproxyLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'envoyproxy'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'envoyproxyLogs', - name: i18n.translate('home.tutorials.envoyproxyLogs.nameTitle', { - defaultMessage: 'Envoy Proxy logs', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.envoyproxyLogs.shortDescription', { - defaultMessage: 'Collect Envoy Proxy logs.', - }), - longDescription: i18n.translate('home.tutorials.envoyproxyLogs.longDescription', { - defaultMessage: - 'This is a Filebeat module for Envoy proxy access log ( https://www.envoyproxy.io/docs/envoy/v1.10.0/configuration/access_log). It supports both standalone deployment and Envoy proxy deployment in Kubernetes. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-envoyproxy.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/envoyproxy.svg', - artifacts: { - dashboards: [ - { - id: '0c610510-5cbd-11e9-8477-077ec9664dbd', - linkLabel: i18n.translate( - 'home.tutorials.envoyproxyLogs.artifacts.dashboards.linkLabel', - { - defaultMessage: 'Envoy Proxy Overview', - } - ), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-envoyproxy.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/envoyproxy_metrics/index.ts b/src/plugins/home/server/tutorials/envoyproxy_metrics/index.ts deleted file mode 100644 index 2ab1b7533ecc..000000000000 --- a/src/plugins/home/server/tutorials/envoyproxy_metrics/index.ts +++ /dev/null @@ -1,69 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function envoyproxyMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'envoyproxy'; - return { - id: 'envoyproxyMetrics', - name: i18n.translate('home.tutorials.envoyproxyMetrics.nameTitle', { - defaultMessage: 'Envoy Proxy metrics', - }), - moduleName, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.envoyproxyMetrics.shortDescription', { - defaultMessage: 'Fetch monitoring metrics from Envoy Proxy.', - }), - longDescription: i18n.translate('home.tutorials.envoyproxyMetrics.longDescription', { - defaultMessage: - 'The `envoyproxy` Metricbeat module fetches monitoring metrics from Envoy Proxy. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-envoyproxy.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/envoyproxy.svg', - artifacts: { - dashboards: [], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-envoyproxy.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/etcd_metrics/index.ts b/src/plugins/home/server/tutorials/etcd_metrics/index.ts deleted file mode 100644 index 31bcb5369323..000000000000 --- a/src/plugins/home/server/tutorials/etcd_metrics/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function etcdMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'etcd'; - return { - id: 'etcdMetrics', - name: i18n.translate('home.tutorials.etcdMetrics.nameTitle', { - defaultMessage: 'Etcd metrics', - }), - moduleName, - isBeta: false, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.etcdMetrics.shortDescription', { - defaultMessage: 'Fetch internal metrics from the Etcd server.', - }), - longDescription: i18n.translate('home.tutorials.etcdMetrics.longDescription', { - defaultMessage: - 'The `etcd` Metricbeat module fetches internal metrics from Etcd. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-etcd.html', - }, - }), - euiIconType: 'logoEtcd', - artifacts: { - application: { - label: i18n.translate('home.tutorials.etcdMetrics.artifacts.application.label', { - defaultMessage: 'Discover', - }), - path: '/app/discover#/', - }, - dashboards: [], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-etcd.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/f5_logs/index.ts b/src/plugins/home/server/tutorials/f5_logs/index.ts deleted file mode 100644 index b8391545eb64..000000000000 --- a/src/plugins/home/server/tutorials/f5_logs/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function f5LogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'f5'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'f5Logs', - name: i18n.translate('home.tutorials.f5Logs.nameTitle', { - defaultMessage: 'F5 logs', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.f5Logs.shortDescription', { - defaultMessage: 'Collect F5 Big-IP Access Policy Manager logs over syslog or from a file.', - }), - longDescription: i18n.translate('home.tutorials.f5Logs.longDescription', { - defaultMessage: - 'This is a module for receiving Big-IP Access Policy Manager logs over Syslog or a file. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-f5.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/f5.svg', - artifacts: { - dashboards: [], - application: { - path: '/app/security', - label: i18n.translate('home.tutorials.f5Logs.artifacts.dashboards.linkLabel', { - defaultMessage: 'Security App', - }), - }, - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-f5.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/fortinet_logs/index.ts b/src/plugins/home/server/tutorials/fortinet_logs/index.ts deleted file mode 100644 index 6622f07fa777..000000000000 --- a/src/plugins/home/server/tutorials/fortinet_logs/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function fortinetLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'fortinet'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'fortinetLogs', - name: i18n.translate('home.tutorials.fortinetLogs.nameTitle', { - defaultMessage: 'Fortinet logs', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.fortinetLogs.shortDescription', { - defaultMessage: 'Collect Fortinet FortiOS logs over syslog.', - }), - longDescription: i18n.translate('home.tutorials.fortinetLogs.longDescription', { - defaultMessage: - 'This is a module for Fortinet FortiOS logs sent in the syslog format. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-fortinet.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/fortinet.svg', - artifacts: { - dashboards: [], - application: { - path: '/app/security', - label: i18n.translate('home.tutorials.fortinetLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'Security App', - }), - }, - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-fortinet.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/golang_metrics/index.ts b/src/plugins/home/server/tutorials/golang_metrics/index.ts deleted file mode 100644 index 1f20d2f48bba..000000000000 --- a/src/plugins/home/server/tutorials/golang_metrics/index.ts +++ /dev/null @@ -1,79 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function golangMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'golang'; - return { - id: moduleName + 'Metrics', - name: i18n.translate('home.tutorials.golangMetrics.nameTitle', { - defaultMessage: 'Golang metrics', - }), - moduleName, - isBeta: true, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.golangMetrics.shortDescription', { - defaultMessage: 'Fetch internal metrics from a Golang app.', - }), - longDescription: i18n.translate('home.tutorials.golangMetrics.longDescription', { - defaultMessage: - 'The `{moduleName}` Metricbeat module fetches internal metrics from a Golang app. \ -[Learn more]({learnMoreLink}).', - values: { - moduleName, - learnMoreLink: `{config.docs.beats.metricbeat}/metricbeat-module-${moduleName}.html`, - }, - }), - euiIconType: 'logoGolang', - artifacts: { - dashboards: [ - { - id: 'f2dc7320-f519-11e6-a3c9-9d1f7c42b045-ecs', - linkLabel: i18n.translate('home.tutorials.golangMetrics.artifacts.dashboards.linkLabel', { - defaultMessage: 'Golang metrics dashboard', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-' + moduleName + '.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/googlecloud_logs/index.ts b/src/plugins/home/server/tutorials/googlecloud_logs/index.ts deleted file mode 100644 index 6118c215ea94..000000000000 --- a/src/plugins/home/server/tutorials/googlecloud_logs/index.ts +++ /dev/null @@ -1,83 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function googlecloudLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'googlecloud'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'googlecloudLogs', - name: i18n.translate('home.tutorials.googlecloudLogs.nameTitle', { - defaultMessage: 'Google Cloud logs', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.googlecloudLogs.shortDescription', { - defaultMessage: 'Collect Google Cloud audit, firewall, and VPC flow logs.', - }), - longDescription: i18n.translate('home.tutorials.googlecloudLogs.longDescription', { - defaultMessage: - 'This is a module for Google Cloud logs. It supports reading audit, VPC flow, \ - and firewall logs that have been exported from Stackdriver to a Google Pub/Sub \ - topic sink. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-googlecloud.html', - }, - }), - euiIconType: 'logoGoogleG', - artifacts: { - dashboards: [ - { - id: '6576c480-73a2-11ea-a345-f985c61fe654', - linkLabel: i18n.translate( - 'home.tutorials.googlecloudLogs.artifacts.dashboards.linkLabel', - { - defaultMessage: 'Audit Logs Dashbaord', - } - ), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-googlecloud.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/googlecloud_metrics/index.ts b/src/plugins/home/server/tutorials/googlecloud_metrics/index.ts deleted file mode 100644 index b19fdb456f9b..000000000000 --- a/src/plugins/home/server/tutorials/googlecloud_metrics/index.ts +++ /dev/null @@ -1,82 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function googlecloudMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'googlecloud'; - return { - id: 'googlecloudMetrics', - name: i18n.translate('home.tutorials.googlecloudMetrics.nameTitle', { - defaultMessage: 'Google Cloud metrics', - }), - moduleName, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.googlecloudMetrics.shortDescription', { - defaultMessage: - 'Fetch monitoring metrics from Google Cloud Platform using Stackdriver Monitoring API.', - }), - longDescription: i18n.translate('home.tutorials.googlecloudMetrics.longDescription', { - defaultMessage: - 'The `googlecloud` Metricbeat module fetches monitoring metrics from Google Cloud Platform using Stackdriver Monitoring API. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-googlecloud.html', - }, - }), - euiIconType: 'logoGCP', - isBeta: false, - artifacts: { - dashboards: [ - { - id: 'f40ee870-5e4a-11ea-a4f6-717338406083', - linkLabel: i18n.translate( - 'home.tutorials.googlecloudMetrics.artifacts.dashboards.linkLabel', - { - defaultMessage: 'Google Cloud metrics dashboard', - } - ), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-googlecloud.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/gsuite_logs/index.ts b/src/plugins/home/server/tutorials/gsuite_logs/index.ts deleted file mode 100644 index b2dc118cc60a..000000000000 --- a/src/plugins/home/server/tutorials/gsuite_logs/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function gsuiteLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'gsuite'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'gsuiteLogs', - name: i18n.translate('home.tutorials.gsuiteLogs.nameTitle', { - defaultMessage: 'GSuite logs', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.gsuiteLogs.shortDescription', { - defaultMessage: 'Collect GSuite activity reports.', - }), - longDescription: i18n.translate('home.tutorials.gsuiteLogs.longDescription', { - defaultMessage: - 'This is a module for ingesting data from the different GSuite audit reports APIs. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-gsuite.html', - }, - }), - euiIconType: 'logoGoogleG', - artifacts: { - dashboards: [], - application: { - path: '/app/security', - label: i18n.translate('home.tutorials.gsuiteLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'Security App', - }), - }, - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-gsuite.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/haproxy_logs/index.ts b/src/plugins/home/server/tutorials/haproxy_logs/index.ts deleted file mode 100644 index 0eb3b84291ff..000000000000 --- a/src/plugins/home/server/tutorials/haproxy_logs/index.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function haproxyLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'haproxy'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'haproxyLogs', - name: i18n.translate('home.tutorials.haproxyLogs.nameTitle', { - defaultMessage: 'HAProxy logs', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.haproxyLogs.shortDescription', { - defaultMessage: 'Collect HAProxy logs.', - }), - longDescription: i18n.translate('home.tutorials.haproxyLogs.longDescription', { - defaultMessage: - 'The module collects and parses logs from a ( `haproxy`) process. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-haproxy.html', - }, - }), - euiIconType: 'logoHAproxy', - artifacts: { - dashboards: [ - { - id: '3560d580-aa34-11e8-9c06-877f0445e3e0-ecs', - linkLabel: i18n.translate('home.tutorials.haproxyLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'HAProxy Overview', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-haproxy.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/haproxy_metrics/index.ts b/src/plugins/home/server/tutorials/haproxy_metrics/index.ts deleted file mode 100644 index be275726877a..000000000000 --- a/src/plugins/home/server/tutorials/haproxy_metrics/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function haproxyMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'haproxy'; - return { - id: 'haproxyMetrics', - name: i18n.translate('home.tutorials.haproxyMetrics.nameTitle', { - defaultMessage: 'HAProxy metrics', - }), - moduleName, - isBeta: false, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.haproxyMetrics.shortDescription', { - defaultMessage: 'Fetch internal metrics from the HAProxy server.', - }), - longDescription: i18n.translate('home.tutorials.haproxyMetrics.longDescription', { - defaultMessage: - 'The `haproxy` Metricbeat module fetches internal metrics from HAProxy. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-haproxy.html', - }, - }), - euiIconType: 'logoHAproxy', - artifacts: { - application: { - label: i18n.translate('home.tutorials.haproxyMetrics.artifacts.application.label', { - defaultMessage: 'Discover', - }), - path: '/app/discover#/', - }, - dashboards: [], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-haproxy.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/ibmmq_logs/index.ts b/src/plugins/home/server/tutorials/ibmmq_logs/index.ts deleted file mode 100644 index 388a5dd3463c..000000000000 --- a/src/plugins/home/server/tutorials/ibmmq_logs/index.ts +++ /dev/null @@ -1,77 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function ibmmqLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'ibmmq'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'ibmmqLogs', - name: i18n.translate('home.tutorials.ibmmqLogs.nameTitle', { - defaultMessage: 'IBM MQ logs', - }), - moduleName, - category: TutorialsCategory.LOGGING, - shortDescription: i18n.translate('home.tutorials.ibmmqLogs.shortDescription', { - defaultMessage: 'Collect IBM MQ logs with Filebeat.', - }), - longDescription: i18n.translate('home.tutorials.ibmmqLogs.longDescription', { - defaultMessage: 'Collect IBM MQ logs with Filebeat. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-ibmmq.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/ibmmq.svg', - artifacts: { - dashboards: [ - { - id: 'ba1d8830-7c7b-11e9-9645-e37efaf5baff', - linkLabel: i18n.translate('home.tutorials.ibmmqLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'IBM MQ Events', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-ibmmq.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/ibmmq_metrics/index.ts b/src/plugins/home/server/tutorials/ibmmq_metrics/index.ts deleted file mode 100644 index b3c7643c68f0..000000000000 --- a/src/plugins/home/server/tutorials/ibmmq_metrics/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function ibmmqMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'ibmmq'; - return { - id: 'ibmmqMetrics', - name: i18n.translate('home.tutorials.ibmmqMetrics.nameTitle', { - defaultMessage: 'IBM MQ metrics', - }), - moduleName, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.ibmmqMetrics.shortDescription', { - defaultMessage: 'Fetch monitoring metrics from IBM MQ instances.', - }), - longDescription: i18n.translate('home.tutorials.ibmmqMetrics.longDescription', { - defaultMessage: - 'The `ibmmq` Metricbeat module fetches monitoring metrics from IBM MQ instances \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-ibmmq.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/ibmmq.svg', - isBeta: true, - artifacts: { - application: { - label: i18n.translate('home.tutorials.ibmmqMetrics.artifacts.application.label', { - defaultMessage: 'Discover', - }), - path: '/app/discover#/', - }, - dashboards: [], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-ibmmq.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/icinga_logs/index.ts b/src/plugins/home/server/tutorials/icinga_logs/index.ts deleted file mode 100644 index c33ea03e4c01..000000000000 --- a/src/plugins/home/server/tutorials/icinga_logs/index.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function icingaLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'icinga'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'icingaLogs', - name: i18n.translate('home.tutorials.icingaLogs.nameTitle', { - defaultMessage: 'Icinga logs', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.icingaLogs.shortDescription', { - defaultMessage: 'Collect Icinga main, debug, and startup logs.', - }), - longDescription: i18n.translate('home.tutorials.icingaLogs.longDescription', { - defaultMessage: - 'The module parses the main, debug, and startup logs of [Icinga](https://www.icinga.com/products/icinga-2/). \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-icinga.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/icinga.svg', - artifacts: { - dashboards: [ - { - id: 'f693d260-2417-11e7-a83b-d5f4cebac9ff-ecs', - linkLabel: i18n.translate('home.tutorials.icingaLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'Icinga Main Log', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-icinga.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/iis_logs/index.ts b/src/plugins/home/server/tutorials/iis_logs/index.ts deleted file mode 100644 index 169fc85653e8..000000000000 --- a/src/plugins/home/server/tutorials/iis_logs/index.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function iisLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'iis'; - const platforms = ['WINDOWS'] as const; - return { - id: 'iisLogs', - name: i18n.translate('home.tutorials.iisLogs.nameTitle', { - defaultMessage: 'IIS logs', - }), - moduleName, - category: TutorialsCategory.LOGGING, - shortDescription: i18n.translate('home.tutorials.iisLogs.shortDescription', { - defaultMessage: 'Collect and parse access and error logs created by the IIS HTTP server.', - }), - longDescription: i18n.translate('home.tutorials.iisLogs.longDescription', { - defaultMessage: - 'The `iis` Filebeat module parses access and error logs created by the IIS HTTP server. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-iis.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/iis.svg', - artifacts: { - dashboards: [ - { - id: '4278ad30-fe16-11e7-a3b0-d13028918f9f-ecs', - linkLabel: i18n.translate('home.tutorials.iisLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'IIS logs dashboard', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-iis.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/iis_metrics/index.ts b/src/plugins/home/server/tutorials/iis_metrics/index.ts deleted file mode 100644 index d47933fa32a9..000000000000 --- a/src/plugins/home/server/tutorials/iis_metrics/index.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function iisMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'iis'; - return { - id: 'iisMetrics', - name: i18n.translate('home.tutorials.iisMetrics.nameTitle', { - defaultMessage: 'IIS Metrics', - }), - moduleName, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.iisMetrics.shortDescription', { - defaultMessage: 'Collect IIS server related metrics.', - }), - longDescription: i18n.translate('home.tutorials.iisMetrics.longDescription', { - defaultMessage: - 'The `iis` Metricbeat module collects metrics from IIS server and the application pools and websites running. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-iis.html', - }, - }), - isBeta: true, - euiIconType: '/plugins/home/assets/tutorials/logos/iis.svg', - artifacts: { - dashboards: [ - { - id: 'ebc23240-8572-11ea-91bc-ab084c7ec0e7', - linkLabel: i18n.translate('home.tutorials.iisMetrics.artifacts.dashboards.linkLabel', { - defaultMessage: 'IIS metrics dashboard', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-iis.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/imperva_logs/index.ts b/src/plugins/home/server/tutorials/imperva_logs/index.ts deleted file mode 100644 index 32249c202d0d..000000000000 --- a/src/plugins/home/server/tutorials/imperva_logs/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function impervaLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'imperva'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'impervaLogs', - name: i18n.translate('home.tutorials.impervaLogs.nameTitle', { - defaultMessage: 'Imperva logs', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.impervaLogs.shortDescription', { - defaultMessage: 'Collect Imperva SecureSphere logs over syslog or from a file.', - }), - longDescription: i18n.translate('home.tutorials.impervaLogs.longDescription', { - defaultMessage: - 'This is a module for receiving Imperva SecureSphere logs over Syslog or a file. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-imperva.html', - }, - }), - euiIconType: 'logoLogging', - artifacts: { - dashboards: [], - application: { - path: '/app/security', - label: i18n.translate('home.tutorials.impervaLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'Security App', - }), - }, - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-imperva.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/infoblox_logs/index.ts b/src/plugins/home/server/tutorials/infoblox_logs/index.ts deleted file mode 100644 index 63980113d8e8..000000000000 --- a/src/plugins/home/server/tutorials/infoblox_logs/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function infobloxLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'infoblox'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'infobloxLogs', - name: i18n.translate('home.tutorials.infobloxLogs.nameTitle', { - defaultMessage: 'Infoblox logs', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.infobloxLogs.shortDescription', { - defaultMessage: 'Collect Infoblox NIOS logs over syslog or from a file.', - }), - longDescription: i18n.translate('home.tutorials.infobloxLogs.longDescription', { - defaultMessage: - 'This is a module for receiving Infoblox NIOS logs over Syslog or a file. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-infoblox.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/infoblox.svg', - artifacts: { - dashboards: [], - application: { - path: '/app/security', - label: i18n.translate('home.tutorials.infobloxLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'Security App', - }), - }, - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-infoblox.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/instructions/auditbeat_instructions.ts b/src/plugins/home/server/tutorials/instructions/auditbeat_instructions.ts deleted file mode 100644 index 875c108e9a72..000000000000 --- a/src/plugins/home/server/tutorials/instructions/auditbeat_instructions.ts +++ /dev/null @@ -1,363 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { INSTRUCTION_VARIANT } from '../../../common/instruction_variant'; -import { getSpaceIdForBeatsTutorial } from './get_space_id_for_beats_tutorial'; -import { Platform, TutorialContext } from '../../services/tutorials/lib/tutorials_registry_types'; - -export const createAuditbeatInstructions = (context?: TutorialContext) => ({ - INSTALL: { - OSX: { - title: i18n.translate('home.tutorials.common.auditbeatInstructions.install.osxTitle', { - defaultMessage: 'Download and install Auditbeat', - }), - textPre: i18n.translate('home.tutorials.common.auditbeatInstructions.install.osxTextPre', { - defaultMessage: 'First time using Auditbeat? See the [Quick Start]({linkUrl}).', - values: { - linkUrl: '{config.docs.beats.auditbeat}/auditbeat-installation-configuration.html', - }, - }), - commands: [ - 'curl -L -O https://artifacts.opensearch.org/downloads/beats/auditbeat/auditbeat-{config.opensearchDashboards.version}-darwin-x64.tar.gz', - 'tar xzvf auditbeat-{config.opensearchDashboards.version}-darwin-x64.tar.gz', - 'cd auditbeat-{config.opensearchDashboards.version}-darwin-x64/', - ], - }, - DEB: { - title: i18n.translate('home.tutorials.common.auditbeatInstructions.install.debTitle', { - defaultMessage: 'Download and install Auditbeat', - }), - textPre: i18n.translate('home.tutorials.common.auditbeatInstructions.install.debTextPre', { - defaultMessage: 'First time using Auditbeat? See the [Quick Start]({linkUrl}).', - values: { - linkUrl: '{config.docs.beats.auditbeat}/auditbeat-installation-configuration.html', - }, - }), - commands: [ - 'curl -L -O https://artifacts.opensearch.org/downloads/beats/auditbeat/auditbeat-{config.opensearchDashboards.version}-amd64.deb', - 'sudo dpkg -i auditbeat-{config.opensearchDashboards.version}-amd64.deb', - ], - textPost: i18n.translate('home.tutorials.common.auditbeatInstructions.install.debTextPost', { - defaultMessage: 'Looking for the 32-bit packages? See the [Download page]({linkUrl}).', - values: { - linkUrl: 'https://opensearch.org/docs/latest/downloads/beats/auditbeat', - }, - }), - }, - RPM: { - title: i18n.translate('home.tutorials.common.auditbeatInstructions.install.rpmTitle', { - defaultMessage: 'Download and install Auditbeat', - }), - textPre: i18n.translate('home.tutorials.common.auditbeatInstructions.install.rpmTextPre', { - defaultMessage: 'First time using Auditbeat? See the [Quick Start]({linkUrl}).', - values: { - linkUrl: '{config.docs.beats.auditbeat}/auditbeat-installation-configuration.html', - }, - }), - commands: [ - 'curl -L -O https://artifacts.opensearch.org/downloads/beats/auditbeat/auditbeat-{config.opensearchDashboards.version}-x64.rpm', - 'sudo rpm -vi auditbeat-{config.opensearchDashboards.version}-x64.rpm', - ], - textPost: i18n.translate('home.tutorials.common.auditbeatInstructions.install.rpmTextPost', { - defaultMessage: 'Looking for the 32-bit packages? See the [Download page]({linkUrl}).', - values: { - linkUrl: 'https://opensearch.org/docs/latest/downloads/beats/auditbeat', - }, - }), - }, - WINDOWS: { - title: i18n.translate('home.tutorials.common.auditbeatInstructions.install.windowsTitle', { - defaultMessage: 'Download and install Auditbeat', - }), - textPre: i18n.translate( - 'home.tutorials.common.auditbeatInstructions.install.windowsTextPre', - { - defaultMessage: - 'First time using Auditbeat? See the [Quick Start]({guideLinkUrl}).\n\ - 1. Download the Auditbeat Windows zip file from the [Download]({auditbeatLinkUrl}) page.\n\ - 2. Extract the contents of the zip file into {folderPath}.\n\ - 3. Rename the `{directoryName}` directory to `Auditbeat`.\n\ - 4. Open a PowerShell prompt as an Administrator (right-click the PowerShell icon and select \ -**Run As Administrator**). If you are running Windows XP, you might need to download and install PowerShell.\n\ - 5. From the PowerShell prompt, run the following commands to install Auditbeat as a Windows service.', - values: { - folderPath: '`C:\\Program Files`', - guideLinkUrl: '{config.docs.beats.auditbeat}/auditbeat-installation-configuration.html', - auditbeatLinkUrl: 'https://opensearch.org/docs/latest/downloads/beats/auditbeat', - directoryName: 'auditbeat-{config.opensearchDashboards.version}-windows', - }, - } - ), - commands: ['cd "C:\\Program Files\\Auditbeat"', '.\\install-service-auditbeat.ps1'], - textPost: i18n.translate( - 'home.tutorials.common.auditbeatInstructions.install.windowsTextPost', - { - defaultMessage: - 'Modify the settings under {propertyName} in the {auditbeatPath} file to point to your OpenSearch installation.', - values: { - propertyName: '`output.opensearch`', - auditbeatPath: '`C:\\Program Files\\Auditbeat\\auditbeat.yml`', - }, - } - ), - }, - }, - START: { - OSX: { - title: i18n.translate('home.tutorials.common.auditbeatInstructions.start.osxTitle', { - defaultMessage: 'Start Auditbeat', - }), - textPre: i18n.translate('home.tutorials.common.auditbeatInstructions.start.osxTextPre', { - defaultMessage: - 'The `setup` command loads the OpenSearch Dashboards dashboards. If the dashboards are already set up, omit this command.', - }), - commands: ['./auditbeat setup', './auditbeat -e'], - }, - DEB: { - title: i18n.translate('home.tutorials.common.auditbeatInstructions.start.debTitle', { - defaultMessage: 'Start Auditbeat', - }), - textPre: i18n.translate('home.tutorials.common.auditbeatInstructions.start.debTextPre', { - defaultMessage: - 'The `setup` command loads the OpenSearch Dashboards dashboards. If the dashboards are already set up, omit this command.', - }), - commands: ['sudo auditbeat setup', 'sudo service auditbeat start'], - }, - RPM: { - title: i18n.translate('home.tutorials.common.auditbeatInstructions.start.rpmTitle', { - defaultMessage: 'Start Auditbeat', - }), - textPre: i18n.translate('home.tutorials.common.auditbeatInstructions.start.rpmTextPre', { - defaultMessage: - 'The `setup` command loads the OpenSearch Dashboards dashboards. If the dashboards are already set up, omit this command.', - }), - commands: ['sudo auditbeat setup', 'sudo service auditbeat start'], - }, - WINDOWS: { - title: i18n.translate('home.tutorials.common.auditbeatInstructions.start.windowsTitle', { - defaultMessage: 'Start Auditbeat', - }), - textPre: i18n.translate('home.tutorials.common.auditbeatInstructions.start.windowsTextPre', { - defaultMessage: - 'The `setup` command loads the OpenSearch Dashboards dashboards. If the dashboards are already set up, omit this command.', - }), - commands: ['.\\auditbeat.exe setup', 'Start-Service auditbeat'], - }, - }, - CONFIG: { - OSX: { - title: i18n.translate('home.tutorials.common.auditbeatInstructions.config.osxTitle', { - defaultMessage: 'Edit the configuration', - }), - textPre: i18n.translate('home.tutorials.common.auditbeatInstructions.config.osxTextPre', { - defaultMessage: 'Modify {path} to set the connection information:', - values: { - path: '`auditbeat.yml`', - }, - }), - commands: [ - 'output.opensearch:', - ' hosts: [""]', - ' username: "opensearch"', - ' password: ""', - 'setup.opensearchDashboards:', - ' host: ""', - getSpaceIdForBeatsTutorial(context), - ], - textPost: i18n.translate('home.tutorials.common.auditbeatInstructions.config.osxTextPost', { - defaultMessage: - 'Where {passwordTemplate} is the password of the `opensearch` user, {opensearchUrlTemplate} is the URL of OpenSearch, \ -and {opensearchDashboardsUrlTemplate} is the URL of OpenSearch Dashboards.', - values: { - passwordTemplate: '``', - opensearchUrlTemplate: '``', - opensearchDashboardsUrlTemplate: '``', - }, - }), - }, - DEB: { - title: i18n.translate('home.tutorials.common.auditbeatInstructions.config.debTitle', { - defaultMessage: 'Edit the configuration', - }), - textPre: i18n.translate('home.tutorials.common.auditbeatInstructions.config.debTextPre', { - defaultMessage: 'Modify {path} to set the connection information:', - values: { - path: '`/etc/auditbeat/auditbeat.yml`', - }, - }), - commands: [ - 'output.opensearch:', - ' hosts: [""]', - ' username: "opensearch"', - ' password: ""', - 'setup.opensearchDashboards:', - ' host: ""', - getSpaceIdForBeatsTutorial(context), - ], - textPost: i18n.translate('home.tutorials.common.auditbeatInstructions.config.debTextPost', { - defaultMessage: - 'Where {passwordTemplate} is the password of the `opensearch` user, {opensearchUrlTemplate} is the URL of OpenSearch, \ -and {opensearchDashboardsUrlTemplate} is the URL of OpenSearch Dashboards.', - values: { - passwordTemplate: '``', - opensearchUrlTemplate: '``', - opensearchDashboardsUrlTemplate: '``', - }, - }), - }, - RPM: { - title: i18n.translate('home.tutorials.common.auditbeatInstructions.config.rpmTitle', { - defaultMessage: 'Edit the configuration', - }), - textPre: i18n.translate('home.tutorials.common.auditbeatInstructions.config.rpmTextPre', { - defaultMessage: 'Modify {path} to set the connection information:', - values: { - path: '`/etc/auditbeat/auditbeat.yml`', - }, - }), - commands: [ - 'output.opensearch:', - ' hosts: [""]', - ' username: "opensearch"', - ' password: ""', - 'setup.opensearchDashboards:', - ' host: ""', - getSpaceIdForBeatsTutorial(context), - ], - textPost: i18n.translate('home.tutorials.common.auditbeatInstructions.config.rpmTextPost', { - defaultMessage: - 'Where {passwordTemplate} is the password of the `opensearch` user, {opensearchUrlTemplate} is the URL of OpenSearch, \ -and {opensearchDashboardsUrlTemplate} is the URL of OpenSearch Dashboards.', - values: { - passwordTemplate: '``', - opensearchUrlTemplate: '``', - opensearchDashboardsUrlTemplate: '``', - }, - }), - }, - WINDOWS: { - title: i18n.translate('home.tutorials.common.auditbeatInstructions.config.windowsTitle', { - defaultMessage: 'Edit the configuration', - }), - textPre: i18n.translate('home.tutorials.common.auditbeatInstructions.config.windowsTextPre', { - defaultMessage: 'Modify {path} to set the connection information:', - values: { - path: '`C:\\Program Files\\Auditbeat\\auditbeat.yml`', - }, - }), - commands: [ - 'output.opensearch:', - ' hosts: [""]', - ' username: "opensearch"', - ' password: ""', - 'setup.opensearchDashboards:', - ' host: ""', - getSpaceIdForBeatsTutorial(context), - ], - textPost: i18n.translate( - 'home.tutorials.common.auditbeatInstructions.config.windowsTextPost', - { - defaultMessage: - 'Where {passwordTemplate} is the password of the `opensearch` user, {opensearchUrlTemplate} is the URL of OpenSearch, \ -and {opensearchDashboardsUrlTemplate} is the URL of OpenSearch Dashboards.', - values: { - passwordTemplate: '``', - opensearchUrlTemplate: '``', - opensearchDashboardsUrlTemplate: '``', - }, - } - ), - }, - }, -}); - -export function auditbeatStatusCheck() { - return { - title: i18n.translate('home.tutorials.common.auditbeatStatusCheck.title', { - defaultMessage: 'Status', - }), - text: i18n.translate('home.tutorials.common.auditbeatStatusCheck.text', { - defaultMessage: 'Check that data is received from Auditbeat', - }), - btnLabel: i18n.translate('home.tutorials.common.auditbeatStatusCheck.buttonLabel', { - defaultMessage: 'Check data', - }), - success: i18n.translate('home.tutorials.common.auditbeatStatusCheck.successText', { - defaultMessage: 'Data successfully received', - }), - error: i18n.translate('home.tutorials.common.auditbeatStatusCheck.errorText', { - defaultMessage: 'No data has been received yet', - }), - opensearchHitsCheck: { - index: 'auditbeat-*', - query: { - bool: { - filter: { - term: { - 'agent.type': 'auditbeat', - }, - }, - }, - }, - }, - }; -} - -export function onPremInstructions(platforms: readonly Platform[], context?: TutorialContext) { - const AUDITBEAT_INSTRUCTIONS = createAuditbeatInstructions(context); - - const variants = []; - for (let i = 0; i < platforms.length; i++) { - const platform = platforms[i]; - const instructions = []; - instructions.push(AUDITBEAT_INSTRUCTIONS.INSTALL[platform]); - instructions.push(AUDITBEAT_INSTRUCTIONS.CONFIG[platform]); - instructions.push(AUDITBEAT_INSTRUCTIONS.START[platform]); - variants.push({ - id: INSTRUCTION_VARIANT[platform], - instructions, - }); - } - return { - instructionSets: [ - { - title: i18n.translate( - 'home.tutorials.common.auditbeat.premInstructions.gettingStarted.title', - { - defaultMessage: 'Getting Started', - } - ), - instructionVariants: variants, - statusCheck: auditbeatStatusCheck(), - }, - ], - }; -} diff --git a/src/plugins/home/server/tutorials/instructions/filebeat_instructions.ts b/src/plugins/home/server/tutorials/instructions/filebeat_instructions.ts deleted file mode 100644 index 0d47f760dd0c..000000000000 --- a/src/plugins/home/server/tutorials/instructions/filebeat_instructions.ts +++ /dev/null @@ -1,424 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { INSTRUCTION_VARIANT } from '../../../common/instruction_variant'; -import { getSpaceIdForBeatsTutorial } from './get_space_id_for_beats_tutorial'; -import { Platform, TutorialContext } from '../../services/tutorials/lib/tutorials_registry_types'; - -export const createFilebeatInstructions = (context?: TutorialContext) => ({ - INSTALL: { - OSX: { - title: i18n.translate('home.tutorials.common.filebeatInstructions.install.osxTitle', { - defaultMessage: 'Download and install Filebeat', - }), - textPre: i18n.translate('home.tutorials.common.filebeatInstructions.install.osxTextPre', { - defaultMessage: 'First time using Filebeat? See the [Quick Start]({linkUrl}).', - values: { - linkUrl: '{config.docs.beats.filebeat}/filebeat-installation-configuration.html', - }, - }), - commands: [ - 'curl -L -O https://artifacts.opensearch.org/downloads/beats/filebeat/filebeat-{config.opensearchDashboards.version}-darwin-x64.tar.gz', - 'tar xzvf filebeat-{config.opensearchDashboards.version}-darwin-x64.tar.gz', - 'cd filebeat-{config.opensearchDashboards.version}-darwin-x64/', - ], - }, - DEB: { - title: i18n.translate('home.tutorials.common.filebeatInstructions.install.debTitle', { - defaultMessage: 'Download and install Filebeat', - }), - textPre: i18n.translate('home.tutorials.common.filebeatInstructions.install.debTextPre', { - defaultMessage: 'First time using Filebeat? See the [Quick Start]({linkUrl}).', - values: { - linkUrl: '{config.docs.beats.filebeat}/filebeat-installation-configuration.html', - }, - }), - commands: [ - 'curl -L -O https://artifacts.opensearch.org/downloads/beats/filebeat/filebeat-{config.opensearchDashboards.version}-amd64.deb', - 'sudo dpkg -i filebeat-{config.opensearchDashboards.version}-amd64.deb', - ], - textPost: i18n.translate('home.tutorials.common.filebeatInstructions.install.debTextPost', { - defaultMessage: 'Looking for the 32-bit packages? See the [Download page]({linkUrl}).', - values: { - linkUrl: 'https://opensearch.org/docs/latest/downloads/beats/filebeat', - }, - }), - }, - RPM: { - title: i18n.translate('home.tutorials.common.filebeatInstructions.install.rpmTitle', { - defaultMessage: 'Download and install Filebeat', - }), - textPre: i18n.translate('home.tutorials.common.filebeatInstructions.install.rpmTextPre', { - defaultMessage: 'First time using Filebeat? See the [Quick Start]({linkUrl}).', - values: { - linkUrl: '{config.docs.beats.filebeat}/filebeat-installation-configuration.html', - }, - }), - commands: [ - 'curl -L -O https://artifacts.opensearch.org/downloads/beats/filebeat/filebeat-{config.opensearchDashboards.version}-x64.rpm', - 'sudo rpm -vi filebeat-{config.opensearchDashboards.version}-x64.rpm', - ], - textPost: i18n.translate('home.tutorials.common.filebeatInstructions.install.rpmTextPost', { - defaultMessage: 'Looking for the 32-bit packages? See the [Download page]({linkUrl}).', - values: { - linkUrl: 'https://opensearch.org/docs/latest/downloads/beats/filebeat', - }, - }), - }, - WINDOWS: { - title: i18n.translate('home.tutorials.common.filebeatInstructions.install.windowsTitle', { - defaultMessage: 'Download and install Filebeat', - }), - textPre: i18n.translate('home.tutorials.common.filebeatInstructions.install.windowsTextPre', { - defaultMessage: - 'First time using Filebeat? See the [Quick Start]({guideLinkUrl}).\n\ - 1. Download the Filebeat Windows zip file from the [Download]({filebeatLinkUrl}) page.\n\ - 2. Extract the contents of the zip file into {folderPath}.\n\ - 3. Rename the `{directoryName}` directory to `Filebeat`.\n\ - 4. Open a PowerShell prompt as an Administrator (right-click the PowerShell icon and select \ -**Run As Administrator**). If you are running Windows XP, you might need to download and install PowerShell.\n\ - 5. From the PowerShell prompt, run the following commands to install Filebeat as a Windows service.', - values: { - folderPath: '`C:\\Program Files`', - guideLinkUrl: '{config.docs.beats.filebeat}/filebeat-installation-configuration.html', - filebeatLinkUrl: 'https://opensearch.org/docs/latest/downloads/beats/filebeat', - directoryName: 'filebeat-{config.opensearchDashboards.version}-windows', - }, - }), - commands: ['cd "C:\\Program Files\\Filebeat"', '.\\install-service-filebeat.ps1'], - textPost: i18n.translate( - 'home.tutorials.common.filebeatInstructions.install.windowsTextPost', - { - defaultMessage: - 'Modify the settings under {propertyName} in the {filebeatPath} file to point to your OpenSearch installation.', - values: { - propertyName: '`output.opensearch`', - filebeatPath: '`C:\\Program Files\\Filebeat\\filebeat.yml`', - }, - } - ), - }, - }, - START: { - OSX: { - title: i18n.translate('home.tutorials.common.filebeatInstructions.start.osxTitle', { - defaultMessage: 'Start Filebeat', - }), - textPre: i18n.translate('home.tutorials.common.filebeatInstructions.start.osxTextPre', { - defaultMessage: - 'The `setup` command loads the OpenSearch Dashboards dashboards. If the dashboards are already set up, omit this command.', - }), - commands: ['./filebeat setup', './filebeat -e'], - }, - DEB: { - title: i18n.translate('home.tutorials.common.filebeatInstructions.start.debTitle', { - defaultMessage: 'Start Filebeat', - }), - textPre: i18n.translate('home.tutorials.common.filebeatInstructions.start.debTextPre', { - defaultMessage: - 'The `setup` command loads the OpenSearch Dashboards dashboards. If the dashboards are already set up, omit this command.', - }), - commands: ['sudo filebeat setup', 'sudo service filebeat start'], - }, - RPM: { - title: i18n.translate('home.tutorials.common.filebeatInstructions.start.rpmTitle', { - defaultMessage: 'Start Filebeat', - }), - textPre: i18n.translate('home.tutorials.common.filebeatInstructions.start.rpmTextPre', { - defaultMessage: - 'The `setup` command loads the OpenSearch Dashboards dashboards. If the dashboards are already set up, omit this command.', - }), - commands: ['sudo filebeat setup', 'sudo service filebeat start'], - }, - WINDOWS: { - title: i18n.translate('home.tutorials.common.filebeatInstructions.start.windowsTitle', { - defaultMessage: 'Start Filebeat', - }), - textPre: i18n.translate('home.tutorials.common.filebeatInstructions.start.windowsTextPre', { - defaultMessage: - 'The `setup` command loads the OpenSearch Dashboards dashboards. If the dashboards are already set up, omit this command.', - }), - commands: ['.\\filebeat.exe setup', 'Start-Service filebeat'], - }, - }, - CONFIG: { - OSX: { - title: i18n.translate('home.tutorials.common.filebeatInstructions.config.osxTitle', { - defaultMessage: 'Edit the configuration', - }), - textPre: i18n.translate('home.tutorials.common.filebeatInstructions.config.osxTextPre', { - defaultMessage: 'Modify {path} to set the connection information:', - values: { - path: '`filebeat.yml`', - }, - }), - commands: [ - 'output.opensearch:', - ' hosts: [""]', - ' username: "opensearch"', - ' password: ""', - 'setup.opensearchDashboards:', - ' host: ""', - getSpaceIdForBeatsTutorial(context), - ], - textPost: i18n.translate('home.tutorials.common.filebeatInstructions.config.osxTextPost', { - defaultMessage: - 'Where {passwordTemplate} is the password of the `opensearch` user, {opensearchUrlTemplate} is the URL of opensearch, \ -and {opensearchDashboardsUrlTemplate} is the URL of OpenSearch Dashboards.', - values: { - passwordTemplate: '``', - opensearchUrlTemplate: '``', - opensearchDashboardsUrlTemplate: '``', - }, - }), - }, - DEB: { - title: i18n.translate('home.tutorials.common.filebeatInstructions.config.debTitle', { - defaultMessage: 'Edit the configuration', - }), - textPre: i18n.translate('home.tutorials.common.filebeatInstructions.config.debTextPre', { - defaultMessage: 'Modify {path} to set the connection information:', - values: { - path: '`/etc/filebeat/filebeat.yml`', - }, - }), - commands: [ - 'output.opensearch:', - ' hosts: [""]', - ' username: "opensearch"', - ' password: ""', - 'setup.opensearchDashboards:', - ' host: ""', - getSpaceIdForBeatsTutorial(context), - ], - textPost: i18n.translate('home.tutorials.common.filebeatInstructions.config.debTextPost', { - defaultMessage: - 'Where {passwordTemplate} is the password of the `opensearch` user, {opensearchUrlTemplate} is the URL of opensearch, \ -and {opensearchDashboardsUrlTemplate} is the URL of OpenSearch Dashboards.', - values: { - passwordTemplate: '``', - opensearchUrlTemplate: '``', - opensearchDashboardsUrlTemplate: '``', - }, - }), - }, - RPM: { - title: i18n.translate('home.tutorials.common.filebeatInstructions.config.rpmTitle', { - defaultMessage: 'Edit the configuration', - }), - textPre: i18n.translate('home.tutorials.common.filebeatInstructions.config.rpmTextPre', { - defaultMessage: 'Modify {path} to set the connection information:', - values: { - path: '`/etc/filebeat/filebeat.yml`', - }, - }), - commands: [ - 'output.opensearch:', - ' hosts: [""]', - ' username: "opensearch"', - ' password: ""', - 'setup.opensearchDashboards:', - ' host: ""', - getSpaceIdForBeatsTutorial(context), - ], - textPost: i18n.translate('home.tutorials.common.filebeatInstructions.config.rpmTextPost', { - defaultMessage: - 'Where {passwordTemplate} is the password of the `opensearch` user, {opensearchUrlTemplate} is the URL of opensearch, \ -and {opensearchDashboardsUrlTemplate} is the URL of OpenSearch Dashboards.', - values: { - passwordTemplate: '``', - opensearchUrlTemplate: '``', - opensearchDashboardsUrlTemplate: '``', - }, - }), - }, - WINDOWS: { - title: i18n.translate('home.tutorials.common.filebeatInstructions.config.windowsTitle', { - defaultMessage: 'Edit the configuration', - }), - textPre: i18n.translate('home.tutorials.common.filebeatInstructions.config.windowsTextPre', { - defaultMessage: 'Modify {path} to set the connection information:', - values: { - path: '`C:\\Program Files\\Filebeat\\filebeat.yml`', - }, - }), - commands: [ - 'output.opensearch:', - ' hosts: [""]', - ' username: "opensearch"', - ' password: ""', - 'setup.opensearchDashboards:', - ' host: ""', - getSpaceIdForBeatsTutorial(context), - ], - textPost: i18n.translate( - 'home.tutorials.common.filebeatInstructions.config.windowsTextPost', - { - defaultMessage: - 'Where {passwordTemplate} is the password of the `opensearch` user, {opensearchUrlTemplate} is the URL of opensearch, \ -and {opensearchDashboardsUrlTemplate} is the URL of OpenSearch Dashboards.', - values: { - passwordTemplate: '``', - opensearchUrlTemplate: '``', - opensearchDashboardsUrlTemplate: '``', - }, - } - ), - }, - }, -}); - -export function filebeatEnableInstructions(moduleName: string) { - return { - OSX: { - title: i18n.translate('home.tutorials.common.filebeatEnableInstructions.osxTitle', { - defaultMessage: 'Enable and configure the {moduleName} module', - values: { moduleName }, - }), - textPre: i18n.translate('home.tutorials.common.filebeatEnableInstructions.osxTextPre', { - defaultMessage: 'From the installation directory, run:', - }), - commands: ['./filebeat modules enable ' + moduleName], - textPost: i18n.translate('home.tutorials.common.filebeatEnableInstructions.osxTextPost', { - defaultMessage: 'Modify the settings in the `modules.d/{moduleName}.yml` file.', - values: { moduleName }, - }), - }, - DEB: { - title: i18n.translate('home.tutorials.common.filebeatEnableInstructions.debTitle', { - defaultMessage: 'Enable and configure the {moduleName} module', - values: { moduleName }, - }), - commands: ['sudo filebeat modules enable ' + moduleName], - textPost: i18n.translate('home.tutorials.common.filebeatEnableInstructions.debTextPost', { - defaultMessage: - 'Modify the settings in the `/etc/filebeat/modules.d/{moduleName}.yml` file.', - values: { moduleName }, - }), - }, - RPM: { - title: i18n.translate('home.tutorials.common.filebeatEnableInstructions.rpmTitle', { - defaultMessage: 'Enable and configure the {moduleName} module', - values: { moduleName }, - }), - commands: ['sudo filebeat modules enable ' + moduleName], - textPost: i18n.translate('home.tutorials.common.filebeatEnableInstructions.rpmTextPost', { - defaultMessage: - 'Modify the settings in the `/etc/filebeat/modules.d/{moduleName}.yml` file.', - values: { moduleName }, - }), - }, - WINDOWS: { - title: i18n.translate('home.tutorials.common.filebeatEnableInstructions.windowsTitle', { - defaultMessage: 'Enable and configure the {moduleName} module', - values: { moduleName }, - }), - textPre: i18n.translate('home.tutorials.common.filebeatEnableInstructions.windowsTextPre', { - defaultMessage: 'From the {path} folder, run:', - values: { path: `C:\\Program Files\\Filebeat` }, - }), - commands: ['filebeat.exe modules enable ' + moduleName], - textPost: i18n.translate('home.tutorials.common.filebeatEnableInstructions.windowsTextPost', { - defaultMessage: 'Modify the settings in the `modules.d/{moduleName}.yml` file.', - values: { moduleName }, - }), - }, - }; -} - -export function filebeatStatusCheck(moduleName: string) { - return { - title: i18n.translate('home.tutorials.common.filebeatStatusCheck.title', { - defaultMessage: 'Module status', - }), - text: i18n.translate('home.tutorials.common.filebeatStatusCheck.text', { - defaultMessage: 'Check that data is received from the Filebeat `{moduleName}` module', - values: { moduleName }, - }), - btnLabel: i18n.translate('home.tutorials.common.filebeatStatusCheck.buttonLabel', { - defaultMessage: 'Check data', - }), - success: i18n.translate('home.tutorials.common.filebeatStatusCheck.successText', { - defaultMessage: 'Data successfully received from this module', - }), - error: i18n.translate('home.tutorials.common.filebeatStatusCheck.errorText', { - defaultMessage: 'No data has been received from this module yet', - }), - opensearchHitsCheck: { - index: 'filebeat-*', - query: { - bool: { - filter: { - term: { - 'event.module': moduleName, - }, - }, - }, - }, - }, - }; -} - -export function onPremInstructions( - moduleName: string, - platforms: readonly Platform[] = [], - context?: TutorialContext -) { - const FILEBEAT_INSTRUCTIONS = createFilebeatInstructions(context); - - const variants = []; - for (let i = 0; i < platforms.length; i++) { - const platform = platforms[i]; - const instructions = []; - instructions.push(FILEBEAT_INSTRUCTIONS.INSTALL[platform]); - instructions.push(FILEBEAT_INSTRUCTIONS.CONFIG[platform]); - instructions.push(filebeatEnableInstructions(moduleName)[platform]); - instructions.push(FILEBEAT_INSTRUCTIONS.START[platform]); - variants.push({ - id: INSTRUCTION_VARIANT[platform], - instructions, - }); - } - return { - instructionSets: [ - { - title: i18n.translate( - 'home.tutorials.common.filebeat.premInstructions.gettingStarted.title', - { - defaultMessage: 'Getting Started', - } - ), - instructionVariants: variants, - statusCheck: filebeatStatusCheck(moduleName), - }, - ], - }; -} diff --git a/src/plugins/home/server/tutorials/instructions/functionbeat_instructions.ts b/src/plugins/home/server/tutorials/instructions/functionbeat_instructions.ts deleted file mode 100644 index add467f39645..000000000000 --- a/src/plugins/home/server/tutorials/instructions/functionbeat_instructions.ts +++ /dev/null @@ -1,367 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { INSTRUCTION_VARIANT } from '../../../common/instruction_variant'; -import { getSpaceIdForBeatsTutorial } from './get_space_id_for_beats_tutorial'; -import { Platform, TutorialContext } from '../../services/tutorials/lib/tutorials_registry_types'; - -export const createFunctionbeatInstructions = (context?: TutorialContext) => ({ - INSTALL: { - OSX: { - title: i18n.translate('home.tutorials.common.functionbeatInstructions.install.osxTitle', { - defaultMessage: 'Download and install Functionbeat', - }), - textPre: i18n.translate('home.tutorials.common.functionbeatInstructions.install.osxTextPre', { - defaultMessage: 'First time using Functionbeat? See the [Quick Start]({link}).', - values: { - link: '{config.docs.beats.functionbeat}/functionbeat-installation-configuration.html', - }, - }), - commands: [ - 'curl -L -O https://artifacts.opensearch.org/downloads/beats/functionbeat/functionbeat-{config.opensearchDashboards.version}-darwin-x64.tar.gz', - 'tar xzvf functionbeat-{config.opensearchDashboards.version}-darwin-x64.tar.gz', - 'cd functionbeat-{config.opensearchDashboards.version}-darwin-x64/', - ], - }, - LINUX: { - title: i18n.translate('home.tutorials.common.functionbeatInstructions.install.linuxTitle', { - defaultMessage: 'Download and install Functionbeat', - }), - textPre: i18n.translate( - 'home.tutorials.common.functionbeatInstructions.install.linuxTextPre', - { - defaultMessage: 'First time using Functionbeat? See the [Quick Start]({link}).', - values: { - link: '{config.docs.beats.functionbeat}/functionbeat-installation-configuration.html', - }, - } - ), - commands: [ - 'curl -L -O https://artifacts.opensearch.org/downloads/beats/functionbeat/functionbeat-{config.opensearchDashboards.version}-linux-x64.tar.gz', - 'tar xzvf functionbeat-{config.opensearchDashboards.version}-linux-x64.tar.gz', - 'cd functionbeat-{config.opensearchDashboards.version}-linux-x64/', - ], - }, - WINDOWS: { - title: i18n.translate('home.tutorials.common.functionbeatInstructions.install.windowsTitle', { - defaultMessage: 'Download and install Functionbeat', - }), - textPre: i18n.translate( - 'home.tutorials.common.functionbeatInstructions.install.windowsTextPre', - { - defaultMessage: - 'First time using Functionbeat? See the [Quick Start]({functionbeatLink}).\n\ - 1. Download the Functionbeat Windows zip file from the [Download]({opensearchLink}) page.\n\ - 2. Extract the contents of the zip file into {folderPath}.\n\ - 3. Rename the {directoryName} directory to `Functionbeat`.\n\ - 4. Open a PowerShell prompt as an Administrator (right-click the PowerShell icon and select \ -**Run As Administrator**). If you are running Windows XP, you might need to download and install PowerShell.\n\ - 5. From the PowerShell prompt, go to the Functionbeat directory:', - values: { - directoryName: '`functionbeat-{config.opensearchDashboards.version}-windows`', - folderPath: '`C:\\Program Files`', - functionbeatLink: - '{config.docs.beats.functionbeat}/functionbeat-installation-configuration.html', - opensearchLink: 'https://opensearch.org/docs/latest/downloads/beats/functionbeat', - }, - } - ), - commands: ['cd "C:\\Program Files\\Functionbeat"'], - }, - }, - DEPLOY: { - OSX_LINUX: { - title: i18n.translate('home.tutorials.common.functionbeatInstructions.deploy.osxTitle', { - defaultMessage: 'Deploy Functionbeat to AWS Lambda', - }), - textPre: i18n.translate('home.tutorials.common.functionbeatInstructions.deploy.osxTextPre', { - defaultMessage: - 'This installs Functionbeat as a Lambda function.\ -The `setup` command checks the opensearch configuration and loads the \ -OpenSearch Dashboards index pattern. It is normally safe to omit this command.', - }), - commands: ['./functionbeat setup', './functionbeat deploy fn-cloudwatch-logs'], - }, - WINDOWS: { - title: i18n.translate('home.tutorials.common.functionbeatInstructions.deploy.windowsTitle', { - defaultMessage: 'Deploy Functionbeat to AWS Lambda', - }), - textPre: i18n.translate( - 'home.tutorials.common.functionbeatInstructions.deploy.windowsTextPre', - { - defaultMessage: - 'This installs Functionbeat as a Lambda function.\ -The `setup` command checks the opensearch configuration and loads the \ -OpenSearch Dashboards index pattern. It is normally safe to omit this command.', - } - ), - commands: ['.\\functionbeat.exe setup', '.\\functionbeat.exe deploy fn-cloudwatch-logs'], - }, - }, - CONFIG: { - OSX_LINUX: { - title: i18n.translate('home.tutorials.common.functionbeatInstructions.config.osxTitle', { - defaultMessage: 'Configure the OpenSearch cluster', - }), - textPre: i18n.translate('home.tutorials.common.functionbeatInstructions.config.osxTextPre', { - defaultMessage: 'Modify {path} to set the connection information:', - values: { - path: '`functionbeat.yml`', - }, - }), - commands: [ - 'output.opensearch:', - ' hosts: [""]', - ' username: "opensearch"', - ' password: ""', - 'setup.opensearchDashboards:', - ' host: ""', - getSpaceIdForBeatsTutorial(context), - ], - textPost: i18n.translate( - 'home.tutorials.common.functionbeatInstructions.config.osxTextPost', - { - defaultMessage: - 'Where {passwordTemplate} is the password of the `opensearch` user, {opensearchUrlTemplate} is the URL of opensearch, \ -and {opensearchDashboardsUrlTemplate} is the URL of OpenSearch Dashboards.', - values: { - passwordTemplate: '``', - opensearchUrlTemplate: '``', - opensearchDashboardsUrlTemplate: '``', - }, - } - ), - }, - WINDOWS: { - title: i18n.translate('home.tutorials.common.functionbeatInstructions.config.windowsTitle', { - defaultMessage: 'Edit the configuration', - }), - textPre: i18n.translate( - 'home.tutorials.common.functionbeatInstructions.config.windowsTextPre', - { - defaultMessage: 'Modify {path} to set the connection information:', - values: { - path: '`C:\\Program Files\\Functionbeat\\functionbeat.yml`', - }, - } - ), - commands: [ - 'output.opensearch:', - ' hosts: [""]', - ' username: "opensearch"', - ' password: ""', - 'setup.opensearchDashboards:', - ' host: ""', - getSpaceIdForBeatsTutorial(context), - ], - textPost: i18n.translate( - 'home.tutorials.common.functionbeatInstructions.config.windowsTextPost', - { - defaultMessage: - 'Where {passwordTemplate} is the password of the `opensearch` user, {opensearchUrlTemplate} is the URL of opensearch, \ -and {opensearchDashboardsUrlTemplate} is the URL of OpenSearch Dashboards.', - values: { - passwordTemplate: '``', - opensearchUrlTemplate: '``', - opensearchDashboardsUrlTemplate: '``', - }, - } - ), - }, - }, -}); - -export function functionbeatEnableInstructions() { - const defaultTitle = i18n.translate( - 'home.tutorials.common.functionbeatEnableOnPremInstructions.defaultTitle', - { - defaultMessage: 'Configure the Cloudwatch log group', - } - ); - const defaultCommands = [ - 'functionbeat.provider.aws.functions:', - ' - name: fn-cloudwatch-logs', - ' enabled: true', - ' type: cloudwatch_logs', - ' triggers:', - ' - log_group_name: ', - 'functionbeat.provider.aws.deploy_bucket: ', - ]; - const defaultTextPost = i18n.translate( - 'home.tutorials.common.functionbeatEnableOnPremInstructions.defaultTextPost', - { - defaultMessage: - 'Where `` is the name of the log group you want to ingest, \ -and `` is a valid S3 bucket name which will be used for staging the \ -Functionbeat deploy.', - } - ); - return { - OSX_LINUX: { - title: defaultTitle, - textPre: i18n.translate( - 'home.tutorials.common.functionbeatEnableOnPremInstructionsOSXLinux.textPre', - { - defaultMessage: 'Modify the settings in the `functionbeat.yml` file.', - } - ), - commands: defaultCommands, - textPost: defaultTextPost, - }, - WINDOWS: { - title: defaultTitle, - textPre: i18n.translate( - 'home.tutorials.common.functionbeatEnableOnPremInstructionsWindows.textPre', - { - defaultMessage: 'Modify the settings in the {path} file.', - values: { - path: '`C:\\Program Files\\Functionbeat\\functionbeat.yml`', - }, - } - ), - commands: defaultCommands, - textPost: defaultTextPost, - }, - }; -} - -export function functionbeatAWSInstructions() { - const defaultTitle = i18n.translate('home.tutorials.common.functionbeatAWSInstructions.title', { - defaultMessage: 'Set AWS credentials', - }); - const defaultPre = i18n.translate('home.tutorials.common.functionbeatAWSInstructions.textPre', { - defaultMessage: 'Set your AWS account credentials in the environment:', - }); - const defaultPost = i18n.translate('home.tutorials.common.functionbeatAWSInstructions.textPost', { - defaultMessage: - 'Where `` and `` are your account credentials and \ -`us-east-1` is the desired region.', - }); - - return { - OSX_LINUX: { - title: defaultTitle, - textPre: defaultPre, - commands: [ - 'export AWS_ACCESS_KEY_ID=', - 'export AWS_SECRET_ACCESS_KEY=', - 'export AWS_DEFAULT_REGION=us-east-1', - ], - textPost: defaultPost, - }, - WINDOWS: { - title: defaultTitle, - textPre: defaultPre, - commands: [ - 'set AWS_ACCESS_KEY_ID=', - 'set AWS_SECRET_ACCESS_KEY=', - 'set AWS_DEFAULT_REGION=us-east-1', - ], - textPost: defaultPost, - }, - }; -} - -export function functionbeatStatusCheck() { - return { - title: i18n.translate('home.tutorials.common.functionbeatStatusCheck.title', { - defaultMessage: 'Functionbeat status', - }), - text: i18n.translate('home.tutorials.common.functionbeatStatusCheck.text', { - defaultMessage: 'Check that data is received from Functionbeat', - }), - btnLabel: i18n.translate('home.tutorials.common.functionbeatStatusCheck.buttonLabel', { - defaultMessage: 'Check data', - }), - success: i18n.translate('home.tutorials.common.functionbeatStatusCheck.successText', { - defaultMessage: 'Data successfully received from Functionbeat', - }), - error: i18n.translate('home.tutorials.common.functionbeatStatusCheck.errorText', { - defaultMessage: 'No data has been received from Functionbeat yet', - }), - opensearchHitsCheck: { - index: 'functionbeat-*', - query: { - match_all: {}, - }, - }, - }; -} - -export function onPremInstructions(platforms: Platform[], context?: TutorialContext) { - const FUNCTIONBEAT_INSTRUCTIONS = createFunctionbeatInstructions(context); - - return { - instructionSets: [ - { - title: i18n.translate( - 'home.tutorials.common.functionbeat.premInstructions.gettingStarted.title', - { - defaultMessage: 'Getting Started', - } - ), - instructionVariants: [ - { - id: INSTRUCTION_VARIANT.OSX, - instructions: [ - FUNCTIONBEAT_INSTRUCTIONS.INSTALL.OSX, - functionbeatAWSInstructions().OSX_LINUX, - functionbeatEnableInstructions().OSX_LINUX, - FUNCTIONBEAT_INSTRUCTIONS.CONFIG.OSX_LINUX, - FUNCTIONBEAT_INSTRUCTIONS.DEPLOY.OSX_LINUX, - ], - }, - { - id: INSTRUCTION_VARIANT.LINUX, - instructions: [ - FUNCTIONBEAT_INSTRUCTIONS.INSTALL.LINUX, - functionbeatAWSInstructions().OSX_LINUX, - functionbeatEnableInstructions().OSX_LINUX, - FUNCTIONBEAT_INSTRUCTIONS.CONFIG.OSX_LINUX, - FUNCTIONBEAT_INSTRUCTIONS.DEPLOY.OSX_LINUX, - ], - }, - { - id: INSTRUCTION_VARIANT.WINDOWS, - instructions: [ - FUNCTIONBEAT_INSTRUCTIONS.INSTALL.WINDOWS, - functionbeatAWSInstructions().WINDOWS, - functionbeatEnableInstructions().WINDOWS, - FUNCTIONBEAT_INSTRUCTIONS.CONFIG.WINDOWS, - FUNCTIONBEAT_INSTRUCTIONS.DEPLOY.WINDOWS, - ], - }, - ], - statusCheck: functionbeatStatusCheck(), - }, - ], - }; -} diff --git a/src/plugins/home/server/tutorials/instructions/get_space_id_for_beats_tutorial.ts b/src/plugins/home/server/tutorials/instructions/get_space_id_for_beats_tutorial.ts deleted file mode 100644 index 2b1082c31c59..000000000000 --- a/src/plugins/home/server/tutorials/instructions/get_space_id_for_beats_tutorial.ts +++ /dev/null @@ -1,45 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { TutorialContext } from '../../services/tutorials/lib/tutorials_registry_types'; - -/** - * Returns valid configuration for a beat.yml file for adding the space id - * if there is an active space and that space is not the default one. - * - * @param {object} context - Context object generated from tutorial factory (see #22760) - */ -export function getSpaceIdForBeatsTutorial(context?: TutorialContext) { - if (!context || !context.spaceId || context.isInDefaultSpace) { - return ''; - } - - return ` space.id: "${context.spaceId}"`; -} diff --git a/src/plugins/home/server/tutorials/instructions/heartbeat_instructions.ts b/src/plugins/home/server/tutorials/instructions/heartbeat_instructions.ts deleted file mode 100644 index 4c85c0dbc960..000000000000 --- a/src/plugins/home/server/tutorials/instructions/heartbeat_instructions.ts +++ /dev/null @@ -1,491 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { INSTRUCTION_VARIANT } from '../../../common/instruction_variant'; -import { getSpaceIdForBeatsTutorial } from './get_space_id_for_beats_tutorial'; -import { Platform, TutorialContext } from '../../services/tutorials/lib/tutorials_registry_types'; - -export const createHeartbeatInstructions = (context?: TutorialContext) => ({ - INSTALL: { - OSX: { - title: i18n.translate('home.tutorials.common.heartbeatInstructions.install.osxTitle', { - defaultMessage: 'Download and install Heartbeat', - }), - textPre: i18n.translate('home.tutorials.common.heartbeatInstructions.install.osxTextPre', { - defaultMessage: 'First time using Heartbeat? See the [Quick Start]({link}).', - values: { link: '{config.docs.beats.heartbeat}/heartbeat-installation-configuration.html' }, - }), - commands: [ - 'curl -L -O https://artifacts.opensearch.org/downloads/beats/heartbeat/heartbeat-{config.opensearchDashboards.version}-darwin-x64.tar.gz', - 'tar xzvf heartbeat-{config.opensearchDashboards.version}-darwin-x64.tar.gz', - 'cd heartbeat-{config.opensearchDashboards.version}-darwin-x64/', - ], - }, - DEB: { - title: i18n.translate('home.tutorials.common.heartbeatInstructions.install.debTitle', { - defaultMessage: 'Download and install Heartbeat', - }), - textPre: i18n.translate('home.tutorials.common.heartbeatInstructions.install.debTextPre', { - defaultMessage: 'First time using Heartbeat? See the [Quick Start]({link}).', - values: { link: '{config.docs.beats.heartbeat}/heartbeat-installation-configuration.html' }, - }), - commands: [ - 'curl -L -O https://artifacts.opensearch.org/downloads/beats/heartbeat/heartbeat-{config.opensearchDashboards.version}-amd64.deb', - 'sudo dpkg -i heartbeat-{config.opensearchDashboards.version}-amd64.deb', - ], - textPost: i18n.translate('home.tutorials.common.heartbeatInstructions.install.debTextPost', { - defaultMessage: 'Looking for the 32-bit packages? See the [Download page]({link}).', - values: { link: 'https://opensearch.org/docs/latest/downloads/beats/heartbeat' }, - }), - }, - RPM: { - title: i18n.translate('home.tutorials.common.heartbeatInstructions.install.rpmTitle', { - defaultMessage: 'Download and install Heartbeat', - }), - textPre: i18n.translate('home.tutorials.common.heartbeatInstructions.install.rpmTextPre', { - defaultMessage: 'First time using Heartbeat? See the [Quick Start]({link}).', - values: { link: '{config.docs.beats.heartbeat}/heartbeat-installation-configuration.html' }, - }), - commands: [ - 'curl -L -O https://artifacts.opensearch.org/downloads/beats/heartbeat/heartbeat-{config.opensearchDashboards.version}-x64.rpm', - 'sudo rpm -vi heartbeat-{config.opensearchDashboards.version}-x64.rpm', - ], - textPost: i18n.translate('home.tutorials.common.heartbeatInstructions.install.debTextPost', { - defaultMessage: 'Looking for the 32-bit packages? See the [Download page]({link}).', - values: { link: 'https://opensearch.org/docs/latest/downloads/beats/heartbeat' }, - }), - }, - WINDOWS: { - title: i18n.translate('home.tutorials.common.heartbeatInstructions.install.windowsTitle', { - defaultMessage: 'Download and install Heartbeat', - }), - textPre: i18n.translate( - 'home.tutorials.common.heartbeatInstructions.install.windowsTextPre', - { - defaultMessage: - 'First time using Heartbeat? See the [Quick Start]({heartbeatLink}).\n\ - 1. Download the Heartbeat Windows zip file from the [Download]({opensearchLink}) page.\n\ - 2. Extract the contents of the zip file into {folderPath}.\n\ - 3. Rename the {directoryName} directory to `Heartbeat`.\n\ - 4. Open a PowerShell prompt as an Administrator (right-click the PowerShell icon and select \ -**Run As Administrator**). If you are running Windows XP, you might need to download and install PowerShell.\n\ - 5. From the PowerShell prompt, run the following commands to install Heartbeat as a Windows service.', - values: { - directoryName: '`heartbeat-{config.opensearchDashboards.version}-windows`', - folderPath: '`C:\\Program Files`', - heartbeatLink: - '{config.docs.beats.heartbeat}/heartbeat-installation-configuration.html', - opensearchLink: 'https://opensearch.org/docs/latest/downloads/beats/heartbeat', - }, - } - ), - commands: ['cd "C:\\Program Files\\Heartbeat"', '.\\install-service-heartbeat.ps1'], - }, - }, - START: { - OSX: { - title: i18n.translate('home.tutorials.common.heartbeatInstructions.start.osxTitle', { - defaultMessage: 'Start Heartbeat', - }), - textPre: i18n.translate('home.tutorials.common.heartbeatInstructions.start.osxTextPre', { - defaultMessage: 'The `setup` command loads the OpenSearch Dashboards index pattern.', - }), - commands: ['./heartbeat setup', './heartbeat -e'], - }, - DEB: { - title: i18n.translate('home.tutorials.common.heartbeatInstructions.start.debTitle', { - defaultMessage: 'Start Heartbeat', - }), - textPre: i18n.translate('home.tutorials.common.heartbeatInstructions.start.debTextPre', { - defaultMessage: 'The `setup` command loads the OpenSearch Dashboards index pattern.', - }), - commands: ['sudo heartbeat setup', 'sudo service heartbeat-elastic start'], - }, - RPM: { - title: i18n.translate('home.tutorials.common.heartbeatInstructions.start.rpmTitle', { - defaultMessage: 'Start Heartbeat', - }), - textPre: i18n.translate('home.tutorials.common.heartbeatInstructions.start.rpmTextPre', { - defaultMessage: 'The `setup` command loads the OpenSearch Dashboards index pattern.', - }), - commands: ['sudo heartbeat setup', 'sudo service heartbeat-elastic start'], - }, - WINDOWS: { - title: i18n.translate('home.tutorials.common.heartbeatInstructions.start.windowsTitle', { - defaultMessage: 'Start Heartbeat', - }), - textPre: i18n.translate('home.tutorials.common.heartbeatInstructions.start.windowsTextPre', { - defaultMessage: 'The `setup` command loads the OpenSearch Dashboards index pattern.', - }), - commands: ['.\\heartbeat.exe setup', 'Start-Service heartbeat'], - }, - }, - CONFIG: { - OSX: { - title: i18n.translate('home.tutorials.common.heartbeatInstructions.config.osxTitle', { - defaultMessage: 'Edit the configuration', - }), - textPre: i18n.translate('home.tutorials.common.heartbeatInstructions.config.osxTextPre', { - defaultMessage: 'Modify {path} to set the connection information:', - values: { - path: '`heartbeat.yml`', - }, - }), - commands: [ - 'output.opensearch:', - ' hosts: [""]', - ' username: "opensearch"', - ' password: ""', - 'setup.opensearchDashboards:', - ' host: ""', - getSpaceIdForBeatsTutorial(context), - ], - textPost: i18n.translate('home.tutorials.common.heartbeatInstructions.config.osxTextPost', { - defaultMessage: - 'Where {passwordTemplate} is the password of the `opensearch` user, {opensearchUrlTemplate} is the URL of opensearch, \ -and {opensearchDashboardsUrlTemplate} is the URL of OpenSearch Dashboards.', - values: { - passwordTemplate: '``', - opensearchUrlTemplate: '``', - opensearchDashboardsUrlTemplate: '``', - }, - }), - }, - DEB: { - title: i18n.translate('home.tutorials.common.heartbeatInstructions.config.debTitle', { - defaultMessage: 'Edit the configuration', - }), - textPre: i18n.translate('home.tutorials.common.heartbeatInstructions.config.debTextPre', { - defaultMessage: 'Modify {path} to set the connection information:', - values: { - path: '`/etc/heartbeat/heartbeat.yml`', - }, - }), - commands: [ - 'output.opensearch:', - ' hosts: [""]', - ' username: "opensearch"', - ' password: ""', - 'setup.opensearchDashboards:', - ' host: ""', - getSpaceIdForBeatsTutorial(context), - ], - textPost: i18n.translate('home.tutorials.common.heartbeatInstructions.config.debTextPost', { - defaultMessage: - 'Where {passwordTemplate} is the password of the `opensearch` user, {opensearchUrlTemplate} is the URL of opensearch, \ -and {opensearchDashboardsUrlTemplate} is the URL of OpenSearch Dashboards.', - values: { - passwordTemplate: '``', - opensearchUrlTemplate: '``', - opensearchDashboardsUrlTemplate: '``', - }, - }), - }, - RPM: { - title: i18n.translate('home.tutorials.common.heartbeatInstructions.config.rpmTitle', { - defaultMessage: 'Edit the configuration', - }), - textPre: i18n.translate('home.tutorials.common.heartbeatInstructions.config.rpmTextPre', { - defaultMessage: 'Modify {path} to set the connection information:', - values: { - path: '`/etc/heartbeat/heartbeat.yml`', - }, - }), - commands: [ - 'output.opensearch:', - ' hosts: [""]', - ' username: "opensearch"', - ' password: ""', - 'setup.opensearchDashboards:', - ' host: ""', - getSpaceIdForBeatsTutorial(context), - ], - textPost: i18n.translate('home.tutorials.common.heartbeatInstructions.config.rpmTextPost', { - defaultMessage: - 'Where {passwordTemplate} is the password of the `opensearch` user, {opensearchUrlTemplate} is the URL of opensearch, \ -and {opensearchDashboardsUrlTemplate} is the URL of OpenSearch Dashboards.', - values: { - passwordTemplate: '``', - opensearchUrlTemplate: '``', - opensearchDashboardsUrlTemplate: '``', - }, - }), - }, - WINDOWS: { - title: i18n.translate('home.tutorials.common.heartbeatInstructions.config.windowsTitle', { - defaultMessage: 'Edit the configuration', - }), - textPre: i18n.translate('home.tutorials.common.heartbeatInstructions.config.windowsTextPre', { - defaultMessage: 'Modify {path} to set the connection information:', - values: { - path: '`C:\\Program Files\\Heartbeat\\heartbeat.yml`', - }, - }), - commands: [ - 'output.opensearch:', - ' hosts: [""]', - ' username: "opensearch"', - ' password: ""', - 'setup.opensearchDashboards:', - ' host: ""', - getSpaceIdForBeatsTutorial(context), - ], - textPost: i18n.translate( - 'home.tutorials.common.heartbeatInstructions.config.windowsTextPost', - { - defaultMessage: - 'Where {passwordTemplate} is the password of the `opensearch` user, {opensearchUrlTemplate} is the URL of opensearch, \ -and {opensearchDashboardsUrlTemplate} is the URL of OpenSearch Dashboards.', - values: { - passwordTemplate: '``', - opensearchUrlTemplate: '``', - opensearchDashboardsUrlTemplate: '``', - }, - } - ), - }, - }, -}); - -export function heartbeatEnableInstructionsOnPrem() { - const defaultTitle = i18n.translate( - 'home.tutorials.common.heartbeatEnableOnPremInstructions.defaultTitle', - { - defaultMessage: 'Edit the configuration - Add monitors', - } - ); - const defaultCommands = [ - 'heartbeat.monitors:', - '- type: http', - ' urls: [""]', - ' schedule: "@every 10s"', - ]; - const defaultTextPost = i18n.translate( - 'home.tutorials.common.heartbeatEnableOnPremInstructions.defaultTextPost', - { - defaultMessage: - 'Where {hostTemplate} is your monitored URL, For more details on how to configure Monitors in \ - Heartbeat, read the [Heartbeat configuration docs.]({configureLink})', - values: { - configureLink: '{config.docs.beats.heartbeat}/configuring-howto-heartbeat.html', - hostTemplate: '``', - }, - } - ); - return { - OSX: { - title: defaultTitle, - textPre: i18n.translate( - 'home.tutorials.common.heartbeatEnableOnPremInstructions.osxTextPre', - { - defaultMessage: 'Edit the `heartbeat.monitors` setting in the `heartbeat.yml` file.', - } - ), - commands: defaultCommands, - textPost: defaultTextPost, - }, - DEB: { - title: defaultTitle, - textPre: i18n.translate( - 'home.tutorials.common.heartbeatEnableOnPremInstructions.debTextPre', - { - defaultMessage: 'Edit the `heartbeat.monitors` setting in the `heartbeat.yml` file.', - } - ), - commands: defaultCommands, - textPost: defaultTextPost, - }, - RPM: { - title: defaultTitle, - textPre: i18n.translate( - 'home.tutorials.common.heartbeatEnableOnPremInstructions.rpmTextPre', - { - defaultMessage: 'Edit the `heartbeat.monitors` setting in the `heartbeat.yml` file.', - } - ), - commands: defaultCommands, - textPost: defaultTextPost, - }, - WINDOWS: { - title: defaultTitle, - textPre: i18n.translate( - 'home.tutorials.common.heartbeatEnableOnPremInstructions.windowsTextPre', - { - defaultMessage: 'Edit the `heartbeat.monitors` setting in the `heartbeat.yml` file.', - } - ), - commands: defaultCommands, - textPost: defaultTextPost, - }, - }; -} - -export function heartbeatEnableInstructionsCloud() { - const defaultTitle = i18n.translate( - 'home.tutorials.common.heartbeatEnableCloudInstructions.defaultTitle', - { - defaultMessage: 'Edit the configuration - Add monitors', - } - ); - const defaultCommands = [ - 'heartbeat.monitors:', - '- type: http', - ' urls: ["http://opensearch.org"]', - ' schedule: "@every 10s"', - ]; - const defaultTextPost = i18n.translate( - 'home.tutorials.common.heartbeatEnableCloudInstructions.defaultTextPost', - { - defaultMessage: - 'For more details on how to configure Monitors in Heartbeat, read the [Heartbeat configuration docs.]({configureLink})', - values: { configureLink: '{config.docs.beats.heartbeat}/configuring-howto-heartbeat.html' }, - } - ); - return { - OSX: { - title: defaultTitle, - textPre: i18n.translate('home.tutorials.common.heartbeatEnableCloudInstructions.osxTextPre', { - defaultMessage: 'Edit the `heartbeat.monitors` setting in the `heartbeat.yml` file.', - }), - commands: defaultCommands, - textPost: defaultTextPost, - }, - DEB: { - title: defaultTitle, - textPre: i18n.translate('home.tutorials.common.heartbeatEnableCloudInstructions.debTextPre', { - defaultMessage: 'Edit the `heartbeat.monitors` setting in the `heartbeat.yml` file.', - }), - commands: defaultCommands, - textPost: defaultTextPost, - }, - RPM: { - title: defaultTitle, - textPre: i18n.translate('home.tutorials.common.heartbeatEnableCloudInstructions.rpmTextPre', { - defaultMessage: 'Edit the `heartbeat.monitors` setting in the `heartbeat.yml` file.', - }), - commands: defaultCommands, - textPost: defaultTextPost, - }, - WINDOWS: { - title: defaultTitle, - textPre: i18n.translate( - 'home.tutorials.common.heartbeatEnableCloudInstructions.windowsTextPre', - { - defaultMessage: 'Edit the `heartbeat.monitors` setting in the `heartbeat.yml` file.', - } - ), - commands: defaultCommands, - textPost: defaultTextPost, - }, - }; -} - -export function heartbeatStatusCheck() { - return { - title: i18n.translate('home.tutorials.common.heartbeatStatusCheck.title', { - defaultMessage: 'Heartbeat status', - }), - text: i18n.translate('home.tutorials.common.heartbeatStatusCheck.text', { - defaultMessage: 'Check that data is received from Heartbeat', - }), - btnLabel: i18n.translate('home.tutorials.common.heartbeatStatusCheck.buttonLabel', { - defaultMessage: 'Check data', - }), - success: i18n.translate('home.tutorials.common.heartbeatStatusCheck.successText', { - defaultMessage: 'Data successfully received from Heartbeat', - }), - error: i18n.translate('home.tutorials.common.heartbeatStatusCheck.errorText', { - defaultMessage: 'No data has been received from Heartbeat yet', - }), - opensearchHitsCheck: { - index: 'heartbeat-*', - query: { - match_all: {}, - }, - }, - }; -} - -export function onPremInstructions(platforms: Platform[], context?: TutorialContext) { - const HEARTBEAT_INSTRUCTIONS = createHeartbeatInstructions(context); - - return { - instructionSets: [ - { - title: i18n.translate( - 'home.tutorials.common.heartbeat.premInstructions.gettingStarted.title', - { - defaultMessage: 'Getting Started', - } - ), - instructionVariants: [ - { - id: INSTRUCTION_VARIANT.OSX, - instructions: [ - HEARTBEAT_INSTRUCTIONS.INSTALL.OSX, - HEARTBEAT_INSTRUCTIONS.CONFIG.OSX, - heartbeatEnableInstructionsOnPrem().OSX, - HEARTBEAT_INSTRUCTIONS.START.OSX, - ], - }, - { - id: INSTRUCTION_VARIANT.DEB, - instructions: [ - HEARTBEAT_INSTRUCTIONS.INSTALL.DEB, - HEARTBEAT_INSTRUCTIONS.CONFIG.DEB, - heartbeatEnableInstructionsOnPrem().DEB, - HEARTBEAT_INSTRUCTIONS.START.DEB, - ], - }, - { - id: INSTRUCTION_VARIANT.RPM, - instructions: [ - HEARTBEAT_INSTRUCTIONS.INSTALL.RPM, - HEARTBEAT_INSTRUCTIONS.CONFIG.RPM, - heartbeatEnableInstructionsOnPrem().RPM, - HEARTBEAT_INSTRUCTIONS.START.RPM, - ], - }, - { - id: INSTRUCTION_VARIANT.WINDOWS, - instructions: [ - HEARTBEAT_INSTRUCTIONS.INSTALL.WINDOWS, - HEARTBEAT_INSTRUCTIONS.CONFIG.WINDOWS, - heartbeatEnableInstructionsOnPrem().WINDOWS, - HEARTBEAT_INSTRUCTIONS.START.WINDOWS, - ], - }, - ], - statusCheck: heartbeatStatusCheck(), - }, - ], - }; -} diff --git a/src/plugins/home/server/tutorials/instructions/logstash_instructions.ts b/src/plugins/home/server/tutorials/instructions/logstash_instructions.ts deleted file mode 100644 index defab1fc357b..000000000000 --- a/src/plugins/home/server/tutorials/instructions/logstash_instructions.ts +++ /dev/null @@ -1,117 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; - -export const createLogstashInstructions = () => ({ - INSTALL: { - OSX: [ - { - title: i18n.translate('home.tutorials.common.logstashInstructions.install.java.osxTitle', { - defaultMessage: 'Download and install the Java Runtime Environment', - }), - textPre: i18n.translate( - 'home.tutorials.common.logstashInstructions.install.java.osxTextPre', - { - defaultMessage: 'Follow the installation instructions [here]({link}).', - values: { - link: 'https://docs.oracle.com/javase/8/docs/technotes/guides/install/mac_jre.html', - }, - } - ), - }, - { - title: i18n.translate( - 'home.tutorials.common.logstashInstructions.install.logstash.osxTitle', - { - defaultMessage: 'Download and install Logstash', - } - ), - textPre: i18n.translate( - 'home.tutorials.common.logstashInstructions.install.logstash.osxTextPre', - { - defaultMessage: 'First time using Logstash? See the [Getting Started Guide]({link}).', - values: { - link: - '{config.docs.base_url}guide/en/logstash/current/getting-started-with-logstash.html', - }, - } - ), - commands: [ - 'curl -L -O https://artifacts.opensearch.org/downloads/logstash/logstash-{config.opensearchDashboards.version}.tar.gz', - 'tar xzvf logstash-{config.opensearchDashboards.version}.tar.gz', - ], - }, - ], - WINDOWS: [ - { - title: i18n.translate( - 'home.tutorials.common.logstashInstructions.install.java.windowsTitle', - { - defaultMessage: 'Download and install the Java Runtime Environment', - } - ), - textPre: i18n.translate( - 'home.tutorials.common.logstashInstructions.install.java.windowsTextPre', - { - defaultMessage: 'Follow the installation instructions [here]({link}).', - values: { - link: - 'https://docs.oracle.com/javase/8/docs/technotes/guides/install/windows_jre_install.html', - }, - } - ), - }, - { - title: i18n.translate( - 'home.tutorials.common.logstashInstructions.install.logstash.windowsTitle', - { - defaultMessage: 'Download and install Logstash', - } - ), - textPre: i18n.translate( - 'home.tutorials.common.logstashInstructions.install.logstash.windowsTextPre', - { - defaultMessage: - 'First time using Logstash? See the [Getting Started Guide]({logstashLink}).\n\ - 1. [Download]({opensearchLink}) the Logstash Windows zip file.\n\ - 2. Extract the contents of the zip file.', - values: { - logstashLink: - '{config.docs.base_url}guide/en/logstash/current/getting-started-with-logstash.html', - opensearchLink: - 'https://artifacts.opensearch.org/downloads/logstash/logstash-{config.opensearchDashboards.version}.zip', - }, - } - ), - }, - ], - }, -}); diff --git a/src/plugins/home/server/tutorials/instructions/metricbeat_instructions.ts b/src/plugins/home/server/tutorials/instructions/metricbeat_instructions.ts deleted file mode 100644 index 9a14075eab75..000000000000 --- a/src/plugins/home/server/tutorials/instructions/metricbeat_instructions.ts +++ /dev/null @@ -1,447 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { INSTRUCTION_VARIANT } from '../../../common/instruction_variant'; -import { getSpaceIdForBeatsTutorial } from './get_space_id_for_beats_tutorial'; -import { TutorialContext } from '../../services/tutorials/lib/tutorials_registry_types'; - -export const createMetricbeatInstructions = (context?: TutorialContext) => ({ - INSTALL: { - OSX: { - title: i18n.translate('home.tutorials.common.metricbeatInstructions.install.osxTitle', { - defaultMessage: 'Download and install Metricbeat', - }), - textPre: i18n.translate('home.tutorials.common.metricbeatInstructions.install.osxTextPre', { - defaultMessage: 'First time using Metricbeat? See the [Quick Start]({link}).', - values: { - link: '{config.docs.beats.metricbeat}/metricbeat-installation-configuration.html', - }, - }), - commands: [ - 'curl -L -O https://artifacts.opensearch.org/downloads/beats/metricbeat/metricbeat-{config.opensearchDashboards.version}-darwin-x64.tar.gz', - 'tar xzvf metricbeat-{config.opensearchDashboards.version}-darwin-x64.tar.gz', - 'cd metricbeat-{config.opensearchDashboards.version}-darwin-x64/', - ], - }, - DEB: { - title: i18n.translate('home.tutorials.common.metricbeatInstructions.install.debTitle', { - defaultMessage: 'Download and install Metricbeat', - }), - textPre: i18n.translate('home.tutorials.common.metricbeatInstructions.install.debTextPre', { - defaultMessage: 'First time using Metricbeat? See the [Quick Start]({link}).', - values: { - link: '{config.docs.beats.metricbeat}/metricbeat-installation-configuration.html', - }, - }), - commands: [ - 'curl -L -O https://artifacts.opensearch.org/downloads/beats/metricbeat/metricbeat-{config.opensearchDashboards.version}-amd64.deb', - 'sudo dpkg -i metricbeat-{config.opensearchDashboards.version}-amd64.deb', - ], - textPost: i18n.translate('home.tutorials.common.metricbeatInstructions.install.debTextPost', { - defaultMessage: 'Looking for the 32-bit packages? See the [Download page]({link}).', - values: { link: 'https://opensearch.org/docs/latest/downloads/beats/metricbeat' }, - }), - }, - RPM: { - title: i18n.translate('home.tutorials.common.metricbeatInstructions.install.rpmTitle', { - defaultMessage: 'Download and install Metricbeat', - }), - textPre: i18n.translate('home.tutorials.common.metricbeatInstructions.install.rpmTextPre', { - defaultMessage: 'First time using Metricbeat? See the [Quick Start]({link}).', - values: { - link: '{config.docs.beats.metricbeat}/metricbeat-installation-configuration.html', - }, - }), - commands: [ - 'curl -L -O https://artifacts.opensearch.org/downloads/beats/metricbeat/metricbeat-{config.opensearchDashboards.version}-x64.rpm', - 'sudo rpm -vi metricbeat-{config.opensearchDashboards.version}-x64.rpm', - ], - textPost: i18n.translate('home.tutorials.common.metricbeatInstructions.install.debTextPost', { - defaultMessage: 'Looking for the 32-bit packages? See the [Download page]({link}).', - values: { link: 'https://opensearch.org/docs/latest/downloads/beats/metricbeat' }, - }), - }, - WINDOWS: { - title: i18n.translate('home.tutorials.common.metricbeatInstructions.install.windowsTitle', { - defaultMessage: 'Download and install Metricbeat', - }), - textPre: i18n.translate( - 'home.tutorials.common.metricbeatInstructions.install.windowsTextPre', - { - defaultMessage: - 'First time using Metricbeat? See the [Quick Start]({metricbeatLink}).\n\ - 1. Download the Metricbeat Windows zip file from the [Download]({opensearchLink}) page.\n\ - 2. Extract the contents of the zip file into {folderPath}.\n\ - 3. Rename the {directoryName} directory to `Metricbeat`.\n\ - 4. Open a PowerShell prompt as an Administrator (right-click the PowerShell icon and select \ -**Run As Administrator**). If you are running Windows XP, you might need to download and install PowerShell.\n\ - 5. From the PowerShell prompt, run the following commands to install Metricbeat as a Windows service.', - values: { - directoryName: '`metricbeat-{config.opensearchDashboards.version}-windows`', - folderPath: '`C:\\Program Files`', - metricbeatLink: - '{config.docs.beats.metricbeat}/metricbeat-installation-configuration.html', - opensearchLink: 'https://opensearch.org/docs/latest/downloads/beats/metricbeat', - }, - } - ), - commands: ['cd "C:\\Program Files\\Metricbeat"', '.\\install-service-metricbeat.ps1'], - textPost: i18n.translate( - 'home.tutorials.common.metricbeatInstructions.install.windowsTextPost', - { - defaultMessage: - 'Modify the settings under `output.opensearch` in the {path} file to point to your opensearch installation.', - values: { path: '`C:\\Program Files\\Metricbeat\\metricbeat.yml`' }, - } - ), - }, - }, - START: { - OSX: { - title: i18n.translate('home.tutorials.common.metricbeatInstructions.start.osxTitle', { - defaultMessage: 'Start Metricbeat', - }), - textPre: i18n.translate('home.tutorials.common.metricbeatInstructions.start.osxTextPre', { - defaultMessage: - 'The `setup` command loads the OpenSearch Dashboards dashboards. If the dashboards are already set up, omit this command.', - }), - commands: ['./metricbeat setup', './metricbeat -e'], - }, - DEB: { - title: i18n.translate('home.tutorials.common.metricbeatInstructions.start.debTitle', { - defaultMessage: 'Start Metricbeat', - }), - textPre: i18n.translate('home.tutorials.common.metricbeatInstructions.start.debTextPre', { - defaultMessage: - 'The `setup` command loads the OpenSearch Dashboards dashboards. If the dashboards are already set up, omit this command.', - }), - commands: ['sudo metricbeat setup', 'sudo service metricbeat start'], - }, - RPM: { - title: i18n.translate('home.tutorials.common.metricbeatInstructions.start.rpmTitle', { - defaultMessage: 'Start Metricbeat', - }), - textPre: i18n.translate('home.tutorials.common.metricbeatInstructions.start.rpmTextPre', { - defaultMessage: - 'The `setup` command loads the OpenSearch Dashboards dashboards. If the dashboards are already set up, omit this command.', - }), - commands: ['sudo metricbeat setup', 'sudo service metricbeat start'], - }, - WINDOWS: { - title: i18n.translate('home.tutorials.common.metricbeatInstructions.start.windowsTitle', { - defaultMessage: 'Start Metricbeat', - }), - textPre: i18n.translate('home.tutorials.common.metricbeatInstructions.start.windowsTextPre', { - defaultMessage: - 'The `setup` command loads the OpenSearch Dashboards dashboards. If the dashboards are already set up, omit this command.', - }), - commands: ['.\\metricbeat.exe setup', 'Start-Service metricbeat'], - }, - }, - CONFIG: { - OSX: { - title: i18n.translate('home.tutorials.common.metricbeatInstructions.config.osxTitle', { - defaultMessage: 'Edit the configuration', - }), - textPre: i18n.translate('home.tutorials.common.metricbeatInstructions.config.osxTextPre', { - defaultMessage: 'Modify {path} to set the connection information:', - values: { - path: '`metricbeat.yml`', - }, - }), - commands: [ - 'output.opensearch:', - ' hosts: [""]', - ' username: "opensearch"', - ' password: ""', - 'setup.opensearchDashboards:', - ' host: ""', - getSpaceIdForBeatsTutorial(context), - ], - textPost: i18n.translate('home.tutorials.common.metricbeatInstructions.config.osxTextPost', { - defaultMessage: - 'Where {passwordTemplate} is the password of the `opensearch` user, {opensearchUrlTemplate} is the URL of opensearch, \ -and {opensearchDashboardsUrlTemplate} is the URL of OpenSearch Dashboards.', - values: { - passwordTemplate: '``', - opensearchUrlTemplate: '``', - opensearchDashboardsUrlTemplate: '``', - }, - }), - }, - DEB: { - title: i18n.translate('home.tutorials.common.metricbeatInstructions.config.debTitle', { - defaultMessage: 'Edit the configuration', - }), - textPre: i18n.translate('home.tutorials.common.metricbeatInstructions.config.debTextPre', { - defaultMessage: 'Modify {path} to set the connection information:', - values: { - path: '`/etc/metricbeat/metricbeat.yml`', - }, - }), - commands: [ - 'output.opensearch:', - ' hosts: [""]', - ' username: "opensearch"', - ' password: ""', - 'setup.opensearchDashboards:', - ' host: ""', - getSpaceIdForBeatsTutorial(context), - ], - textPost: i18n.translate('home.tutorials.common.metricbeatInstructions.config.debTextPost', { - defaultMessage: - 'Where {passwordTemplate} is the password of the `opensearch` user, {opensearchUrlTemplate} is the URL of opensearch, \ -and {opensearchDashboardsUrlTemplate} is the URL of OpenSearch Dashboards.', - values: { - passwordTemplate: '``', - opensearchUrlTemplate: '``', - opensearchDashboardsUrlTemplate: '``', - }, - }), - }, - RPM: { - title: i18n.translate('home.tutorials.common.metricbeatInstructions.config.rpmTitle', { - defaultMessage: 'Edit the configuration', - }), - textPre: i18n.translate('home.tutorials.common.metricbeatInstructions.config.rpmTextPre', { - defaultMessage: 'Modify {path} to set the connection information:', - values: { - path: '`/etc/metricbeat/metricbeat.yml`', - }, - }), - commands: [ - 'output.opensearch:', - ' hosts: [""]', - ' username: "opensearch"', - ' password: ""', - 'setup.opensearchDashboards:', - ' host: ""', - getSpaceIdForBeatsTutorial(context), - ], - textPost: i18n.translate('home.tutorials.common.metricbeatInstructions.config.rpmTextPost', { - defaultMessage: - 'Where {passwordTemplate} is the password of the `opensearch` user, {opensearchUrlTemplate} is the URL of opensearch, \ -and {opensearchDashboardsUrlTemplate} is the URL of OpenSearch Dashboards.', - values: { - passwordTemplate: '``', - opensearchUrlTemplate: '``', - opensearchDashboardsUrlTemplate: '``', - }, - }), - }, - WINDOWS: { - title: i18n.translate('home.tutorials.common.metricbeatInstructions.config.windowsTitle', { - defaultMessage: 'Edit the configuration', - }), - textPre: i18n.translate( - 'home.tutorials.common.metricbeatInstructions.config.windowsTextPre', - { - defaultMessage: 'Modify {path} to set the connection information:', - values: { - path: '`C:\\Program Files\\Metricbeat\\metricbeat.yml`', - }, - } - ), - commands: [ - 'output.opensearch:', - ' hosts: [""]', - ' username: "opensearch"', - ' password: ""', - 'setup.opensearchDashboards:', - ' host: ""', - getSpaceIdForBeatsTutorial(context), - ], - textPost: i18n.translate( - 'home.tutorials.common.metricbeatInstructions.config.windowsTextPost', - { - defaultMessage: - 'Where {passwordTemplate} is the password of the `opensearch` user, {opensearchUrlTemplate} is the URL of opensearch, \ -and {opensearchDashboardsUrlTemplate} is the URL of OpenSearch Dashboards.', - values: { - passwordTemplate: '``', - opensearchUrlTemplate: '``', - opensearchDashboardsUrlTemplate: '``', - }, - } - ), - }, - }, -}); - -export function metricbeatEnableInstructions(moduleName: string) { - return { - OSX: { - title: i18n.translate('home.tutorials.common.metricbeatEnableInstructions.osxTitle', { - defaultMessage: 'Enable and configure the {moduleName} module', - values: { moduleName }, - }), - textPre: i18n.translate('home.tutorials.common.metricbeatEnableInstructions.osxTextPre', { - defaultMessage: 'From the installation directory, run:', - }), - commands: ['./metricbeat modules enable ' + moduleName], - textPost: i18n.translate('home.tutorials.common.metricbeatEnableInstructions.osxTextPost', { - defaultMessage: 'Modify the settings in the `modules.d/{moduleName}.yml` file.', - values: { moduleName }, - }), - }, - DEB: { - title: i18n.translate('home.tutorials.common.metricbeatEnableInstructions.debTitle', { - defaultMessage: 'Enable and configure the {moduleName} module', - values: { moduleName }, - }), - commands: ['sudo metricbeat modules enable ' + moduleName], - textPost: i18n.translate('home.tutorials.common.metricbeatEnableInstructions.debTextPost', { - defaultMessage: - 'Modify the settings in the `/etc/metricbeat/modules.d/{moduleName}.yml` file.', - values: { moduleName }, - }), - }, - RPM: { - title: i18n.translate('home.tutorials.common.metricbeatEnableInstructions.rpmTitle', { - defaultMessage: 'Enable and configure the {moduleName} module', - values: { moduleName }, - }), - commands: ['sudo metricbeat modules enable ' + moduleName], - textPost: i18n.translate('home.tutorials.common.metricbeatEnableInstructions.rpmTextPost', { - defaultMessage: - 'Modify the settings in the `/etc/metricbeat/modules.d/{moduleName}.yml` file.', - values: { moduleName }, - }), - }, - WINDOWS: { - title: i18n.translate('home.tutorials.common.metricbeatEnableInstructions.windowsTitle', { - defaultMessage: 'Enable and configure the {moduleName} module', - values: { moduleName }, - }), - textPre: i18n.translate('home.tutorials.common.metricbeatEnableInstructions.windowsTextPre', { - defaultMessage: 'From the {path} folder, run:', - values: { path: `C:\\Program Files\\Metricbeat` }, - }), - commands: ['.\\metricbeat.exe modules enable ' + moduleName], - textPost: i18n.translate( - 'home.tutorials.common.metricbeatEnableInstructions.windowsTextPost', - { - defaultMessage: 'Modify the settings in the `modules.d/{moduleName}.yml` file.', - values: { moduleName }, - } - ), - }, - }; -} - -export function metricbeatStatusCheck(moduleName: string) { - return { - title: i18n.translate('home.tutorials.common.metricbeatStatusCheck.title', { - defaultMessage: 'Module status', - }), - text: i18n.translate('home.tutorials.common.metricbeatStatusCheck.text', { - defaultMessage: 'Check that data is received from the Metricbeat `{moduleName}` module', - values: { moduleName }, - }), - btnLabel: i18n.translate('home.tutorials.common.metricbeatStatusCheck.buttonLabel', { - defaultMessage: 'Check data', - }), - success: i18n.translate('home.tutorials.common.metricbeatStatusCheck.successText', { - defaultMessage: 'Data successfully received from this module', - }), - error: i18n.translate('home.tutorials.common.metricbeatStatusCheck.errorText', { - defaultMessage: 'No data has been received from this module yet', - }), - opensearchHitsCheck: { - index: 'metricbeat-*', - query: { - bool: { - filter: { - term: { - 'event.module': moduleName, - }, - }, - }, - }, - }, - }; -} - -export function onPremInstructions(moduleName: string, context?: TutorialContext) { - const METRICBEAT_INSTRUCTIONS = createMetricbeatInstructions(context); - - return { - instructionSets: [ - { - title: i18n.translate( - 'home.tutorials.common.metricbeat.premInstructions.gettingStarted.title', - { - defaultMessage: 'Getting Started', - } - ), - instructionVariants: [ - { - id: INSTRUCTION_VARIANT.OSX, - instructions: [ - METRICBEAT_INSTRUCTIONS.INSTALL.OSX, - METRICBEAT_INSTRUCTIONS.CONFIG.OSX, - metricbeatEnableInstructions(moduleName).OSX, - METRICBEAT_INSTRUCTIONS.START.OSX, - ], - }, - { - id: INSTRUCTION_VARIANT.DEB, - instructions: [ - METRICBEAT_INSTRUCTIONS.INSTALL.DEB, - METRICBEAT_INSTRUCTIONS.CONFIG.DEB, - metricbeatEnableInstructions(moduleName).DEB, - METRICBEAT_INSTRUCTIONS.START.DEB, - ], - }, - { - id: INSTRUCTION_VARIANT.RPM, - instructions: [ - METRICBEAT_INSTRUCTIONS.INSTALL.RPM, - METRICBEAT_INSTRUCTIONS.CONFIG.RPM, - metricbeatEnableInstructions(moduleName).RPM, - METRICBEAT_INSTRUCTIONS.START.RPM, - ], - }, - { - id: INSTRUCTION_VARIANT.WINDOWS, - instructions: [ - METRICBEAT_INSTRUCTIONS.INSTALL.WINDOWS, - METRICBEAT_INSTRUCTIONS.CONFIG.WINDOWS, - metricbeatEnableInstructions(moduleName).WINDOWS, - METRICBEAT_INSTRUCTIONS.START.WINDOWS, - ], - }, - ], - statusCheck: metricbeatStatusCheck(moduleName), - }, - ], - }; -} diff --git a/src/plugins/home/server/tutorials/instructions/param_types.ts b/src/plugins/home/server/tutorials/instructions/param_types.ts deleted file mode 100644 index f5c04d8e6490..000000000000 --- a/src/plugins/home/server/tutorials/instructions/param_types.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export const PARAM_TYPES = { - NUMBER: 'number', - STRING: 'string', -}; diff --git a/src/plugins/home/server/tutorials/instructions/winlogbeat_instructions.ts b/src/plugins/home/server/tutorials/instructions/winlogbeat_instructions.ts deleted file mode 100644 index 099750e7aa42..000000000000 --- a/src/plugins/home/server/tutorials/instructions/winlogbeat_instructions.ts +++ /dev/null @@ -1,183 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { INSTRUCTION_VARIANT } from '../../../common/instruction_variant'; -import { getSpaceIdForBeatsTutorial } from './get_space_id_for_beats_tutorial'; -import { TutorialContext } from '../../services/tutorials/lib/tutorials_registry_types'; - -export const createWinlogbeatInstructions = (context?: TutorialContext) => ({ - INSTALL: { - WINDOWS: { - title: i18n.translate('home.tutorials.common.winlogbeatInstructions.install.windowsTitle', { - defaultMessage: 'Download and install Winlogbeat', - }), - textPre: i18n.translate( - 'home.tutorials.common.winlogbeatInstructions.install.windowsTextPre', - { - defaultMessage: - 'First time using Winlogbeat? See the [Quick Start]({winlogbeatLink}).\n\ - 1. Download the Winlogbeat Windows zip file from the [Download]({opensearchLink}) page.\n\ - 2. Extract the contents of the zip file into {folderPath}.\n\ - 3. Rename the {directoryName} directory to `Winlogbeat`.\n\ - 4. Open a PowerShell prompt as an Administrator (right-click the PowerShell icon and select \ -**Run As Administrator**). If you are running Windows XP, you might need to download and install PowerShell.\n\ - 5. From the PowerShell prompt, run the following commands to install Winlogbeat as a Windows service.', - values: { - directoryName: '`winlogbeat-{config.opensearchDashboards.version}-windows`', - folderPath: '`C:\\Program Files`', - winlogbeatLink: - '{config.docs.beats.winlogbeat}/winlogbeat-installation-configuration.html', - opensearchLink: 'https://opensearch.org/downloads/beats/winlogbeat', - }, - } - ), - commands: ['cd "C:\\Program Files\\Winlogbeat"', '.\\install-service-winlogbeat.ps1'], - textPost: i18n.translate( - 'home.tutorials.common.winlogbeatInstructions.install.windowsTextPost', - { - defaultMessage: - 'Modify the settings under `output.opensearch` in the {path} file to point to your opensearch installation.', - values: { path: '`C:\\Program Files\\Winlogbeat\\winlogbeat.yml`' }, - } - ), - }, - }, - START: { - WINDOWS: { - title: i18n.translate('home.tutorials.common.winlogbeatInstructions.start.windowsTitle', { - defaultMessage: 'Start Winlogbeat', - }), - textPre: i18n.translate('home.tutorials.common.winlogbeatInstructions.start.windowsTextPre', { - defaultMessage: - 'The `setup` command loads the OpenSearch Dashboards dashboards. If the dashboards are already set up, omit this command.', - }), - commands: ['.\\winlogbeat.exe setup', 'Start-Service winlogbeat'], - }, - }, - CONFIG: { - WINDOWS: { - title: i18n.translate('home.tutorials.common.winlogbeatInstructions.config.windowsTitle', { - defaultMessage: 'Edit the configuration', - }), - textPre: i18n.translate( - 'home.tutorials.common.winlogbeatInstructions.config.windowsTextPre', - { - defaultMessage: 'Modify {path} to set the connection information:', - values: { - path: '`C:\\Program Files\\Winlogbeat\\winlogbeat.yml`', - }, - } - ), - commands: [ - 'output.opensearch:', - ' hosts: [""]', - ' username: "opensearch"', - ' password: ""', - 'setup.opensearchDashboards:', - ' host: ""', - getSpaceIdForBeatsTutorial(context), - ], - textPost: i18n.translate( - 'home.tutorials.common.winlogbeatInstructions.config.windowsTextPost', - { - defaultMessage: - 'Where {passwordTemplate} is the password of the `opensearch` user, {opensearchUrlTemplate} is the URL of opensearch, \ -and {opensearchDashboardsUrlTemplate} is the URL of OpenSearch Dashboards.', - values: { - passwordTemplate: '``', - opensearchUrlTemplate: '``', - opensearchDashboardsUrlTemplate: '``', - }, - } - ), - }, - }, -}); - -export function winlogbeatStatusCheck() { - return { - title: i18n.translate('home.tutorials.common.winlogbeatStatusCheck.title', { - defaultMessage: 'Module status', - }), - text: i18n.translate('home.tutorials.common.winlogbeatStatusCheck.text', { - defaultMessage: 'Check that data is received from Winlogbeat', - }), - btnLabel: i18n.translate('home.tutorials.common.winlogbeatStatusCheck.buttonLabel', { - defaultMessage: 'Check data', - }), - success: i18n.translate('home.tutorials.common.winlogbeatStatusCheck.successText', { - defaultMessage: 'Data successfully received', - }), - error: i18n.translate('home.tutorials.common.winlogbeatStatusCheck.errorText', { - defaultMessage: 'No data has been received yet', - }), - opensearchHitsCheck: { - index: 'winlogbeat-*', - query: { - bool: { - filter: { - term: { - 'agent.type': 'winlogbeat', - }, - }, - }, - }, - }, - }; -} - -export function onPremInstructions(context?: TutorialContext) { - const WINLOGBEAT_INSTRUCTIONS = createWinlogbeatInstructions(context); - - return { - instructionSets: [ - { - title: i18n.translate( - 'home.tutorials.common.winlogbeat.premInstructions.gettingStarted.title', - { - defaultMessage: 'Getting Started', - } - ), - instructionVariants: [ - { - id: INSTRUCTION_VARIANT.WINDOWS, - instructions: [ - WINLOGBEAT_INSTRUCTIONS.INSTALL.WINDOWS, - WINLOGBEAT_INSTRUCTIONS.CONFIG.WINDOWS, - WINLOGBEAT_INSTRUCTIONS.START.WINDOWS, - ], - }, - ], - statusCheck: winlogbeatStatusCheck(), - }, - ], - }; -} diff --git a/src/plugins/home/server/tutorials/iptables_logs/index.ts b/src/plugins/home/server/tutorials/iptables_logs/index.ts deleted file mode 100644 index 8eadda84411f..000000000000 --- a/src/plugins/home/server/tutorials/iptables_logs/index.ts +++ /dev/null @@ -1,81 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function iptablesLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'iptables'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'iptablesLogs', - name: i18n.translate('home.tutorials.iptablesLogs.nameTitle', { - defaultMessage: 'Iptables logs', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.iptablesLogs.shortDescription', { - defaultMessage: 'Collect iptables and ip6tables logs.', - }), - longDescription: i18n.translate('home.tutorials.iptablesLogs.longDescription', { - defaultMessage: - 'This is a module for iptables and ip6tables logs. It parses logs received \ - over the network via syslog or from a file. Also, it understands the prefix \ - added by some Ubiquiti firewalls, which includes the rule set name, rule \ - number and the action performed on the traffic (allow/deny). \ - [Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-iptables.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/linux.svg', - artifacts: { - dashboards: [ - { - id: 'ceefb9e0-1f51-11e9-93ed-f7e068f4aebb-ecs', - linkLabel: i18n.translate('home.tutorials.iptablesLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'Iptables Overview', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-iptables.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/juniper_logs/index.ts b/src/plugins/home/server/tutorials/juniper_logs/index.ts deleted file mode 100644 index 8c0b2a0a73b9..000000000000 --- a/src/plugins/home/server/tutorials/juniper_logs/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function juniperLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'juniper'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'juniperLogs', - name: i18n.translate('home.tutorials.juniperLogs.nameTitle', { - defaultMessage: 'Juniper Logs', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.juniperLogs.shortDescription', { - defaultMessage: 'Collect Juniper JUNOS logs over syslog or from a file.', - }), - longDescription: i18n.translate('home.tutorials.juniperLogs.longDescription', { - defaultMessage: - 'This is a module for receiving Juniper JUNOS logs over Syslog or a file. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-juniper.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/juniper.svg', - artifacts: { - dashboards: [], - application: { - path: '/app/security', - label: i18n.translate('home.tutorials.juniperLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'Security App', - }), - }, - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-juniper.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/kafka_logs/index.ts b/src/plugins/home/server/tutorials/kafka_logs/index.ts deleted file mode 100644 index fb5a71c7d172..000000000000 --- a/src/plugins/home/server/tutorials/kafka_logs/index.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function kafkaLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'kafka'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'kafkaLogs', - name: i18n.translate('home.tutorials.kafkaLogs.nameTitle', { - defaultMessage: 'Kafka logs', - }), - moduleName, - category: TutorialsCategory.LOGGING, - shortDescription: i18n.translate('home.tutorials.kafkaLogs.shortDescription', { - defaultMessage: 'Collect and parse logs created by Kafka.', - }), - longDescription: i18n.translate('home.tutorials.kafkaLogs.longDescription', { - defaultMessage: - 'The `kafka` Filebeat module parses logs created by Kafka. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-kafka.html', - }, - }), - euiIconType: 'logoKafka', - artifacts: { - dashboards: [ - { - id: '943caca0-87ee-11e7-ad9c-db80de0bf8d3-ecs', - linkLabel: i18n.translate('home.tutorials.kafkaLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'Kafka logs dashboard', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-kafka.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/kafka_metrics/index.ts b/src/plugins/home/server/tutorials/kafka_metrics/index.ts deleted file mode 100644 index f9c8cc3a9b71..000000000000 --- a/src/plugins/home/server/tutorials/kafka_metrics/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function kafkaMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'kafka'; - return { - id: 'kafkaMetrics', - name: i18n.translate('home.tutorials.kafkaMetrics.nameTitle', { - defaultMessage: 'Kafka metrics', - }), - moduleName, - isBeta: false, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.kafkaMetrics.shortDescription', { - defaultMessage: 'Fetch internal metrics from the Kafka server.', - }), - longDescription: i18n.translate('home.tutorials.kafkaMetrics.longDescription', { - defaultMessage: - 'The `kafka` Metricbeat module fetches internal metrics from Kafka. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-kafka.html', - }, - }), - euiIconType: 'logoKafka', - artifacts: { - application: { - label: i18n.translate('home.tutorials.kafkaMetrics.artifacts.application.label', { - defaultMessage: 'Discover', - }), - path: '/app/discover#/', - }, - dashboards: [], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-kafka.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/kubernetes_metrics/index.ts b/src/plugins/home/server/tutorials/kubernetes_metrics/index.ts deleted file mode 100644 index afb884a40bdf..000000000000 --- a/src/plugins/home/server/tutorials/kubernetes_metrics/index.ts +++ /dev/null @@ -1,80 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function kubernetesMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'kubernetes'; - return { - id: 'kubernetesMetrics', - name: i18n.translate('home.tutorials.kubernetesMetrics.nameTitle', { - defaultMessage: 'Kubernetes metrics', - }), - moduleName, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.kubernetesMetrics.shortDescription', { - defaultMessage: 'Fetch metrics from your Kubernetes installation.', - }), - longDescription: i18n.translate('home.tutorials.kubernetesMetrics.longDescription', { - defaultMessage: - 'The `kubernetes` Metricbeat module fetches metrics from the Kubernetes APIs. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-kubernetes.html', - }, - }), - euiIconType: 'logoKubernetes', - artifacts: { - dashboards: [ - { - id: 'AV4RGUqo5NkDleZmzKuZ-ecs', - linkLabel: i18n.translate( - 'home.tutorials.kubernetesMetrics.artifacts.dashboards.linkLabel', - { - defaultMessage: 'Kubernetes metrics dashboard', - } - ), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-kubernetes.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/logstash_logs/index.ts b/src/plugins/home/server/tutorials/logstash_logs/index.ts deleted file mode 100644 index e6124122b4b5..000000000000 --- a/src/plugins/home/server/tutorials/logstash_logs/index.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function logstashLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'logstash'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'logstashLogs', - name: i18n.translate('home.tutorials.logstashLogs.nameTitle', { - defaultMessage: 'Logstash logs', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.logstashLogs.shortDescription', { - defaultMessage: 'Collect Logstash main and slow logs.', - }), - longDescription: i18n.translate('home.tutorials.logstashLogs.longDescription', { - defaultMessage: - 'The modules parse Logstash regular logs and the slow log, it will support the plain text format and the JSON format. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-logstash.html', - }, - }), - euiIconType: 'logoLogstash', - artifacts: { - dashboards: [ - { - id: 'Filebeat-Logstash-Log-Dashboard-ecs', - linkLabel: i18n.translate('home.tutorials.logstashLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'Logstash Logs', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-logstash.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/logstash_metrics/index.ts b/src/plugins/home/server/tutorials/logstash_metrics/index.ts deleted file mode 100644 index 1a0f11ada0d3..000000000000 --- a/src/plugins/home/server/tutorials/logstash_metrics/index.ts +++ /dev/null @@ -1,77 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function logstashMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'logstash'; - return { - id: moduleName + 'Metrics', - name: i18n.translate('home.tutorials.logstashMetrics.nameTitle', { - defaultMessage: 'Logstash metrics', - }), - moduleName, - isBeta: false, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.logstashMetrics.shortDescription', { - defaultMessage: 'Fetch internal metrics from a Logstash server.', - }), - longDescription: i18n.translate('home.tutorials.logstashMetrics.longDescription', { - defaultMessage: - 'The `{moduleName}` Metricbeat module fetches internal metrics from a Logstash server. \ -[Learn more]({learnMoreLink}).', - values: { - moduleName, - learnMoreLink: `{config.docs.beats.metricbeat}/metricbeat-module-${moduleName}.html`, - }, - }), - euiIconType: 'logoLogstash', - artifacts: { - application: { - label: i18n.translate('home.tutorials.logstashMetrics.artifacts.application.label', { - defaultMessage: 'Discover', - }), - path: '/app/discover#/', - }, - dashboards: [], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-' + moduleName + '.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/memcached_metrics/index.ts b/src/plugins/home/server/tutorials/memcached_metrics/index.ts deleted file mode 100644 index ad4dc9f7eb63..000000000000 --- a/src/plugins/home/server/tutorials/memcached_metrics/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function memcachedMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'memcached'; - return { - id: 'memcachedMetrics', - name: i18n.translate('home.tutorials.memcachedMetrics.nameTitle', { - defaultMessage: 'Memcached metrics', - }), - moduleName, - isBeta: false, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.memcachedMetrics.shortDescription', { - defaultMessage: 'Fetch internal metrics from the Memcached server.', - }), - longDescription: i18n.translate('home.tutorials.memcachedMetrics.longDescription', { - defaultMessage: - 'The `memcached` Metricbeat module fetches internal metrics from Memcached. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-memcached.html', - }, - }), - euiIconType: 'logoMemcached', - artifacts: { - application: { - label: i18n.translate('home.tutorials.memcachedMetrics.artifacts.application.label', { - defaultMessage: 'Discover', - }), - path: '/app/discover#/', - }, - dashboards: [], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-memcached.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/microsoft_logs/index.ts b/src/plugins/home/server/tutorials/microsoft_logs/index.ts deleted file mode 100644 index 8f8c391ff445..000000000000 --- a/src/plugins/home/server/tutorials/microsoft_logs/index.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function microsoftLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'microsoft'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'microsoftLogs', - name: i18n.translate('home.tutorials.microsoftLogs.nameTitle', { - defaultMessage: 'Microsoft Defender ATP logs', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.microsoftLogs.shortDescription', { - defaultMessage: 'Collect Microsoft Defender ATP alerts.', - }), - longDescription: i18n.translate('home.tutorials.microsoftLogs.longDescription', { - defaultMessage: - 'Collect Microsoft Defender ATP alerts for use with OpenSearch Security. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-microsoft.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/microsoft.svg', - artifacts: { - dashboards: [ - { - id: '65402c30-ca6a-11ea-9d4d-9737a63aaa55', - linkLabel: i18n.translate('home.tutorials.microsoftLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'Microsoft ATP Overview', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-microsoft.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/misp_logs/index.ts b/src/plugins/home/server/tutorials/misp_logs/index.ts deleted file mode 100644 index 55e73c17a725..000000000000 --- a/src/plugins/home/server/tutorials/misp_logs/index.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function mispLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'misp'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'mispLogs', - name: i18n.translate('home.tutorials.mispLogs.nameTitle', { - defaultMessage: 'MISP threat intel logs', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.mispLogs.shortDescription', { - defaultMessage: 'Collect MISP threat intelligence data with Filebeat.', - }), - longDescription: i18n.translate('home.tutorials.mispLogs.longDescription', { - defaultMessage: - 'This is a filebeat module for reading threat intel information from the MISP platform ( https://www.circl.lu/doc/misp/). It uses the httpjson input to access the MISP REST API interface. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-misp.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/misp.svg', - artifacts: { - dashboards: [ - { - id: 'c6cac9e0-f105-11e9-9a88-690b10c8ee99', - linkLabel: i18n.translate('home.tutorials.mispLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'MISP Overview', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-misp.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/mongodb_logs/index.ts b/src/plugins/home/server/tutorials/mongodb_logs/index.ts deleted file mode 100644 index 3d5c31e5b4b5..000000000000 --- a/src/plugins/home/server/tutorials/mongodb_logs/index.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function mongodbLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'mongodb'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'mongodbLogs', - name: i18n.translate('home.tutorials.mongodbLogs.nameTitle', { - defaultMessage: 'MongoDB logs', - }), - moduleName, - category: TutorialsCategory.LOGGING, - shortDescription: i18n.translate('home.tutorials.mongodbLogs.shortDescription', { - defaultMessage: 'Collect MongoDB logs.', - }), - longDescription: i18n.translate('home.tutorials.mongodbLogs.longDescription', { - defaultMessage: - 'The module collects and parses logs created by [MongoDB](https://www.mongodb.com/). \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-mongodb.html', - }, - }), - euiIconType: 'logoMongodb', - artifacts: { - dashboards: [ - { - id: 'abcf35b0-0a82-11e8-bffe-ff7d4f68cf94-ecs', - linkLabel: i18n.translate('home.tutorials.mongodbLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'MongoDB Overview', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-mongodb.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/mongodb_metrics/index.ts b/src/plugins/home/server/tutorials/mongodb_metrics/index.ts deleted file mode 100644 index d37ea911cb7e..000000000000 --- a/src/plugins/home/server/tutorials/mongodb_metrics/index.ts +++ /dev/null @@ -1,80 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function mongodbMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'mongodb'; - return { - id: 'mongodbMetrics', - name: i18n.translate('home.tutorials.mongodbMetrics.nameTitle', { - defaultMessage: 'MongoDB metrics', - }), - moduleName, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.mongodbMetrics.shortDescription', { - defaultMessage: 'Fetch internal metrics from MongoDB.', - }), - longDescription: i18n.translate('home.tutorials.mongodbMetrics.longDescription', { - defaultMessage: - 'The `mongodb` Metricbeat module fetches internal metrics from the MongoDB server. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-mongodb.html', - }, - }), - euiIconType: 'logoMongodb', - artifacts: { - dashboards: [ - { - id: 'Metricbeat-MongoDB-ecs', - linkLabel: i18n.translate( - 'home.tutorials.mongodbMetrics.artifacts.dashboards.linkLabel', - { - defaultMessage: 'MongoDB metrics dashboard', - } - ), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-mongodb.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/mssql_logs/index.ts b/src/plugins/home/server/tutorials/mssql_logs/index.ts deleted file mode 100644 index 891a8fb6764b..000000000000 --- a/src/plugins/home/server/tutorials/mssql_logs/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function mssqlLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'mssql'; - const platforms = ['DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'mssqlLogs', - name: i18n.translate('home.tutorials.mssqlLogs.nameTitle', { - defaultMessage: 'MSSQL logs', - }), - moduleName, - category: TutorialsCategory.LOGGING, - shortDescription: i18n.translate('home.tutorials.mssqlLogs.shortDescription', { - defaultMessage: 'Collect MSSQL logs.', - }), - longDescription: i18n.translate('home.tutorials.mssqlLogs.longDescription', { - defaultMessage: - 'The module parses error logs created by MSSQL. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-mssql.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/microsoft.svg', - artifacts: { - dashboards: [], - application: { - label: i18n.translate('home.tutorials.mssqlLogs.artifacts.application.label', { - defaultMessage: 'Discover', - }), - path: '/app/discover#/', - }, - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-mssql.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/mssql_metrics/index.ts b/src/plugins/home/server/tutorials/mssql_metrics/index.ts deleted file mode 100644 index 4709cc765ab3..000000000000 --- a/src/plugins/home/server/tutorials/mssql_metrics/index.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function mssqlMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'mssql'; - return { - id: 'mssqlMetrics', - name: i18n.translate('home.tutorials.mssqlMetrics.nameTitle', { - defaultMessage: 'Microsoft SQL Server Metrics', - }), - moduleName, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.mssqlMetrics.shortDescription', { - defaultMessage: 'Fetch monitoring metrics from a Microsoft SQL Server instance', - }), - longDescription: i18n.translate('home.tutorials.mssqlMetrics.longDescription', { - defaultMessage: - 'The `mssql` Metricbeat module fetches monitoring, log and performance metrics from a Microsoft SQL Server instance. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-mssql.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/mssql.svg', - isBeta: false, - artifacts: { - dashboards: [ - { - id: 'a2ead240-18bb-11e9-9836-f37dedd3b411-ecs', - linkLabel: i18n.translate('home.tutorials.mssqlMetrics.artifacts.dashboards.linkLabel', { - defaultMessage: 'Microsoft SQL Server metrics dashboard', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-mssql.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/munin_metrics/index.ts b/src/plugins/home/server/tutorials/munin_metrics/index.ts deleted file mode 100644 index e46941abf2f3..000000000000 --- a/src/plugins/home/server/tutorials/munin_metrics/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function muninMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'munin'; - return { - id: 'muninMetrics', - name: i18n.translate('home.tutorials.muninMetrics.nameTitle', { - defaultMessage: 'Munin metrics', - }), - moduleName, - euiIconType: '/plugins/home/assets/tutorials/logos/munin.svg', - isBeta: true, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.muninMetrics.shortDescription', { - defaultMessage: 'Fetch internal metrics from the Munin server.', - }), - longDescription: i18n.translate('home.tutorials.muninMetrics.longDescription', { - defaultMessage: - 'The `munin` Metricbeat module fetches internal metrics from Munin. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-munin.html', - }, - }), - artifacts: { - application: { - label: i18n.translate('home.tutorials.muninMetrics.artifacts.application.label', { - defaultMessage: 'Discover', - }), - path: '/app/discover#/', - }, - dashboards: [], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-munin.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/mysql_logs/index.ts b/src/plugins/home/server/tutorials/mysql_logs/index.ts deleted file mode 100644 index b0f5ab1af118..000000000000 --- a/src/plugins/home/server/tutorials/mysql_logs/index.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function mysqlLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'mysql'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'mysqlLogs', - name: i18n.translate('home.tutorials.mysqlLogs.nameTitle', { - defaultMessage: 'MySQL logs', - }), - moduleName, - category: TutorialsCategory.LOGGING, - shortDescription: i18n.translate('home.tutorials.mysqlLogs.shortDescription', { - defaultMessage: 'Collect and parse error and slow logs created by MySQL.', - }), - longDescription: i18n.translate('home.tutorials.mysqlLogs.longDescription', { - defaultMessage: - 'The `mysql` Filebeat module parses error and slow logs created by MySQL. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-mysql.html', - }, - }), - euiIconType: 'logoMySQL', - artifacts: { - dashboards: [ - { - id: 'Filebeat-MySQL-Dashboard-ecs', - linkLabel: i18n.translate('home.tutorials.mysqlLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'MySQL logs dashboard', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-mysql.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/mysql_metrics/index.ts b/src/plugins/home/server/tutorials/mysql_metrics/index.ts deleted file mode 100644 index 1a4e958cd422..000000000000 --- a/src/plugins/home/server/tutorials/mysql_metrics/index.ts +++ /dev/null @@ -1,77 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function mysqlMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'mysql'; - return { - id: 'mysqlMetrics', - name: i18n.translate('home.tutorials.mysqlMetrics.nameTitle', { - defaultMessage: 'MySQL metrics', - }), - moduleName, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.mysqlMetrics.shortDescription', { - defaultMessage: 'Fetch internal metrics from MySQL.', - }), - longDescription: i18n.translate('home.tutorials.mysqlMetrics.longDescription', { - defaultMessage: - 'The `mysql` Metricbeat module fetches internal metrics from the MySQL server. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-mysql.html', - }, - }), - euiIconType: 'logoMySQL', - artifacts: { - dashboards: [ - { - id: '66881e90-0006-11e7-bf7f-c9acc3d3e306-ecs', - linkLabel: i18n.translate('home.tutorials.mysqlMetrics.artifacts.dashboards.linkLabel', { - defaultMessage: 'MySQL metrics dashboard', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-mysql.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/nats_logs/index.ts b/src/plugins/home/server/tutorials/nats_logs/index.ts deleted file mode 100644 index 16294ff657ba..000000000000 --- a/src/plugins/home/server/tutorials/nats_logs/index.ts +++ /dev/null @@ -1,79 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function natsLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'nats'; - const platforms = ['DEB', 'RPM'] as const; - return { - id: 'natsLogs', - name: i18n.translate('home.tutorials.natsLogs.nameTitle', { - defaultMessage: 'NATS logs', - }), - moduleName, - category: TutorialsCategory.LOGGING, - isBeta: true, - shortDescription: i18n.translate('home.tutorials.natsLogs.shortDescription', { - defaultMessage: 'Collect and parse logs created by Nats.', - }), - longDescription: i18n.translate('home.tutorials.natsLogs.longDescription', { - defaultMessage: - 'The `nats` Filebeat module parses logs created by Nats. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-nats.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/nats.svg', - artifacts: { - dashboards: [ - { - id: 'Filebeat-nats-overview-ecs', - linkLabel: i18n.translate('home.tutorials.natsLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'NATS logs dashboard', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-nats.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/nats_metrics/index.ts b/src/plugins/home/server/tutorials/nats_metrics/index.ts deleted file mode 100644 index 8f3fcac03259..000000000000 --- a/src/plugins/home/server/tutorials/nats_metrics/index.ts +++ /dev/null @@ -1,77 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function natsMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'nats'; - return { - id: 'natsMetrics', - name: i18n.translate('home.tutorials.natsMetrics.nameTitle', { - defaultMessage: 'NATS metrics', - }), - moduleName, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.natsMetrics.shortDescription', { - defaultMessage: 'Fetch monitoring metrics from the Nats server.', - }), - longDescription: i18n.translate('home.tutorials.natsMetrics.longDescription', { - defaultMessage: - 'The `nats` Metricbeat module fetches monitoring metrics from Nats. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-nats.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/nats.svg', - artifacts: { - dashboards: [ - { - id: 'Metricbeat-Nats-Dashboard-ecs', - linkLabel: i18n.translate('home.tutorials.natsMetrics.artifacts.dashboards.linkLabel', { - defaultMessage: 'NATS metrics dashboard', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-nats.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/netflow_logs/index.ts b/src/plugins/home/server/tutorials/netflow_logs/index.ts deleted file mode 100644 index d49ce65aeedc..000000000000 --- a/src/plugins/home/server/tutorials/netflow_logs/index.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function netflowLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'netflow'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'netflowLogs', - name: i18n.translate('home.tutorials.netflowLogs.nameTitle', { - defaultMessage: 'NetFlow / IPFIX Collector', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.netflowLogs.shortDescription', { - defaultMessage: 'Collect NetFlow and IPFIX flow records.', - }), - longDescription: i18n.translate('home.tutorials.netflowLogs.longDescription', { - defaultMessage: - 'This is a module for receiving NetFlow and IPFIX flow records over UDP. This input supports NetFlow versions 1, 5, 6, 7, 8 and 9, as well as IPFIX. For NetFlow versions older than 9, fields are mapped automatically to NetFlow v9. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-netflow.html', - }, - }), - euiIconType: 'logoBeats', - artifacts: { - dashboards: [ - { - id: '34e26884-161a-4448-9556-43b5bf2f62a2', - linkLabel: i18n.translate('home.tutorials.netflowLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'Netflow Overview', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-netflow.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/netscout_logs/index.ts b/src/plugins/home/server/tutorials/netscout_logs/index.ts deleted file mode 100644 index 8315ea87506b..000000000000 --- a/src/plugins/home/server/tutorials/netscout_logs/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function netscoutLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'netscout'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'netscoutLogs', - name: i18n.translate('home.tutorials.netscoutLogs.nameTitle', { - defaultMessage: 'Arbor Peakflow logs', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.netscoutLogs.shortDescription', { - defaultMessage: 'Collect Netscout Arbor Peakflow SP logs over syslog or from a file.', - }), - longDescription: i18n.translate('home.tutorials.netscoutLogs.longDescription', { - defaultMessage: - 'This is a module for receiving Arbor Peakflow SP logs over Syslog or a file. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-netscout.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/netscout.svg', - artifacts: { - dashboards: [], - application: { - path: '/app/security', - label: i18n.translate('home.tutorials.netscoutLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'Security App', - }), - }, - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-netscout.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/nginx_logs/index.ts b/src/plugins/home/server/tutorials/nginx_logs/index.ts deleted file mode 100644 index d1c7e08a7fc8..000000000000 --- a/src/plugins/home/server/tutorials/nginx_logs/index.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function nginxLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'nginx'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'nginxLogs', - name: i18n.translate('home.tutorials.nginxLogs.nameTitle', { - defaultMessage: 'Nginx logs', - }), - moduleName, - category: TutorialsCategory.LOGGING, - shortDescription: i18n.translate('home.tutorials.nginxLogs.shortDescription', { - defaultMessage: 'Collect and parse access and error logs created by the Nginx HTTP server.', - }), - longDescription: i18n.translate('home.tutorials.nginxLogs.longDescription', { - defaultMessage: - 'The `nginx` Filebeat module parses access and error logs created by the Nginx HTTP server. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-nginx.html', - }, - }), - euiIconType: 'logoNginx', - artifacts: { - dashboards: [ - { - id: '55a9e6e0-a29e-11e7-928f-5dbe6f6f5519-ecs', - linkLabel: i18n.translate('home.tutorials.nginxLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'Nginx logs dashboard', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-nginx.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/nginx_metrics/index.ts b/src/plugins/home/server/tutorials/nginx_metrics/index.ts deleted file mode 100644 index c9ce79c469ae..000000000000 --- a/src/plugins/home/server/tutorials/nginx_metrics/index.ts +++ /dev/null @@ -1,82 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function nginxMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'nginx'; - return { - id: 'nginxMetrics', - name: i18n.translate('home.tutorials.nginxMetrics.nameTitle', { - defaultMessage: 'Nginx metrics', - }), - moduleName, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.nginxMetrics.shortDescription', { - defaultMessage: 'Fetch internal metrics from the Nginx HTTP server.', - }), - longDescription: i18n.translate('home.tutorials.nginxMetrics.longDescription', { - defaultMessage: - 'The `nginx` Metricbeat module fetches internal metrics from the Nginx HTTP server. \ -The module scrapes the server status data from the web page generated by the \ -{statusModuleLink}, \ -which must be enabled in your Nginx installation. \ -[Learn more]({learnMoreLink}).', - values: { - statusModuleLink: - '[ngx_http_stub_status_module](http://nginx.org/en/docs/http/ngx_http_stub_status_module.html)', - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-nginx.html', - }, - }), - euiIconType: 'logoNginx', - artifacts: { - dashboards: [ - { - id: '023d2930-f1a5-11e7-a9ef-93c69af7b129-ecs', - linkLabel: i18n.translate('home.tutorials.nginxMetrics.artifacts.dashboards.linkLabel', { - defaultMessage: 'Nginx metrics dashboard', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-nginx.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/o365_logs/index.ts b/src/plugins/home/server/tutorials/o365_logs/index.ts deleted file mode 100644 index a9c67b2049af..000000000000 --- a/src/plugins/home/server/tutorials/o365_logs/index.ts +++ /dev/null @@ -1,81 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function o365LogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'o365'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'o365Logs', - name: i18n.translate('home.tutorials.o365Logs.nameTitle', { - defaultMessage: 'Office 365 logs', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.o365Logs.shortDescription', { - defaultMessage: 'Collect Office 365 activity logs via the Office 365 API.', - }), - longDescription: i18n.translate('home.tutorials.o365Logs.longDescription', { - defaultMessage: - 'This is a module for Office 365 logs received via one of the Office 365 \ - API endpoints. It currently supports user, admin, system, and policy \ - actions and events from Office 365 and Azure AD activity logs exposed \ - by the Office 365 Management Activity API. \ - [Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-o365.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/o365.svg', - artifacts: { - dashboards: [ - { - id: '712e2c00-685d-11ea-8d6a-292ef5d68366', - linkLabel: i18n.translate('home.tutorials.o365Logs.artifacts.dashboards.linkLabel', { - defaultMessage: 'O365 Audit Dashboard', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-o365.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/okta_logs/index.ts b/src/plugins/home/server/tutorials/okta_logs/index.ts deleted file mode 100644 index 9ce6bc0802a8..000000000000 --- a/src/plugins/home/server/tutorials/okta_logs/index.ts +++ /dev/null @@ -1,79 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function oktaLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'okta'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'oktaLogs', - name: i18n.translate('home.tutorials.oktaLogs.nameTitle', { - defaultMessage: 'Okta logs', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.oktaLogs.shortDescription', { - defaultMessage: 'Collect the Okta system log via the Okta API.', - }), - longDescription: i18n.translate('home.tutorials.oktaLogs.longDescription', { - defaultMessage: - 'The Okta module collects events from the [Okta API](https://developer.okta.com/docs/reference/). \ - Specifically this supports reading from the [Okta System Log API](https://developer.okta.com/docs/reference/api/system-log/). \ - [Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-okta.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/okta.svg', - artifacts: { - dashboards: [ - { - id: '749203a0-67b1-11ea-a76f-bf44814e437d', - linkLabel: i18n.translate('home.tutorials.oktaLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'Okta Overview', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-okta.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/openmetrics_metrics/index.ts b/src/plugins/home/server/tutorials/openmetrics_metrics/index.ts deleted file mode 100644 index d7bf0bdbd60c..000000000000 --- a/src/plugins/home/server/tutorials/openmetrics_metrics/index.ts +++ /dev/null @@ -1,69 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function openmetricsMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'openmetrics'; - return { - id: 'openmetricsMetrics', - name: i18n.translate('home.tutorials.openmetricsMetrics.nameTitle', { - defaultMessage: 'OpenMetrics metrics', - }), - moduleName, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.openmetricsMetrics.shortDescription', { - defaultMessage: 'Fetch metrics from an endpoint that serves metrics in OpenMetrics format.', - }), - longDescription: i18n.translate('home.tutorials.openmetricsMetrics.longDescription', { - defaultMessage: - 'The `openmetrics` Metricbeat module fetches metrics from an endpoint that serves metrics in OpenMetrics format. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-openmetrics.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/openmetrics.svg', - artifacts: { - dashboards: [], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-openmetrics.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/opensearch_dashboards_logs/index.ts b/src/plugins/home/server/tutorials/opensearch_dashboards_logs/index.ts deleted file mode 100644 index 7de5253c7f33..000000000000 --- a/src/plugins/home/server/tutorials/opensearch_dashboards_logs/index.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function opensearchDashboardsLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'opensearchDashboards'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'opensearchDashboardsLogs', - name: i18n.translate('home.tutorials.opensearchDashboardsLogs.nameTitle', { - defaultMessage: 'OpenSearch Dashboards Logs', - }), - moduleName, - category: TutorialsCategory.LOGGING, - shortDescription: i18n.translate('home.tutorials.opensearchDashboardsLogs.shortDescription', { - defaultMessage: 'Collect OpenSearch Dashboards logs.', - }), - longDescription: i18n.translate('home.tutorials.opensearchDashboardsLogs.longDescription', { - defaultMessage: 'This is the OpenSearch Dashboards module. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-opensearch-dashboards.html', - }, - }), - euiIconType: 'inputOutput', - artifacts: { - dashboards: [], - application: { - label: i18n.translate( - 'home.tutorials.opensearchDashboardsLogs.artifacts.application.label', - { - defaultMessage: 'Discover', - } - ), - path: '/app/discover#/', - }, - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-opensearch-dashboards.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/opensearch_dashboards_metrics/index.ts b/src/plugins/home/server/tutorials/opensearch_dashboards_metrics/index.ts deleted file mode 100644 index 7eaec9b72a99..000000000000 --- a/src/plugins/home/server/tutorials/opensearch_dashboards_metrics/index.ts +++ /dev/null @@ -1,84 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function opensearchDashboardsMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'opensearchDashboards'; - return { - id: 'opensearchDashboardsMetrics', - name: i18n.translate('home.tutorials.opensearchDashboardsMetrics.nameTitle', { - defaultMessage: 'OpenSearch Dashboards metrics', - }), - moduleName, - isBeta: false, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate( - 'home.tutorials.opensearchDashboardsMetrics.shortDescription', - { - defaultMessage: 'Fetch internal metrics from OpenSearch Dashboards.', - } - ), - longDescription: i18n.translate('home.tutorials.opensearchDashboardsMetrics.longDescription', { - defaultMessage: - 'The `OpenSearch Dashboards` Metricbeat module fetches internal metrics from OpenSearch Dashboards. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: - '{config.docs.beats.metricbeat}/metricbeat-module-opensearch-dashboards.html', - }, - }), - euiIconType: 'inputOutput', - artifacts: { - application: { - label: i18n.translate( - 'home.tutorials.opensearchDashboardsMetrics.artifacts.application.label', - { - defaultMessage: 'Discover', - } - ), - path: '/app/discover#/', - }, - dashboards: [], - exportedFields: { - documentationUrl: - '{config.docs.beats.metricbeat}/exported-fields-opensearch-dashboards.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/opensearch_logs/index.ts b/src/plugins/home/server/tutorials/opensearch_logs/index.ts deleted file mode 100644 index 86f8caebd0ac..000000000000 --- a/src/plugins/home/server/tutorials/opensearch_logs/index.ts +++ /dev/null @@ -1,77 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function opensearchLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'opensearch'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'opensearchLogs', - name: i18n.translate('home.tutorials.opensearchLogs.nameTitle', { - defaultMessage: 'OpenSearch logs', - }), - moduleName, - category: TutorialsCategory.LOGGING, - isBeta: true, - shortDescription: i18n.translate('home.tutorials.opensearchLogs.shortDescription', { - defaultMessage: 'Collect and parse logs created by OpenSearch.', - }), - longDescription: i18n.translate('home.tutorials.opensearchLogs.longDescription', { - defaultMessage: - 'The `OpenSearch` Filebeat module parses logs created by OpenSearch. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-opensearch.html', - }, - }), - euiIconType: '/ui/logos/opensearch_mark.svg', - artifacts: { - application: { - label: i18n.translate('home.tutorials.opensearchLogs.artifacts.application.label', { - defaultMessage: 'Discover', - }), - path: '/app/discover#/', - }, - dashboards: [], - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-opensearch.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/opensearch_metrics/index.ts b/src/plugins/home/server/tutorials/opensearch_metrics/index.ts deleted file mode 100644 index 38694ca9d588..000000000000 --- a/src/plugins/home/server/tutorials/opensearch_metrics/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function opensearchMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'opensearch'; - return { - id: 'opensearchMetrics', - name: i18n.translate('home.tutorials.opensearchMetrics.nameTitle', { - defaultMessage: 'OpenSearch metrics', - }), - moduleName, - isBeta: false, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.opensearchMetrics.shortDescription', { - defaultMessage: 'Fetch internal metrics from OpenSearch.', - }), - longDescription: i18n.translate('home.tutorials.opensearchMetrics.longDescription', { - defaultMessage: - 'The `opensearch` Metricbeat module fetches internal metrics from OpenSearch. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-opensearch.html', - }, - }), - euiIconType: '/ui/logos/opensearch_mark.svg', - artifacts: { - application: { - label: i18n.translate('home.tutorials.opensearchMetrics.artifacts.application.label', { - defaultMessage: 'Discover', - }), - path: '/app/discover#/', - }, - dashboards: [], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-opensearch.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/oracle_metrics/index.ts b/src/plugins/home/server/tutorials/oracle_metrics/index.ts deleted file mode 100644 index 81557d3d4d0b..000000000000 --- a/src/plugins/home/server/tutorials/oracle_metrics/index.ts +++ /dev/null @@ -1,77 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function oracleMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'oracle'; - return { - id: moduleName + 'Metrics', - name: i18n.translate('home.tutorials.oracleMetrics.nameTitle', { - defaultMessage: 'oracle metrics', - }), - moduleName, - isBeta: false, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.oracleMetrics.shortDescription', { - defaultMessage: 'Fetch internal metrics from a Oracle server.', - }), - longDescription: i18n.translate('home.tutorials.oracleMetrics.longDescription', { - defaultMessage: - 'The `{moduleName}` Metricbeat module fetches internal metrics from a Oracle server. \ -[Learn more]({learnMoreLink}).', - values: { - moduleName, - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-' + moduleName + '.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/oracle.svg', - artifacts: { - application: { - label: i18n.translate('home.tutorials.oracleMetrics.artifacts.application.label', { - defaultMessage: 'Discover', - }), - path: '/app/opensearch-dashboards#/discover', - }, - dashboards: [], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-' + moduleName + '.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/osquery_logs/index.ts b/src/plugins/home/server/tutorials/osquery_logs/index.ts deleted file mode 100644 index 1610b438013e..000000000000 --- a/src/plugins/home/server/tutorials/osquery_logs/index.ts +++ /dev/null @@ -1,82 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function osqueryLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'osquery'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'osqueryLogs', - name: i18n.translate('home.tutorials.osqueryLogs.nameTitle', { - defaultMessage: 'Osquery logs', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.osqueryLogs.shortDescription', { - defaultMessage: 'Collect osquery logs in JSON format.', - }), - longDescription: i18n.translate('home.tutorials.osqueryLogs.longDescription', { - defaultMessage: - 'The module collects and decodes the result logs written by \ - [osqueryd](https://osquery.readthedocs.io/en/latest/introduction/using-osqueryd/) in \ - the JSON format. To set up osqueryd follow the osquery installation instructions for \ - your operating system and configure the `filesystem` logging driver (the default). \ - Make sure UTC timestamps are enabled. \ - [Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-osquery.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/osquery.svg', - artifacts: { - dashboards: [ - { - id: '69f5ae20-eb02-11e7-8f04-51231daa5b05-ecs', - linkLabel: i18n.translate('home.tutorials.osqueryLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'Osquery Compliance Pack', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-osquery.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/panw_logs/index.ts b/src/plugins/home/server/tutorials/panw_logs/index.ts deleted file mode 100644 index d633b1034327..000000000000 --- a/src/plugins/home/server/tutorials/panw_logs/index.ts +++ /dev/null @@ -1,81 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function panwLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'panw'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'panwLogs', - name: i18n.translate('home.tutorials.panwLogs.nameTitle', { - defaultMessage: 'Palo Alto Networks PAN-OS logs', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.panwLogs.shortDescription', { - defaultMessage: - 'Collect Palo Alto Networks PAN-OS threat and traffic logs over syslog or from a log file.', - }), - longDescription: i18n.translate('home.tutorials.panwLogs.longDescription', { - defaultMessage: - 'This is a module for Palo Alto Networks PAN-OS firewall monitoring \ - logs received over Syslog or read from a file. It currently supports \ - messages of Traffic and Threat types. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-panw.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/paloalto.svg', - artifacts: { - dashboards: [ - { - id: 'e40ba240-7572-11e9-976e-65a8f47cc4c1', - linkLabel: i18n.translate('home.tutorials.panwLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'PANW Network Flows', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-panw.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/php_fpm_metrics/index.ts b/src/plugins/home/server/tutorials/php_fpm_metrics/index.ts deleted file mode 100644 index 2089e5d8154f..000000000000 --- a/src/plugins/home/server/tutorials/php_fpm_metrics/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function phpfpmMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'php_fpm'; - return { - id: 'phpfpmMetrics', - name: i18n.translate('home.tutorials.phpFpmMetrics.nameTitle', { - defaultMessage: 'PHP-FPM metrics', - }), - moduleName, - category: TutorialsCategory.METRICS, - isBeta: false, - shortDescription: i18n.translate('home.tutorials.phpFpmMetrics.shortDescription', { - defaultMessage: 'Fetch internal metrics from PHP-FPM.', - }), - longDescription: i18n.translate('home.tutorials.phpFpmMetrics.longDescription', { - defaultMessage: - 'The `php_fpm` Metricbeat module fetches internal metrics from the PHP-FPM server. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-php_fpm.html', - }, - }), - euiIconType: 'logoPhp', - artifacts: { - dashboards: [ - /* { - id: 'TODO', - linkLabel: 'PHP-FPM metrics dashboard', - isOverview: true - }*/ - ], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-php_fpm.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/postgresql_logs/index.ts b/src/plugins/home/server/tutorials/postgresql_logs/index.ts deleted file mode 100644 index ebd4eb988700..000000000000 --- a/src/plugins/home/server/tutorials/postgresql_logs/index.ts +++ /dev/null @@ -1,81 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function postgresqlLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'postgresql'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'postgresqlLogs', - name: i18n.translate('home.tutorials.postgresqlLogs.nameTitle', { - defaultMessage: 'PostgreSQL logs', - }), - moduleName, - category: TutorialsCategory.LOGGING, - shortDescription: i18n.translate('home.tutorials.postgresqlLogs.shortDescription', { - defaultMessage: 'Collect and parse error and slow logs created by PostgreSQL.', - }), - longDescription: i18n.translate('home.tutorials.postgresqlLogs.longDescription', { - defaultMessage: - 'The `postgresql` Filebeat module parses error and slow logs created by PostgreSQL. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-postgresql.html', - }, - }), - euiIconType: 'logoPostgres', - artifacts: { - dashboards: [ - { - id: '158be870-87f4-11e7-ad9c-db80de0bf8d3-ecs', - linkLabel: i18n.translate( - 'home.tutorials.postgresqlLogs.artifacts.dashboards.linkLabel', - { - defaultMessage: 'PostgreSQL logs dashboard', - } - ), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-postgresql.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/postgresql_metrics/index.ts b/src/plugins/home/server/tutorials/postgresql_metrics/index.ts deleted file mode 100644 index dd4464084636..000000000000 --- a/src/plugins/home/server/tutorials/postgresql_metrics/index.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function postgresqlMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'postgresql'; - return { - id: 'postgresqlMetrics', - name: i18n.translate('home.tutorials.postgresqlMetrics.nameTitle', { - defaultMessage: 'PostgreSQL metrics', - }), - moduleName, - category: TutorialsCategory.METRICS, - isBeta: false, - shortDescription: i18n.translate('home.tutorials.postgresqlMetrics.shortDescription', { - defaultMessage: 'Fetch internal metrics from PostgreSQL.', - }), - longDescription: i18n.translate('home.tutorials.postgresqlMetrics.longDescription', { - defaultMessage: - 'The `postgresql` Metricbeat module fetches internal metrics from the PostgreSQL server. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-postgresql.html', - }, - }), - euiIconType: 'logoPostgres', - artifacts: { - dashboards: [ - /* - { - id: 'TODO', - linkLabel: 'PostgreSQL metrics dashboard', - isOverview: true - } - */ - ], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-postgresql.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/prometheus_metrics/index.ts b/src/plugins/home/server/tutorials/prometheus_metrics/index.ts deleted file mode 100644 index 14e0cf53f3ff..000000000000 --- a/src/plugins/home/server/tutorials/prometheus_metrics/index.ts +++ /dev/null @@ -1,77 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function prometheusMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'prometheus'; - return { - id: moduleName + 'Metrics', - name: i18n.translate('home.tutorials.prometheusMetrics.nameTitle', { - defaultMessage: 'Prometheus metrics', - }), - moduleName, - isBeta: false, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.prometheusMetrics.shortDescription', { - defaultMessage: 'Fetch metrics from a Prometheus exporter.', - }), - longDescription: i18n.translate('home.tutorials.prometheusMetrics.longDescription', { - defaultMessage: - 'The `{moduleName}` Metricbeat module fetches metrics from Prometheus endpoint. \ -[Learn more]({learnMoreLink}).', - values: { - moduleName, - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-' + moduleName + '.html', - }, - }), - euiIconType: 'logoPrometheus', - artifacts: { - application: { - label: i18n.translate('home.tutorials.prometheusMetrics.artifacts.application.label', { - defaultMessage: 'Discover', - }), - path: '/app/discover#/', - }, - dashboards: [], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-' + moduleName + '.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/rabbitmq_logs/index.ts b/src/plugins/home/server/tutorials/rabbitmq_logs/index.ts deleted file mode 100644 index 62c0396f1640..000000000000 --- a/src/plugins/home/server/tutorials/rabbitmq_logs/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function rabbitmqLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'rabbitmq'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'rabbitmqLogs', - name: i18n.translate('home.tutorials.rabbitmqLogs.nameTitle', { - defaultMessage: 'RabbitMQ logs', - }), - moduleName, - category: TutorialsCategory.LOGGING, - shortDescription: i18n.translate('home.tutorials.rabbitmqLogs.shortDescription', { - defaultMessage: 'Collect RabbitMQ logs.', - }), - longDescription: i18n.translate('home.tutorials.rabbitmqLogs.longDescription', { - defaultMessage: - 'This is the module for parsing [RabbitMQ log files](https://www.rabbitmq.com/logging.html) \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-rabbitmq.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/rabbitmq.svg', - artifacts: { - dashboards: [], - application: { - label: i18n.translate('home.tutorials.rabbitmqLogs.artifacts.application.label', { - defaultMessage: 'Discover', - }), - path: '/app/discover#/', - }, - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-rabbitmq.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/rabbitmq_metrics/index.ts b/src/plugins/home/server/tutorials/rabbitmq_metrics/index.ts deleted file mode 100644 index 50cae7495f67..000000000000 --- a/src/plugins/home/server/tutorials/rabbitmq_metrics/index.ts +++ /dev/null @@ -1,81 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function rabbitmqMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'rabbitmq'; - return { - id: 'rabbitmqMetrics', - name: i18n.translate('home.tutorials.rabbitmqMetrics.nameTitle', { - defaultMessage: 'RabbitMQ metrics', - }), - moduleName, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.rabbitmqMetrics.shortDescription', { - defaultMessage: 'Fetch internal metrics from the RabbitMQ server.', - }), - longDescription: i18n.translate('home.tutorials.rabbitmqMetrics.longDescription', { - defaultMessage: - 'The `rabbitmq` Metricbeat module fetches internal metrics from the RabbitMQ server. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-rabbitmq.html', - }, - }), - euiIconType: 'logoRabbitmq', - isBeta: false, - artifacts: { - dashboards: [ - { - id: 'AV4YobKIge1VCbKU_qVo-ecs', - linkLabel: i18n.translate( - 'home.tutorials.rabbitmqMetrics.artifacts.dashboards.linkLabel', - { - defaultMessage: 'RabbitMQ metrics dashboard', - } - ), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-rabbitmq.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/radware_logs/index.ts b/src/plugins/home/server/tutorials/radware_logs/index.ts deleted file mode 100644 index 942793817e9d..000000000000 --- a/src/plugins/home/server/tutorials/radware_logs/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function radwareLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'radware'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'radwareLogs', - name: i18n.translate('home.tutorials.radwareLogs.nameTitle', { - defaultMessage: 'Radware DefensePro logs', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.radwareLogs.shortDescription', { - defaultMessage: 'Collect Radware DefensePro logs over syslog or from a file.', - }), - longDescription: i18n.translate('home.tutorials.radwareLogs.longDescription', { - defaultMessage: - 'This is a module for receiving Radware DefensePro logs over Syslog or a file. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-radware.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/radware.svg', - artifacts: { - dashboards: [], - application: { - path: '/app/security', - label: i18n.translate('home.tutorials.radwareLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'Security App', - }), - }, - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-radware.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/redis_logs/index.ts b/src/plugins/home/server/tutorials/redis_logs/index.ts deleted file mode 100644 index 1a73c8c9a5e1..000000000000 --- a/src/plugins/home/server/tutorials/redis_logs/index.ts +++ /dev/null @@ -1,84 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function redisLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'redis'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'redisLogs', - name: i18n.translate('home.tutorials.redisLogs.nameTitle', { - defaultMessage: 'Redis logs', - }), - moduleName, - category: TutorialsCategory.LOGGING, - shortDescription: i18n.translate('home.tutorials.redisLogs.shortDescription', { - defaultMessage: 'Collect and parse error and slow logs created by Redis.', - }), - longDescription: i18n.translate('home.tutorials.redisLogs.longDescription', { - defaultMessage: - 'The `redis` Filebeat module parses error and slow logs created by Redis. \ -For Redis to write error logs, make sure the `logfile` option, from the \ -Redis configuration file, is set to `redis-server.log`. \ -The slow logs are read directly from Redis via the `SLOWLOG` command. \ -For Redis to record slow logs, make sure the `slowlog-log-slower-than` \ -option is set. \ -Note that the `slowlog` fileset is experimental. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-redis.html', - }, - }), - euiIconType: 'logoRedis', - artifacts: { - dashboards: [ - { - id: '7fea2930-478e-11e7-b1f0-cb29bac6bf8b-ecs', - linkLabel: i18n.translate('home.tutorials.redisLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'Redis logs dashboard', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-redis.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/redis_metrics/index.ts b/src/plugins/home/server/tutorials/redis_metrics/index.ts deleted file mode 100644 index d6d63dfc4787..000000000000 --- a/src/plugins/home/server/tutorials/redis_metrics/index.ts +++ /dev/null @@ -1,77 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function redisMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'redis'; - return { - id: 'redisMetrics', - name: i18n.translate('home.tutorials.redisMetrics.nameTitle', { - defaultMessage: 'Redis metrics', - }), - moduleName, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.redisMetrics.shortDescription', { - defaultMessage: 'Fetch internal metrics from Redis.', - }), - longDescription: i18n.translate('home.tutorials.redisMetrics.longDescription', { - defaultMessage: - 'The `redis` Metricbeat module fetches internal metrics from the Redis server. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-redis.html', - }, - }), - euiIconType: 'logoRedis', - artifacts: { - dashboards: [ - { - id: 'AV4YjZ5pux-M-tCAunxK-ecs', - linkLabel: i18n.translate('home.tutorials.redisMetrics.artifacts.dashboards.linkLabel', { - defaultMessage: 'Redis metrics dashboard', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-redis.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/redisenterprise_metrics/index.ts b/src/plugins/home/server/tutorials/redisenterprise_metrics/index.ts deleted file mode 100644 index ef0d922d63f1..000000000000 --- a/src/plugins/home/server/tutorials/redisenterprise_metrics/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function redisenterpriseMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'redisenterprise'; - return { - id: 'redisenterpriseMetrics', - name: i18n.translate('home.tutorials.redisenterpriseMetrics.nameTitle', { - defaultMessage: 'Redis Enterprise metrics', - }), - moduleName, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.redisenterpriseMetrics.shortDescription', { - defaultMessage: 'Fetch monitoring metrics from Redis Enterprise Server.', - }), - longDescription: i18n.translate('home.tutorials.redisenterpriseMetrics.longDescription', { - defaultMessage: - 'The `redisenterprise` Metricbeat module fetches monitoring metrics from Redis Enterprise Server \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-redisenterprise.html', - }, - }), - euiIconType: 'logoRedis', - isBeta: true, - artifacts: { - application: { - label: i18n.translate('home.tutorials.redisenterpriseMetrics.artifacts.application.label', { - defaultMessage: 'Discover', - }), - path: '/app/discover#/', - }, - dashboards: [], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-redisenterprise.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/register.ts b/src/plugins/home/server/tutorials/register.ts deleted file mode 100644 index 0f6b045b2c38..000000000000 --- a/src/plugins/home/server/tutorials/register.ts +++ /dev/null @@ -1,251 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { activemqLogsSpecProvider } from './activemq_logs'; -import { activemqMetricsSpecProvider } from './activemq_metrics'; -import { aerospikeMetricsSpecProvider } from './aerospike_metrics'; -import { apacheLogsSpecProvider } from './apache_logs'; -import { apacheMetricsSpecProvider } from './apache_metrics'; -import { auditbeatSpecProvider } from './auditbeat'; -import { auditdLogsSpecProvider } from './auditd_logs'; -import { awsLogsSpecProvider } from './aws_logs'; -import { awsMetricsSpecProvider } from './aws_metrics'; -import { azureLogsSpecProvider } from './azure_logs'; -import { azureMetricsSpecProvider } from './azure_metrics'; -import { barracudaLogsSpecProvider } from './barracuda_logs'; -import { bluecoatLogsSpecProvider } from './bluecoat_logs'; -import { cefLogsSpecProvider } from './cef_logs'; -import { cephMetricsSpecProvider } from './ceph_metrics'; -import { checkpointLogsSpecProvider } from './checkpoint_logs'; -import { ciscoLogsSpecProvider } from './cisco_logs'; -import { cloudwatchLogsSpecProvider } from './cloudwatch_logs'; -import { cockroachdbMetricsSpecProvider } from './cockroachdb_metrics'; -import { consulMetricsSpecProvider } from './consul_metrics'; -import { corednsLogsSpecProvider } from './coredns_logs'; -import { corednsMetricsSpecProvider } from './coredns_metrics'; -import { couchbaseMetricsSpecProvider } from './couchbase_metrics'; -import { couchdbMetricsSpecProvider } from './couchdb_metrics'; -import { crowdstrikeLogsSpecProvider } from './crowdstrike_logs'; -import { cylanceLogsSpecProvider } from './cylance_logs'; -import { dockerMetricsSpecProvider } from './docker_metrics'; -import { dropwizardMetricsSpecProvider } from './dropwizard_metrics'; -import { opensearchLogsSpecProvider } from './opensearch_logs'; -import { opensearchMetricsSpecProvider } from './opensearch_metrics'; -import { envoyproxyLogsSpecProvider } from './envoyproxy_logs'; -import { envoyproxyMetricsSpecProvider } from './envoyproxy_metrics'; -import { etcdMetricsSpecProvider } from './etcd_metrics'; -import { f5LogsSpecProvider } from './f5_logs'; -import { fortinetLogsSpecProvider } from './fortinet_logs'; -import { golangMetricsSpecProvider } from './golang_metrics'; -import { googlecloudLogsSpecProvider } from './googlecloud_logs'; -import { googlecloudMetricsSpecProvider } from './googlecloud_metrics'; -import { gsuiteLogsSpecProvider } from './gsuite_logs'; -import { haproxyLogsSpecProvider } from './haproxy_logs'; -import { haproxyMetricsSpecProvider } from './haproxy_metrics'; -import { ibmmqLogsSpecProvider } from './ibmmq_logs'; -import { ibmmqMetricsSpecProvider } from './ibmmq_metrics'; -import { icingaLogsSpecProvider } from './icinga_logs'; -import { iisLogsSpecProvider } from './iis_logs'; -import { iisMetricsSpecProvider } from './iis_metrics'; -import { impervaLogsSpecProvider } from './imperva_logs'; -import { infobloxLogsSpecProvider } from './infoblox_logs'; -import { iptablesLogsSpecProvider } from './iptables_logs'; -import { juniperLogsSpecProvider } from './juniper_logs'; -import { kafkaLogsSpecProvider } from './kafka_logs'; -import { kafkaMetricsSpecProvider } from './kafka_metrics'; -import { opensearchDashboardsLogsSpecProvider } from './opensearch_dashboards_logs'; -import { opensearchDashboardsMetricsSpecProvider } from './opensearch_dashboards_metrics'; -import { kubernetesMetricsSpecProvider } from './kubernetes_metrics'; -import { logstashLogsSpecProvider } from './logstash_logs'; -import { logstashMetricsSpecProvider } from './logstash_metrics'; -import { memcachedMetricsSpecProvider } from './memcached_metrics'; -import { microsoftLogsSpecProvider } from './microsoft_logs'; -import { mispLogsSpecProvider } from './misp_logs'; -import { mongodbLogsSpecProvider } from './mongodb_logs'; -import { mongodbMetricsSpecProvider } from './mongodb_metrics'; -import { mssqlLogsSpecProvider } from './mssql_logs'; -import { mssqlMetricsSpecProvider } from './mssql_metrics'; -import { muninMetricsSpecProvider } from './munin_metrics'; -import { mysqlLogsSpecProvider } from './mysql_logs'; -import { mysqlMetricsSpecProvider } from './mysql_metrics'; -import { natsLogsSpecProvider } from './nats_logs'; -import { natsMetricsSpecProvider } from './nats_metrics'; -import { netflowLogsSpecProvider } from './netflow_logs'; -import { netscoutLogsSpecProvider } from './netscout_logs'; -import { nginxLogsSpecProvider } from './nginx_logs'; -import { nginxMetricsSpecProvider } from './nginx_metrics'; -import { o365LogsSpecProvider } from './o365_logs'; -import { oktaLogsSpecProvider } from './okta_logs'; -import { openmetricsMetricsSpecProvider } from './openmetrics_metrics'; -import { oracleMetricsSpecProvider } from './oracle_metrics'; -import { osqueryLogsSpecProvider } from './osquery_logs'; -import { panwLogsSpecProvider } from './panw_logs'; -import { phpfpmMetricsSpecProvider } from './php_fpm_metrics'; -import { postgresqlLogsSpecProvider } from './postgresql_logs'; -import { postgresqlMetricsSpecProvider } from './postgresql_metrics'; -import { prometheusMetricsSpecProvider } from './prometheus_metrics'; -import { rabbitmqLogsSpecProvider } from './rabbitmq_logs'; -import { rabbitmqMetricsSpecProvider } from './rabbitmq_metrics'; -import { radwareLogsSpecProvider } from './radware_logs'; -import { redisLogsSpecProvider } from './redis_logs'; -import { redisMetricsSpecProvider } from './redis_metrics'; -import { redisenterpriseMetricsSpecProvider } from './redisenterprise_metrics'; -import { santaLogsSpecProvider } from './santa_logs'; -import { sonicwallLogsSpecProvider } from './sonicwall_logs'; -import { sophosLogsSpecProvider } from './sophos_logs'; -import { squidLogsSpecProvider } from './squid_logs'; -import { stanMetricsSpecProvider } from './stan_metrics'; -import { statsdMetricsSpecProvider } from './statsd_metrics'; -import { suricataLogsSpecProvider } from './suricata_logs'; -import { systemLogsSpecProvider } from './system_logs'; -import { systemMetricsSpecProvider } from './system_metrics'; -import { tomcatLogsSpecProvider } from './tomcat_logs'; -import { traefikLogsSpecProvider } from './traefik_logs'; -import { traefikMetricsSpecProvider } from './traefik_metrics'; -import { uptimeMonitorsSpecProvider } from './uptime_monitors'; -import { uwsgiMetricsSpecProvider } from './uwsgi_metrics'; -import { vSphereMetricsSpecProvider } from './vsphere_metrics'; -import { windowsEventLogsSpecProvider } from './windows_event_logs'; -import { windowsMetricsSpecProvider } from './windows_metrics'; -import { zeekLogsSpecProvider } from './zeek_logs'; -import { zookeeperMetricsSpecProvider } from './zookeeper_metrics'; -import { zscalerLogsSpecProvider } from './zscaler_logs'; - -export const builtInTutorials = [ - systemLogsSpecProvider, - systemMetricsSpecProvider, - apacheLogsSpecProvider, - apacheMetricsSpecProvider, - opensearchLogsSpecProvider, - iisLogsSpecProvider, - kafkaLogsSpecProvider, - logstashLogsSpecProvider, - nginxLogsSpecProvider, - nginxMetricsSpecProvider, - mysqlLogsSpecProvider, - mysqlMetricsSpecProvider, - mongodbMetricsSpecProvider, - osqueryLogsSpecProvider, - phpfpmMetricsSpecProvider, - postgresqlMetricsSpecProvider, - postgresqlLogsSpecProvider, - rabbitmqMetricsSpecProvider, - redisLogsSpecProvider, - redisMetricsSpecProvider, - suricataLogsSpecProvider, - dockerMetricsSpecProvider, - kubernetesMetricsSpecProvider, - uwsgiMetricsSpecProvider, - netflowLogsSpecProvider, - traefikLogsSpecProvider, - cephMetricsSpecProvider, - aerospikeMetricsSpecProvider, - couchbaseMetricsSpecProvider, - dropwizardMetricsSpecProvider, - opensearchMetricsSpecProvider, - etcdMetricsSpecProvider, - haproxyMetricsSpecProvider, - kafkaMetricsSpecProvider, - opensearchDashboardsMetricsSpecProvider, - memcachedMetricsSpecProvider, - muninMetricsSpecProvider, - vSphereMetricsSpecProvider, - windowsMetricsSpecProvider, - windowsEventLogsSpecProvider, - golangMetricsSpecProvider, - logstashMetricsSpecProvider, - prometheusMetricsSpecProvider, - zookeeperMetricsSpecProvider, - uptimeMonitorsSpecProvider, - cloudwatchLogsSpecProvider, - awsMetricsSpecProvider, - mssqlMetricsSpecProvider, - natsMetricsSpecProvider, - natsLogsSpecProvider, - zeekLogsSpecProvider, - corednsMetricsSpecProvider, - corednsLogsSpecProvider, - auditbeatSpecProvider, - iptablesLogsSpecProvider, - ciscoLogsSpecProvider, - envoyproxyLogsSpecProvider, - couchdbMetricsSpecProvider, - consulMetricsSpecProvider, - cockroachdbMetricsSpecProvider, - traefikMetricsSpecProvider, - awsLogsSpecProvider, - activemqLogsSpecProvider, - activemqMetricsSpecProvider, - azureMetricsSpecProvider, - ibmmqLogsSpecProvider, - ibmmqMetricsSpecProvider, - stanMetricsSpecProvider, - envoyproxyMetricsSpecProvider, - statsdMetricsSpecProvider, - redisenterpriseMetricsSpecProvider, - openmetricsMetricsSpecProvider, - oracleMetricsSpecProvider, - iisMetricsSpecProvider, - azureLogsSpecProvider, - googlecloudMetricsSpecProvider, - auditdLogsSpecProvider, - barracudaLogsSpecProvider, - bluecoatLogsSpecProvider, - cefLogsSpecProvider, - checkpointLogsSpecProvider, - crowdstrikeLogsSpecProvider, - cylanceLogsSpecProvider, - f5LogsSpecProvider, - fortinetLogsSpecProvider, - googlecloudLogsSpecProvider, - gsuiteLogsSpecProvider, - haproxyLogsSpecProvider, - icingaLogsSpecProvider, - impervaLogsSpecProvider, - infobloxLogsSpecProvider, - juniperLogsSpecProvider, - opensearchDashboardsLogsSpecProvider, - microsoftLogsSpecProvider, - mispLogsSpecProvider, - mongodbLogsSpecProvider, - mssqlLogsSpecProvider, - netscoutLogsSpecProvider, - o365LogsSpecProvider, - oktaLogsSpecProvider, - panwLogsSpecProvider, - rabbitmqLogsSpecProvider, - radwareLogsSpecProvider, - santaLogsSpecProvider, - sonicwallLogsSpecProvider, - sophosLogsSpecProvider, - squidLogsSpecProvider, - tomcatLogsSpecProvider, - zscalerLogsSpecProvider, -]; diff --git a/src/plugins/home/server/tutorials/santa_logs/index.ts b/src/plugins/home/server/tutorials/santa_logs/index.ts deleted file mode 100644 index 0008a3adb73d..000000000000 --- a/src/plugins/home/server/tutorials/santa_logs/index.ts +++ /dev/null @@ -1,79 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function santaLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'santa'; - const platforms = ['OSX'] as const; - return { - id: 'santaLogs', - name: i18n.translate('home.tutorials.santaLogs.nameTitle', { - defaultMessage: 'Google Santa logs', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.santaLogs.shortDescription', { - defaultMessage: 'Collect Google Santa logs about process executions on MacOS.', - }), - longDescription: i18n.translate('home.tutorials.santaLogs.longDescription', { - defaultMessage: - 'The module collects and parses logs from [Google Santa](https://github.com/google/santa), \ - a security tool for macOS that monitors process executions and can denylist/allowlist binaries. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-santa.html', - }, - }), - euiIconType: 'logoLogging', - artifacts: { - dashboards: [ - { - id: '161855f0-ff6a-11e8-93c5-d5ecd1b3e307-ecs', - linkLabel: i18n.translate('home.tutorials.santaLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'Santa Overview', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-santa.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/sonicwall_logs/index.ts b/src/plugins/home/server/tutorials/sonicwall_logs/index.ts deleted file mode 100644 index 0cd3bcd90cfc..000000000000 --- a/src/plugins/home/server/tutorials/sonicwall_logs/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function sonicwallLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'sonicwall'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'sonicwallLogs', - name: i18n.translate('home.tutorials.sonicwallLogs.nameTitle', { - defaultMessage: 'Sonicwall FW logs', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.sonicwallLogs.shortDescription', { - defaultMessage: 'Collect Sonicwall-FW logs over syslog or from a file.', - }), - longDescription: i18n.translate('home.tutorials.sonicwallLogs.longDescription', { - defaultMessage: - 'This is a module for receiving Sonicwall-FW logs over Syslog or a file. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-sonicwall.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/sonicwall.svg', - artifacts: { - dashboards: [], - application: { - path: '/app/security', - label: i18n.translate('home.tutorials.radwareLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'Security App', - }), - }, - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-sonicwall.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/sophos_logs/index.ts b/src/plugins/home/server/tutorials/sophos_logs/index.ts deleted file mode 100644 index fc3935e13238..000000000000 --- a/src/plugins/home/server/tutorials/sophos_logs/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function sophosLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'sophos'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'sophosLogs', - name: i18n.translate('home.tutorials.sophosLogs.nameTitle', { - defaultMessage: 'Sophos logs', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.sophosLogs.shortDescription', { - defaultMessage: 'Collect Sophos XG SFOS logs over syslog.', - }), - longDescription: i18n.translate('home.tutorials.sophosLogs.longDescription', { - defaultMessage: - 'This is a module for Sophos Products, currently it supports XG SFOS logs sent in the syslog format. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-sophos.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/sophos.svg', - artifacts: { - dashboards: [], - application: { - path: '/app/security', - label: i18n.translate('home.tutorials.sophosLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'Security App', - }), - }, - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-sophos.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/squid_logs/index.ts b/src/plugins/home/server/tutorials/squid_logs/index.ts deleted file mode 100644 index b88d1e995df7..000000000000 --- a/src/plugins/home/server/tutorials/squid_logs/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function squidLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'squid'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'squidLogs', - name: i18n.translate('home.tutorials.squidLogs.nameTitle', { - defaultMessage: 'Squid logs', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.squidLogs.shortDescription', { - defaultMessage: 'Collect Squid logs over syslog or from a file.', - }), - longDescription: i18n.translate('home.tutorials.squidLogs.longDescription', { - defaultMessage: - 'This is a module for receiving Squid logs over Syslog or a file. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-squid.html', - }, - }), - euiIconType: 'logoLogging', - artifacts: { - dashboards: [], - application: { - path: '/app/security', - label: i18n.translate('home.tutorials.squidLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'Security App', - }), - }, - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-squid.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/stan_metrics/index.ts b/src/plugins/home/server/tutorials/stan_metrics/index.ts deleted file mode 100644 index 49be1e327e96..000000000000 --- a/src/plugins/home/server/tutorials/stan_metrics/index.ts +++ /dev/null @@ -1,77 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function stanMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'stan'; - return { - id: 'stanMetrics', - name: i18n.translate('home.tutorials.stanMetrics.nameTitle', { - defaultMessage: 'STAN metrics', - }), - moduleName, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.stanMetrics.shortDescription', { - defaultMessage: 'Fetch monitoring metrics from the STAN server.', - }), - longDescription: i18n.translate('home.tutorials.stanMetrics.longDescription', { - defaultMessage: - 'The `stan` Metricbeat module fetches monitoring metrics from STAN. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-stan.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/stan.svg', - artifacts: { - dashboards: [ - { - id: 'dbf2e220-37ce-11ea-a9c8-152a657da3ab', - linkLabel: i18n.translate('home.tutorials.stanMetrics.artifacts.dashboards.linkLabel', { - defaultMessage: 'Stan metrics dashboard', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-stan.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/statsd_metrics/index.ts b/src/plugins/home/server/tutorials/statsd_metrics/index.ts deleted file mode 100644 index 1fbba2156324..000000000000 --- a/src/plugins/home/server/tutorials/statsd_metrics/index.ts +++ /dev/null @@ -1,66 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory, TutorialSchema } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { TutorialContext } from '../../services/tutorials/lib/tutorials_registry_types'; - -export function statsdMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'statsd'; - return { - id: 'statsdMetrics', - name: i18n.translate('home.tutorials.statsdMetrics.nameTitle', { - defaultMessage: 'Statsd metrics', - }), - moduleName, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.statsdMetrics.shortDescription', { - defaultMessage: 'Fetch monitoring metrics from statsd.', - }), - longDescription: i18n.translate('home.tutorials.statsdMetrics.longDescription', { - defaultMessage: - 'The `statsd` Metricbeat module fetches monitoring metrics from statsd. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-statsd.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/statsd.svg', - artifacts: { - dashboards: [], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-statsd.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/suricata_logs/index.ts b/src/plugins/home/server/tutorials/suricata_logs/index.ts deleted file mode 100644 index 89091930add9..000000000000 --- a/src/plugins/home/server/tutorials/suricata_logs/index.ts +++ /dev/null @@ -1,79 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function suricataLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'suricata'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'suricataLogs', - name: i18n.translate('home.tutorials.suricataLogs.nameTitle', { - defaultMessage: 'Suricata logs', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.suricataLogs.shortDescription', { - defaultMessage: 'Collect Suricata IDS/IPS/NSM logs.', - }), - longDescription: i18n.translate('home.tutorials.suricataLogs.longDescription', { - defaultMessage: - 'This is a module to the Suricata IDS/IPS/NSM log. It parses logs that are \ - in the [Suricata Eve JSON format](https://suricata.readthedocs.io/en/latest/output/eve/eve-json-format.html). \ - [Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-suricata.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/suricata.svg', - artifacts: { - dashboards: [ - { - id: '78289c40-86da-11e8-b59d-21efb914e65c-ecs', - linkLabel: i18n.translate('home.tutorials.suricataLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'Suricata Events Overview', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-suricata.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/system_logs/index.ts b/src/plugins/home/server/tutorials/system_logs/index.ts deleted file mode 100644 index 0809fba7455c..000000000000 --- a/src/plugins/home/server/tutorials/system_logs/index.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function systemLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'system'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'systemLogs', - name: i18n.translate('home.tutorials.systemLogs.nameTitle', { - defaultMessage: 'System logs', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.systemLogs.shortDescription', { - defaultMessage: 'Collect system logs of common Unix/Linux based distributions.', - }), - longDescription: i18n.translate('home.tutorials.systemLogs.longDescription', { - defaultMessage: - 'The module collects and parses logs created by the system logging service of common Unix/Linux based distributions. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-system.html', - }, - }), - euiIconType: 'logoLogging', - artifacts: { - dashboards: [ - { - id: 'Filebeat-syslog-dashboard-ecs', - linkLabel: i18n.translate('home.tutorials.systemLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'System Syslog Dashboard', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-system.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/system_metrics/index.ts b/src/plugins/home/server/tutorials/system_metrics/index.ts deleted file mode 100644 index 903591207884..000000000000 --- a/src/plugins/home/server/tutorials/system_metrics/index.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function systemMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'system'; - return { - id: 'systemMetrics', - name: i18n.translate('home.tutorials.systemMetrics.nameTitle', { - defaultMessage: 'System metrics', - }), - moduleName, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.systemMetrics.shortDescription', { - defaultMessage: 'Collect CPU, memory, network, and disk statistics from the host.', - }), - longDescription: i18n.translate('home.tutorials.systemMetrics.longDescription', { - defaultMessage: - 'The `system` Metricbeat module collects CPU, memory, network, and disk statistics from the host. \ -It collects system wide statistics and statistics per process and filesystem. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-system.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/system.svg', - artifacts: { - dashboards: [ - { - id: 'Metricbeat-system-overview-ecs', - linkLabel: i18n.translate('home.tutorials.systemMetrics.artifacts.dashboards.linkLabel', { - defaultMessage: 'System metrics dashboard', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-system.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/tomcat_logs/index.ts b/src/plugins/home/server/tutorials/tomcat_logs/index.ts deleted file mode 100644 index d7e6742f7e49..000000000000 --- a/src/plugins/home/server/tutorials/tomcat_logs/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function tomcatLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'tomcat'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'tomcatLogs', - name: i18n.translate('home.tutorials.tomcatLogs.nameTitle', { - defaultMessage: 'Tomcat logs', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.tomcatLogs.shortDescription', { - defaultMessage: 'Collect Apache Tomcat logs over syslog or from a file.', - }), - longDescription: i18n.translate('home.tutorials.tomcatLogs.longDescription', { - defaultMessage: - 'This is a module for receiving Apache Tomcat logs over Syslog or a file. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-tomcat.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/tomcat.svg', - artifacts: { - dashboards: [], - application: { - path: '/app/security', - label: i18n.translate('home.tutorials.tomcatLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'Security App', - }), - }, - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-tomcat.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/traefik_logs/index.ts b/src/plugins/home/server/tutorials/traefik_logs/index.ts deleted file mode 100644 index de4767ac49af..000000000000 --- a/src/plugins/home/server/tutorials/traefik_logs/index.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function traefikLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'traefik'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'traefikLogs', - name: i18n.translate('home.tutorials.traefikLogs.nameTitle', { - defaultMessage: 'Traefik logs', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.traefikLogs.shortDescription', { - defaultMessage: 'Collect Traefik access logs.', - }), - longDescription: i18n.translate('home.tutorials.traefikLogs.longDescription', { - defaultMessage: - 'The module parses access logs created by [Træfik](https://traefik.io/). \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-traefik.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/traefik.svg', - artifacts: { - dashboards: [ - { - id: 'Filebeat-Traefik-Dashboard-ecs', - linkLabel: i18n.translate('home.tutorials.traefikLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'Traefik Access Logs', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-traefik.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/traefik_metrics/index.ts b/src/plugins/home/server/tutorials/traefik_metrics/index.ts deleted file mode 100644 index 07d201f860b5..000000000000 --- a/src/plugins/home/server/tutorials/traefik_metrics/index.ts +++ /dev/null @@ -1,66 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory, TutorialSchema } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { TutorialContext } from '../../services/tutorials/lib/tutorials_registry_types'; - -export function traefikMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'traefik'; - return { - id: 'traefikMetrics', - name: i18n.translate('home.tutorials.traefikMetrics.nameTitle', { - defaultMessage: 'Traefik metrics', - }), - moduleName, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.traefikMetrics.shortDescription', { - defaultMessage: 'Fetch monitoring metrics from Traefik.', - }), - longDescription: i18n.translate('home.tutorials.traefikMetrics.longDescription', { - defaultMessage: - 'The `traefik` Metricbeat module fetches monitoring metrics from Traefik. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-traefik.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/traefik.svg', - artifacts: { - dashboards: [], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-traefik.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/uptime_monitors/index.ts b/src/plugins/home/server/tutorials/uptime_monitors/index.ts deleted file mode 100644 index f01fcfe700f2..000000000000 --- a/src/plugins/home/server/tutorials/uptime_monitors/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/heartbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function uptimeMonitorsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'uptime'; - return { - id: 'uptimeMonitors', - name: i18n.translate('home.tutorials.uptimeMonitors.nameTitle', { - defaultMessage: 'Uptime Monitors', - }), - moduleName, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.uptimeMonitors.shortDescription', { - defaultMessage: 'Monitor services for their availability', - }), - longDescription: i18n.translate('home.tutorials.uptimeMonitors.longDescription', { - defaultMessage: - 'Monitor services for their availability with active probing. \ - Given a list of URLs, Heartbeat asks the simple question: Are you alive? \ - [Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.heartbeat}/heartbeat-installation-configuration.html', - }, - }), - euiIconType: 'uptimeApp', - artifacts: { - dashboards: [], - application: { - path: '/app/uptime', - label: i18n.translate('home.tutorials.uptimeMonitors.artifacts.dashboards.linkLabel', { - defaultMessage: 'Uptime App', - }), - }, - exportedFields: { - documentationUrl: '{config.docs.beats.heartbeat}/exported-fields.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions([], context), - }; -} diff --git a/src/plugins/home/server/tutorials/uwsgi_metrics/index.ts b/src/plugins/home/server/tutorials/uwsgi_metrics/index.ts deleted file mode 100644 index ed887eb1c00e..000000000000 --- a/src/plugins/home/server/tutorials/uwsgi_metrics/index.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function uwsgiMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'uwsgi'; - return { - id: 'uwsgiMetrics', - name: i18n.translate('home.tutorials.uwsgiMetrics.nameTitle', { - defaultMessage: 'uWSGI metrics', - }), - moduleName, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.uwsgiMetrics.shortDescription', { - defaultMessage: 'Fetch internal metrics from the uWSGI server.', - }), - longDescription: i18n.translate('home.tutorials.uwsgiMetrics.longDescription', { - defaultMessage: - 'The `uwsgi` Metricbeat module fetches internal metrics from the uWSGI server. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-uwsgi.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/uwsgi.svg', - isBeta: false, - artifacts: { - dashboards: [ - { - id: '32fca290-f0af-11e7-b9ff-9f96241065de-ecs', - linkLabel: i18n.translate('home.tutorials.uwsgiMetrics.artifacts.dashboards.linkLabel', { - defaultMessage: 'uWSGI metrics dashboard', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-uwsgi.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/vsphere_metrics/index.ts b/src/plugins/home/server/tutorials/vsphere_metrics/index.ts deleted file mode 100644 index c817a02903e5..000000000000 --- a/src/plugins/home/server/tutorials/vsphere_metrics/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function vSphereMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'vsphere'; - return { - id: 'vsphereMetrics', - name: i18n.translate('home.tutorials.vsphereMetrics.nameTitle', { - defaultMessage: 'vSphere metrics', - }), - moduleName, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.vsphereMetrics.shortDescription', { - defaultMessage: 'Fetch internal metrics from vSphere.', - }), - longDescription: i18n.translate('home.tutorials.vsphereMetrics.longDescription', { - defaultMessage: - 'The `vsphere` Metricbeat module fetches internal metrics from a vSphere cluster. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-vsphere.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/vsphere.svg', - isBeta: true, - artifacts: { - application: { - label: i18n.translate('home.tutorials.vsphereMetrics.artifacts.application.label', { - defaultMessage: 'Discover', - }), - path: '/app/discover#/', - }, - dashboards: [], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-vsphere.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/windows_event_logs/index.ts b/src/plugins/home/server/tutorials/windows_event_logs/index.ts deleted file mode 100644 index 09ed6fbb385c..000000000000 --- a/src/plugins/home/server/tutorials/windows_event_logs/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/winlogbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function windowsEventLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'windows'; - return { - id: 'windowsEventLogs', - name: i18n.translate('home.tutorials.windowsEventLogs.nameTitle', { - defaultMessage: 'Windows Event Log', - }), - moduleName, - isBeta: false, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.windowsEventLogs.shortDescription', { - defaultMessage: 'Fetch logs from the Windows Event Log.', - }), - longDescription: i18n.translate('home.tutorials.windowsEventLogs.longDescription', { - defaultMessage: - 'Use Winlogbeat to collect the logs from the Windows Event Log. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.winlogbeat}/index.html', - }, - }), - euiIconType: 'logoWindows', - artifacts: { - application: { - label: i18n.translate('home.tutorials.windowsEventLogs.artifacts.application.label', { - defaultMessage: 'SIEM App', - }), - path: '/app/siem', - }, - dashboards: [], - exportedFields: { - documentationUrl: '{config.docs.beats.winlogbeat}/exported-fields.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(context), - }; -} diff --git a/src/plugins/home/server/tutorials/windows_metrics/index.ts b/src/plugins/home/server/tutorials/windows_metrics/index.ts deleted file mode 100644 index a6a74894a730..000000000000 --- a/src/plugins/home/server/tutorials/windows_metrics/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function windowsMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'windows'; - return { - id: 'windowsMetrics', - name: i18n.translate('home.tutorials.windowsMetrics.nameTitle', { - defaultMessage: 'Windows metrics', - }), - moduleName, - isBeta: false, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.windowsMetrics.shortDescription', { - defaultMessage: 'Fetch internal metrics from Windows.', - }), - longDescription: i18n.translate('home.tutorials.windowsMetrics.longDescription', { - defaultMessage: - 'The `windows` Metricbeat module fetches internal metrics from Windows. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-windows.html', - }, - }), - euiIconType: 'logoWindows', - artifacts: { - application: { - label: i18n.translate('home.tutorials.windowsMetrics.artifacts.application.label', { - defaultMessage: 'Discover', - }), - path: '/app/discover#/', - }, - dashboards: [], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-windows.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/zeek_logs/index.ts b/src/plugins/home/server/tutorials/zeek_logs/index.ts deleted file mode 100644 index 42946dbedda6..000000000000 --- a/src/plugins/home/server/tutorials/zeek_logs/index.ts +++ /dev/null @@ -1,79 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function zeekLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'zeek'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'zeekLogs', - name: i18n.translate('home.tutorials.zeekLogs.nameTitle', { - defaultMessage: 'Zeek logs', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.zeekLogs.shortDescription', { - defaultMessage: 'Collect Zeek network security monitoring logs.', - }), - longDescription: i18n.translate('home.tutorials.zeekLogs.longDescription', { - defaultMessage: - 'This is a module for Zeek, which used to be called Bro. It parses logs \ - that are in the [Zeek JSON format](https://www.zeek.org/manual/release/logs/index.html). \ - [Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-zeek.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/zeek.svg', - artifacts: { - dashboards: [ - { - id: '7cbb5410-3700-11e9-aa6d-ff445a78330c', - linkLabel: i18n.translate('home.tutorials.zeekLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'Zeek Overview', - }), - isOverview: true, - }, - ], - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-zeek.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/home/server/tutorials/zookeeper_metrics/index.ts b/src/plugins/home/server/tutorials/zookeeper_metrics/index.ts deleted file mode 100644 index 9ab46408e889..000000000000 --- a/src/plugins/home/server/tutorials/zookeeper_metrics/index.ts +++ /dev/null @@ -1,77 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/metricbeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function zookeeperMetricsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'zookeeper'; - return { - id: moduleName + 'Metrics', - name: i18n.translate('home.tutorials.zookeeperMetrics.nameTitle', { - defaultMessage: 'Zookeeper metrics', - }), - moduleName, - euiIconType: '/plugins/home/assets/tutorials/logos/zookeeper.svg', - isBeta: false, - category: TutorialsCategory.METRICS, - shortDescription: i18n.translate('home.tutorials.zookeeperMetrics.shortDescription', { - defaultMessage: 'Fetch internal metrics from a Zookeeper server.', - }), - longDescription: i18n.translate('home.tutorials.zookeeperMetrics.longDescription', { - defaultMessage: - 'The `{moduleName}` Metricbeat module fetches internal metrics from a Zookeeper server. \ -[Learn more]({learnMoreLink}).', - values: { - moduleName, - learnMoreLink: '{config.docs.beats.metricbeat}/metricbeat-module-' + moduleName + '.html', - }, - }), - artifacts: { - application: { - label: i18n.translate('home.tutorials.zookeeperMetrics.artifacts.application.label', { - defaultMessage: 'Discover', - }), - path: '/app/discover#/', - }, - dashboards: [], - exportedFields: { - documentationUrl: '{config.docs.beats.metricbeat}/exported-fields-' + moduleName + '.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, context), - }; -} diff --git a/src/plugins/home/server/tutorials/zscaler_logs/index.ts b/src/plugins/home/server/tutorials/zscaler_logs/index.ts deleted file mode 100644 index ee081c170ce1..000000000000 --- a/src/plugins/home/server/tutorials/zscaler_logs/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { TutorialsCategory } from '../../services/tutorials'; -import { onPremInstructions } from '../instructions/filebeat_instructions'; -import { - TutorialContext, - TutorialSchema, -} from '../../services/tutorials/lib/tutorials_registry_types'; - -export function zscalerLogsSpecProvider(context: TutorialContext): TutorialSchema { - const moduleName = 'zscaler'; - const platforms = ['OSX', 'DEB', 'RPM', 'WINDOWS'] as const; - return { - id: 'zscalerLogs', - name: i18n.translate('home.tutorials.zscalerLogs.nameTitle', { - defaultMessage: 'Zscaler Logs', - }), - moduleName, - category: TutorialsCategory.SECURITY_SOLUTION, - shortDescription: i18n.translate('home.tutorials.zscalerLogs.shortDescription', { - defaultMessage: 'This is a module for receiving Zscaler NSS logs over Syslog or a file.', - }), - longDescription: i18n.translate('home.tutorials.zscalerLogs.longDescription', { - defaultMessage: - 'This is a module for receiving Zscaler NSS logs over Syslog or a file. \ -[Learn more]({learnMoreLink}).', - values: { - learnMoreLink: '{config.docs.beats.filebeat}/filebeat-module-zscaler.html', - }, - }), - euiIconType: '/plugins/home/assets/tutorials/logos/zscaler.svg', - artifacts: { - dashboards: [], - application: { - path: '/app/security', - label: i18n.translate('home.tutorials.zscalerLogs.artifacts.dashboards.linkLabel', { - defaultMessage: 'Security App', - }), - }, - exportedFields: { - documentationUrl: '{config.docs.beats.filebeat}/exported-fields-zscaler.html', - }, - }, - completionTimeMinutes: 10, - onPrem: onPremInstructions(moduleName, platforms, context), - }; -} diff --git a/src/plugins/index_pattern_management/public/components/create_button/create_button.tsx b/src/plugins/index_pattern_management/public/components/create_button/create_button.tsx index af67a5e317ce..28ba3b7afca6 100644 --- a/src/plugins/index_pattern_management/public/components/create_button/create_button.tsx +++ b/src/plugins/index_pattern_management/public/components/create_button/create_button.tsx @@ -28,8 +28,6 @@ * under the License. */ -// @ts-ignore -import { euiColorAccent } from '@elastic/eui/dist/eui_theme_light.json'; import React, { Component, Fragment } from 'react'; import { @@ -148,7 +146,7 @@ export class CreateButton extends Component { private renderBetaBadge = () => { return ( - + + +
+ + +`; + +exports[`StepDataSource should render normally with hideLocalCluster true 1`] = ` + + +
+ + +`; + +exports[`StepDataSource should render normally with hideLocalCluster undefined 1`] = `
{ goToNextStep={() => {}} isNextStepDisabled={true} stepInfo={{ totalStepNumber: 0, currentStepNumber: 0 }} + hideLocalCluster={false} /> ); @@ -53,6 +54,7 @@ describe('Header', () => { goToNextStep={() => {}} isNextStepDisabled={true} stepInfo={{ totalStepNumber: 0, currentStepNumber: 0 }} + hideLocalCluster={false} /> ); @@ -79,6 +81,7 @@ describe('Header', () => { goToNextStep={() => {}} isNextStepDisabled={true} stepInfo={{ totalStepNumber: 0, currentStepNumber: 0 }} + hideLocalCluster={false} /> ); @@ -96,4 +99,23 @@ describe('Header', () => { .prop('isDisabled') ).toEqual(false); }); + + it('should disable next step when local cluster option is hidden and no other option selected', () => { + const component = shallowWithIntl( +
{}} + dataSourceRef={{ type: 'type', id: 'id', title: 'title' }!} + goToNextStep={() => {}} + isNextStepDisabled={true} + stepInfo={{ totalStepNumber: 0, currentStepNumber: 0 }} + hideLocalCluster={true} + /> + ); + + expect( + component + .find('[data-test-subj="createIndexPatternStepDataSourceNextStepButton"]') + .prop('isDisabled') + ).toEqual(true); + }); }); diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_data_source/components/header/header.tsx b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_data_source/components/header/header.tsx index e5a6fdf60c0e..bf011d5d14ba 100644 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_data_source/components/header/header.tsx +++ b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_data_source/components/header/header.tsx @@ -34,14 +34,22 @@ interface HeaderProps { goToNextStep: (dataSourceRef: DataSourceRef) => void; isNextStepDisabled: boolean; stepInfo: StepInfo; + hideLocalCluster: boolean; } export const Header: React.FC = (props: HeaderProps) => { - const { dataSourceRef, onDataSourceSelected, goToNextStep, isNextStepDisabled, stepInfo } = props; + const { + dataSourceRef, + onDataSourceSelected, + goToNextStep, + isNextStepDisabled, + stepInfo, + hideLocalCluster, + } = props; const { currentStepNumber, totalStepNumber } = stepInfo; - const [defaultChecked, setDefaultChecked] = useState(true); - const [dataSourceChecked, setDataSourceChecked] = useState(false); + const [defaultChecked, setDefaultChecked] = useState(!hideLocalCluster); + const [dataSourceChecked, setDataSourceChecked] = useState(hideLocalCluster); const [dataSources, setDataSources] = useState([]); const [isLoading, setIsLoading] = useState(false); @@ -113,34 +121,38 @@ export const Header: React.FC = (props: HeaderProps) => { defaultMessage="Pick a data source within which to configure index patterns." /> - - + + + } + checked={defaultChecked} + onChange={(e) => onChangeDefaultChecked(e)} + compressed /> - } - checked={defaultChecked} - onChange={(e) => onChangeDefaultChecked(e)} - compressed - /> - - + + } + checked={dataSourceChecked} + onChange={(e) => onChangeDataSourceChecked(e)} + compressed /> - } - checked={dataSourceChecked} - onChange={(e) => onChangeDataSourceChecked(e)} - compressed - /> + + )} {dataSourceChecked && ( diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_data_source/step_data_source.test.tsx b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_data_source/step_data_source.test.tsx index 42c5ffa4cee6..3ce1e5f441b3 100644 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_data_source/step_data_source.test.tsx +++ b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_data_source/step_data_source.test.tsx @@ -8,7 +8,7 @@ import { shallow } from 'enzyme'; import { StepDataSource } from './step_data_source'; describe('StepDataSource', () => { - it('should render normally', () => { + it('should render normally with hideLocalCluster undefined', () => { const component = shallow( {}} @@ -18,4 +18,28 @@ describe('StepDataSource', () => { expect(component).toMatchSnapshot(); }); + + it('should render normally with hideLocalCluster false', () => { + const component = shallow( + {}} + stepInfo={{ totalStepNumber: 0, currentStepNumber: 0 }} + hideLocalCluster={false} + /> + ); + + expect(component).toMatchSnapshot(); + }); + + it('should render normally with hideLocalCluster true', () => { + const component = shallow( + {}} + stepInfo={{ totalStepNumber: 0, currentStepNumber: 0 }} + hideLocalCluster={true} + /> + ); + + expect(component).toMatchSnapshot(); + }); }); diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_data_source/step_data_source.tsx b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_data_source/step_data_source.tsx index fa91b455ae6c..4dcd4661884b 100644 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_data_source/step_data_source.tsx +++ b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_data_source/step_data_source.tsx @@ -13,10 +13,11 @@ import { Header } from './components/header'; interface StepDataSourceProps { goToNextStep: (dataSourceRef: DataSourceRef) => void; stepInfo: StepInfo; + hideLocalCluster: boolean; } export const StepDataSource = (props: StepDataSourceProps) => { - const { goToNextStep, stepInfo } = props; + const { goToNextStep, stepInfo, hideLocalCluster } = props; const [selectedDataSource, setSelectedDataSource] = useState(); const [isNextStepDisabled, setIsNextStepDisabled] = useState(true); @@ -37,6 +38,7 @@ export const StepDataSource = (props: StepDataSourceProps) => { goToNextStep={() => goToNextStep(selectedDataSource!)} isNextStepDisabled={isNextStepDisabled} stepInfo={stepInfo} + hideLocalCluster={hideLocalCluster} /> ); diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/create_index_pattern_wizard.tsx b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/create_index_pattern_wizard.tsx index 37669ba43e9d..337efa752aee 100644 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/create_index_pattern_wizard.tsx +++ b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/create_index_pattern_wizard.tsx @@ -280,6 +280,8 @@ export class CreateIndexPatternWizard extends Component< currentStepNumber: getCurrentStepNumber(step, this.dataSourceEnabled), }; + const hideLocalCluster = this.context.services.hideLocalCluster; + if (isInitiallyLoadingIndices) { return ; } @@ -291,7 +293,11 @@ export class CreateIndexPatternWizard extends Component< {header} - + ); } diff --git a/src/plugins/index_pattern_management/public/management_app/mount_management_section.tsx b/src/plugins/index_pattern_management/public/management_app/mount_management_section.tsx index ff8345429d16..db2d45f10db1 100644 --- a/src/plugins/index_pattern_management/public/management_app/mount_management_section.tsx +++ b/src/plugins/index_pattern_management/public/management_app/mount_management_section.tsx @@ -40,6 +40,7 @@ import { ScopedHistory, StartServicesAccessor, } from 'src/core/public'; +import { DataSourcePluginSetup } from 'src/plugins/data_source/public'; import { EuiPage, EuiPageBody } from '@elastic/eui'; import { @@ -68,15 +69,17 @@ const readOnlyBadge = { export async function mountManagementSection( getStartServices: StartServicesAccessor, params: AppMountParameters, - getMlCardState: () => MlCardState + getMlCardState: () => MlCardState, + dataSource?: DataSourcePluginSetup ) { const [ { chrome, application, savedObjects, uiSettings, notifications, overlays, http, docLinks }, - { data, dataSource }, + { data }, indexPatternManagementStart, ] = await getStartServices(); const canSave = Boolean(application.capabilities.indexPatterns.save); - const dataSourceEnabled = !!dataSource; + const dataSourceEnabled = dataSource?.dataSourceEnabled ?? false; + const hideLocalCluster = dataSource?.hideLocalCluster ?? false; if (!canSave) { chrome.setBadge(readOnlyBadge); @@ -107,6 +110,7 @@ export async function mountManagementSection( setBreadcrumbs: setBreadcrumbsScope, getMlCardState, dataSourceEnabled, + hideLocalCluster, }; ReactDOM.render( diff --git a/src/plugins/index_pattern_management/public/mocks.ts b/src/plugins/index_pattern_management/public/mocks.ts index 295bd7e3faee..dacf876c2f6c 100644 --- a/src/plugins/index_pattern_management/public/mocks.ts +++ b/src/plugins/index_pattern_management/public/mocks.ts @@ -53,6 +53,9 @@ const createSetupContract = (): IndexPatternManagementSetup => ({ environment: { update: jest.fn(), }, + columns: { + register: jest.fn(), + }, }); const createStartContract = (): IndexPatternManagementStart => ({ diff --git a/src/plugins/index_pattern_management/public/plugin.ts b/src/plugins/index_pattern_management/public/plugin.ts index 758c81ee538f..f7b4461a10f7 100644 --- a/src/plugins/index_pattern_management/public/plugin.ts +++ b/src/plugins/index_pattern_management/public/plugin.ts @@ -39,7 +39,7 @@ import { ScopedHistory, } from 'src/core/public'; import { DataPublicPluginStart } from 'src/plugins/data/public'; -import { DataSourcePluginStart } from 'src/plugins/data_source/public'; +import { DataSourcePluginSetup, DataSourcePluginStart } from 'src/plugins/data_source/public'; import { UrlForwardingSetup } from '../../url_forwarding/public'; import { IndexPatternManagementService, @@ -53,6 +53,7 @@ import { reactRouterNavigate } from '../../opensearch_dashboards_react/public'; export interface IndexPatternManagementSetupDependencies { urlForwarding: UrlForwardingSetup; + dataSource?: DataSourcePluginSetup; } export interface IndexPatternManagementStartDependencies { @@ -84,9 +85,11 @@ export class IndexPatternManagementPlugin public setup( core: CoreSetup, - { urlForwarding }: IndexPatternManagementSetupDependencies + dependencies: IndexPatternManagementSetupDependencies ) { const newAppPath = IPM_APP_ID; + const { urlForwarding, dataSource } = dependencies; + const legacyPatternsPath = 'management/opensearch-dashboards/index_patterns'; urlForwarding.forwardApp( @@ -131,8 +134,11 @@ export class IndexPatternManagementPlugin basePath: params.appBasePath, }; - return mountManagementSection(core.getStartServices, managementParams, () => - this.indexPatternManagementService.environmentService.getEnvironment().ml() + return mountManagementSection( + core.getStartServices, + managementParams, + () => this.indexPatternManagementService.environmentService.getEnvironment().ml(), + dataSource ); }, }); diff --git a/src/plugins/index_pattern_management/public/types.ts b/src/plugins/index_pattern_management/public/types.ts index 24dc2cd59c31..7b2cd8575a7e 100644 --- a/src/plugins/index_pattern_management/public/types.ts +++ b/src/plugins/index_pattern_management/public/types.ts @@ -59,6 +59,7 @@ export interface IndexPatternManagmentContext { setBreadcrumbs: ManagementAppMountParams['setBreadcrumbs']; getMlCardState: () => MlCardState; dataSourceEnabled: boolean; + hideLocalCluster: boolean; } export type IndexPatternManagmentContextValue = OpenSearchDashboardsReactContextValue< diff --git a/src/plugins/inspector/public/views/requests/components/details/req_details_request.tsx b/src/plugins/inspector/public/views/requests/components/details/req_details_request.tsx index fe758e128322..adc00588307a 100644 --- a/src/plugins/inspector/public/views/requests/components/details/req_details_request.tsx +++ b/src/plugins/inspector/public/views/requests/components/details/req_details_request.tsx @@ -31,6 +31,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { EuiCodeBlock } from '@elastic/eui'; +import { stringify } from '@osd/std'; import { Request } from '../../../../../common/adapters/request/types'; import { RequestDetailsProps } from '../types'; @@ -55,7 +56,7 @@ export class RequestDetailsRequest extends Component { isCopyable data-test-subj="inspectorRequestBody" > - {JSON.stringify(json, null, 2)} + {stringify(json, null, 2)} ); } diff --git a/src/plugins/inspector/public/views/requests/components/details/req_details_response.tsx b/src/plugins/inspector/public/views/requests/components/details/req_details_response.tsx index d5cd80ec35e2..4693b0dbfba5 100644 --- a/src/plugins/inspector/public/views/requests/components/details/req_details_response.tsx +++ b/src/plugins/inspector/public/views/requests/components/details/req_details_response.tsx @@ -31,6 +31,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { EuiCodeBlock } from '@elastic/eui'; +import { stringify } from '@osd/std'; import { Request } from '../../../../../common/adapters/request/types'; import { RequestDetailsProps } from '../types'; @@ -58,7 +59,7 @@ export class RequestDetailsResponse extends Component { isCopyable data-test-subj="inspectorResponseBody" > - {JSON.stringify(responseJSON, null, 2)} + {stringify(responseJSON, null, 2)} ); } diff --git a/src/plugins/maps_legacy/server/index.ts b/src/plugins/maps_legacy/server/index.ts index f6240d8aef50..d5ea908025e9 100644 --- a/src/plugins/maps_legacy/server/index.ts +++ b/src/plugins/maps_legacy/server/index.ts @@ -52,7 +52,7 @@ export const config: PluginConfigDescriptor = { schema: configSchema, deprecations: ({ renameFromRoot }) => [ renameFromRoot('map.includeElasticMapsService', 'map.includeOpenSearchMapsService'), - renameFromRoot('map.proxyOpenSearchMapsServiceInMaps', 'map.proxyElasticMapsServiceInMaps'), + renameFromRoot('map.proxyElasticMapsServiceInMaps', 'map.proxyOpenSearchMapsServiceInMaps'), renameFromRoot( 'map.regionmap.includeElasticMapsService', 'map.regionmap.includeOpenSearchMapsService' diff --git a/src/plugins/opensearch_dashboards_legacy/public/angular/angular_config.tsx b/src/plugins/opensearch_dashboards_legacy/public/angular/angular_config.tsx deleted file mode 100644 index fbe36a289d70..000000000000 --- a/src/plugins/opensearch_dashboards_legacy/public/angular/angular_config.tsx +++ /dev/null @@ -1,380 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { - ICompileProvider, - IHttpProvider, - IHttpService, - ILocationProvider, - IModule, - IRootScopeService, -} from 'angular'; -import $ from 'jquery'; -import { set } from '@elastic/safer-lodash-set'; -import { get } from 'lodash'; -import * as Rx from 'rxjs'; -import { ChromeBreadcrumb, EnvironmentMode, PackageInfo } from 'opensearch-dashboards/public'; -import { History } from 'history'; - -import { CoreStart } from 'opensearch-dashboards/public'; -import { isSystemApiRequest } from '../utils'; -import { formatAngularHttpError, isAngularHttpError } from '../notify/lib'; - -export interface RouteConfiguration { - controller?: string | ((...args: any[]) => void); - redirectTo?: string; - resolveRedirectTo?: (...args: any[]) => void; - reloadOnSearch?: boolean; - reloadOnUrl?: boolean; - outerAngularWrapperRoute?: boolean; - resolve?: object; - template?: string; - k7Breadcrumbs?: (...args: any[]) => ChromeBreadcrumb[]; - requireUICapability?: string; -} - -/** - * Detects whether a given angular route is a dummy route that doesn't - * require any action. There are two ways this can happen: - * If `outerAngularWrapperRoute` is set on the route config object, - * it means the local application service set up this route on the outer angular - * and the internal routes will handle the hooks. - * - * If angular did not detect a route and it is the local angular, we are currently - * navigating away from a URL controlled by a local angular router and the - * application will get unmounted. In this case the outer router will handle - * the hooks. - * @param $route Injected $route dependency - * @param isLocalAngular Flag whether this is the local angular router - */ -function isDummyRoute($route: any, isLocalAngular: boolean) { - return ( - ($route.current && $route.current.$$route && $route.current.$$route.outerAngularWrapperRoute) || - (!$route.current && isLocalAngular) - ); -} - -export const configureAppAngularModule = ( - angularModule: IModule, - newPlatform: { - core: CoreStart; - readonly env: { - mode: Readonly; - packageInfo: Readonly; - }; - }, - isLocalAngular: boolean, - getHistory?: () => History -) => { - const core = 'core' in newPlatform ? newPlatform.core : newPlatform; - const packageInfo = newPlatform.env.packageInfo; - - angularModule - .value('osdVersion', packageInfo.version) - .value('buildNum', packageInfo.buildNum) - .value('buildSha', packageInfo.buildSha) - .value('opensearchUrl', getOpenSearchUrl(core)) - .value('uiCapabilities', core.application.capabilities) - .config(setupCompileProvider(newPlatform.env.mode.dev)) - .config(setupLocationProvider()) - .config($setupXsrfRequestInterceptor(packageInfo.version)) - .run(capture$httpLoadingCount(core)) - .run(digestOnHashChange(getHistory)) - .run($setupBreadcrumbsAutoClear(core, isLocalAngular)) - .run($setupBadgeAutoClear(core, isLocalAngular)) - .run($setupHelpExtensionAutoClear(core, isLocalAngular)) - .run($setupUICapabilityRedirect(core)); -}; - -const getOpenSearchUrl = (newPlatform: CoreStart) => { - const a = document.createElement('a'); - a.href = newPlatform.http.basePath.prepend('/opensearch'); - const protocolPort = /https/.test(a.protocol) ? 443 : 80; - const port = a.port || protocolPort; - return { - host: a.hostname, - port, - protocol: a.protocol, - pathname: a.pathname, - }; -}; - -const digestOnHashChange = (getHistory?: () => History) => ($rootScope: IRootScopeService) => { - if (!getHistory) return; - const unlisten = getHistory().listen(() => { - // dispatch synthetic hash change event to update hash history objects and angular routing - // this is necessary because hash updates triggered by using popState won't trigger this event naturally. - // this has to happen in the next tick to not change the existing timing of angular digest cycles. - setTimeout(() => { - window.dispatchEvent(new HashChangeEvent('hashchange')); - }, 0); - }); - $rootScope.$on('$destroy', unlisten); -}; - -const setupCompileProvider = (devMode: boolean) => ($compileProvider: ICompileProvider) => { - if (!devMode) { - $compileProvider.debugInfoEnabled(false); - } -}; - -const setupLocationProvider = () => ($locationProvider: ILocationProvider) => { - $locationProvider.html5Mode({ - enabled: false, - requireBase: false, - rewriteLinks: false, - }); - - $locationProvider.hashPrefix(''); -}; - -export const $setupXsrfRequestInterceptor = (version: string) => { - // Configure jQuery prefilter - $.ajaxPrefilter(({ osdXsrfToken = true }: any, originalOptions, jqXHR) => { - if (osdXsrfToken) { - jqXHR.setRequestHeader('osd-xsrf', 'osd-legacy'); - // ToDo: Remove next; `osd-version` incorrectly used for satisfying XSRF protection - jqXHR.setRequestHeader('osd-version', version); - } - }); - - return ($httpProvider: IHttpProvider) => { - // Configure $httpProvider interceptor - $httpProvider.interceptors.push(() => { - return { - request(opts) { - const { osdXsrfToken = true } = opts as any; - if (osdXsrfToken) { - set(opts, ['headers', 'osd-xsrf'], 'osd-legacy'); - // ToDo: Remove next; `osd-version` incorrectly used for satisfying XSRF protection - set(opts, ['headers', 'osd-version'], version); - } - return opts; - }, - }; - }); - }; -}; - -/** - * Injected into angular module by ui/chrome angular integration - * and adds a root-level watcher that will capture the count of - * active $http requests on each digest loop and expose the count to - * the core.loadingCount api - */ -const capture$httpLoadingCount = (newPlatform: CoreStart) => ( - $rootScope: IRootScopeService, - $http: IHttpService -) => { - newPlatform.http.addLoadingCountSource( - new Rx.Observable((observer) => { - const unwatch = $rootScope.$watch(() => { - const reqs = $http.pendingRequests || []; - observer.next(reqs.filter((req) => !isSystemApiRequest(req)).length); - }); - - return unwatch; - }) - ); -}; - -/** - * integrates with angular to automatically redirect to home if required - * capability is not met - */ -const $setupUICapabilityRedirect = (newPlatform: CoreStart) => ( - $rootScope: IRootScopeService, - $injector: any -) => { - const isOpenSearchDashboardsAppRoute = window.location.pathname.endsWith( - '/app/opensearch-dashboards' - ); - // this feature only works within opensearch dashboards app for now after everything is - // switched to the application service, this can be changed to handle all - // apps. - if (!isOpenSearchDashboardsAppRoute) { - return; - } - $rootScope.$on( - '$routeChangeStart', - (event, { $$route: route }: { $$route?: RouteConfiguration } = {}) => { - if (!route || !route.requireUICapability) { - return; - } - - if (!get(newPlatform.application.capabilities, route.requireUICapability)) { - $injector.get('$location').url('/home'); - event.preventDefault(); - } - } - ); -}; - -/** - * internal angular run function that will be called when angular bootstraps and - * lets us integrate with the angular router so that we can automatically clear - * the breadcrumbs if we switch to a OpenSearch Dashboards app that does not use breadcrumbs correctly - */ -const $setupBreadcrumbsAutoClear = (newPlatform: CoreStart, isLocalAngular: boolean) => ( - $rootScope: IRootScopeService, - $injector: any -) => { - // A flag used to determine if we should automatically - // clear the breadcrumbs between angular route changes. - let breadcrumbSetSinceRouteChange = false; - const $route = $injector.has('$route') ? $injector.get('$route') : {}; - - // reset breadcrumbSetSinceRouteChange any time the breadcrumbs change, even - // if it was done directly through the new platform - newPlatform.chrome.getBreadcrumbs$().subscribe({ - next() { - breadcrumbSetSinceRouteChange = true; - }, - }); - - $rootScope.$on('$routeChangeStart', () => { - breadcrumbSetSinceRouteChange = false; - }); - - $rootScope.$on('$routeChangeSuccess', () => { - if (isDummyRoute($route, isLocalAngular)) { - return; - } - const current = $route.current || {}; - - if (breadcrumbSetSinceRouteChange || (current.$$route && current.$$route.redirectTo)) { - return; - } - - const k7BreadcrumbsProvider = current.k7Breadcrumbs; - if (!k7BreadcrumbsProvider) { - newPlatform.chrome.setBreadcrumbs([]); - return; - } - - try { - newPlatform.chrome.setBreadcrumbs($injector.invoke(k7BreadcrumbsProvider)); - } catch (error) { - if (isAngularHttpError(error)) { - error = formatAngularHttpError(error); - } - newPlatform.fatalErrors.add(error, 'location'); - } - }); -}; - -/** - * internal angular run function that will be called when angular bootstraps and - * lets us integrate with the angular router so that we can automatically clear - * the badge if we switch to a OpenSearch Dashboards app that does not use the badge correctly - */ -const $setupBadgeAutoClear = (newPlatform: CoreStart, isLocalAngular: boolean) => ( - $rootScope: IRootScopeService, - $injector: any -) => { - // A flag used to determine if we should automatically - // clear the badge between angular route changes. - let badgeSetSinceRouteChange = false; - const $route = $injector.has('$route') ? $injector.get('$route') : {}; - - $rootScope.$on('$routeChangeStart', () => { - badgeSetSinceRouteChange = false; - }); - - $rootScope.$on('$routeChangeSuccess', () => { - if (isDummyRoute($route, isLocalAngular)) { - return; - } - const current = $route.current || {}; - - if (badgeSetSinceRouteChange || (current.$$route && current.$$route.redirectTo)) { - return; - } - - const badgeProvider = current.badge; - if (!badgeProvider) { - newPlatform.chrome.setBadge(undefined); - return; - } - - try { - newPlatform.chrome.setBadge($injector.invoke(badgeProvider)); - } catch (error) { - if (isAngularHttpError(error)) { - error = formatAngularHttpError(error); - } - newPlatform.fatalErrors.add(error, 'location'); - } - }); -}; - -/** - * internal angular run function that will be called when angular bootstraps and - * lets us integrate with the angular router so that we can automatically clear - * the helpExtension if we switch to a OpenSearch Dashboards app that does not set its own - * helpExtension - */ -const $setupHelpExtensionAutoClear = (newPlatform: CoreStart, isLocalAngular: boolean) => ( - $rootScope: IRootScopeService, - $injector: any -) => { - /** - * reset helpExtensionSetSinceRouteChange any time the helpExtension changes, even - * if it was done directly through the new platform - */ - let helpExtensionSetSinceRouteChange = false; - newPlatform.chrome.getHelpExtension$().subscribe({ - next() { - helpExtensionSetSinceRouteChange = true; - }, - }); - - const $route = $injector.has('$route') ? $injector.get('$route') : {}; - - $rootScope.$on('$routeChangeStart', () => { - if (isDummyRoute($route, isLocalAngular)) { - return; - } - helpExtensionSetSinceRouteChange = false; - }); - - $rootScope.$on('$routeChangeSuccess', () => { - if (isDummyRoute($route, isLocalAngular)) { - return; - } - const current = $route.current || {}; - - if (helpExtensionSetSinceRouteChange || (current.$$route && current.$$route.redirectTo)) { - return; - } - - newPlatform.chrome.setHelpExtension(current.helpExtension); - }); -}; diff --git a/src/plugins/opensearch_dashboards_legacy/public/angular/index.ts b/src/plugins/opensearch_dashboards_legacy/public/angular/index.ts deleted file mode 100644 index c492de510093..000000000000 --- a/src/plugins/opensearch_dashboards_legacy/public/angular/index.ts +++ /dev/null @@ -1,38 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -// @ts-ignore -export { PromiseServiceCreator } from './promises'; -// @ts-ignore -export { watchMultiDecorator } from './watch_multi'; -export * from './angular_config'; -// @ts-ignore -export { createTopNavDirective, createTopNavHelper, loadOsdTopNavDirectives } from './osd_top_nav'; -export { subscribeWithScope } from './subscribe_with_scope'; diff --git a/src/plugins/opensearch_dashboards_legacy/public/angular/osd_top_nav.js b/src/plugins/opensearch_dashboards_legacy/public/angular/osd_top_nav.js deleted file mode 100644 index 11835005b60c..000000000000 --- a/src/plugins/opensearch_dashboards_legacy/public/angular/osd_top_nav.js +++ /dev/null @@ -1,140 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import angular from 'angular'; -import 'ngreact'; - -export function createTopNavDirective() { - return { - restrict: 'E', - template: '', - compile: (elem) => { - const child = document.createElement('osd-top-nav-helper'); - - // Copy attributes to the child directive - for (const attr of elem[0].attributes) { - child.setAttribute(attr.name, attr.value); - } - - // Add a special attribute that will change every time that one - // of the config array's disableButton function return value changes. - child.setAttribute('disabled-buttons', 'disabledButtons'); - - // Append helper directive - elem.append(child); - - const linkFn = ($scope, _, $attr) => { - // Watch config changes - $scope.$watch( - () => { - const config = $scope.$eval($attr.config) || []; - return config.map((item) => { - // Copy key into id, as it's a reserved react propery. - // This is done for Angular directive backward compatibility. - // In React only id is recognized. - if (item.key && !item.id) { - item.id = item.key; - } - - // Watch the disableButton functions - if (typeof item.disableButton === 'function') { - return item.disableButton(); - } - return item.disableButton; - }); - }, - (newVal) => { - $scope.disabledButtons = newVal; - }, - true - ); - }; - - return linkFn; - }, - }; -} - -export const createTopNavHelper = ({ TopNavMenu }) => (reactDirective) => { - return reactDirective(TopNavMenu, [ - ['config', { watchDepth: 'value' }], - ['setMenuMountPoint', { watchDepth: 'reference' }], - ['disabledButtons', { watchDepth: 'reference' }], - - ['query', { watchDepth: 'reference' }], - ['savedQuery', { watchDepth: 'reference' }], - ['intl', { watchDepth: 'reference' }], - - ['onQuerySubmit', { watchDepth: 'reference' }], - ['onFiltersUpdated', { watchDepth: 'reference' }], - ['onRefreshChange', { watchDepth: 'reference' }], - ['onClearSavedQuery', { watchDepth: 'reference' }], - ['onSaved', { watchDepth: 'reference' }], - ['onSavedQueryUpdated', { watchDepth: 'reference' }], - ['onSavedQueryIdChange', { watchDepth: 'reference' }], - - ['indexPatterns', { watchDepth: 'collection' }], - ['filters', { watchDepth: 'collection' }], - - // All modifiers default to true. - // Set to false to hide subcomponents. - 'showSearchBar', - 'showQueryBar', - 'showQueryInput', - 'showSaveQuery', - 'showDatePicker', - 'showFilterBar', - - 'appName', - 'screenTitle', - 'dateRangeFrom', - 'dateRangeTo', - 'savedQueryId', - 'isRefreshPaused', - 'refreshInterval', - 'disableAutoFocus', - 'showAutoRefreshOnly', - - // temporary flag to use the stateful components - 'useDefaultBehaviors', - ]); -}; - -let isLoaded = false; - -export function loadOsdTopNavDirectives(navUi) { - if (!isLoaded) { - isLoaded = true; - angular - .module('opensearchDashboards') - .directive('osdTopNav', createTopNavDirective) - .directive('osdTopNavHelper', createTopNavHelper(navUi)); - } -} diff --git a/src/plugins/opensearch_dashboards_legacy/public/angular/promises.js b/src/plugins/opensearch_dashboards_legacy/public/angular/promises.js deleted file mode 100644 index 690bc5489d10..000000000000 --- a/src/plugins/opensearch_dashboards_legacy/public/angular/promises.js +++ /dev/null @@ -1,140 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import _ from 'lodash'; - -export function PromiseServiceCreator($q, $timeout) { - function Promise(fn) { - if (typeof this === 'undefined') - throw new Error('Promise constructor must be called with "new"'); - - const defer = $q.defer(); - try { - fn(defer.resolve, defer.reject); - } catch (e) { - defer.reject(e); - } - return defer.promise; - } - - Promise.all = Promise.props = $q.all; - Promise.resolve = function (val) { - const defer = $q.defer(); - defer.resolve(val); - return defer.promise; - }; - Promise.reject = function (reason) { - const defer = $q.defer(); - defer.reject(reason); - return defer.promise; - }; - Promise.cast = $q.when; - Promise.delay = function (ms) { - return $timeout(_.noop, ms); - }; - Promise.method = function (fn) { - return function () { - const args = Array.prototype.slice.call(arguments); - return Promise.try(fn, args, this); - }; - }; - Promise.nodeify = function (promise, cb) { - promise.then(function (val) { - cb(void 0, val); - }, cb); - }; - Promise.map = function (arr, fn) { - return Promise.all( - arr.map(function (i, el, list) { - return Promise.try(fn, [i, el, list]); - }) - ); - }; - Promise.each = function (arr, fn) { - const queue = arr.slice(0); - let i = 0; - return (function next() { - if (!queue.length) return arr; - return Promise.try(fn, [arr.shift(), i++]).then(next); - })(); - }; - Promise.is = function (obj) { - // $q doesn't create instances of any constructor, promises are just objects with a then function - // https://github.com/angular/angular.js/blob/58f5da86645990ef984353418cd1ed83213b111e/src/ng/q.js#L335 - return obj && typeof obj.then === 'function'; - }; - Promise.halt = _.once(function () { - const promise = new Promise(() => {}); - promise.then = _.constant(promise); - promise.catch = _.constant(promise); - return promise; - }); - Promise.try = function (fn, args, ctx) { - if (typeof fn !== 'function') { - return Promise.reject(new TypeError('fn must be a function')); - } - - let value; - - if (Array.isArray(args)) { - try { - value = fn.apply(ctx, args); - } catch (e) { - return Promise.reject(e); - } - } else { - try { - value = fn.call(ctx, args); - } catch (e) { - return Promise.reject(e); - } - } - - return Promise.resolve(value); - }; - Promise.fromNode = function (takesCbFn) { - return new Promise(function (resolve, reject) { - takesCbFn(function (err, ...results) { - if (err) reject(err); - else if (results.length > 1) resolve(results); - else resolve(results[0]); - }); - }); - }; - Promise.race = function (iterable) { - return new Promise((resolve, reject) => { - for (const i of iterable) { - Promise.resolve(i).then(resolve, reject); - } - }); - }; - - return Promise; -} diff --git a/src/plugins/opensearch_dashboards_legacy/public/angular/subscribe_with_scope.test.ts b/src/plugins/opensearch_dashboards_legacy/public/angular/subscribe_with_scope.test.ts deleted file mode 100644 index 3784988fc818..000000000000 --- a/src/plugins/opensearch_dashboards_legacy/public/angular/subscribe_with_scope.test.ts +++ /dev/null @@ -1,208 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import * as Rx from 'rxjs'; -import { subscribeWithScope } from './subscribe_with_scope'; - -// eslint-disable-next-line prefer-const -let $rootScope: Scope; - -class Scope { - public $$phase?: string; - public $root = $rootScope; - public $apply = jest.fn((fn: () => void) => fn()); -} - -$rootScope = new Scope(); - -afterEach(() => { - jest.clearAllMocks(); -}); - -it('subscribes to the passed observable, returns subscription', () => { - const $scope = new Scope(); - - const unsubSpy = jest.fn(); - const subSpy = jest.fn(() => unsubSpy); - const observable = new Rx.Observable(subSpy); - - const subscription = subscribeWithScope($scope as any, observable); - expect(subSpy).toHaveBeenCalledTimes(1); - expect(unsubSpy).not.toHaveBeenCalled(); - - subscription.unsubscribe(); - - expect(subSpy).toHaveBeenCalledTimes(1); - expect(unsubSpy).toHaveBeenCalledTimes(1); -}); - -it('calls observer.next() if already in a digest cycle, wraps in $scope.$apply if not', () => { - const subject = new Rx.Subject(); - const nextSpy = jest.fn(); - const $scope = new Scope(); - - subscribeWithScope($scope as any, subject, { next: nextSpy }); - - subject.next(); - expect($scope.$apply).toHaveBeenCalledTimes(1); - expect(nextSpy).toHaveBeenCalledTimes(1); - - jest.clearAllMocks(); - - $rootScope.$$phase = '$digest'; - subject.next(); - expect($scope.$apply).not.toHaveBeenCalled(); - expect(nextSpy).toHaveBeenCalledTimes(1); -}); - -it('reports fatalError if observer.next() throws', () => { - const fatalError = jest.fn(); - const $scope = new Scope(); - subscribeWithScope( - $scope as any, - Rx.of(undefined), - { - next() { - throw new Error('foo bar'); - }, - }, - fatalError - ); - - expect(fatalError.mock.calls).toMatchInlineSnapshot(` -Array [ - Array [ - [Error: foo bar], - ], -] -`); -}); - -it('reports fatal error if observer.error is not defined and observable errors', () => { - const fatalError = jest.fn(); - const $scope = new Scope(); - const error = new Error('foo'); - error.stack = `${error.message}\n---stack trace ---`; - subscribeWithScope($scope as any, Rx.throwError(error), undefined, fatalError); - - expect(fatalError.mock.calls).toMatchInlineSnapshot(` -Array [ - Array [ - [Error: Uncaught error in subscribeWithScope(): foo ----stack trace ---], - ], -] -`); -}); - -it('reports fatal error if observer.error throws', () => { - const fatalError = jest.fn(); - const $scope = new Scope(); - subscribeWithScope( - $scope as any, - Rx.throwError(new Error('foo')), - { - error: () => { - throw new Error('foo'); - }, - }, - fatalError - ); - - expect(fatalError.mock.calls).toMatchInlineSnapshot(` -Array [ - Array [ - [Error: foo], - ], -] -`); -}); - -it('does not report fatal error if observer.error handles the error', () => { - const fatalError = jest.fn(); - const $scope = new Scope(); - subscribeWithScope( - $scope as any, - Rx.throwError(new Error('foo')), - { - error: () => { - // noop, swallow error - }, - }, - fatalError - ); - - expect(fatalError.mock.calls).toEqual([]); -}); - -it('reports fatal error if observer.complete throws', () => { - const fatalError = jest.fn(); - const $scope = new Scope(); - subscribeWithScope( - $scope as any, - Rx.EMPTY, - { - complete: () => { - throw new Error('foo'); - }, - }, - fatalError - ); - - expect(fatalError.mock.calls).toMatchInlineSnapshot(` -Array [ - Array [ - [Error: foo], - ], -] -`); -}); - -it('preserves the context of the observer functions', () => { - const $scope = new Scope(); - const observer = { - next() { - expect(this).toBe(observer); - }, - complete() { - expect(this).toBe(observer); - }, - }; - - subscribeWithScope($scope as any, Rx.of([1, 2, 3]), observer); - - const observer2 = { - error() { - expect(this).toBe(observer); - }, - }; - - subscribeWithScope($scope as any, Rx.throwError(new Error('foo')), observer2); -}); diff --git a/src/plugins/opensearch_dashboards_legacy/public/angular/subscribe_with_scope.ts b/src/plugins/opensearch_dashboards_legacy/public/angular/subscribe_with_scope.ts deleted file mode 100644 index f8cb102379b6..000000000000 --- a/src/plugins/opensearch_dashboards_legacy/public/angular/subscribe_with_scope.ts +++ /dev/null @@ -1,96 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { IScope } from 'angular'; -import * as Rx from 'rxjs'; -import { AngularHttpError } from '../notify/lib'; - -type FatalErrorFn = (error: AngularHttpError | Error | string, location?: string) => void; - -function callInDigest($scope: IScope, fn: () => void, fatalError?: FatalErrorFn) { - try { - // this is terrible, but necessary to synchronously deliver subscription values - // to angular scopes. This is required by some APIs, like the `config` service, - // and beneficial for root level directives where additional digest cycles make - // opensearch dashboards sluggish to load. - // - // If you copy this code elsewhere you better have a good reason :) - if ($scope.$root.$$phase) { - fn(); - } else { - $scope.$apply(() => fn()); - } - } catch (error) { - if (fatalError) { - fatalError(error); - } - } -} - -/** - * Subscribe to an observable at a $scope, ensuring that the digest cycle - * is run for subscriber hooks and routing errors to fatalError if not handled. - */ -export function subscribeWithScope( - $scope: IScope, - observable: Rx.Observable, - observer?: Rx.PartialObserver, - fatalError?: FatalErrorFn -) { - return observable.subscribe({ - next(value) { - if (observer && observer.next) { - callInDigest($scope, () => observer.next!(value), fatalError); - } - }, - error(error) { - callInDigest( - $scope, - () => { - if (observer && observer.error) { - observer.error(error); - } else { - throw new Error( - `Uncaught error in subscribeWithScope(): ${ - error ? error.stack || error.message : error - }` - ); - } - }, - fatalError - ); - }, - complete() { - if (observer && observer.complete) { - callInDigest($scope, () => observer.complete!(), fatalError); - } - }, - }); -} diff --git a/src/plugins/opensearch_dashboards_legacy/public/angular/watch_multi.js b/src/plugins/opensearch_dashboards_legacy/public/angular/watch_multi.js deleted file mode 100644 index 8dfcb0f59420..000000000000 --- a/src/plugins/opensearch_dashboards_legacy/public/angular/watch_multi.js +++ /dev/null @@ -1,159 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import _ from 'lodash'; - -export function watchMultiDecorator($provide) { - $provide.decorator('$rootScope', function ($delegate) { - /** - * Watch multiple expressions with a single callback. Along - * with making code simpler it also merges all of the watcher - * handlers within a single tick. - * - * # expression format - * expressions can be specified in one of the following ways: - * 1. string that evaluates to a value on scope. Creates a regular $watch - * expression. - * 'someScopeValue.prop' === $scope.$watch('someScopeValue.prop', fn); - * - * 2. #1 prefixed with '[]', which uses $watchCollection rather than $watch. - * '[]expr' === $scope.$watchCollection('expr', fn); - * - * 3. #1 prefixed with '=', which uses $watch with objectEquality turned on - * '=expr' === $scope.$watch('expr', fn, true); - * - * 4. a function that will be called, like a normal function water - * - * 5. an object with any of the properties: - * `get`: the getter called on each iteration - * `deep`: a flag to turn on objectEquality in $watch - * `fn`: the watch registration function ($scope.$watch or $scope.$watchCollection) - * - * @param {array[string|function|obj]} expressions - the list of expressions to $watch - * @param {Function} fn - the callback function - * @return {Function} - an unwatch function, just like the return value of $watch - */ - $delegate.constructor.prototype.$watchMulti = function (expressions, fn) { - if (!Array.isArray(expressions)) { - throw new TypeError('expected an array of expressions to watch'); - } - - if (!_.isFunction(fn)) { - throw new TypeError('expected a function that is triggered on each watch'); - } - const $scope = this; - const vals = new Array(expressions.length); - const prev = new Array(expressions.length); - let fire = false; - let init = 0; - const neededInits = expressions.length; - - // first, register all of the multi-watchers - const unwatchers = expressions.map(function (expr, i) { - expr = normalizeExpression($scope, expr); - if (!expr) return; - - return expr.fn.call( - $scope, - expr.get, - function (newVal, oldVal) { - if (newVal === oldVal) { - init += 1; - } - - vals[i] = newVal; - prev[i] = oldVal; - fire = true; - }, - expr.deep - ); - }); - - // then, the watcher that checks to see if any of - // the other watchers triggered this cycle - let flip = false; - unwatchers.push( - $scope.$watch( - function () { - if (init < neededInits) return init; - - if (fire) { - fire = false; - flip = !flip; - } - return flip; - }, - function () { - if (init < neededInits) return false; - - fn(vals.slice(0), prev.slice(0)); - vals.forEach(function (v, i) { - prev[i] = v; - }); - } - ) - ); - - return function () { - unwatchers.forEach((listener) => listener()); - }; - }; - - function normalizeExpression($scope, expr) { - if (!expr) return; - const norm = { - fn: $scope.$watch, - deep: false, - }; - - if (_.isFunction(expr)) return _.assign(norm, { get: expr }); - if (_.isObject(expr)) return _.assign(norm, expr); - if (!_.isString(expr)) return; - - if (expr.substr(0, 2) === '[]') { - return _.assign(norm, { - fn: $scope.$watchCollection, - get: expr.substr(2), - }); - } - - if (expr.charAt(0) === '=') { - return _.assign(norm, { - deep: true, - get: expr.substr(1), - }); - } - - return _.assign(norm, { get: expr }); - } - - return $delegate; - }); -} diff --git a/src/plugins/opensearch_dashboards_legacy/public/angular_bootstrap/bind_html/bind_html.js b/src/plugins/opensearch_dashboards_legacy/public/angular_bootstrap/bind_html/bind_html.js deleted file mode 100755 index 5e6f2edea608..000000000000 --- a/src/plugins/opensearch_dashboards_legacy/public/angular_bootstrap/bind_html/bind_html.js +++ /dev/null @@ -1,28 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* eslint-disable */ - -import angular from 'angular'; - -export function initBindHtml() { - angular - .module('ui.bootstrap.bindHtml', []) - - .directive('bindHtmlUnsafe', function() { - return function(scope, element, attr) { - element.addClass('ng-binding').data('$binding', attr.bindHtmlUnsafe); - scope.$watch(attr.bindHtmlUnsafe, function bindHtmlUnsafeWatchAction(value) { - element.html(value || ''); - }); - }; - }); -} diff --git a/src/plugins/opensearch_dashboards_legacy/public/angular_bootstrap/index.ts b/src/plugins/opensearch_dashboards_legacy/public/angular_bootstrap/index.ts deleted file mode 100644 index 63b0431ebb29..000000000000 --- a/src/plugins/opensearch_dashboards_legacy/public/angular_bootstrap/index.ts +++ /dev/null @@ -1,61 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* eslint-disable */ - -import { once } from 'lodash'; -import angular from 'angular'; - -// @ts-ignore -import { initBindHtml } from './bind_html/bind_html'; -// @ts-ignore -import { initBootstrapTooltip } from './tooltip/tooltip'; - -import tooltipPopup from './tooltip/tooltip_popup.html'; - -import tooltipUnsafePopup from './tooltip/tooltip_html_unsafe_popup.html'; - -export const initAngularBootstrap = once(() => { - /* - * angular-ui-bootstrap - * http://angular-ui.github.io/bootstrap/ - - * Version: 0.12.1 - 2015-02-20 - * License: MIT - */ - angular.module('ui.bootstrap', [ - 'ui.bootstrap.tpls', - 'ui.bootstrap.bindHtml', - 'ui.bootstrap.tooltip', - ]); - - angular.module('ui.bootstrap.tpls', [ - 'template/tooltip/tooltip-html-unsafe-popup.html', - 'template/tooltip/tooltip-popup.html', - ]); - - initBindHtml(); - initBootstrapTooltip(); - - angular.module('template/tooltip/tooltip-html-unsafe-popup.html', []).run([ - '$templateCache', - function($templateCache: any) { - $templateCache.put('template/tooltip/tooltip-html-unsafe-popup.html', tooltipUnsafePopup); - }, - ]); - - angular.module('template/tooltip/tooltip-popup.html', []).run([ - '$templateCache', - function($templateCache: any) { - $templateCache.put('template/tooltip/tooltip-popup.html', tooltipPopup); - }, - ]); -}); diff --git a/src/plugins/opensearch_dashboards_legacy/public/angular_bootstrap/tooltip/position.js b/src/plugins/opensearch_dashboards_legacy/public/angular_bootstrap/tooltip/position.js deleted file mode 100755 index 2f322e2b42e2..000000000000 --- a/src/plugins/opensearch_dashboards_legacy/public/angular_bootstrap/tooltip/position.js +++ /dev/null @@ -1,178 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* eslint-disable */ - -import angular from 'angular'; - -export function initBootstrapPosition() { - angular - .module('ui.bootstrap.position', []) - - /** - * A set of utility methods that can be use to retrieve position of DOM elements. - * It is meant to be used where we need to absolute-position DOM elements in - * relation to other, existing elements (this is the case for tooltips, popovers, - * typeahead suggestions etc.). - */ - .factory('$position', [ - '$document', - '$window', - function($document, $window) { - function getStyle(el, cssprop) { - if (el.currentStyle) { - //IE - return el.currentStyle[cssprop]; - } else if ($window.getComputedStyle) { - return $window.getComputedStyle(el)[cssprop]; - } - // finally try and get inline style - return el.style[cssprop]; - } - - /** - * Checks if a given element is statically positioned - * @param element - raw DOM element - */ - function isStaticPositioned(element) { - return (getStyle(element, 'position') || 'static') === 'static'; - } - - /** - * returns the closest, non-statically positioned parentOffset of a given element - * @param element - */ - const parentOffsetEl = function(element) { - const docDomEl = $document[0]; - let offsetParent = element.offsetParent || docDomEl; - while (offsetParent && offsetParent !== docDomEl && isStaticPositioned(offsetParent)) { - offsetParent = offsetParent.offsetParent; - } - return offsetParent || docDomEl; - }; - - return { - /** - * Provides read-only equivalent of jQuery's position function: - * http://api.jquery.com/position/ - */ - position: function(element) { - const elBCR = this.offset(element); - let offsetParentBCR = { top: 0, left: 0 }; - const offsetParentEl = parentOffsetEl(element[0]); - if (offsetParentEl != $document[0]) { - offsetParentBCR = this.offset(angular.element(offsetParentEl)); - offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop; - offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft; - } - - const boundingClientRect = element[0].getBoundingClientRect(); - return { - width: boundingClientRect.width || element.prop('offsetWidth'), - height: boundingClientRect.height || element.prop('offsetHeight'), - top: elBCR.top - offsetParentBCR.top, - left: elBCR.left - offsetParentBCR.left, - }; - }, - - /** - * Provides read-only equivalent of jQuery's offset function: - * http://api.jquery.com/offset/ - */ - offset: function(element) { - const boundingClientRect = element[0].getBoundingClientRect(); - return { - width: boundingClientRect.width || element.prop('offsetWidth'), - height: boundingClientRect.height || element.prop('offsetHeight'), - top: - boundingClientRect.top + - ($window.pageYOffset || $document[0].documentElement.scrollTop), - left: - boundingClientRect.left + - ($window.pageXOffset || $document[0].documentElement.scrollLeft), - }; - }, - - /** - * Provides coordinates for the targetEl in relation to hostEl - */ - positionElements: function(hostEl, targetEl, positionStr, appendToBody) { - const positionStrParts = positionStr.split('-'); - const pos0 = positionStrParts[0]; - const pos1 = positionStrParts[1] || 'center'; - - let hostElPos; - let targetElWidth; - let targetElHeight; - let targetElPos; - - hostElPos = appendToBody ? this.offset(hostEl) : this.position(hostEl); - - targetElWidth = targetEl.prop('offsetWidth'); - targetElHeight = targetEl.prop('offsetHeight'); - - const shiftWidth = { - center: function() { - return hostElPos.left + hostElPos.width / 2 - targetElWidth / 2; - }, - left: function() { - return hostElPos.left; - }, - right: function() { - return hostElPos.left + hostElPos.width; - }, - }; - - const shiftHeight = { - center: function() { - return hostElPos.top + hostElPos.height / 2 - targetElHeight / 2; - }, - top: function() { - return hostElPos.top; - }, - bottom: function() { - return hostElPos.top + hostElPos.height; - }, - }; - - switch (pos0) { - case 'right': - targetElPos = { - top: shiftHeight[pos1](), - left: shiftWidth[pos0](), - }; - break; - case 'left': - targetElPos = { - top: shiftHeight[pos1](), - left: hostElPos.left - targetElWidth, - }; - break; - case 'bottom': - targetElPos = { - top: shiftHeight[pos0](), - left: shiftWidth[pos1](), - }; - break; - default: - targetElPos = { - top: hostElPos.top - targetElHeight, - left: shiftWidth[pos1](), - }; - break; - } - - return targetElPos; - }, - }; - }, - ]); -} diff --git a/src/plugins/opensearch_dashboards_legacy/public/angular_bootstrap/tooltip/tooltip.js b/src/plugins/opensearch_dashboards_legacy/public/angular_bootstrap/tooltip/tooltip.js deleted file mode 100755 index 086fa6a7d6df..000000000000 --- a/src/plugins/opensearch_dashboards_legacy/public/angular_bootstrap/tooltip/tooltip.js +++ /dev/null @@ -1,434 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* eslint-disable */ - -import angular from 'angular'; - -import { initBootstrapPosition } from './position'; - -export function initBootstrapTooltip() { - initBootstrapPosition(); - /** - * The following features are still outstanding: animation as a - * function, placement as a function, inside, support for more triggers than - * just mouse enter/leave, html tooltips, and selector delegation. - */ - angular - .module('ui.bootstrap.tooltip', ['ui.bootstrap.position']) - - /** - * The $tooltip service creates tooltip- and popover-like directives as well as - * houses global options for them. - */ - .provider('$tooltip', function() { - // The default options tooltip and popover. - const defaultOptions = { - placement: 'top', - animation: true, - popupDelay: 0, - }; - - // Default hide triggers for each show trigger - const triggerMap = { - mouseenter: 'mouseleave', - click: 'click', - focus: 'blur', - }; - - // The options specified to the provider globally. - const globalOptions = {}; - - /** - * `options({})` allows global configuration of all tooltips in the - * application. - * - * var app = angular.module( 'App', ['ui.bootstrap.tooltip'], function( $tooltipProvider ) { - * // place tooltips left instead of top by default - * $tooltipProvider.options( { placement: 'left' } ); - * }); - */ - this.options = function(value) { - angular.extend(globalOptions, value); - }; - - /** - * This allows you to extend the set of trigger mappings available. E.g.: - * - * $tooltipProvider.setTriggers( 'openTrigger': 'closeTrigger' ); - */ - this.setTriggers = function setTriggers(triggers) { - angular.extend(triggerMap, triggers); - }; - - /** - * This is a helper function for translating camel-case to snake-case. - */ - function snake_case(name) { - const regexp = /[A-Z]/g; - const separator = '-'; - return name.replace(regexp, function(letter, pos) { - return (pos ? separator : '') + letter.toLowerCase(); - }); - } - - /** - * Returns the actual instance of the $tooltip service. - * TODO support multiple triggers - */ - this.$get = [ - '$window', - '$compile', - '$timeout', - '$document', - '$position', - '$interpolate', - function($window, $compile, $timeout, $document, $position, $interpolate) { - return function $tooltip(type, prefix, defaultTriggerShow) { - const options = angular.extend({}, defaultOptions, globalOptions); - - /** - * Returns an object of show and hide triggers. - * - * If a trigger is supplied, - * it is used to show the tooltip; otherwise, it will use the `trigger` - * option passed to the `$tooltipProvider.options` method; else it will - * default to the trigger supplied to this directive factory. - * - * The hide trigger is based on the show trigger. If the `trigger` option - * was passed to the `$tooltipProvider.options` method, it will use the - * mapped trigger from `triggerMap` or the passed trigger if the map is - * undefined; otherwise, it uses the `triggerMap` value of the show - * trigger; else it will just use the show trigger. - */ - function getTriggers(trigger) { - const show = trigger || options.trigger || defaultTriggerShow; - const hide = triggerMap[show] || show; - return { - show: show, - hide: hide, - }; - } - - const directiveName = snake_case(type); - - const startSym = $interpolate.startSymbol(); - const endSym = $interpolate.endSymbol(); - const template = - '
' + - '
'; - - return { - restrict: 'EA', - compile: function(tElem, tAttrs) { - const tooltipLinker = $compile(template); - - return function link(scope, element, attrs) { - let tooltip; - let tooltipLinkedScope; - let transitionTimeout; - let popupTimeout; - let appendToBody = angular.isDefined(options.appendToBody) - ? options.appendToBody - : false; - let triggers = getTriggers(undefined); - const hasEnableExp = angular.isDefined(attrs[prefix + 'Enable']); - let ttScope = scope.$new(true); - - const positionTooltip = function() { - const ttPosition = $position.positionElements( - element, - tooltip, - ttScope.placement, - appendToBody - ); - ttPosition.top += 'px'; - ttPosition.left += 'px'; - - // Now set the calculated positioning. - tooltip.css(ttPosition); - }; - - // By default, the tooltip is not open. - // TODO add ability to start tooltip opened - ttScope.isOpen = false; - - function toggleTooltipBind() { - if (!ttScope.isOpen) { - showTooltipBind(); - } else { - hideTooltipBind(); - } - } - - // Show the tooltip with delay if specified, otherwise show it immediately - function showTooltipBind() { - if (hasEnableExp && !scope.$eval(attrs[prefix + 'Enable'])) { - return; - } - - prepareTooltip(); - - if (ttScope.popupDelay) { - // Do nothing if the tooltip was already scheduled to pop-up. - // This happens if show is triggered multiple times before any hide is triggered. - if (!popupTimeout) { - popupTimeout = $timeout(show, ttScope.popupDelay, false); - popupTimeout - .then(reposition => reposition()) - .catch(error => { - // if the timeout is canceled then the string `canceled` is thrown. To prevent - // this from triggering an 'unhandled promise rejection' in angular 1.5+ the - // $timeout service explicitly tells $q that the promise it generated is "handled" - // but that does not include down chain promises like the one created by calling - // `popupTimeout.then()`. Because of this we need to ignore the "canceled" string - // and only propagate real errors - if (error !== 'canceled') { - throw error; - } - }); - } - } else { - show()(); - } - } - - function hideTooltipBind() { - scope.$evalAsync(function() { - hide(); - }); - } - - // Show the tooltip popup element. - function show() { - popupTimeout = null; - - // If there is a pending remove transition, we must cancel it, lest the - // tooltip be mysteriously removed. - if (transitionTimeout) { - $timeout.cancel(transitionTimeout); - transitionTimeout = null; - } - - // Don't show empty tooltips. - if (!ttScope.content) { - return angular.noop; - } - - createTooltip(); - - // Set the initial positioning. - tooltip.css({ top: 0, left: 0, display: 'block' }); - ttScope.$digest(); - - positionTooltip(); - - // And show the tooltip. - ttScope.isOpen = true; - ttScope.$digest(); // digest required as $apply is not called - - // Return positioning function as promise callback for correct - // positioning after draw. - return positionTooltip; - } - - // Hide the tooltip popup element. - function hide() { - // First things first: we don't show it anymore. - ttScope.isOpen = false; - - //if tooltip is going to be shown after delay, we must cancel this - $timeout.cancel(popupTimeout); - popupTimeout = null; - - // And now we remove it from the DOM. However, if we have animation, we - // need to wait for it to expire beforehand. - // FIXME: this is a placeholder for a port of the transitions library. - if (ttScope.animation) { - if (!transitionTimeout) { - transitionTimeout = $timeout(removeTooltip, 500); - } - } else { - removeTooltip(); - } - } - - function createTooltip() { - // There can only be one tooltip element per directive shown at once. - if (tooltip) { - removeTooltip(); - } - tooltipLinkedScope = ttScope.$new(); - tooltip = tooltipLinker(tooltipLinkedScope, function(tooltip) { - if (appendToBody) { - $document.find('body').append(tooltip); - } else { - element.after(tooltip); - } - }); - } - - function removeTooltip() { - transitionTimeout = null; - if (tooltip) { - tooltip.remove(); - tooltip = null; - } - if (tooltipLinkedScope) { - tooltipLinkedScope.$destroy(); - tooltipLinkedScope = null; - } - } - - function prepareTooltip() { - prepPlacement(); - prepPopupDelay(); - } - - /** - * Observe the relevant attributes. - */ - attrs.$observe(type, function(val) { - ttScope.content = val; - - if (!val && ttScope.isOpen) { - hide(); - } - }); - - attrs.$observe(prefix + 'Title', function(val) { - ttScope.title = val; - }); - - function prepPlacement() { - const val = attrs[prefix + 'Placement']; - ttScope.placement = angular.isDefined(val) ? val : options.placement; - } - - function prepPopupDelay() { - const val = attrs[prefix + 'PopupDelay']; - const delay = parseInt(val, 10); - ttScope.popupDelay = !isNaN(delay) ? delay : options.popupDelay; - } - - const unregisterTriggers = function() { - element.unbind(triggers.show, showTooltipBind); - element.unbind(triggers.hide, hideTooltipBind); - }; - - function prepTriggers() { - const val = attrs[prefix + 'Trigger']; - unregisterTriggers(); - - triggers = getTriggers(val); - - if (triggers.show === triggers.hide) { - element.bind(triggers.show, toggleTooltipBind); - } else { - element.bind(triggers.show, showTooltipBind); - element.bind(triggers.hide, hideTooltipBind); - } - } - - prepTriggers(); - - const animation = scope.$eval(attrs[prefix + 'Animation']); - ttScope.animation = angular.isDefined(animation) - ? !!animation - : options.animation; - - const appendToBodyVal = scope.$eval(attrs[prefix + 'AppendToBody']); - appendToBody = angular.isDefined(appendToBodyVal) - ? appendToBodyVal - : appendToBody; - - // if a tooltip is attached to we need to remove it on - // location change as its parent scope will probably not be destroyed - // by the change. - if (appendToBody) { - scope.$on( - '$locationChangeSuccess', - function closeTooltipOnLocationChangeSuccess() { - if (ttScope.isOpen) { - hide(); - } - } - ); - } - - // Make sure tooltip is destroyed and removed. - scope.$on('$destroy', function onDestroyTooltip() { - $timeout.cancel(transitionTimeout); - $timeout.cancel(popupTimeout); - unregisterTriggers(); - removeTooltip(); - ttScope = null; - }); - }; - }, - }; - }; - }, - ]; - }) - - .directive('tooltip', [ - '$tooltip', - function($tooltip) { - return $tooltip('tooltip', 'tooltip', 'mouseenter'); - }, - ]) - - .directive('tooltipPopup', function() { - return { - restrict: 'EA', - replace: true, - scope: { content: '@', placement: '@', animation: '&', isOpen: '&' }, - templateUrl: 'template/tooltip/tooltip-popup.html', - }; - }) - - .directive('tooltipHtmlUnsafe', [ - '$tooltip', - function($tooltip) { - return $tooltip('tooltipHtmlUnsafe', 'tooltip', 'mouseenter'); - }, - ]) - - .directive('tooltipHtmlUnsafePopup', function() { - return { - restrict: 'EA', - replace: true, - scope: { content: '@', placement: '@', animation: '&', isOpen: '&' }, - templateUrl: 'template/tooltip/tooltip-html-unsafe-popup.html', - }; - }); -} diff --git a/src/plugins/opensearch_dashboards_legacy/public/angular_bootstrap/tooltip/tooltip_html_unsafe_popup.html b/src/plugins/opensearch_dashboards_legacy/public/angular_bootstrap/tooltip/tooltip_html_unsafe_popup.html deleted file mode 100644 index b48bf7049890..000000000000 --- a/src/plugins/opensearch_dashboards_legacy/public/angular_bootstrap/tooltip/tooltip_html_unsafe_popup.html +++ /dev/null @@ -1,4 +0,0 @@ -
-
-
-
\ No newline at end of file diff --git a/src/plugins/opensearch_dashboards_legacy/public/angular_bootstrap/tooltip/tooltip_popup.html b/src/plugins/opensearch_dashboards_legacy/public/angular_bootstrap/tooltip/tooltip_popup.html deleted file mode 100644 index eed4ca7d9301..000000000000 --- a/src/plugins/opensearch_dashboards_legacy/public/angular_bootstrap/tooltip/tooltip_popup.html +++ /dev/null @@ -1,4 +0,0 @@ -
-
-
-
\ No newline at end of file diff --git a/src/plugins/opensearch_dashboards_legacy/public/index.ts b/src/plugins/opensearch_dashboards_legacy/public/index.ts index 67cd6def9f0f..cdbdb7eee6a9 100644 --- a/src/plugins/opensearch_dashboards_legacy/public/index.ts +++ b/src/plugins/opensearch_dashboards_legacy/public/index.ts @@ -36,8 +36,4 @@ export const plugin = (initializerContext: PluginInitializerContext) => export * from './plugin'; -export { initAngularBootstrap } from './angular_bootstrap'; -export { PaginateDirectiveProvider, PaginateControlsDirectiveProvider } from './paginate/paginate'; -export * from './angular'; export * from './notify'; -export * from './utils'; diff --git a/src/plugins/opensearch_dashboards_legacy/public/notify/lib/add_fatal_error.ts b/src/plugins/opensearch_dashboards_legacy/public/notify/lib/add_fatal_error.ts index beb6f81e3ec7..e04ce9928ed3 100644 --- a/src/plugins/opensearch_dashboards_legacy/public/notify/lib/add_fatal_error.ts +++ b/src/plugins/opensearch_dashboards_legacy/public/notify/lib/add_fatal_error.ts @@ -29,21 +29,15 @@ */ import { FatalErrorsSetup } from '../../../../../core/public'; -import { - AngularHttpError, - formatAngularHttpError, - isAngularHttpError, -} from './format_angular_http_error'; +/** + * Adds an error to the list of fatal errors. + * @deprecated Use `core.fatalErrors.add` instead + */ export function addFatalError( fatalErrors: FatalErrorsSetup, - error: AngularHttpError | Error | string, + error: Error | string, location?: string ) { - // add support for angular http errors to newPlatformFatalErrors - if (isAngularHttpError(error)) { - error = formatAngularHttpError(error); - } - fatalErrors.add(error, location); } diff --git a/src/plugins/opensearch_dashboards_legacy/public/notify/lib/format_angular_http_error.ts b/src/plugins/opensearch_dashboards_legacy/public/notify/lib/format_angular_http_error.ts deleted file mode 100644 index 68b3701814b1..000000000000 --- a/src/plugins/opensearch_dashboards_legacy/public/notify/lib/format_angular_http_error.ts +++ /dev/null @@ -1,69 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { i18n } from '@osd/i18n'; -import { IHttpResponse } from 'angular'; - -export type AngularHttpError = IHttpResponse<{ message: string }>; - -export function isAngularHttpError(error: any): error is AngularHttpError { - return ( - error && - typeof error.status === 'number' && - typeof error.statusText === 'string' && - error.data && - typeof error.data.message === 'string' - ); -} - -export function formatAngularHttpError(error: AngularHttpError) { - // is an Angular $http "error object" - if (error.status === -1) { - // status = -1 indicates that the request was failed to reach the server - return i18n.translate( - 'opensearch_dashboards_legacy.notify.fatalError.unavailableServerErrorMessage', - { - defaultMessage: - 'An HTTP request has failed to connect. ' + - 'Please check if the OpenSearch Dashboards server is running and that your browser has a working connection, ' + - 'or contact your system administrator.', - } - ); - } - - return i18n.translate('opensearch_dashboards_legacy.notify.fatalError.errorStatusMessage', { - defaultMessage: 'Error {errStatus} {errStatusText}: {errMessage}', - values: { - errStatus: error.status, - errStatusText: error.statusText, - errMessage: error.data.message, - }, - }); -} diff --git a/src/plugins/opensearch_dashboards_legacy/public/notify/lib/index.ts b/src/plugins/opensearch_dashboards_legacy/public/notify/lib/index.ts index 22a8631dfee3..28bdec0d1884 100644 --- a/src/plugins/opensearch_dashboards_legacy/public/notify/lib/index.ts +++ b/src/plugins/opensearch_dashboards_legacy/public/notify/lib/index.ts @@ -31,9 +31,4 @@ export { formatOpenSearchMsg } from './format_opensearch_msg'; export { formatMsg } from './format_msg'; export { formatStack } from './format_stack'; -export { - isAngularHttpError, - formatAngularHttpError, - AngularHttpError, -} from './format_angular_http_error'; export { addFatalError } from './add_fatal_error'; diff --git a/src/plugins/opensearch_dashboards_legacy/public/notify/toasts/TOAST_NOTIFICATIONS.md b/src/plugins/opensearch_dashboards_legacy/public/notify/toasts/TOAST_NOTIFICATIONS.md index de6a51f3927d..4e4d55586825 100644 --- a/src/plugins/opensearch_dashboards_legacy/public/notify/toasts/TOAST_NOTIFICATIONS.md +++ b/src/plugins/opensearch_dashboards_legacy/public/notify/toasts/TOAST_NOTIFICATIONS.md @@ -1,6 +1,6 @@ # Toast notifications -Use this service to surface toasts in the bottom-right corner of the screen. After a brief delay, they'll disappear. They're useful for notifying the user of state changes. See [the EUI docs](https://elastic.github.io/eui/) for more information on toasts and their role within the UI. +Use this service to surface toasts in the bottom-right corner of the screen. After a brief delay, they'll disappear. They're useful for notifying the user of state changes. See [the OUI docs](https://oui.opensearch.org/) for more information on toasts and their role within the UI. ## Importing the module @@ -57,7 +57,7 @@ toastNotifications.remove(toast); ### Configuration options -If you want to configure the toast further you can provide an object instead of a string. The properties of this object correspond to the `propTypes` accepted by the `EuiToast` component. Refer to [the EUI docs](https://elastic.github.io/eui/) for info on these `propTypes`. +If you want to configure the toast further you can provide an object instead of a string. The properties of this object correspond to the `propTypes` accepted by the `OuiToast` component. Refer to [the OUI docs](https://oui.opensearch.org/) for info on these `propTypes`. ```js toastNotifications.add({ diff --git a/src/plugins/opensearch_dashboards_legacy/public/paginate/_paginate.scss b/src/plugins/opensearch_dashboards_legacy/public/paginate/_paginate.scss deleted file mode 100644 index ec346aa843c1..000000000000 --- a/src/plugins/opensearch_dashboards_legacy/public/paginate/_paginate.scss +++ /dev/null @@ -1,58 +0,0 @@ -/* stylelint-disable-next-line selector-type-no-unknown */ -paginate { - display: block; - - /* stylelint-disable-next-line selector-type-no-unknown */ - paginate-controls { - display: flex; - align-items: center; - padding: $euiSizeXS $euiSizeXS $euiSizeS; - text-align: center; - - .pagination-other-pages { - flex: 1 0 auto; - display: flex; - justify-content: center; - } - - .pagination-other-pages-list { - flex: 0 0 auto; - display: flex; - justify-content: center; - padding: 0; - margin: 0; - list-style: none; - - > li { - flex: 0 0 auto; - user-select: none; - - a { - text-decoration: none; - background-color: $euiColorLightestShade; - margin-left: $euiSizeXS / 2; - padding: $euiSizeS $euiSizeM; - } - - a:hover { - text-decoration: underline; - } - - &.active a { - text-decoration: none !important; - font-weight: $euiFontWeightBold; - color: $euiColorDarkShade; - cursor: default; - } - } - } - - .pagination-size { - flex: 0 0 auto; - - input[type="number"] { - width: 3em; - } - } - } -} diff --git a/src/plugins/opensearch_dashboards_legacy/public/paginate/paginate.d.ts b/src/plugins/opensearch_dashboards_legacy/public/paginate/paginate.d.ts deleted file mode 100644 index 60f462aa5ffe..000000000000 --- a/src/plugins/opensearch_dashboards_legacy/public/paginate/paginate.d.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export function PaginateDirectiveProvider($parse: any, $compile: any): any; -export function PaginateControlsDirectiveProvider(): any; diff --git a/src/plugins/opensearch_dashboards_legacy/public/paginate/paginate.js b/src/plugins/opensearch_dashboards_legacy/public/paginate/paginate.js deleted file mode 100644 index 1f78a7f715d9..000000000000 --- a/src/plugins/opensearch_dashboards_legacy/public/paginate/paginate.js +++ /dev/null @@ -1,242 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import _ from 'lodash'; -import { i18n } from '@osd/i18n'; -import './_paginate.scss'; -import paginateControlsTemplate from './paginate_controls.html'; - -export function PaginateDirectiveProvider($parse, $compile) { - return { - restrict: 'E', - scope: true, - link: { - pre: function ($scope, $el, attrs) { - if (_.isUndefined(attrs.bottomControls)) attrs.bottomControls = true; - if ($el.find('paginate-controls.paginate-bottom').length === 0 && attrs.bottomControls) { - $el.append($compile('')($scope)); - } - }, - post: function ($scope, $el, attrs) { - if (_.isUndefined(attrs.topControls)) attrs.topControls = false; - if ($el.find('paginate-controls.paginate-top').length === 0 && attrs.topControls) { - $el.prepend($compile('')($scope)); - } - - const paginate = $scope.paginate; - - // add some getters to the controller powered by attributes - paginate.getList = $parse(attrs.list); - paginate.perPageProp = attrs.perPageProp; - - if (attrs.perPage) { - paginate.perPage = attrs.perPage; - $scope.showSelector = false; - } else { - $scope.showSelector = true; - } - - paginate.otherWidthGetter = $parse(attrs.otherWidth); - - paginate.init(); - }, - }, - controllerAs: 'paginate', - controller: function ($scope, $document) { - const self = this; - const ALL = 0; - const allSizeTitle = i18n.translate( - 'opensearch_dashboards_legacy.paginate.size.allDropDownOptionLabel', - { - defaultMessage: 'All', - } - ); - - self.sizeOptions = [ - { title: '10', value: 10 }, - { title: '25', value: 25 }, - { title: '100', value: 100 }, - { title: allSizeTitle, value: ALL }, - ]; - - // setup the watchers, called in the post-link function - self.init = function () { - self.perPage = _.parseInt(self.perPage) || $scope[self.perPageProp]; - - $scope.$watchMulti(['paginate.perPage', self.perPageProp, self.otherWidthGetter], function ( - vals, - oldVals - ) { - const intChanges = vals[0] !== oldVals[0]; - - if (intChanges) { - if (!setPerPage(self.perPage)) { - // if we are not able to set the external value, - // render now, otherwise wait for the external value - // to trigger the watcher again - self.renderList(); - } - return; - } - - self.perPage = _.parseInt(self.perPage) || $scope[self.perPageProp]; - if (self.perPage == null) { - self.perPage = ALL; - return; - } - - self.renderList(); - }); - - $scope.$watch('page', self.changePage); - $scope.$watchCollection(self.getList, function (list) { - $scope.list = list; - self.renderList(); - }); - }; - - self.goToPage = function (number) { - if (number) { - if (number.hasOwnProperty('number')) number = number.number; - $scope.page = $scope.pages[number - 1] || $scope.pages[0]; - } - }; - - self.goToTop = function goToTop() { - $document.scrollTop(0); - }; - - self.renderList = function () { - $scope.pages = []; - if (!$scope.list) return; - - const perPage = _.parseInt(self.perPage); - const count = perPage ? Math.ceil($scope.list.length / perPage) : 1; - - _.times(count, function (i) { - let page; - - if (perPage) { - const start = perPage * i; - page = $scope.list.slice(start, start + perPage); - } else { - page = $scope.list.slice(0); - } - - page.number = i + 1; - page.i = i; - - page.count = count; - page.first = page.number === 1; - page.last = page.number === count; - page.firstItem = (page.number - 1) * perPage + 1; - page.lastItem = Math.min(page.number * perPage, $scope.list.length); - - page.prev = $scope.pages[i - 1]; - if (page.prev) page.prev.next = page; - - $scope.pages.push(page); - }); - - // set the new page, or restore the previous page number - if ($scope.page && $scope.page.i < $scope.pages.length) { - $scope.page = $scope.pages[$scope.page.i]; - } else { - $scope.page = $scope.pages[0]; - } - - if ($scope.page && $scope.onPageChanged) { - $scope.onPageChanged($scope.page); - } - }; - - self.changePage = function (page) { - if (!page) { - $scope.otherPages = null; - return; - } - - // setup the list of the other pages to link to - $scope.otherPages = []; - const width = +self.otherWidthGetter($scope) || 5; - let left = page.i - Math.round((width - 1) / 2); - let right = left + width - 1; - - // shift neg count from left to right - if (left < 0) { - right += 0 - left; - left = 0; - } - - // shift extra right nums to left - const lastI = page.count - 1; - if (right > lastI) { - right = lastI; - left = right - width + 1; - } - - for (let i = left; i <= right; i++) { - const other = $scope.pages[i]; - - if (!other) continue; - - $scope.otherPages.push(other); - if (other.last) $scope.otherPages.containsLast = true; - if (other.first) $scope.otherPages.containsFirst = true; - } - - if ($scope.onPageChanged) { - $scope.onPageChanged($scope.page); - } - }; - - function setPerPage(val) { - let $ppParent = $scope; - - while ($ppParent && !_.has($ppParent, self.perPageProp)) { - $ppParent = $ppParent.$parent; - } - - if ($ppParent) { - $ppParent[self.perPageProp] = val; - return true; - } - } - }, - }; -} - -export function PaginateControlsDirectiveProvider() { - // this directive is automatically added by paginate if not found within it's $el - return { - restrict: 'E', - template: paginateControlsTemplate, - }; -} diff --git a/src/plugins/opensearch_dashboards_legacy/public/paginate/paginate_controls.html b/src/plugins/opensearch_dashboards_legacy/public/paginate/paginate_controls.html deleted file mode 100644 index 3cabb2c61ede..000000000000 --- a/src/plugins/opensearch_dashboards_legacy/public/paginate/paginate_controls.html +++ /dev/null @@ -1,98 +0,0 @@ - - -
-
    -
  • - -
  • -
  • - -
  • - -
  • - - ... -
  • - -
  • - -
  • - -
  • - ... - -
  • - -
  • - -
  • -
  • - -
  • -
-
- -
-
- - -
-
diff --git a/src/plugins/opensearch_dashboards_legacy/public/plugin.ts b/src/plugins/opensearch_dashboards_legacy/public/plugin.ts index 473c10b47079..5f6a3e2a4961 100644 --- a/src/plugins/opensearch_dashboards_legacy/public/plugin.ts +++ b/src/plugins/opensearch_dashboards_legacy/public/plugin.ts @@ -47,7 +47,7 @@ export class OpenSearchDashboardsLegacyPlugin { * Used to power dashboard mode. Should be removed when dashboard mode is removed eventually. * @deprecated */ - dashboardConfig: getDashboardConfig(!application.capabilities.dashboard.showWriteControls), + dashboardConfig: getDashboardConfig(!application.capabilities?.dashboard?.showWriteControls), /** * Loads the font-awesome icon font. Should be removed once the last consumer has migrated to EUI * @deprecated diff --git a/src/plugins/opensearch_dashboards_legacy/public/utils/index.ts b/src/plugins/opensearch_dashboards_legacy/public/utils/index.ts deleted file mode 100644 index 6313548a1be1..000000000000 --- a/src/plugins/opensearch_dashboards_legacy/public/utils/index.ts +++ /dev/null @@ -1,37 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export * from './system_api'; -// @ts-ignore -export { OsdAccessibleClickProvider } from './osd_accessible_click'; -// @ts-ignore -export { PrivateProvider, IPrivate } from './private'; -// @ts-ignore -export { registerListenEventListener } from './register_listen_event_listener'; diff --git a/src/plugins/opensearch_dashboards_legacy/public/utils/osd_accessible_click.js b/src/plugins/opensearch_dashboards_legacy/public/utils/osd_accessible_click.js deleted file mode 100644 index 6c49ff8de4bb..000000000000 --- a/src/plugins/opensearch_dashboards_legacy/public/utils/osd_accessible_click.js +++ /dev/null @@ -1,82 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { accessibleClickKeys, keys } from '@elastic/eui'; - -export function OsdAccessibleClickProvider() { - return { - restrict: 'A', - controller: ($element) => { - $element.on('keydown', (e) => { - // Prevent a scroll from occurring if the user has hit space. - if (e.key === keys.SPACE) { - e.preventDefault(); - } - }); - }, - link: (scope, element, attrs) => { - // The whole point of this directive is to hack in functionality that native buttons provide - // by default. - const elementType = element.prop('tagName'); - - if (elementType === 'BUTTON') { - throw new Error(`osdAccessibleClick doesn't need to be used on a button.`); - } - - if (elementType === 'A' && attrs.href !== undefined) { - throw new Error( - `osdAccessibleClick doesn't need to be used on a link if it has a href attribute.` - ); - } - - // We're emulating a click action, so we should already have a regular click handler defined. - if (!attrs.ngClick) { - throw new Error('osdAccessibleClick requires ng-click to be defined on its element.'); - } - - // If the developer hasn't already specified attributes required for accessibility, add them. - if (attrs.tabindex === undefined) { - element.attr('tabindex', '0'); - } - - if (attrs.role === undefined) { - element.attr('role', 'button'); - } - - element.on('keyup', (e) => { - // Support keyboard accessibility by emulating mouse click on ENTER or SPACE keypress. - if (accessibleClickKeys[e.key]) { - // Delegate to the click handler on the element (assumed to be ng-click). - element.click(); - } - }); - }, - }; -} diff --git a/src/plugins/opensearch_dashboards_legacy/public/utils/private.d.ts b/src/plugins/opensearch_dashboards_legacy/public/utils/private.d.ts deleted file mode 100644 index fe264fc193fc..000000000000 --- a/src/plugins/opensearch_dashboards_legacy/public/utils/private.d.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export type IPrivate = (provider: (...injectable: any[]) => T) => T; diff --git a/src/plugins/opensearch_dashboards_legacy/public/utils/private.js b/src/plugins/opensearch_dashboards_legacy/public/utils/private.js deleted file mode 100644 index 1a3a0a596559..000000000000 --- a/src/plugins/opensearch_dashboards_legacy/public/utils/private.js +++ /dev/null @@ -1,214 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/** - * # `Private()` - * Private module loader, used to merge angular and require js dependency styles - * by allowing a require.js module to export a single provider function that will - * create a value used within an angular application. This provider can declare - * angular dependencies by listing them as arguments, and can be require additional - * Private modules. - * - * ## Define a private module provider: - * ```js - * export default function PingProvider($http) { - * this.ping = function () { - * return $http.head('/health-check'); - * }; - * }; - * ``` - * - * ## Require a private module: - * ```js - * export default function ServerHealthProvider(Private, Promise) { - * let ping = Private(require('ui/ping')); - * return { - * check: Promise.method(function () { - * let attempts = 0; - * return (function attempt() { - * attempts += 1; - * return ping.ping() - * .catch(function (err) { - * if (attempts < 3) return attempt(); - * }) - * }()) - * .then(function () { - * return true; - * }) - * .catch(function () { - * return false; - * }); - * }) - * } - * }; - * ``` - * - * # `Private.stub(provider, newInstance)` - * `Private.stub()` replaces the instance of a module with another value. This is all we have needed until now. - * - * ```js - * beforeEach(inject(function ($injector, Private) { - * Private.stub( - * // since this module just exports a function, we need to change - * // what Private returns in order to modify it's behavior - * require('ui/agg_response/hierarchical/_build_split'), - * sinon.stub().returns(fakeSplit) - * ); - * })); - * ``` - * - * # `Private.swap(oldProvider, newProvider)` - * This new method does an 1-for-1 swap of module providers, unlike `stub()` which replaces a modules instance. - * Pass the module you want to swap out, and the one it should be replaced with, then profit. - * - * Note: even though this example shows `swap()` being called in a config - * function, it can be called from anywhere. It is particularly useful - * in this scenario though. - * - * ```js - * beforeEach(module('opensearchDashboards', function (PrivateProvider) { - * PrivateProvider.swap( - * function StubbedRedirectProvider($decorate) { - * // $decorate is a function that will instantiate the original module when called - * return sinon.spy($decorate()); - * } - * ); - * })); - * ``` - * - * @param {[type]} prov [description] - */ -import _ from 'lodash'; - -const nextId = _.partial(_.uniqueId, 'privateProvider#'); - -function name(fn) { - return fn.name || fn.toString().split('\n').shift(); -} - -export function PrivateProvider() { - const provider = this; - - // one cache/swaps per Provider - const cache = {}; - const swaps = {}; - - // return the uniq id for this function - function identify(fn) { - if (typeof fn !== 'function') { - throw new TypeError('Expected private module "' + fn + '" to be a function'); - } - - if (fn.$$id) return fn.$$id; - else return (fn.$$id = nextId()); - } - - provider.stub = function (fn, instance) { - cache[identify(fn)] = instance; - return instance; - }; - - provider.swap = function (fn, prov) { - const id = identify(fn); - swaps[id] = prov; - }; - - provider.$get = [ - '$injector', - function PrivateFactory($injector) { - // prevent circular deps by tracking where we came from - const privPath = []; - const pathToString = function () { - return privPath.map(name).join(' -> '); - }; - - // call a private provider and return the instance it creates - function instantiate(prov, locals) { - if (~privPath.indexOf(prov)) { - throw new Error( - 'Circular reference to "' + - name(prov) + - '"' + - ' found while resolving private deps: ' + - pathToString() - ); - } - - privPath.push(prov); - - const context = {}; - let instance = $injector.invoke(prov, context, locals); - if (!_.isObject(instance)) instance = context; - - privPath.pop(); - return instance; - } - - // retrieve an instance from cache or create and store on - function get(id, prov, $delegateId, $delegateProv) { - if (cache[id]) return cache[id]; - - let instance; - - if ($delegateId != null && $delegateProv != null) { - instance = instantiate(prov, { - $decorate: _.partial(get, $delegateId, $delegateProv), - }); - } else { - instance = instantiate(prov); - } - - return (cache[id] = instance); - } - - // main api, get the appropriate instance for a provider - function Private(prov) { - let id = identify(prov); - let $delegateId; - let $delegateProv; - - if (swaps[id]) { - $delegateId = id; - $delegateProv = prov; - - prov = swaps[$delegateId]; - id = identify(prov); - } - - return get(id, prov, $delegateId, $delegateProv); - } - - Private.stub = provider.stub; - Private.swap = provider.swap; - - return Private; - }, - ]; -} diff --git a/src/plugins/opensearch_dashboards_legacy/public/utils/register_listen_event_listener.js b/src/plugins/opensearch_dashboards_legacy/public/utils/register_listen_event_listener.js deleted file mode 100644 index 19652d94cf4f..000000000000 --- a/src/plugins/opensearch_dashboards_legacy/public/utils/register_listen_event_listener.js +++ /dev/null @@ -1,47 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export function registerListenEventListener($rootScope) { - /** - * Helper that registers an event listener, and removes that listener when - * the $scope is destroyed. - * - * @param {EventEmitter} emitter - the event emitter to listen to - * @param {string} eventName - the event name - * @param {Function} handler - the event handler - * @return {undefined} - */ - $rootScope.constructor.prototype.$listen = function (emitter, eventName, handler) { - emitter.on(eventName, handler); - this.$on('$destroy', function () { - emitter.off(eventName, handler); - }); - }; -} diff --git a/src/plugins/opensearch_dashboards_legacy/public/utils/system_api.ts b/src/plugins/opensearch_dashboards_legacy/public/utils/system_api.ts deleted file mode 100644 index 2675bbc084fb..000000000000 --- a/src/plugins/opensearch_dashboards_legacy/public/utils/system_api.ts +++ /dev/null @@ -1,62 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { IRequestConfig } from 'angular'; - -const SYSTEM_REQUEST_HEADER_NAME = 'osd-system-request'; -const LEGACY_SYSTEM_API_HEADER_NAME = 'osd-system-api'; - -/** - * Adds a custom header designating request as system API - * @param originalHeaders Object representing set of headers - * @return Object representing set of headers, with system API header added in - */ -export function addSystemApiHeader(originalHeaders: Record) { - const systemApiHeaders = { - [SYSTEM_REQUEST_HEADER_NAME]: true, - }; - return { - ...originalHeaders, - ...systemApiHeaders, - }; -} - -/** - * Returns true if request is a system API request; false otherwise - * - * @param request Object Request object created by $http service - * @return true if request is a system API request; false otherwise - */ -export function isSystemApiRequest(request: IRequestConfig) { - const { headers } = request; - return ( - headers && (!!headers[SYSTEM_REQUEST_HEADER_NAME] || !!headers[LEGACY_SYSTEM_API_HEADER_NAME]) - ); -} diff --git a/src/plugins/opensearch_dashboards_overview/public/components/overview/__snapshots__/overview.test.tsx.snap b/src/plugins/opensearch_dashboards_overview/public/components/overview/__snapshots__/overview.test.tsx.snap index 6d92c8759243..1619a4fea3ff 100644 --- a/src/plugins/opensearch_dashboards_overview/public/components/overview/__snapshots__/overview.test.tsx.snap +++ b/src/plugins/opensearch_dashboards_overview/public/components/overview/__snapshots__/overview.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Overview renders with solutions and features 1`] = ` +exports[`Overview renders with news without solutions and without features 1`] = `
-
- -

- -

-
- - - - - - } - image="/plugins/opensearchDashboardsOverview/assets/solutions_opensearch_dashboards_light_2x.png" - title="OpenSearch Dashboards" - titleElement="h3" - titleSize="xs" - /> - - - - - - } - image="/plugins/opensearchDashboardsOverview/assets/solutions_solution_2_light_2x.png" - title="Solution two" - titleElement="h3" - titleSize="xs" - /> - - - - - - } - image="/plugins/opensearchDashboardsOverview/assets/solutions_solution_3_light_2x.png" - title="Solution three" - titleElement="h3" - titleSize="xs" - /> - - - - - - } - image="/plugins/opensearchDashboardsOverview/assets/solutions_solution_4_light_2x.png" - title="Solution four" - titleElement="h3" - titleSize="xs" - /> - - - -
- - -
+ +`; + +exports[`Overview renders with news, solutions, and features 1`] = ` +
+ + } + /> +
+
+ +

+ +

+
+ + +
+
+
+`; + +exports[`Overview renders with news, with solutions, and without features 1`] = ` +
+ + } + /> +
+
+ +

+ +

+
+ + +
+
+
+`; + +exports[`Overview renders with news, without solutions, and with features 1`] = ` +
+ + } + /> +
+
+ +

+ +

+
+ + +
+
+
+`; + +exports[`Overview renders without news and features, with solutions 1`] = ` +
+ + } + /> +
+
+ +

+ +

+
+ + +
+
+
+`; + +exports[`Overview renders without news and solutions, with features 1`] = ` +
+ + } + /> +
+
+ +

+ +

+
+ + +
+