diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..685b0bec --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +# This should match the owning team set up in https://github.com/orgs/opensearch-project/teams +* @opensearch-project/dashboards-reports \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_template.md b/.github/ISSUE_TEMPLATE/bug_template.md index 8af6ebb5..183ab2c4 100644 --- a/.github/ISSUE_TEMPLATE/bug_template.md +++ b/.github/ISSUE_TEMPLATE/bug_template.md @@ -2,7 +2,7 @@ name: 🐛 Bug report about: Create a report to help us improve title: "[BUG]" -labels: 'bug, untriaged, Beta' +labels: 'bug, untriaged' assignees: '' --- diff --git a/.github/draft-release-notes-config.yml b/.github/draft-release-notes-config.yml new file mode 100644 index 00000000..24a9d6e8 --- /dev/null +++ b/.github/draft-release-notes-config.yml @@ -0,0 +1,45 @@ +# The overall template of the release notes +template: | + Compatible with OpenSearch and OpenSearch Dashboards Version $RESOLVED_VERSION + $CHANGES + +# Setting the formatting and sorting for the release notes body +name-template: Version $RESOLVED_VERSION +change-template: "* $TITLE ([#$NUMBER](https://github.com/opensearch-project/dashboards-reports/pull/$NUMBER))" +sort-by: merged_at +sort-direction: ascending +replacers: + - search: '##' + replace: '###' + +# Organizing the tagged PRs into unified categories +categories: + - title: 'Breaking Changes' + labels: + - 'Breaking Changes' + - title: 'Features' + labels: + - 'feature' + - title: 'Enhancements' + labels: + - 'enhancement' + - title: 'Bug Fixes' + labels: + - 'bug' + - title: 'Infrastructure' + labels: + - 'infra' + - 'test' + - 'dependencies' + - 'github actions' + - title: 'Documentation' + labels: + - 'documentation' + - title: 'Maintenance' + labels: + - "version compatibility" + - "maintenance" + - title: 'Refactoring' + labels: + - 'refactor' + - 'code quality' diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml new file mode 100644 index 00000000..e47d8d88 --- /dev/null +++ b/.github/workflows/backport.yml @@ -0,0 +1,28 @@ +name: Backport +on: + pull_request_target: + types: + - closed + - labeled + +jobs: + backport: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + name: Backport + steps: + - name: GitHub App token + id: github_app_token + uses: tibdex/github-app-token@v1.5.0 + with: + app_id: ${{ secrets.APP_ID }} + private_key: ${{ secrets.APP_PRIVATE_KEY }} + installation_id: 22958780 + + - name: Backport + uses: VachaShah/backport@v1.1.4 + with: + github_token: ${{ steps.github_app_token.outputs.token }} + branch_name: backport/backport-${{ github.event.number }} diff --git a/.github/workflows/dashboards-reports-release-workflow.yml b/.github/workflows/dashboards-reports-release-workflow.yml index 131aac0d..bceefb9d 100644 --- a/.github/workflows/dashboards-reports-release-workflow.yml +++ b/.github/workflows/dashboards-reports-release-workflow.yml @@ -7,8 +7,9 @@ on: env: PLUGIN_NAME: reportsDashboards + ARTIFACT_NAME: reports-dashboards OPENSEARCH_VERSION: '1.0' - OPENSEARCH_PLUGIN_VERSION: 1.0.0.0-rc1 + OPENSEARCH_PLUGIN_VERSION: 1.0.0.0 jobs: build: @@ -35,7 +36,7 @@ jobs: - name: Setup Node uses: actions/setup-node@v1 with: - node-version: "10.23.1" + node-version: "10.24.1" - name: Move Dashboards Reports to Plugins Dir run: mv dashboards-reports OpenSearch-Dashboards/plugins/${{ env.PLUGIN_NAME }} @@ -69,18 +70,18 @@ jobs: cd build mkdir -p ./{linux-x64,linux-arm64,windows-x64}/OpenSearch-Dashboards/${{ env.PLUGIN_NAME }} - cp ./${{ env.PLUGIN_NAME }}-*.zip ./linux-x64/${{ env.PLUGIN_NAME }}-${{ env.OPENSEARCH_PLUGIN_VERSION }}-linux-x64.zip - cp ./${{ env.PLUGIN_NAME }}-*.zip ./linux-arm64/${{ env.PLUGIN_NAME }}-${{ env.OPENSEARCH_PLUGIN_VERSION }}-linux-arm64.zip - mv ./${{ env.PLUGIN_NAME }}-*.zip ./windows-x64/${{ env.PLUGIN_NAME }}-${{ env.OPENSEARCH_PLUGIN_VERSION }}-windows-x64.zip + cp ./${{ env.PLUGIN_NAME }}-*.zip ./linux-x64/${{ env.ARTIFACT_NAME }}-${{ env.OPENSEARCH_PLUGIN_VERSION }}-linux-x64.zip + cp ./${{ env.PLUGIN_NAME }}-*.zip ./linux-arm64/${{ env.ARTIFACT_NAME }}-${{ env.OPENSEARCH_PLUGIN_VERSION }}-linux-arm64.zip + mv ./${{ env.PLUGIN_NAME }}-*.zip ./windows-x64/${{ env.ARTIFACT_NAME }}-${{ env.OPENSEARCH_PLUGIN_VERSION }}-windows-x64.zip s3_prefix="s3://staging.artifacts.opendistroforelasticsearch.amazon.com/snapshots/kibana-plugins/reports/" cd linux-x64 - wget https://github.com/opendistro-for-elasticsearch/kibana-reports/releases/download/chromium-1.12.0.0/chromium-linux-x64.zip + wget https://github.com/opensearch-project/dashboards-reports/releases/download/chromium-1.12.0.0/chromium-linux-x64.zip unzip chromium-linux-x64.zip -d ./OpenSearch-Dashboards/${{ env.PLUGIN_NAME }} rm chromium-linux-x64.zip - zip -ur ./${{ env.PLUGIN_NAME }}-*.zip ./OpenSearch-Dashboards - linux_x64_artifact=`ls ./${{ env.PLUGIN_NAME }}-${{ env.OPENSEARCH_PLUGIN_VERSION }}-linux-x64.zip` + zip -ur ./${{ env.ARTIFACT_NAME }}-*.zip ./OpenSearch-Dashboards + linux_x64_artifact=`ls ./${{ env.ARTIFACT_NAME }}-${{ env.OPENSEARCH_PLUGIN_VERSION }}-linux-x64.zip` #Inject build number before the suffix and upload to S3 linux_x64_artifact_outfile=`basename ${linux_x64_artifact%.zip}-build-${GITHUB_RUN_NUMBER}.zip` @@ -89,11 +90,11 @@ jobs: cd .. cd linux-arm64 - wget https://github.com/opendistro-for-elasticsearch/kibana-reports/releases/download/chromium-1.12.0.0/chromium-linux-arm64.zip + wget https://github.com/opensearch-project/dashboards-reports/releases/download/chromium-1.12.0.0/chromium-linux-arm64.zip unzip chromium-linux-arm64.zip -d ./OpenSearch-Dashboards/${{ env.PLUGIN_NAME }} rm chromium-linux-arm64.zip - zip -ur ./${{ env.PLUGIN_NAME }}-*.zip ./OpenSearch-Dashboards - linux_arm64_artifact=`ls ./${{ env.PLUGIN_NAME }}-${{ env.OPENSEARCH_PLUGIN_VERSION }}-linux-arm64.zip` + zip -ur ./${{ env.ARTIFACT_NAME }}-*.zip ./OpenSearch-Dashboards + linux_arm64_artifact=`ls ./${{ env.ARTIFACT_NAME }}-${{ env.OPENSEARCH_PLUGIN_VERSION }}-linux-arm64.zip` #Inject build number before the suffix and upload to S3 linux_arm64_artifact_outfile=`basename ${linux_arm64_artifact%.zip}-build-${GITHUB_RUN_NUMBER}.zip` @@ -102,11 +103,11 @@ jobs: cd .. cd windows-x64 - wget https://github.com/opendistro-for-elasticsearch/kibana-reports/releases/download/chromium-1.12.0.0/chromium-windows-x64.zip + wget https://github.com/opensearch-project/dashboards-reports/releases/download/chromium-1.12.0.0/chromium-windows-x64.zip unzip chromium-windows-x64.zip -d ./OpenSearch-Dashboards/${{ env.PLUGIN_NAME }} rm chromium-windows-x64.zip - zip -ur ./${{ env.PLUGIN_NAME }}-*.zip ./OpenSearch-Dashboards - windows_x64_artifact=`ls ./${{ env.PLUGIN_NAME }}-${{ env.OPENSEARCH_PLUGIN_VERSION }}-windows-x64.zip` + zip -ur ./${{ env.ARTIFACT_NAME }}-*.zip ./OpenSearch-Dashboards + windows_x64_artifact=`ls ./${{ env.ARTIFACT_NAME }}-${{ env.OPENSEARCH_PLUGIN_VERSION }}-windows-x64.zip` #Inject build number before the suffix and upload to S3 windows_x64_artifact_outfile=`basename ${windows_x64_artifact%.zip}-build-${GITHUB_RUN_NUMBER}.zip` diff --git a/.github/workflows/dashboards-reports-test-and-build-workflow.yml b/.github/workflows/dashboards-reports-test-and-build-workflow.yml index 1fc6eaa9..6fff6394 100644 --- a/.github/workflows/dashboards-reports-test-and-build-workflow.yml +++ b/.github/workflows/dashboards-reports-test-and-build-workflow.yml @@ -4,8 +4,9 @@ on: [pull_request, push] env: PLUGIN_NAME: reportsDashboards - OPENSEARCH_VERSION: '1.0' - OPENSEARCH_PLUGIN_VERSION: 1.0.0.0-rc1 + ARTIFACT_NAME: reports-dashboards + OPENSEARCH_VERSION: '1.3' + OPENSEARCH_PLUGIN_VERSION: 1.3.4.0 jobs: build: @@ -24,13 +25,14 @@ jobs: - name: Setup Node uses: actions/setup-node@v1 with: - node-version: "10.23.1" + node-version: "10.24.1" - name: Move Dashboards Reports to Plugins Dir run: mv dashboards-reports OpenSearch-Dashboards/plugins/${{ env.PLUGIN_NAME }} - name: Add Chromium Binary to Reporting for Testing run: | + sudo apt update sudo apt install -y libnss3-dev fonts-liberation libfontconfig1 cd OpenSearch-Dashboards/plugins/${{ env.PLUGIN_NAME }} wget https://github.com/opendistro-for-elasticsearch/kibana-reports/releases/download/chromium-1.12.0.0/chromium-linux-x64.zip @@ -49,7 +51,14 @@ jobs: with: timeout_minutes: 30 max_attempts: 3 - command: cd OpenSearch-Dashboards/plugins/${{ env.PLUGIN_NAME }}; yarn test + command: cd OpenSearch-Dashboards/plugins/${{ env.PLUGIN_NAME }}; yarn test --coverage + + - name: Upload coverage + uses: codecov/codecov-action@v1 + with: + flags: dashboards-reports + directory: OpenSearch-Dashboards/plugins/ + token: ${{ secrets.CODECOV_TOKEN }} - name: Build Artifact run: | @@ -58,45 +67,45 @@ jobs: cd build mkdir -p ./{linux-x64,linux-arm64,windows-x64}/opensearch-dashboards/${{ env.PLUGIN_NAME }} - cp ./${{ env.PLUGIN_NAME }}-*.zip ./linux-x64/${{ env.PLUGIN_NAME }}-${{ env.OPENSEARCH_PLUGIN_VERSION }}-linux-x64.zip - cp ./${{ env.PLUGIN_NAME }}-*.zip ./linux-arm64/${{ env.PLUGIN_NAME }}-${{ env.OPENSEARCH_PLUGIN_VERSION }}-linux-arm64.zip - mv ./${{ env.PLUGIN_NAME }}-*.zip ./windows-x64/${{ env.PLUGIN_NAME }}-${{ env.OPENSEARCH_PLUGIN_VERSION }}-windows-x64.zip + cp ./${{ env.PLUGIN_NAME }}-*.zip ./linux-x64/${{ env.ARTIFACT_NAME }}-${{ env.OPENSEARCH_PLUGIN_VERSION }}-linux-x64.zip + cp ./${{ env.PLUGIN_NAME }}-*.zip ./linux-arm64/${{ env.ARTIFACT_NAME }}-${{ env.OPENSEARCH_PLUGIN_VERSION }}-linux-arm64.zip + mv ./${{ env.PLUGIN_NAME }}-*.zip ./windows-x64/${{ env.ARTIFACT_NAME }}-${{ env.OPENSEARCH_PLUGIN_VERSION }}-windows-x64.zip cd linux-x64 - wget https://github.com/opendistro-for-elasticsearch/kibana-reports/releases/download/chromium-1.12.0.0/chromium-linux-x64.zip + wget https://github.com/opensearch-project/dashboards-reports/releases/download/chromium-1.12.0.0/chromium-linux-x64.zip unzip chromium-linux-x64.zip -d ./opensearch-dashboards/${{ env.PLUGIN_NAME }} - zip -ur ./${{ env.PLUGIN_NAME }}-*.zip ./opensearch-dashboards - mv ./${{ env.PLUGIN_NAME }}-*.zip .. + zip -ur ./${{ env.ARTIFACT_NAME }}-*.zip ./opensearch-dashboards + mv ./${{ env.ARTIFACT_NAME }}-*.zip .. cd .. cd linux-arm64 - wget https://github.com/opendistro-for-elasticsearch/kibana-reports/releases/download/chromium-1.12.0.0/chromium-linux-arm64.zip + wget https://github.com/opensearch-project/dashboards-reports/releases/download/chromium-1.12.0.0/chromium-linux-arm64.zip unzip chromium-linux-arm64.zip -d ./opensearch-dashboards/${{ env.PLUGIN_NAME }} - zip -ur ./${{ env.PLUGIN_NAME }}-*.zip ./opensearch-dashboards - mv ./${{ env.PLUGIN_NAME }}-*.zip .. + zip -ur ./${{ env.ARTIFACT_NAME }}-*.zip ./opensearch-dashboards + mv ./${{ env.ARTIFACT_NAME }}-*.zip .. cd .. cd windows-x64 - wget https://github.com/opendistro-for-elasticsearch/kibana-reports/releases/download/chromium-1.12.0.0/chromium-windows-x64.zip + wget https://github.com/opensearch-project/dashboards-reports/releases/download/chromium-1.12.0.0/chromium-windows-x64.zip unzip chromium-windows-x64.zip -d ./opensearch-dashboards/${{ env.PLUGIN_NAME }} - zip -ur ./${{ env.PLUGIN_NAME }}-*.zip ./opensearch-dashboards - mv ./${{ env.PLUGIN_NAME }}-*.zip .. + zip -ur ./${{ env.ARTIFACT_NAME }}-*.zip ./opensearch-dashboards + mv ./${{ env.ARTIFACT_NAME }}-*.zip .. cd .. - name: Upload Artifact For Linux x64 uses: actions/upload-artifact@v1 with: name: dashboards-reports-linux-x64 - path: OpenSearch-Dashboards/plugins/${{ env.PLUGIN_NAME }}/build/${{ env.PLUGIN_NAME }}-${{ env.OPENSEARCH_PLUGIN_VERSION }}-linux-x64.zip + path: OpenSearch-Dashboards/plugins/${{ env.PLUGIN_NAME }}/build/${{ env.ARTIFACT_NAME }}-${{ env.OPENSEARCH_PLUGIN_VERSION }}-linux-x64.zip - name: Upload Artifact For Linux arm64 uses: actions/upload-artifact@v1 with: name: dashboards-reports-linux-arm64 - path: OpenSearch-Dashboards/plugins/${{ env.PLUGIN_NAME }}/build/${{ env.PLUGIN_NAME }}-${{ env.OPENSEARCH_PLUGIN_VERSION }}-linux-arm64.zip + path: OpenSearch-Dashboards/plugins/${{ env.PLUGIN_NAME }}/build/${{ env.ARTIFACT_NAME }}-${{ env.OPENSEARCH_PLUGIN_VERSION }}-linux-arm64.zip - name: Upload Artifact For Windows uses: actions/upload-artifact@v1 with: name: dashboards-reports-windows-x64 - path: OpenSearch-Dashboards/plugins/${{ env.PLUGIN_NAME }}/build/${{ env.PLUGIN_NAME }}-${{ env.OPENSEARCH_PLUGIN_VERSION }}-windows-x64.zip + path: OpenSearch-Dashboards/plugins/${{ env.PLUGIN_NAME }}/build/${{ env.ARTIFACT_NAME }}-${{ env.OPENSEARCH_PLUGIN_VERSION }}-windows-x64.zip diff --git a/.github/workflows/dco.yml b/.github/workflows/dco.yml new file mode 100644 index 00000000..cf30ea89 --- /dev/null +++ b/.github/workflows/dco.yml @@ -0,0 +1,18 @@ +name: Developer Certificate of Origin Check + +on: [pull_request] + +jobs: + check: + runs-on: ubuntu-latest + + steps: + - name: Get PR Commits + id: 'get-pr-commits' + uses: tim-actions/get-pr-commits@v1.1.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + - name: DCO Check + uses: tim-actions/dco@v1.1.0 + with: + commits: ${{ steps.get-pr-commits.outputs.commits }} diff --git a/.github/workflows/delete_backport_branch.yml b/.github/workflows/delete_backport_branch.yml new file mode 100644 index 00000000..387a124b --- /dev/null +++ b/.github/workflows/delete_backport_branch.yml @@ -0,0 +1,15 @@ +name: Delete merged branch of the backport PRs +on: + pull_request: + types: + - closed + +jobs: + delete-branch: + runs-on: ubuntu-latest + if: startsWith(github.event.pull_request.head.ref,'backport/') + steps: + - name: Delete merged branch + uses: SvanBoxel/delete-merged-branch@main + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/draft-release-notes-workflow.yml b/.github/workflows/draft-release-notes-workflow.yml new file mode 100644 index 00000000..23ff3682 --- /dev/null +++ b/.github/workflows/draft-release-notes-workflow.yml @@ -0,0 +1,21 @@ +name: Release Drafter + +on: + push: + branches: + - main + +jobs: + update_release_draft: + name: Update draft release notes + runs-on: ubuntu-latest + steps: + # Drafts your next Release notes as Pull Requests are merged into "main" + - name: Update draft release notes + uses: release-drafter/release-drafter@v5 + with: + config-name: draft-release-notes-config.yml + tag: (None) + version: 1.3.4.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/link-checker.yml b/.github/workflows/link-checker.yml new file mode 100644 index 00000000..b50f79bf --- /dev/null +++ b/.github/workflows/link-checker.yml @@ -0,0 +1,23 @@ +name: Link Checker +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + linkchecker: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: lychee Link Checker + id: lychee + uses: lycheeverse/lychee-action@master + with: + args: --accept=200,403,429 "./**/*.md" "./**/*.txt" --exclude "https://centos.pkgs.org/7/*" + env: + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} + - name: Fail if there were link errors + run: exit ${{ steps.lychee.outputs.exit_code }} diff --git a/.github/workflows/reports-scheduler-test-and-build-workflow.yml b/.github/workflows/reports-scheduler-test-and-build-workflow.yml index 28b088f5..d8a7f862 100644 --- a/.github/workflows/reports-scheduler-test-and-build-workflow.yml +++ b/.github/workflows/reports-scheduler-test-and-build-workflow.yml @@ -2,57 +2,47 @@ name: Test and Build Reports Scheduler on: [push, pull_request] +env: + OPENSEARCH_VERSION: '1.3.4-SNAPSHOT' + jobs: build: + strategy: + matrix: + java: + - 8 + - 11 + - 14 runs-on: ubuntu-latest steps: - - name: Set up JDK 1.14 + - name: Set up JDK ${{ matrix.java }} uses: actions/setup-java@v1 with: - java-version: 1.14 - - # dependencies: OpenSearch - - name: Checkout OpenSearch - uses: actions/checkout@v2 - with: - repository: 'opensearch-project/OpenSearch' - path: OpenSearch - ref: '1.0' - - name: Build OpenSearch - working-directory: ./OpenSearch - run: ./gradlew publishToMavenLocal -Dbuild.version_qualifier=rc1 -Dbuild.snapshot=false - - # dependencies: common-utils - - name: Checkout common-utils - uses: actions/checkout@v2 - with: - repository: 'opensearch-project/common-utils' - ref: 'main' - path: common-utils - - name: Build common-utils - working-directory: ./common-utils - run: ./gradlew publishToMavenLocal -Dopensearch.version=1.0.0-rc1 - - # dependencies: job-scheduler - - name: Checkout job-scheduler - uses: actions/checkout@v2 - with: - repository: 'opensearch-project/job-scheduler' - ref: 'main' - path: job-scheduler - - name: Build job-scheduler - working-directory: ./job-scheduler - run: ./gradlew publishToMavenLocal -Dopensearch.version=1.0.0-rc1 -Dbuild.snapshot=false + java-version: ${{ matrix.java }} # reports-scheduler - name: Checkout Reports Scheduler uses: actions/checkout@v2 + - name: RunBackwards Compatibility Tests + run: | + cd reports-scheduler + echo "Running backwards compatibility tests ..." + ./gradlew bwcTestSuite + + - name: Build with Gradle run: | cd reports-scheduler - ./gradlew build -Dopensearch.version=1.0.0-rc1 + ./gradlew build -Dopensearch.version=${{ env.OPENSEARCH_VERSION }} + + - name: Upload coverage + uses: codecov/codecov-action@v1 + with: + flags: reports-scheduler + directory: reports-scheduler/ + token: ${{ secrets.CODECOV_TOKEN }} - name: Create Artifact Path run: | @@ -64,3 +54,4 @@ jobs: with: name: reports-scheduler path: reports-scheduler-builds + diff --git a/.whitesource b/.whitesource new file mode 100644 index 00000000..84ec8caa --- /dev/null +++ b/.whitesource @@ -0,0 +1,20 @@ +{ + "scanSettings": { + "configMode": "AUTO", + "configExternalURL": "", + "projectToken": "", + "baseBranches": [] + }, + "checkRunSettings": { + "vulnerableCheckRunConclusionLevel": "failure", + "displayMode": "diff" + }, + "issueSettings": { + "minSeverityLevel": "LOW" + }, + "remediateSettings": { + "workflowRules": { + "enabled": true + } + } +} \ No newline at end of file diff --git a/ADMINS.md b/ADMINS.md new file mode 100644 index 00000000..bcdc8f2a --- /dev/null +++ b/ADMINS.md @@ -0,0 +1,7 @@ +## Admins + +| Admin | GitHub ID | Affiliation | +| --------------- | --------------------------------------- | ----------- | +| Henri Yandell | [hyandell](https://github.com/hyandell) | Amazon | + +[This document](https://github.com/opensearch-project/.github/blob/main/ADMINS.md) explains what admins do in this repo, and how they should be doing it. If you're interested in becoming a maintainer, see [MAINTAINERS](MAINTAINERS.md). If you're interested in contributing, see [CONTRIBUTING](CONTRIBUTING.md). diff --git a/MAINTAINERS.md b/MAINTAINERS.md index 30d38c01..fe4d6207 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -1,5 +1,3 @@ -# Dashboards Reports Maintainers - ## Maintainers | Maintainer | GitHub ID | Affiliation | |------------------------|---------------------------------------------------|-------------| @@ -7,3 +5,5 @@ | David Cui | [davidcui-amzn](https://github.com/davidcui-amzn) | Amazon | | Joshua Li | [joshuali925](https://github.com/joshuali925) | Amazon | | Zhongnan Su | [zhongnansu](https://github.com/zhongnansu) | Amazon | + +[This document](https://github.com/opensearch-project/.github/blob/main/MAINTAINERS.md) explains what maintainers do in this repo, and how they should be doing it. If you're interested in contributing, see [CONTRIBUTING](CONTRIBUTING.md). diff --git a/NOTICE b/NOTICE deleted file mode 100644 index be83767d..00000000 --- a/NOTICE +++ /dev/null @@ -1,12 +0,0 @@ -OpenSearch -Copyright 2021 OpenSearch Contributors - -This product includes software developed by -Elasticsearch (http://www.elastic.co). -Copyright 2009-2018 Elasticsearch - -This product includes software developed by The Apache Software -Foundation (http://www.apache.org/). - -This product includes software developed by -Joda.org (http://www.joda.org/). diff --git a/NOTICE.txt b/NOTICE.txt new file mode 100644 index 00000000..731cb600 --- /dev/null +++ b/NOTICE.txt @@ -0,0 +1,2 @@ +OpenSearch (https://opensearch.org/) +Copyright OpenSearch Contributors diff --git a/README.md b/README.md index 965e86ae..9c1cca06 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,114 @@ -[![OpenSearch Dashboards Reports CI](https://github.com/opensearch-project/dashboards-reports/workflows/Test%20and%20Build%20OpenSearch%20Dashboards%20Reports/badge.svg)](https://github.com/opensearch-project/dashboards-reports/actions?query=workflow%3A%22Test+and+Build+OpenSearch+Dashboards+Reports%22) -[![Reports Scheduler CI](https://github.com/opensearch-project/dashboards-reports/workflows/Test%20and%20Build%20Reports%20Scheduler/badge.svg)](https://github.com/opensearch-project/dashboards-reports/actions?query=workflow%3A%22Test+and+Build+Reports+Scheduler%22) -[![codecov](https://codecov.io/gh/opensearch-project/dashboards-reports/branch/dev/graph/badge.svg?token=FBVYQSZD3B)](https://codecov.io/gh/opensearch-project/dashboards-reports) -[![Documentation](https://img.shields.io/badge/documentation-blue.svg)](https://docs-beta.opensearch.org/docs/opensearch-dashboards/reporting/) -![PRs welcome!](https://img.shields.io/badge/PRs-welcome!-success) + + +- [OpenSearch Dashboards Reports](#opensearch-dashboards-reports) +- [Code Summary](#code-summary) +- [Documentation](#documentation) +- [Contributing](#contributing) +- [Setup](#setup-&-build) +- [Notifications Integration](#notifications-integration) +- [Troubleshooting](#troubleshooting) +- [Code of Conduct](#code-of-conduct) +- [Security](#security) +- [License](#license) +- [Copyright](#copyright) # OpenSearch Dashboards Reports -OpenSearch Dashboards Reports allows ‘Report Owner’ (engineers, including but not limited to developers, DevOps, IT Engineer, and IT admin) export and share reports from OpenSearch Dashboards dashboards, saved search, alerts and visualizations. It helps automate the process of scheduling reports on an on-demand or a periodical basis (on cron schedules as well). Further, it also automates the process of exporting and sharing reports triggered for various alerts. The feature is present in the Dashboard, Discover, and Visualization tabs. Scheduled reports can be sent to (shared with) self or various stakeholders within the organization such as, including but not limited to, executives, managers, engineers (developers, DevOps, IT Engineer) in the form of pdf, hyperlinks, csv, excel via various channels such as email, slack, Amazon Chime. However, in order to export, schedule and share reports, report owners should have the necessary permissions as defined under Roles and Privileges. +OpenSearch Dashboards Reports allows ‘Report Owner’ (engineers, including but not limited to developers, DevOps, IT Engineer, and IT admin) export and share reports from OpenSearch Dashboards dashboards, saved search, alerts and visualizations. It helps automate the process of scheduling reports on an on-demand or a periodical basis (on cron schedules as well). Further, it also automates the process of exporting and sharing reports triggered for various alerts. The feature is present in the Dashboard, Discover, and Visualization tabs. We are currently working on integrating Dashboards Reports with Notifications to enable sharing functionality. After the support is introduced, scheduled reports can be sent to (shared with) self or various stakeholders within the organization. These stakeholders include but are not limited to, executives, managers, engineers (developers, DevOps, IT Engineer) in the form of pdf, hyperlinks, csv, excel via various channels such as email, Slack, and Amazon Chime. However, in order to export, schedule and share reports, report owners should have the necessary permissions as defined under Roles and Privileges. + +## Code Summary + +### Reports-Scheduler + +| | | +| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Test and build | [![Observability OpenSearch Build CI][reports-scheduler-build-badge]][reports-scheduler-build-link] | +| Code coverage | [![codecov][reports-scheduler-codecov-badge]][codecov-link] | +| Distribution build tests | [![OpenSearch IT tests][reports-scheduler-it-badge]][reports-scheduler-it-link] [![OpenSearch IT code][reports-scheduler-it-code-badge]][reports-scheduler-it-code-link] | +| Backward compatibility tests | [![BWC tests][bwc-tests-badge]][bwc-tests-link] | + +### Dashboard-Reports + +| | | +| ------------------------ | ------------------------------------------------------------------------------------------------------------------ | +| Test and build | [![Observability Dashboards CI][dashboard-reports-build-badge]][dashboard-reports-build-link] | +| Code coverage | [![codecov][dashboard-reports-codecov-badge]][codecov-link] | +| Distribution build tests | [![cypress tests][cypress-test-badge]][cypress-test-link] [![cypress code][cypress-code-badge]][cypress-code-link] | + +### Repository Checks + +| | | +| ------------ | --------------------------------------------------------------- | +| DCO Checker | [![Developer certificate of origin][dco-badge]][dco-badge-link] | +| Link Checker | [![Link Checker][link-check-badge]][link-check-link] | + +### Issues + +| | +| -------------------------------------------------------------- | +| [![good first issues open][good-first-badge]][good-first-link] | +| [![features open][feature-badge]][feature-link] | +| [![enhancements open][enhancement-badge]][enhancement-link] | +| [![bugs open][bug-badge]][bug-link] | +| [![untriaged open][untriaged-badge]][untriaged-link] | +| [![nolabel open][nolabel-badge]][nolabel-link] | + +[dco-badge]: https://github.com/opensearch-project/dashboards-reports/actions/workflows/dco.yml/badge.svg +[dco-badge-link]: https://github.com/opensearch-project/dashboards-reports/actions/workflows/dco.yml +[link-check-badge]: https://github.com/opensearch-project/dashboards-reports/actions/workflows/link-checker.yml/badge.svg +[link-check-link]: https://github.com/opensearch-project/dashboards-reports/actions/workflows/link-checker.yml +[dashboard-reports-build-badge]: https://github.com/opensearch-project/dashboards-reports/actions/workflows/dashboards-reports-test-and-build-workflow.yml/badge.svg +[dashboard-reports-build-link]: https://github.com/opensearch-project/dashboards-reports/actions/workflows/dashboards-reports-test-and-build-workflow.yml +[reports-scheduler-build-badge]: https://github.com/opensearch-project/dashboards-reports/actions/workflows/reports-scheduler-test-and-build-workflow.yml/badge.svg +[reports-scheduler-build-link]: https://github.com/opensearch-project/dashboards-reports/actions/workflows/reports-scheduler-test-and-build-workflow.yml +[dashboard-reports-codecov-badge]: https://codecov.io/gh/opensearch-project/dashboards-reports/branch/main/graphs/badge.svg?flag=dashboards-reports +[reports-scheduler-codecov-badge]: https://codecov.io/gh/opensearch-project/dashboards-reports/branch/main/graphs/badge.svg?flag=reports-scheduler +[codecov-link]: https://codecov.io/gh/opensearch-project/dashboards-reports +[cypress-test-badge]: https://img.shields.io/badge/Cypress%20tests-in%20progress-yellow +[cypress-test-link]: https://github.com/opensearch-project/opensearch-build/issues/1124 +[cypress-code-badge]: https://img.shields.io/badge/Cypress%20code-blue +[cypress-code-link]: https://github.com/opensearch-project/dashboards-reports/tree/main/dashboards-reports/.cypress/integration +[reports-scheduler-it-badge]: https://img.shields.io/badge/Reports%20Scheduler%20IT%20tests-in%20progress-yellow +[reports-scheduler-it-link]: https://github.com/opensearch-project/opensearch-build/issues/1124 +[reports-scheduler-it-code-badge]: https://img.shields.io/badge/Reports%20Scheduler%20code-blue +[reports-scheduler-it-code-link]: https://github.com/opensearch-project/dashboards-reports/blob/main/reports-scheduler/src/test/kotlin/org/opensearch/reportsscheduler/ReportsSchedulerPluginIT.kt +[bwc-tests-badge]: https://img.shields.io/badge/BWC%20tests-in%20progress-yellow +[bwc-tests-link]: https://github.com/opensearch-project/dashboards-reports/pull/244/files +[good-first-badge]: https://img.shields.io/github/issues/opensearch-project/dashboards-reports/good%20first%20issue.svg +[good-first-link]: https://github.com/opensearch-project/dashboards-reports/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22+ +[feature-badge]: https://img.shields.io/github/issues/opensearch-project/dashboards-reports/feature%20request.svg +[feature-link]: https://github.com/opensearch-project/dashboards-reports/issues?q=is%3Aopen+is%3Aissue+label%3A%22feature+request%22+ +[bug-badge]: https://img.shields.io/github/issues/opensearch-project/dashboards-reports/bug.svg +[bug-link]: https://github.com/opensearch-project/dashboards-reports/issues?q=is%3Aopen+is%3Aissue+label%3Abug+ +[enhancement-badge]: https://img.shields.io/github/issues/opensearch-project/dashboards-reports/enhancement.svg +[enhancement-link]: https://github.com/opensearch-project/dashboards-reports/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement+ +[untriaged-badge]: https://img.shields.io/github/issues/opensearch-project/dashboards-reports/untriaged.svg +[untriaged-link]: https://github.com/opensearch-project/dashboards-reports/issues?q=is%3Aopen+is%3Aissue+label%3Auntriaged+ +[nolabel-badge]: https://img.shields.io/github/issues-search/opensearch-project/dashboards-reports?color=yellow&label=no%20label%20issues&query=is%3Aopen%20is%3Aissue%20no%3Alabel +[nolabel-link]: https://github.com/opensearch-project/dashboards-reports/issues?q=is%3Aopen+is%3Aissue+no%3Alabel+ + +## Documentation + +Please see our technical [documentation](https://opensearch.org/docs/dashboards/reporting/) to learn more about its features. + +## Contributing -# Request for Comments ( RFC ) - -Please add your feature requests here [ New Requests ](https://github.com/opensearch-project/dashboards-reports/issues) and view project progress here [RFCs](https://github.com/opensearch-project/dashboards-reports/projects/1). +We welcome you to get involved in development, documentation, testing the OpenSearch Dashboards reports plugin. See our [CONTRIBUTING.md](./CONTRIBUTING.md) and join in. ## Setup & Build -Complete OpenSearch Dashboards Report feature is composed of 2 plugins. Refer to README in each plugin folder for more details. +Complete OpenSearch Dashboards Report feature is composed of 2 plugins. - [OpenSearch Dashboards reports plugin](./dashboards-reports/README.md) -- [Reports scheduler ES plugin](./reports-scheduler/README.md)(TODO) +- OpenSearch Reports scheduler plugin + +## Notifications Integration + +OpenSearch Dashboards Reports integration with [Notifications](https://github.com/opensearch-project/notifications) is currently in progress. Tracking [here](https://github.com/opensearch-project/dashboards-reports/issues/72) ## Troubleshooting -#### Fail to launch Chromium +### Fail to launch Chromium There could be two reasons for this problem @@ -29,22 +116,41 @@ There could be two reasons for this problem 2. Missing additional dependencies. Please refer to [additional dependencies section](./dashboards-reports/rendering-engine/headless-chrome/README.md#additional-libaries) to install required dependencies according to your operating system. -## Contributing to OpenSearch Dashboards reports +### Missing Font Dependencies -We welcome you to get involved in development, documentation, testing the OpenSearch Dashboards reports plugin. See our [CONTRIBUTING.md](./CONTRIBUTING.md) and join in. +Chromium may not have all of the dependencies you may require to be able to view all of the content of your reports. + +If you are using a CentOS/RHEL system, install the following packages: + +- [`ipa-gothic-fonts`](https://centos.pkgs.org/7/centos-x86_64/ipa-gothic-fonts-003.03-5.el7.noarch.rpm.html) +- [`xorg-x11-fonts-100dpi`](https://centos.pkgs.org/7/centos-x86_64/xorg-x11-fonts-100dpi-7.5-9.el7.noarch.rpm.html) +- [`xorg-x11-fonts-75dpi`](https://centos.pkgs.org/7/centos-x86_64/xorg-x11-fonts-75dpi-7.5-9.el7.noarch.rpm.html) +- [`xorg-x11-utils`](https://centos.pkgs.org/7/centos-x86_64/xorg-x11-utils-7.5-23.el7.x86_64.rpm.html) +- [`xorg-x11-fonts-cyrillic`](https://centos.pkgs.org/7/centos-x86_64/xorg-x11-fonts-cyrillic-7.5-9.el7.noarch.rpm.html) +- [`xorg-x11-fonts-Type1`](https://centos.pkgs.org/7/centos-x86_64/xorg-x11-fonts-Type1-7.5-9.el7.noarch.rpm.html) +- [`xorg-x11-fonts-misc`](https://centos.pkgs.org/7/centos-x86_64/xorg-x11-fonts-misc-7.5-9.el7.noarch.rpm.html) +- [`fontconfig`](https://www.freedesktop.org/wiki/Software/fontconfig/) +- [`freetype`](https://freetype.org/) + +If you are using a Ubuntu/Debian system, install the following packages: + +- [`fonts-liberation`](https://packages.debian.org/search?keywords=fonts-liberation) +- [`libfontconfig1`](https://packages.debian.org/sid/libfontconfig1) + +The installation command for both systems can be found [here](./dashboards-reports/rendering-engine/headless-chrome/README.md). ## Code of Conduct -This project has adopted an [Open Source Code of Conduct](https://opensearch.org/codeofconduct.html). +This project has adopted the [Amazon Open Source Code of Conduct](CODE_OF_CONDUCT.md). For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq), or contact [opensource-codeofconduct@amazon.com](mailto:opensource-codeofconduct@amazon.com) with any additional questions or comments. -## Security issue notifications +## Security If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public GitHub issue. ## License -See the [LICENSE](./LICENSE.txt) file for our project's licensing. We will ask you to confirm the licensing of your contribution. +See the [LICENSE](./LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. ## Copyright -Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +Copyright OpenSearch Contributors. See [NOTICE](NOTICE.txt) for details. diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 00000000..baf47475 --- /dev/null +++ b/RELEASING.md @@ -0,0 +1 @@ +This project follows [OpenSearch branching, labelling, and releasing](https://github.com/opensearch-project/.github/blob/main/RELEASING.md). \ No newline at end of file diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..0b85ca04 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,3 @@ +## Reporting a Vulnerability + +If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/) or directly via email to aws-security@amazon.com. Please do **not** create a public GitHub issue. \ No newline at end of file diff --git a/dashboards-reports/.babelrc b/dashboards-reports/.babelrc deleted file mode 100644 index e26ee76e..00000000 --- a/dashboards-reports/.babelrc +++ /dev/null @@ -1,18 +0,0 @@ -{ - "presets": [ - [ - "@babel/preset-env", - { - "targets": { "node": "10" } - } - ], - "@babel/preset-react", - "@babel/preset-typescript" - ], - "plugins": [ - "@babel/plugin-transform-modules-commonjs", - ["@babel/plugin-transform-runtime", { "regenerator": true }], - "@babel/plugin-proposal-class-properties", - "@babel/plugin-proposal-object-rest-spread" - ] -} diff --git a/dashboards-reports/.cypress/integration/01-create.spec.ts b/dashboards-reports/.cypress/integration/01-create.spec.ts index 10a706b7..d047aeff 100644 --- a/dashboards-reports/.cypress/integration/01-create.spec.ts +++ b/dashboards-reports/.cypress/integration/01-create.spec.ts @@ -1,89 +1,248 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. */ -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT 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 { visitReportingLandingPage } from "../support/utils"; + +describe('Adding sample data', () => { + it('Adds sample data', () => { + cy.visit(`${Cypress.env('opensearchDashboards')}/app/home#/tutorial_directory/sampleData`); + cy.get('div[data-test-subj="sampleDataSetCardflights"]').contains(/(Add|View) data/).click(); + cy.wait(3000); + cy.visit(`${Cypress.env('opensearchDashboards')}/app/home#/tutorial_directory/sampleData`); + cy.get('div[data-test-subj="sampleDataSetCardecommerce"]').contains(/(Add|View) data/).click(); + cy.wait(3000); + cy.visit(`${Cypress.env('opensearchDashboards')}/app/home#/tutorial_directory/sampleData`); + cy.get('div[data-test-subj="sampleDataSetCardlogs"]').contains(/(Add|View) data/).click(); + cy.wait(3000); + }); +}); describe('Cypress', () => { it('Visits Reporting homepage', () => { - cy.visit(`${Cypress.env('opensearchDashboards')}/app/reports-dashboards#/`); - cy.location('pathname', { timeout: 60000 }).should( - 'include', - '/reports-dashboards' - ); + visitReportingLandingPage(); }); it('Visit Create page', () => { - cy.visit(`${Cypress.env('opensearchDashboards')}/app/reports-dashboards#/`); - cy.location('pathname', { timeout: 60000 }).should( - 'include', - '/reports-dashboards' - ); - cy.wait(12500); // wait for the page to load - cy.get('#createReportHomepageButton').click({ force: true }); + visitCreateReportDefinitionPage(); + }); + + it('Create a new on-demand dashboard report definition', () => { + visitCreateReportDefinitionPage(); + setReportDefinitionName('Cypress dashboard on-demand report'); + setReportDefinitionDescription('Description for cypress test'); + selectReportSourceComboBox(); + + // // select drop-down option in report source list + cy.contains('[Logs] Web Traffic').click(); + + cy.wait(500); + clickCreateReportDefinitionButton(); + cy.wait(3000); + verifyOnReportingLandingPage(); }); - it('Create a new on-demand report definition', () => { - cy.visit(`${Cypress.env('opensearchDashboards')}/app/reports-dashboards#/`); - cy.location('pathname', { timeout: 60000 }).should( - 'include', - '/reports-dashboards' - ); - cy.wait(12500); - cy.get('#createReportHomepageButton').click(); + it('Create a new on-demand visualization report definition', ()=> { + visitCreateReportDefinitionPage(); + setReportDefinitionName('Cypress vis on-demand report'); + setReportDefinitionDescription('Description for cypress test'); + selectReportSource('#visualizationReportSource'); + selectReportSourceComboBox(); + cy.wait(500); + clickCreateReportDefinitionButton(); + cy.wait(3000); + verifyOnReportingLandingPage(); + }); - // enter a report name - cy.get('#reportSettingsName').type('Create cypress test on-demand report'); + it('Create a new on-demand saved search report definition', () => { + visitCreateReportDefinitionPage(); + setReportDefinitionName('Cypress saved search on-demand report'); + setReportDefinitionDescription('Description for cypress test'); + selectReportSource('#savedSearchReportSource'); + selectReportSourceComboBox(); + cy.wait(500); + clickCreateReportDefinitionButton(); + cy.wait(3000); + verifyOnReportingLandingPage(); + }); - // enter a report description - cy.get('#reportSettingsDescription').type('Description for cypress test'); + it('Create a new dashboard daily recurring report definition', () => { + visitCreateReportDefinitionPage(); + setReportDefinitionName('Cypress dashboard daily scheduled report'); + setReportDefinitionDescription('Description for cypress test'); + selectReportSourceComboBox(); - // select a report source - cy.get('.euiComboBox').click({ force: true }); + // select drop-down option in report source list + cy.contains('[Logs] Web Traffic').click(); - // create an on-demand report definition - cy.get('#createNewReportDefinition').click({ force: true }); + cy.wait(500); + setReportTriggerToSchedule(); + clickCreateReportDefinitionButton(); + cy.wait(3000); + verifyOnReportingLandingPage(); }); - it('Create a new scheduled report definition', () => { - cy.visit(`${Cypress.env('opensearchDashboards')}/app/reports-dashboards#/`); - cy.location('pathname', { timeout: 60000 }).should( - 'include', - '/reports-dashboards' - ); - cy.wait(12500); - cy.get('#createReportHomepageButton').click(); + it('Create a new visualization daily recurring report definition', () => { + visitCreateReportDefinitionPage(); + setReportDefinitionName('Cypress vis daily scheduled report'); + setReportDefinitionDescription('Description for cypress test'); + selectReportSource('#visualizationReportSource'); + selectReportSourceComboBox(); + cy.wait(500); + setReportTriggerToSchedule(); + clickCreateReportDefinitionButton(); + cy.wait(3000); + verifyOnReportingLandingPage(); + }); - // enter a report name - cy.get('#reportSettingsName').type('Create cypress test scheduled report'); + it('Create a new saved search daily recurring report definition', () => { + visitCreateReportDefinitionPage(); + setReportDefinitionName('Cypress search daily scheduled report'); + setReportDefinitionDescription('Description for cypress test'); + selectReportSource('#savedSearchReportSource'); + selectReportSourceComboBox(); + setReportTriggerToSchedule(); + clickCreateReportDefinitionButton(); + cy.wait(3000); + verifyOnReportingLandingPage(); + }); - // enter a report description - cy.get('#reportSettingsDescription').type('Description for cypress test'); + it('Create a new dashboard interval recurring report definition', () => { + visitCreateReportDefinitionPage(); + setReportDefinitionName('Cypress dashboard recurring report'); + setReportDefinitionDescription('Description for cypress test'); + selectReportSourceComboBox(); - // set report trigger to Schedule option - cy.get('[type="radio"]').check({ force: true }); + // select drop-down option in report source list + cy.contains('[Logs] Web Traffic').click(); - // create scheduled report definition - cy.get('#createNewReportDefinition').click({ force: true }); + cy.wait(500); + setReportTriggerToSchedule(); + selectIntervalScheduleFrequency(); + inputTextIntoField('#recurringByIntervalNumber', '5'); + clickCreateReportDefinitionButton(); + cy.wait(3000); + verifyOnReportingLandingPage(); + }); + + it('Create a new visualization interval recurring report definition', () => { + visitCreateReportDefinitionPage(); + setReportDefinitionName('Cypress vis interval recurring report'); + selectReportSource('#visualizationReportSource'); + selectReportSourceComboBox(); + setReportTriggerToSchedule(); + selectIntervalScheduleFrequency(); + inputTextIntoField('#recurringByIntervalNumber', '5'); + clickCreateReportDefinitionButton(); + cy.wait(3000); + verifyOnReportingLandingPage(); + }); + + it('Create a new saved search interval recurring report definition', () => { + visitCreateReportDefinitionPage(); + setReportDefinitionName('Cypress saved search interval recurring report'); + selectReportSource('#savedSearchReportSource'); + selectReportSourceComboBox(); + setReportTriggerToSchedule(); + selectIntervalScheduleFrequency(); + inputTextIntoField('#recurringByIntervalNumber', '5'); + clickCreateReportDefinitionButton(); + cy.wait(3000); + verifyOnReportingLandingPage(); + }); + + it('Create a dashboard cron-based report definition', () => { + visitCreateReportDefinitionPage(); + setReportDefinitionName('Cypress dashboard cron definition'); + selectReportSourceComboBox(); + + // select drop-down option in report source list + cy.contains('[Logs] Web Traffic').click(); + cy.wait(500); + setReportTriggerToSchedule(); + selectCronBasedRequestTime(); + inputTextIntoField('#cronExpressionFieldText', '0 12 * * *'); + clickCreateReportDefinitionButton(); + cy.wait(3000); + verifyOnReportingLandingPage(); + }); + + it('Create a visualization cron-based report definition', () => { + visitCreateReportDefinitionPage(); + setReportDefinitionName('Cypress vis cron definition'); + selectReportSource('#visualizationReportSource'); + selectReportSourceComboBox(); + setReportTriggerToSchedule(); + selectCronBasedRequestTime(); + inputTextIntoField('#cronExpressionFieldText', '0 12 * * *'); + cy.wait(500); + clickCreateReportDefinitionButton(); + cy.wait(3000); + verifyOnReportingLandingPage(); + }); + + it('Create a saved search cron-based report definition', () => { + visitCreateReportDefinitionPage(); + setReportDefinitionName('Cypress search cron definition'); + selectReportSource('#savedSearchReportSource'); + selectReportSourceComboBox(); + setReportTriggerToSchedule(); + selectCronBasedRequestTime(); + inputTextIntoField('#cronExpressionFieldText', '0 12 * * *'); + cy.wait(500); + clickCreateReportDefinitionButton(); + cy.wait(3000); + verifyOnReportingLandingPage(); }); }); + +function visitCreateReportDefinitionPage() { + cy.visit(`${Cypress.env('opensearchDashboards')}/app/reports-dashboards#/`); + cy.location('pathname', { timeout: 60000 }).should( + 'include', + '/reports-dashboards' + ); + cy.wait(3000); + cy.get('#createReportHomepageButton').click(); +} + +function setReportDefinitionName(name: string) { + cy.get('#reportSettingsName').type(name); +} + +function setReportDefinitionDescription(description: string) { + cy.get('#reportSettingsDescription').type(description); +} + +function selectReportSource(name: string) { + cy.get(name).click({force: true}); +} + +function selectReportSourceComboBox() { + cy.get('[data-test-subj="comboBoxInput"]').eq(0).click({ force: true }); +} + +function setReportTriggerToSchedule() { + cy.get('#Schedule').check({ force: true }); +} + +function selectIntervalScheduleFrequency() { + cy.get('#recurringFrequencySelect').select('By interval'); +} + +function selectCronBasedRequestTime() { + cy.contains('Cron based').click({ force: true }); +} + +function inputTextIntoField(selector: string, text: string) { + cy.get(selector).type(text); +} + +function clickCreateReportDefinitionButton() { + cy.get('#createNewReportDefinition').click({ force: true }); +} + +function verifyOnReportingLandingPage() { + cy.get('#reportDefinitionDetailsLink').should('exist'); +} \ No newline at end of file diff --git a/dashboards-reports/.cypress/integration/02-edit.spec.ts b/dashboards-reports/.cypress/integration/02-edit.spec.ts index be7a0220..a87e77d4 100644 --- a/dashboards-reports/.cypress/integration/02-edit.spec.ts +++ b/dashboards-reports/.cypress/integration/02-edit.spec.ts @@ -1,27 +1,6 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. */ describe('Cypress', () => { @@ -51,9 +30,14 @@ describe('Cypress', () => { cy.get('#reportSettingsDescription').type(' update description'); cy.get('#editReportDefinitionButton').click({ force: true }); + + cy.wait(12500); + + // check that re-direct to home page + cy.get('#reportDefinitionDetailsLink').should('exist'); }); - it('Visit edit page, change report source and trigger', () => { + it('Visit edit page, change report trigger', () => { cy.visit(`${Cypress.env('opensearchDashboards')}/app/reports-dashboards#/`); cy.location('pathname', { timeout: 60000 }).should( 'include', @@ -71,13 +55,18 @@ describe('Cypress', () => { cy.url().should('include', 'edit'); cy.wait(1000); - cy.get('#visualizationReportSource').check({ force: true }); + cy.get('#reportDefinitionTriggerTypes > div:nth-child(2)').click({ force: true }); cy.get('#Schedule').check({ force: true }); cy.get('#editReportDefinitionButton').click({ force: true }); + + cy.wait(12500); + + // check that re-direct to home page + cy.get('#reportDefinitionDetailsLink').should('exist'); }); - it('Visit edit page, change report source back', () => { + it('Visit edit page, change report trigger back', () => { cy.visit(`${Cypress.env('opensearchDashboards')}/app/reports-dashboards#/`); cy.location('pathname', { timeout: 60000 }).should( 'include', @@ -96,8 +85,13 @@ describe('Cypress', () => { cy.wait(1000); - cy.get('#dashboardReportSource').check({ force: true }); + cy.get('#reportDefinitionTriggerTypes > div:nth-child(1)').click({ force: true }); cy.get('#editReportDefinitionButton').click({ force: true }); + + cy.wait(12500); + + // check that re-direct to home page + cy.get('#reportDefinitionDetailsLink').should('exist'); }); }); diff --git a/dashboards-reports/.cypress/integration/03-details.spec.ts b/dashboards-reports/.cypress/integration/03-details.spec.ts index db3ce62f..166da435 100644 --- a/dashboards-reports/.cypress/integration/03-details.spec.ts +++ b/dashboards-reports/.cypress/integration/03-details.spec.ts @@ -1,68 +1,111 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. */ -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT 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 { visitReportingLandingPage } from "../support/utils"; describe('Cypress', () => { it('Visit report definition details page', () => { - cy.visit(`${Cypress.env('opensearchDashboards')}/app/reports-dashboards#/`); - cy.location('pathname', { timeout: 60000 }).should( - 'include', - '/reports-dashboards' - ); + visitReportingLandingPage(); + cy.wait(5000); + visitReportDefinitionDetailsPage(); + verifyReportDefinitionDetailsURL(); + verifyDeleteDefinitionButtonExists(); + verifyGenerateReportFromFileFormatExists(); + }); - cy.wait(12500); + it('Go to edit report definition from report definition details', () => { + visitReportingLandingPage(); + cy.wait(5000); + visitReportDefinitionDetailsPage(); + verifyEditDefinitionButtonExists(); + clickEditReportDefinitionButton(); + verifyEditReportDefinitionURL(); + }); - cy.get('#reportDefinitionDetailsLink').first().click(); + it('Verify report source URL on report definition details', () => { + visitReportingLandingPage(); + cy.wait(5000); + visitReportDefinitionDetailsPage(); + verifyReportDefinitionSourceURLExists(); + + }); - cy.url().should('include', 'report_definition_details'); + it('Delete report definition from details page', () => { + visitReportingLandingPage(); + cy.wait(5000); + visitReportDefinitionDetailsPage(); + verifyDeleteDefinitionButtonExists(); + deleteReportDefinition(); + verifyDeleteSuccess(); + }); - cy.get('#deleteReportDefinitionButton').should('exist'); + it('Visit report details page', () => { + visitReportingLandingPage(); + cy.wait(5000); + visitReportDetailsPage(); + verifyReportDetailsURL(); + }); - cy.get('#editReportDefinitionButton').should('exist'); + it('Verify report source URL on report details', () => { + visitReportingLandingPage(); + cy.wait(5000); + visitReportDetailsPage(); + verifyReportDetailsSourceURLExists(); + }); +}); - if (cy.get('body').contains('Schedule details')) { - cy.wait(1000); - cy.get('#changeStatusFromDetailsButton').click(); - } else { - cy.wait(1000); - cy.get('#generateReportFromDetailsButton').click(); - } +function visitReportDefinitionDetailsPage() { + cy.get('#reportDefinitionDetailsLink').first().click(); +} - cy.get('#deleteReportDefinitionButton').click(); - }); +function verifyReportDefinitionDetailsURL() { + cy.url().should('include', 'report_definition_details'); +} - it('Visit report details page', () => { - cy.visit(`${Cypress.env('opensearchDashboards')}/app/reports-dashboards#/`); - cy.location('pathname', { timeout: 60000 }).should( - 'include', - '/reports-dashboards' - ); +function verifyDeleteDefinitionButtonExists() { + cy.get('#deleteReportDefinitionButton').should('exist'); +} - cy.wait(12500); - cy.get('#reportDetailsLink').first().click(); +function verifyEditDefinitionButtonExists() { + cy.get('#editReportDefinitionButton').should('exist'); +} - cy.url().should('include', 'report_details'); - }); -}); +function clickEditReportDefinitionButton() { + cy.get('#editReportDefinitionButton').click({ force: true }); +} + +function verifyEditReportDefinitionURL() { + cy.url().should('include', 'edit'); +} + +function verifyReportDefinitionSourceURLExists() { + cy.get('#reportDefinitionSourceURL').should('exist'); +} + +function verifyReportDetailsSourceURLExists() { + cy.get('#reportDetailsSourceURL').should('exist'); +} + +function verifyGenerateReportFromFileFormatExists() { + cy.get('#generateReportFromDetailsFileFormat').should('exist'); +} + +function deleteReportDefinition() { + cy.get('#deleteReportDefinitionButton').click(); + cy.wait(500); + cy.get('button.euiButton:nth-child(2)').click({ force: true }); +} + +function verifyDeleteSuccess() { + cy.get('#deleteReportDefinitionSuccessToast').should('exist'); +} + +function visitReportDetailsPage() { + cy.get('#reportDetailsLink').first().click(); +} + +function verifyReportDetailsURL() { + cy.url().should('include', 'report_details'); +} diff --git a/dashboards-reports/.cypress/integration/04-download.spec.ts b/dashboards-reports/.cypress/integration/04-download.spec.ts index aa3e3e57..af7e6409 100644 --- a/dashboards-reports/.cypress/integration/04-download.spec.ts +++ b/dashboards-reports/.cypress/integration/04-download.spec.ts @@ -1,27 +1,6 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. */ describe('Cypress', () => { diff --git a/dashboards-reports/.cypress/plugins/index.js b/dashboards-reports/.cypress/plugins/index.js index 5097724e..8ac1f106 100644 --- a/dashboards-reports/.cypress/plugins/index.js +++ b/dashboards-reports/.cypress/plugins/index.js @@ -1,27 +1,6 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. */ /// diff --git a/dashboards-reports/.cypress/support/commands.js b/dashboards-reports/.cypress/support/commands.js index 34a32088..dab9071d 100644 --- a/dashboards-reports/.cypress/support/commands.js +++ b/dashboards-reports/.cypress/support/commands.js @@ -1,27 +1,6 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT 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 { ADMIN_AUTH } = require('./constants'); diff --git a/dashboards-reports/.cypress/support/constants.js b/dashboards-reports/.cypress/support/constants.js index f033ff8e..1001fd49 100644 --- a/dashboards-reports/.cypress/support/constants.js +++ b/dashboards-reports/.cypress/support/constants.js @@ -1,30 +1,9 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT 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 ADMIN_AUTH = { username: 'admin', password: 'admin', -}; \ No newline at end of file +}; diff --git a/dashboards-reports/.cypress/support/index.js b/dashboards-reports/.cypress/support/index.js index 9a72b427..c70c2dd5 100644 --- a/dashboards-reports/.cypress/support/index.js +++ b/dashboards-reports/.cypress/support/index.js @@ -1,27 +1,6 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. */ // *********************************************************** diff --git a/dashboards-reports/.cypress/support/utils.js b/dashboards-reports/.cypress/support/utils.js new file mode 100644 index 00000000..a534be13 --- /dev/null +++ b/dashboards-reports/.cypress/support/utils.js @@ -0,0 +1,12 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export function visitReportingLandingPage() { + cy.visit(`${Cypress.env('opensearchDashboards')}/app/reports-dashboards#/`); + cy.location('pathname', { timeout: 60000 }).should( + 'include', + '/reports-dashboards' + ); + } \ No newline at end of file diff --git a/dashboards-reports/.i18nrc.json b/dashboards-reports/.i18nrc.json new file mode 100644 index 00000000..f60aeca5 --- /dev/null +++ b/dashboards-reports/.i18nrc.json @@ -0,0 +1,7 @@ +{ + "paths": { + "opensearch.reports": "public" + }, + "exclude": [], + "translations": ["translations/zh-CN.json", "translations/pl.json"] +} diff --git a/dashboards-reports/opensearch-dashboards-plugin-helpers.dev.json b/dashboards-reports/.opensearch_dashboards-plugin-helpers.json similarity index 67% rename from dashboards-reports/opensearch-dashboards-plugin-helpers.dev.json rename to dashboards-reports/.opensearch_dashboards-plugin-helpers.json index b8a3f352..05b7d7e4 100644 --- a/dashboards-reports/opensearch-dashboards-plugin-helpers.dev.json +++ b/dashboards-reports/.opensearch_dashboards-plugin-helpers.json @@ -3,8 +3,10 @@ "package.json", "tsconfig.json", "yarn.lock", + ".i18nrc.json", "common/**/*", "public/**/*", - "server/**/*" + "server/**/*", + "translations/**/*" ] } diff --git a/dashboards-reports/DEVELOPER_GUIDE.md b/dashboards-reports/DEVELOPER_GUIDE.md new file mode 100644 index 00000000..39e2ffbc --- /dev/null +++ b/dashboards-reports/DEVELOPER_GUIDE.md @@ -0,0 +1,67 @@ +## Developer Guide + +So you want to contribute code to this project? Excellent! We're glad you're here. Here's what you need to do. + +## Install Prerequisites + +### JDK 11 + +OpenSearch builds using Java 11 at a minimum. This means you must have a JDK 11 +installed with the environment variable `JAVA_HOME` referencing the path to Java home +for your JDK 11 installation, e.g. `JAVA_HOME=/usr/lib/jvm/jdk-11`. + +By default, tests use the same runtime as `JAVA_HOME`. However, since OpenSearch +supports JDK 8, the build supports compiling with JDK 11 and testing on a different +version of JDK runtime. To do this, set `RUNTIME_JAVA_HOME` pointing to the Java home of +another JDK installation, e.g. `RUNTIME_JAVA_HOME=/usr/lib/jvm/jdk-8`. + +## Setup + +1. Download OpenSearch for the version that matches the [OpenSearch Dashboards version specified in package.json](./package.json#L7). +1. Download the OpenSearch Dashboards source code for the [version specified in package.json](./package.json#L7) you want to set up. + +1. Change your node version to the version specified in `.node-version` inside the OpenSearch Dashboards root directory. +1. Create a `plugins` directory inside the OpenSearch Dashboards source code directory, if `plugins` directory doesn't exist. +1. Check out this package from version control into the `plugins` directory. + ``` + git clone git@github.com:opensearch-project/dashboards-reports.git plugins --no-checkout + cd plugins + echo 'dashboards-reports/*' >> .git/info/sparse-checkout + git config core.sparseCheckout true + git checkout dev + ``` +1. Run `yarn osd bootstrap` inside `OpenSearch-Dashboards/plugins/dashboards-reports`. + +Ultimately, your directory structure should look like this: + + +```md +. +├── OpenSearch-Dashboards +│ └──plugins +│ └── dashboards-reports +``` + +## Build + +To build the plugin's distributable zip simply run `yarn build`. + +Example output: `./build/reports-dashboards-0.0.1.zip` + +## Run + +- `yarn start` + + Starts OpenSearch Dashboards and includes this plugin. OpenSearch Dashboards will be available on `localhost:5601`. + +- `yarn test:jest` + + Runs the plugin tests. + +### Backports + +The Github workflow in [`backport.yml`](../.github/workflows/backport.yml) creates backport PRs automatically when the original PR +with an appropriate label `backport ` is merged to main with the backport workflow run successfully on the +PR. For example, if a PR on main needs to be backported to `1.x` branch, add a label `backport 1.x` to the PR and make sure the +backport workflow runs on the PR along with other checks. Once this PR is merged to main, the workflow will create a backport PR +to the `1.x` branch. \ No newline at end of file diff --git a/dashboards-reports/README.md b/dashboards-reports/README.md index fcdec109..85204bf8 100644 --- a/dashboards-reports/README.md +++ b/dashboards-reports/README.md @@ -1,70 +1,23 @@ # OpenSearch Dashboards Reports -OpenSearch Dashboards Reports allows ‘Report Owner’ (engineers, including but not limited to developers, DevOps, IT Engineer, and IT admin) export and share reports from OpenSearch Dashboards dashboards, saved search, alerts and visualizations. It helps automate the process of scheduling reports on an on-demand or a periodical basis (on cron schedules as well). Further, it also automates the process of exporting and sharing reports triggered for various alerts. The feature is present in the Dashboard, Discover, and Visualization tabs. Scheduled reports can be sent to (shared with) self or various stakeholders within the organization such as, including but not limited to, executives, managers, engineers (developers, DevOps, IT Engineer) in the form of pdf, hyperlinks, csv, excel via various channels such as email, slack, Amazon Chime. However, in order to export, schedule and share reports, report owners should have the necessary permissions as defined under Roles and Privileges. +OpenSearch Dashboards Reports allows ‘Report Owner’ (engineers, including but not limited to developers, DevOps, IT Engineer, and IT admin) export and share reports from OpenSearch Dashboards dashboards, saved search, alerts and visualizations. It helps automate the process of scheduling reports on an on-demand or a periodical basis (on cron schedules as well). Further, it also automates the process of exporting and sharing reports triggered for various alerts. The feature is present in the Dashboard, Discover, and Visualization tabs. We are currently working on integrating Dashboards Reports with Notifications to enable sharing functionality. After the support is introduced, scheduled reports can be sent to (shared with) self or various stakeholders within the organization. These stakeholders include but are not limited to, executives, managers, engineers (developers, DevOps, IT Engineer) in the form of pdf, hyperlinks, csv, excel via various channels such as email, Slack, and Amazon Chime. However, in order to export, schedule and share reports, report owners should have the necessary permissions as defined under Roles and Privileges. -# Request for Comments ( RFC ) +## Contributing -Please add your feature requests here [ New Requests ](https://github.com/opensearch-project/dashboards-reports/issues) and view project progress here [RFCs](https://github.com/opensearch-project/dashboards-reports/projects/1). - -## Setup - -1. Download OpenSearch for the version that matches the [OpenSearch Dashboards version specified in package.json](./package.json#L7). -1. Download the OpenSearch Dashboards source code for the [version specified in package.json](./package.json#L7) you want to set up. - -1. Change your node version to the version specified in `.node-version` inside the OpenSearch Dashboards root directory. -1. Create a `plugins` directory inside the OpenSearch Dashboards source code directory, if `plugins` directory doesn't exist. -1. Check out this package from version control into the `plugins` directory. - ``` - git clone git@github.com:opensearch-project/dashboards-reports.git plugins --no-checkout - cd plugins - echo 'dashboards-reports/*' >> .git/info/sparse-checkout - git config core.sparseCheckout true - git checkout dev - ``` -1. Run `yarn osd bootstrap` inside `OpenSearch-Dashboards/plugins/dashboards-reports`. - -Ultimately, your directory structure should look like this: - - -```md -. -├── OpenSearch-Dashboards -│ └──plugins -│ └── dashboards-reports -``` - -## Build - -To build the plugin's distributable zip simply run `yarn build`. - -Example output: `./build/reports-dashboards-0.0.1.zip` - -## Run - -- `yarn start` - - Starts OpenSearch Dashboards and includes this plugin. OpenSearch Dashboards will be available on `localhost:5601`. - -- `yarn test:jest` - - Runs the plugin tests. - -## Contributing to OpenSearch Dashboards reports - -We welcome you to get involved in development, documentation, testing the OpenSearch Dashboards reports plugin. See our [CONTRIBUTING.md](./CONTRIBUTING.md) and join in. +See [developer guide](DEVELOPER_GUIDE.md) and [how to contribute to this project](../CONTRIBUTING.md). ## Code of Conduct -This project has adopted an [Open Source Code of Conduct](https://opensearch.org/codeofconduct.html). +This project has adopted the [Amazon Open Source Code of Conduct](../CODE_OF_CONDUCT.md). For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq), or contact [opensource-codeofconduct@amazon.com](mailto:opensource-codeofconduct@amazon.com) with any additional questions or comments. -## Security issue notifications +## Security If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public GitHub issue. ## License -See the [LICENSE](./LICENSE.txt) file for our project's licensing. We will ask you to confirm the licensing of your contribution. +See the [LICENSE](../LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. ## Copyright -Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. +Copyright 2020-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/dashboards-reports/babel.config.js b/dashboards-reports/babel.config.js new file mode 100644 index 00000000..aab76c8a --- /dev/null +++ b/dashboards-reports/babel.config.js @@ -0,0 +1,20 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +module.exports = { + presets: [ + require('@babel/preset-env', { + targets: { node: '10' }, + }), + require('@babel/preset-react'), + require('@babel/preset-typescript'), + ], + plugins: [ + require('@babel/plugin-proposal-class-properties'), + require('@babel/plugin-proposal-object-rest-spread'), + ['@babel/plugin-transform-modules-commonjs', { allowTopLevelThis: true }], + [require('@babel/plugin-transform-runtime'), { regenerator: true }], + ], +}; diff --git a/dashboards-reports/common/index.ts b/dashboards-reports/common/index.ts index 1066d948..ce180a09 100644 --- a/dashboards-reports/common/index.ts +++ b/dashboards-reports/common/index.ts @@ -1,38 +1,13 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. */ -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT 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 PLUGIN_ID = 'reportsDashboards'; -export const PLUGIN_NAME = 'reports-dashboards'; +export const PLUGIN_NAME = 'Reporting'; +export const PLUGIN_ID = 'reports-dashboards'; export const API_PREFIX = '/api/reporting'; -export const NOTIFICATION_API = { - SEND: '/_plugins/_notifications/send', -}; - const BASE_REPORTS_URI = '/_plugins/_reports'; export const OPENSEARCH_REPORTS_API = { @@ -41,5 +16,18 @@ export const OPENSEARCH_REPORTS_API = { LIST_REPORT_INSTANCES: `${BASE_REPORTS_URI}/instances`, REPORT_DEFINITION: `${BASE_REPORTS_URI}/definition`, LIST_REPORT_DEFINITIONS: `${BASE_REPORTS_URI}/definitions`, - POLL_REPORT_INSTANCE: `${BASE_REPORTS_URI}/poll_instance`, }; + +const REPORTING_NOTIFICATIONS_API_PREFIX = '/api/reporting_notifications'; +export const REPORTING_NOTIFICATIONS_DASHBOARDS_API = Object.freeze({ + GET_CONFIGS: `${REPORTING_NOTIFICATIONS_API_PREFIX}/get_configs`, + GET_EVENT: `${REPORTING_NOTIFICATIONS_API_PREFIX}/get_event`, + SEND_TEST_MESSAGE: `${REPORTING_NOTIFICATIONS_API_PREFIX}/test_message`, +}); + +const NOTIFICATIONS_API_BASE_PATH = '/_plugins/_notifications'; +export const NOTIFICATIONS_API = Object.freeze({ + CONFIGS: `${NOTIFICATIONS_API_BASE_PATH}/configs`, + EVENTS: `${NOTIFICATIONS_API_BASE_PATH}/events`, + TEST_MESSAGE: `${NOTIFICATIONS_API_BASE_PATH}/feature/test`, +}); diff --git a/dashboards-reports/cypress.json b/dashboards-reports/cypress.json index 7d041020..e72b2afa 100644 --- a/dashboards-reports/cypress.json +++ b/dashboards-reports/cypress.json @@ -1,11 +1,14 @@ { - "video": false, + "video": true, "fixturesFolder": ".cypress/fixtures", "integrationFolder": ".cypress/integration", "pluginsFile": ".cypress/plugins/index.js", "screenshotsFolder": ".cypress/screenshots", "supportFile": ".cypress/support/index.js", "videosFolder": ".cypress/videos", + "requestTimeout": 60000, + "responseTimeout": 60000, + "defaultCommandTimeout": 60000, "env": { "opensearch": "localhost:9200", "opensearchDashboards": "localhost:5601", diff --git a/dashboards-reports/opensearch_dashboards.json b/dashboards-reports/opensearch_dashboards.json index cbca8ab3..28820b8d 100644 --- a/dashboards-reports/opensearch_dashboards.json +++ b/dashboards-reports/opensearch_dashboards.json @@ -1,9 +1,10 @@ { "id": "reportsDashboards", - "version": "1.0.0.0-rc1", - "opensearchDashboardsVersion": "1.0.0-rc1", + "version": "1.3.4.0", + "opensearchDashboardsVersion": "1.3.4", "requiredPlugins": ["navigation", "data", "opensearchDashboardsUtils"], "optionalPlugins": ["share"], "server": true, - "ui": true + "ui": true, + "configPath": ["opensearch_reporting"] } diff --git a/dashboards-reports/package.json b/dashboards-reports/package.json index aafb29b5..ac7a833b 100644 --- a/dashboards-reports/package.json +++ b/dashboards-reports/package.json @@ -1,13 +1,9 @@ { - "name": "reports_dashboards", - "version": "1.0.0.0-rc1", + "name": "reports-dashboards", + "version": "1.3.4.0", "description": "OpenSearch Dashboards Reports Plugin", "license": "Apache-2.0", "main": "index.ts", - "opensearchDashboards": { - "version": "1.0.0-rc1", - "templateVersion": "1.0.0-rc1" - }, "scripts": { "osd": "node ../../scripts/osd", "opensearch": "node ../../scripts/opensearch", @@ -22,9 +18,8 @@ "dependencies": { "async-mutex": "^0.2.6", "babel-polyfill": "^6.26.0", - "cheerio": "0.22.0", "cron-validator": "^1.1.1", - "dompurify": "^2.1.1", + "dompurify": "^2.3.8", "elastic-builder": "^2.7.1", "enzyme-adapter-react-16": "^1.15.2", "jest-fetch-mock": "^3.0.3", @@ -42,12 +37,11 @@ "react-router-dom": "^5.2.0", "react-toast-notifications": "^2.4.0", "set-interval-async": "1.0.33", - "showdown": "^1.9.1", - "ws": "^7.4.6" + "showdown": "^1.9.1" }, "devDependencies": { "@elastic/eslint-import-resolver-kibana": "link:../../packages/osd-eslint-import-resolver-opensearch-dashboards", - "@types/dompurify": "^2.0.4", + "@types/dompurify": "^2.3.3", "@types/enzyme-adapter-react-16": "^1.0.6", "@types/jsdom": "^16.2.3", "@types/puppeteer-core": "^2.0.0", @@ -66,14 +60,19 @@ "identity-obj-proxy": "^3.0.0", "jest-dom": "^4.0.0", "react-test-renderer": "^16.12.0", - "ts-jest": "^26.1.0", - "tsc": "^1.20150623.0" + "ts-jest": "^26.1.0" }, "resolutions": { "trim": "^1.0.0", "doc-path": "2.1.2", "y18n": "^5.0.5", "lodash": "^4.17.21", - "path-parse": "^1.0.7" + "path-parse": "^1.0.7", + "glob-parent": "^5.1.2", + "css-what": "^5.0.1", + "ansi-regex": "5.0.1", + "json-schema": "0.4.0", + "ws": "^7.4.6", + "minimist": "^1.2.6" } } diff --git a/dashboards-reports/public/application.tsx b/dashboards-reports/public/application.tsx index 32563cb7..5fc2379d 100644 --- a/dashboards-reports/public/application.tsx +++ b/dashboards-reports/public/application.tsx @@ -1,27 +1,6 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT 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'; diff --git a/dashboards-reports/public/components/app.tsx b/dashboards-reports/public/components/app.tsx index a82711ef..4091c0d7 100644 --- a/dashboards-reports/public/components/app.tsx +++ b/dashboards-reports/public/components/app.tsx @@ -1,27 +1,6 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT 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'; @@ -49,6 +28,7 @@ import { Main } from './main/main'; import { ReportDetails } from './main/report_details/report_details'; import { ReportDefinitionDetails } from './main/report_definition_details/report_definition_details'; import { EditReportDefinition } from './report_definitions/edit/edit_report_definition'; +import { i18n } from '@osd/i18n'; export interface CoreInterface { http: CoreStart['http']; @@ -93,7 +73,10 @@ export const ReportsDashboardsApp = ({ path="/report_details/:reportId" render={(props) => ( ( ( ( (
- window.location.href.match( - /(\b[0-9a-f]{8}\b-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-\b[0-9a-f]{12}\b)\?/ - ); +// try to match uuid and user entered custom-id followed by '?' in URL, which would be the saved search id for discover URL +// custom id example: v1s-f00-b4r1-01, Filebeat-Apache-Dashboard-ecs, +const getUuidFromUrl = () => window.location.href.match(/([0-9a-zA-Z-]+)\?/); const isDiscover = () => window.location.href.includes('discover'); // open Download drop-down @@ -280,7 +256,11 @@ function locationHashChanged() { return; } const menuItem = document.createElement('div'); - menuItem.innerHTML = getMenuItem('Reporting'); + menuItem.innerHTML = getMenuItem( + i18n.translate('opensearch.reports.menu.name', { + defaultMessage: 'Reporting', + }) + ); navMenu[0].insertBefore(menuItem.children[0], navMenu[0].lastChild); } catch (e) { console.log(e); diff --git a/dashboards-reports/public/components/context_menu/context_menu_helpers.js b/dashboards-reports/public/components/context_menu/context_menu_helpers.js index abd828c7..3ebe5d76 100644 --- a/dashboards-reports/public/components/context_menu/context_menu_helpers.js +++ b/dashboards-reports/public/components/context_menu/context_menu_helpers.js @@ -1,27 +1,6 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT 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'; @@ -48,15 +27,13 @@ export const getTimeFieldsFromUrl = () => { const url = unhashUrl(window.location.href); let [, fromDateString, toDateString] = url.match(timeRangeMatcher); - fromDateString = fromDateString.replace(/[']+/g, ''); + fromDateString = decodeURIComponent(fromDateString.replace(/[']+/g, '')); // convert time range to from date format in case time range is relative const fromDateFormat = dateMath.parse(fromDateString); - toDateString = toDateString.replace(/[']+/g, ''); - const toDateFormat = dateMath.parse(toDateString); + toDateString = decodeURIComponent(toDateString.replace(/[']+/g, '')); + const toDateFormat = dateMath.parse(toDateString, { roundUp: true }); - const timeDuration = moment.duration( - dateMath.parse(toDateString).diff(dateMath.parse(fromDateString)) - ); + const timeDuration = moment.duration(toDateFormat.diff(fromDateFormat)); return { time_from: fromDateFormat, @@ -71,11 +48,11 @@ export const contextMenuCreateReportDefinition = (baseURI) => { const timeRanges = getTimeFieldsFromUrl(); // check report source - if (baseURI.includes('dashboard')) { + if (/\/app\/dashboards/.test(baseURI)) { reportSource = 'dashboard:'; - } else if (baseURI.includes('visualize')) { + } else if (/\/app\/visualize/.test(baseURI)) { reportSource = 'visualize:'; - } else if (baseURI.includes('discover')) { + } else if (/\/app\/discover/.test(baseURI)) { reportSource = 'discover:'; } reportSource += reportSourceId.toString(); @@ -141,22 +118,23 @@ export const replaceQueryURL = (pageUrl) => { // we unhash the url in case OpenSearch Dashboards advanced UI setting 'state:storeInSessionStorage' is turned on const unhashedUrl = new URL(unhashUrl(pageUrl)); let queryUrl = unhashedUrl.pathname + unhashedUrl.hash; - let [, fromDateString, toDateString] = queryUrl.match(timeRangeMatcher); - fromDateString = fromDateString.replace(/[']+/g, ''); + let [, fromDateStringMatch, toDateStringMatch] = + queryUrl.match(timeRangeMatcher); + const fromDateString = decodeURIComponent(fromDateStringMatch.replace(/[']+/g, '')); // convert time range to from date format in case time range is relative const fromDateFormat = dateMath.parse(fromDateString); - toDateString = toDateString.replace(/[']+/g, ''); - const toDateFormat = dateMath.parse(toDateString); + const toDateString = decodeURIComponent(toDateStringMatch.replace(/[']+/g, '')); + const toDateFormat = dateMath.parse(toDateString, { roundUp: true }); // replace to and from dates with absolute date queryUrl = queryUrl.replace( - fromDateString, + fromDateStringMatch, "'" + fromDateFormat.toISOString() + "'" ); queryUrl = queryUrl.replace( - toDateString + '))', - "'" + toDateFormat.toISOString() + "'))" + toDateStringMatch, + "'" + toDateFormat.toISOString() + "'" ); return queryUrl; }; diff --git a/dashboards-reports/public/components/context_menu/context_menu_ui.js b/dashboards-reports/public/components/context_menu/context_menu_ui.js index 791f8891..0c99641f 100644 --- a/dashboards-reports/public/components/context_menu/context_menu_ui.js +++ b/dashboards-reports/public/components/context_menu/context_menu_ui.js @@ -1,28 +1,9 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. */ -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT 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 getMenuItem = (name) => { return ` @@ -39,8 +20,14 @@ export const popoverMenu = (savedObjectAvailable) => { const button = savedObjectAvailable ? 'button' : 'button disabled'; const popoverHeight = savedObjectAvailable ? '395px' : '380px'; const message = savedObjectAvailable - ? `Files can take a minute or two to generate depending on the size of your source data.` - : `Save this Visualization/Dashboard to enable PDF/PNG reports.`; + ? i18n.translate('opensearch.reports.menu.visual.waitPrompt', { + defaultMessage: + 'Files can take a minute or two to generate depending on the size of your source data.', + }) + : i18n.translate('opensearch.reports.menu.visual.savePrompt', { + defaultMessage: + 'Save this Visualization/Dashboard to enable PDF/PNG reports.', + }); const arrowRight = '100px'; const popoverRight = '77px'; @@ -60,7 +47,10 @@ export const popoverMenu = (savedObjectAvailable) => {
- Generate report + ${i18n.translate( + 'opensearch.reports.menu.visual.generateReport', + { defaultMessage: 'Generate report' } + )}
@@ -73,34 +63,48 @@ export const popoverMenu = (savedObjectAvailable) => { <${button} class="${buttonClass}" type="button" data-test-subj="downloadPanel-GeneratePDF" id="generatePDF"> - Download PDF + ${i18n.translate( + 'opensearch.reports.menu.visual.downloadPdf', + { defaultMessage: 'Download PDF' } + )} <${button} class="${buttonClass}" type="button" data-test-subj="downloadPanel-GeneratePNG" id="generatePNG"> - Download PNG + ${i18n.translate( + 'opensearch.reports.menu.visual.downloadPng', + { defaultMessage: 'Download PNG' } + )}
- Schedule and share + ${i18n.translate( + 'opensearch.reports.menu.visual.scheduleAndShare', + { defaultMessage: 'Schedule and share' } + )}
<${button} class="${buttonClass}" type="button" data-test-subj="downloadPanel-GeneratePDF" id="createReportDefinition"> - Create report definition + ${i18n.translate( + 'opensearch.reports.menu.visual.createReportDefinition', + { defaultMessage: 'Create report definition' } + )}
- View + ${i18n.translate('opensearch.reports.menu.visual.view', { + defaultMessage: 'View', + })}
@@ -111,7 +115,10 @@ export const popoverMenu = (savedObjectAvailable) => { - View reports + ${i18n.translate( + 'opensearch.reports.menu.visual.viewReports', + { defaultMessage: 'View reports' } + )} @@ -133,8 +140,13 @@ export const popoverMenuDiscover = (savedObjectAvailable) => { const button = savedObjectAvailable ? 'button' : 'button disabled'; const popoverHeight = savedObjectAvailable ? '354px' : '322px'; const message = savedObjectAvailable - ? `Files can take a minute or two to generate depending on the size of your source data.` - : `Save this search to enable CSV reports.`; + ? i18n.translate('opensearch.reports.menu.csv.waitPrompt', { + defaultMessage: + 'Files can take a minute or two to generate depending on the size of your source data.', + }) + : i18n.translate('opensearch.reports.menu.csv.savePrompt', { + defaultMessage: 'Save this search to enable CSV reports.', + }); const arrowRight = '60px'; const popoverRight = '77px'; @@ -153,7 +165,12 @@ export const popoverMenuDiscover = (savedObjectAvailable) => {
- Generate and download + ${i18n.translate( + 'opensearch.reports.menu.csv.generateReport', + { + defaultMessage: 'Generate and download', + } + )}
@@ -165,28 +182,41 @@ export const popoverMenuDiscover = (savedObjectAvailable) => { <${button} class="${buttonClass}" type="button" data-test-subj="downloadPanel-GeneratePDF" id="generateCSV"> - Generate CSV + ${i18n.translate( + 'opensearch.reports.menu.csv.generateCsv', + { defaultMessage: 'Generate CSV' } + )}
- Schedule and share + ${i18n.translate( + 'opensearch.reports.menu.scheduleAndShare', + { + defaultMessage: 'Schedule and share', + } + )}
<${button} class="${buttonClass}" type="button" data-test-subj="downloadPanel-GeneratePDF" id="createReportDefinition"> - Create report definition + ${i18n.translate( + 'opensearch.reports.menu.createReportDefinition', + { defaultMessage: 'Create report definition' } + )}
- View + ${i18n.translate('opensearch.reports.menu.csv.view', { + defaultMessage: 'View', + })}
@@ -197,7 +227,10 @@ export const popoverMenuDiscover = (savedObjectAvailable) => { - View reports + ${i18n.translate( + 'opensearch.reports.menu.csv.viewReports', + { defaultMessage: 'View reports' } + )} @@ -214,12 +247,18 @@ export const popoverMenuDiscover = (savedObjectAvailable) => { export const permissionsMissingOnGeneration = () => { return `
-

A new notification appears

+

${i18n.translate( + 'opensearch.reports.menu.newNotificationAppears', + { defaultMessage: 'A new notification appears' } + )}

- Error generating report. + ${i18n.translate( + 'opensearch.reports.menu.errorGeneratingReport', + { defaultMessage: 'Error generating report.' } + )}
-

Insufficient permissions. Reach out to your OpenSearch Dashboards administrator.

+

${i18n.translate('opensearch.reports.menu.insufficientPermissions', { + defaultMessage: + 'Insufficient permissions. Reach out to your OpenSearch Dashboards administrator.', + })}

`; @@ -244,7 +286,10 @@ export const reportGenerationSuccess = () => { role="img" aria-hidden="true"> - Successfully generated report. + ${i18n.translate( + 'opensearch.reports.menu.successfullyGenerated', + { defaultMessage: 'Successfully generated report.' } + )}
-

View +

View Reports.

+ href="reports-dashboards#/" rel="noreferrer">${i18n.translate( + 'opensearch.reports.menu.button.reports', + { defaultMessage: 'Reports' } + )}.

`; }; export const reportGenerationFailure = ( - title = 'Download error', - text = 'There was an error generating this report.' + title = i18n.translate('opensearch.reports.menu.downloadError', { + defaultMessage: 'Download error', + }), + text = i18n.translate('opensearch.reports.menu.errorGeneratingThisReport', { + defaultMessage: 'There was an error generating this report.', + }) ) => { return `
@@ -308,14 +360,26 @@ export const reportGenerationInProgressModal = () => {
-

Generating report

+

${i18n.translate( + 'opensearch.reports.menu.progress.generatingReport', + { defaultMessage: 'Generating report' } + )}

-
Preparing your file for download.
-
You can close this dialog while we continue in the background.
+
${i18n.translate( + 'opensearch.reports.menu.progress.preparingYourFile', + { defaultMessage: 'Preparing your file for download.' } + )}
+
${i18n.translate( + 'opensearch.reports.menu.progress.youCanClose', + { + defaultMessage: + 'You can close this dialog while we continue in the background.', + } + )}
diff --git a/dashboards-reports/public/components/main/__tests__/__snapshots__/main.test.tsx.snap b/dashboards-reports/public/components/main/__tests__/__snapshots__/main.test.tsx.snap index aa427ad3..55d94177 100644 --- a/dashboards-reports/public/components/main/__tests__/__snapshots__/main.test.tsx.snap +++ b/dashboards-reports/public/components/main/__tests__/__snapshots__/main.test.tsx.snap @@ -427,11 +427,11 @@ exports[`
panel render component 1`] = ` - Get started with OpenSearch Dashboards reporting + Get started with OpenSearch Dashboards reporting panel render component 1`] = ` - Get started with OpenSearch Dashboards reporting + Get started with OpenSearch Dashboards reporting panel render component after create success 1`] = ` - Get started with OpenSearch Dashboards reporting + Get started with OpenSearch Dashboards reporting panel render component after create success 1`] = ` - Get started with OpenSearch Dashboards reporting + Get started with OpenSearch Dashboards reporting panel render component after delete success 1`] = ` - Get started with OpenSearch Dashboards reporting + Get started with OpenSearch Dashboards reporting panel render component after delete success 1`] = ` - Get started with OpenSearch Dashboards reporting + Get started with OpenSearch Dashboards reporting panel render component after edit success 1`] = ` - Get started with OpenSearch Dashboards reporting + Get started with OpenSearch Dashboards reporting panel render component after edit success 1`] = ` - Get started with OpenSearch Dashboards reporting + Get started with OpenSearch Dashboards reporting panel render empty table 1`] = ` - Get started with OpenSearch Dashboards reporting + Get started with OpenSearch Dashboards reporting panel render empty component 1`] = ` - Get started with OpenSearch Dashboards reporting + Get started with OpenSearch Dashboards reporting

Generating report

+

+ {i18n.translate( + 'opensearch.reports.loading.generatingReport', + { defaultMessage: 'Generating report' } + )} +

- Preparing your file for download. - You can close this dialog while we continue in the background. + {i18n.translate('opensearch.reports.loading.preparingYourFile', { + defaultMessage: 'Preparing your file for download.', + })} + + + {i18n.translate('opensearch.reports.loading.youCanClose', { + defaultMessage: + 'You can close this dialog while we continue in the background.', + })} @@ -84,7 +74,11 @@ export function GenerateReportLoadingModal(props: { setShowLoading: any; }) { - Close + + {i18n.translate('opensearch.reports.loading.close', { + defaultMessage: 'Close', + })} + @@ -92,4 +86,4 @@ export function GenerateReportLoadingModal(props: { setShowLoading: any; }) {
); -}; \ No newline at end of file +} diff --git a/dashboards-reports/public/components/main/main.tsx b/dashboards-reports/public/components/main/main.tsx index 0b3cc203..bfbb8cb9 100644 --- a/dashboards-reports/public/components/main/main.tsx +++ b/dashboards-reports/public/components/main/main.tsx @@ -1,30 +1,10 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT 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, { Fragment, useState, useEffect } from 'react'; +import { i18n } from '@osd/i18n'; import { EuiFlexGroup, EuiFlexItem, @@ -80,7 +60,10 @@ export function Main(props) { ); } else if (errorType === 'API') { toast = { - title: 'Error generating reports table.', + title: i18n.translate( + 'opensearch.reports.main.errorGeneratingReportsTable.', + { defaultMessage: 'Error generating reports table.' } + ), color: 'danger', iconType: 'alert', id: 'reportsTableErrorToast', @@ -101,7 +84,10 @@ export function Main(props) { ); } else if (errorType === 'API') { toast = { - title: 'Error generating report definitions table.', + title: i18n.translate( + 'opensearch.reports.main.errorGeneratingReportDefinitionsTable.', + { defaultMessage: 'Error generating report definitions table.' } + ), color: 'danger', iconType: 'alert', id: 'reportDefinitionsTableErrorToast', @@ -114,7 +100,12 @@ export function Main(props) { addReportDefinitionsTableErrorToastHandler(errorType); }; - const addErrorOnDemandDownloadToastHandler = (title = 'Error downloading report.', text = '') => { + const addErrorOnDemandDownloadToastHandler = ( + title = i18n.translate('opensearch.reports.main.errorDownloadingReport', { + defaultMessage: 'Error downloading report.', + }), + text = '' + ) => { const errorToast = { title, text, @@ -131,7 +122,10 @@ export function Main(props) { const addSuccessOnDemandDownloadToastHandler = () => { const successToast = { - title: 'Successfully downloaded report.', + title: i18n.translate( + 'opensearch.reports.main.successfullyDownloadedReport', + { defaultMessage: 'Successfully downloaded report.' } + ), color: 'success', iconType: 'check', id: 'onDemandDownloadSuccessToast', @@ -145,7 +139,10 @@ export function Main(props) { const addCreateReportDefinitionSuccessToastHandler = () => { const successToast = { - title: 'Successfully created report definition.', + title: i18n.translate( + 'opensearch.reports.main.successfullyCreatedReportDefinition', + { defaultMessage: 'Successfully created report definition.' } + ), color: 'success', iconType: 'check', id: 'createReportDefinitionSuccessToast', @@ -159,7 +156,10 @@ export function Main(props) { const addEditReportDefinitionSuccessToastHandler = () => { const successToast = { - title: 'Successfully updated report definition.', + title: i18n.translate( + 'opensearch.reports.main.successfullyUpdatedReportDefinition', + { defaultMessage: 'Successfully updated report definition.' } + ), color: 'success', iconType: 'check', id: 'editReportDefinitionSuccessToast', @@ -173,17 +173,20 @@ export function Main(props) { const addDeleteReportDefinitionSuccessToastHandler = () => { const successToast = { - title: 'Successfully deleted report definition.', + title: i18n.translate( + 'opensearch.reports.main.successfullyDeletedReportDefinition', + { defaultMessage: 'Successfully deleted report definition.' } + ), color: 'success', iconType: 'check', - id: 'deleteReportDefinitionSuccessToast' + id: 'deleteReportDefinitionSuccessToast', }; setToasts(toasts.concat(successToast)); - } + }; const handleDeleteReportDefinitionSuccessToast = () => { addDeleteReportDefinitionSuccessToastHandler(); - } + }; const removeToast = (removedToast) => { setToasts(toasts.filter((toast) => toast.id !== removedToast.id)); @@ -197,7 +200,9 @@ export function Main(props) { useEffect(() => { props.setBreadcrumbs([ { - text: 'Reporting', + text: i18n.translate('opensearch.reports.main.title.reporting', { + defaultMessage: 'Reporting', + }), href: '#', }, ]); @@ -272,18 +277,23 @@ export function Main(props) {

- Reports{' '} + {i18n.translate('opensearch.reports.main.title.reports', { + defaultMessage: 'Reports', + })}{' '}

({reportsTableContent.length})

- - Refresh + {i18n.translate( + 'opensearch.reports.main.reports.button.refresh', + { defaultMessage: 'Refresh' } + )} @@ -303,7 +313,10 @@ export function Main(props) {

- Report definitions + {i18n.translate( + 'opensearch.reports.main.title.reportDefinitions', + { defaultMessage: 'Report definitions' } + )}

{' '} ({reportDefinitionsTableContent.length}) @@ -312,12 +325,15 @@ export function Main(props) { - - Refresh + {i18n.translate( + 'opensearch.reports.main.reportDefinitions.button.refresh', + { defaultMessage: 'Refresh' } + )} @@ -328,7 +344,10 @@ export function Main(props) { }} id={'createReportHomepageButton'} > - Create + {i18n.translate( + 'opensearch.reports.main.reportDefinitions.button.create', + { defaultMessage: 'Create' } + )} diff --git a/dashboards-reports/public/components/main/main_utils.tsx b/dashboards-reports/public/components/main/main_utils.tsx index b84a1465..44066c02 100644 --- a/dashboards-reports/public/components/main/main_utils.tsx +++ b/dashboards-reports/public/components/main/main_utils.tsx @@ -1,39 +1,37 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT 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 'babel-polyfill'; +import { i18n } from '@osd/i18n'; import { HttpFetchOptions, HttpSetup } from '../../../../../src/core/public'; +import { uiSettingsService } from '../utils/settings_service'; + +export const getAvailableNotificationsChannels = (configList: any) => { + let availableChannels = []; + for (let i = 0; i < configList.length; ++i) { + let channelEntry = {}; + channelEntry = { + label: configList[i].config.name, + id: configList[i].config_id + } + availableChannels.push(channelEntry); + } + return availableChannels; +} + +type fileFormatsOptions = { + [key: string]: string +} -export const fileFormatsUpper = { +export const fileFormatsUpper: fileFormatsOptions = { csv: 'CSV', pdf: 'PDF', png: 'PNG', }; -export const humanReadableDate = (date) => { +export const humanReadableDate = (date: string | number | Date) => { let readableDate = new Date(date); return ( readableDate.toDateString() + ' @ ' + readableDate.toLocaleTimeString() @@ -54,7 +52,7 @@ export const getFileFormatPrefix = (fileFormat: string) => { return fileFormatPrefix; }; -export const addReportsTableContent = (data) => { +export const addReportsTableContent = (data: string | any[]) => { let reportsTableItems = []; for (let index = 0; index < data.length; ++index) { let item = data[index]; @@ -109,7 +107,7 @@ export const addReportDefinitionsTableContent = (data: any) => { return reportDefinitionsTableItems; }; -export const removeDuplicatePdfFileFormat = (filename) => { +export const removeDuplicatePdfFileFormat = (filename: string) => { return filename.substring(0, filename.length - 4); }; @@ -152,7 +150,7 @@ export const readStreamToFile = async ( }; export const generateReportFromDefinitionId = async ( - reportDefinitionId, + reportDefinitionId: string, httpClient: HttpSetup ) => { let status = false; @@ -162,7 +160,7 @@ export const generateReportFromDefinitionId = async ( headers: { 'Content-Type': 'application/json', }, - query: { timezone: Intl.DateTimeFormat().resolvedOptions().timeZone }, + query: uiSettingsService.getSearchParams(), }) .then(async (response: any) => { // for emailing a report, this API response doesn't have response body @@ -187,7 +185,7 @@ export const generateReportFromDefinitionId = async ( }; export const generateReportById = async ( - reportId, + reportId: string, httpClient: HttpSetup, handleSuccessToast, handleErrorToast, @@ -195,7 +193,7 @@ export const generateReportById = async ( ) => { await httpClient .get(`../api/reporting/generateReport/${reportId}`, { - query: { timezone: Intl.DateTimeFormat().resolvedOptions().timeZone }, + query: uiSettingsService.getSearchParams(), }) .then(async (response) => { //TODO: duplicate code, extract to be a function that can reuse. e.g. handleResponse(response) @@ -211,8 +209,15 @@ export const generateReportById = async ( handlePermissionsMissingToast(); } else if (error.body.statusCode === 503) { handleErrorToast( - 'Error generating report.', - `Timed out generating report ID ${reportId}. Try again later.` + i18n.translate('opensearch.reports.utils.errorTitle', { + defaultMessage: 'Error generating report.', + }), + i18n.translate('opensearch.reports.utils.errorText', { + defaultMessage: + 'Timed out generating report ID {reportId}. Try again later.', + values: { reportId: reportId }, + description: 'Error number toast', + }) ); } else { handleErrorToast(); diff --git a/dashboards-reports/public/components/main/report_definition_details/__tests__/__snapshots__/report_definition_details.test.tsx.snap b/dashboards-reports/public/components/main/report_definition_details/__tests__/__snapshots__/report_definition_details.test.tsx.snap index 690a1d28..21653179 100644 --- a/dashboards-reports/public/components/main/report_definition_details/__tests__/__snapshots__/report_definition_details.test.tsx.snap +++ b/dashboards-reports/public/components/main/report_definition_details/__tests__/__snapshots__/report_definition_details.test.tsx.snap @@ -199,10 +199,13 @@ exports[` panel render 5 hours recurring definition d > + > + +

@@ -220,7 +223,7 @@ exports[` panel render 5 hours recurring definition d
- Last undefined + Last
@@ -240,7 +243,7 @@ exports[` panel render 5 hours recurring definition d > -
-
- - -
- Select or add users -
- - -
-
-
- -
-
-
-
- -
-
-
-
-
-
-
- -
-
-
-
-
- - -
-
    -
  • - -
  • -
  • - -
  • -
  • - -
  • -
  • - -
  • -
-
    -
  • - -
  • -
  • - -
  • -
  • - -
  • -
-
-
-
-
+

+

Dashboard panel: [eCommerce] Top Selling Products[eCommerce] Top Selling Products

Lace-up boots - blackAnkle boots - blackBoots - blackPrint T-shirt - blackJumper - blackLace-up boots - resin coffeeDress with Defined Waist

Dashboard panel: [eCommerce] Orders[eCommerce] Orders

+
+
+
+ + +
1–50 of 1055
+
+
+
+
+ + + + + +
Timecategoryskutaxful_total_pricetotal_quantity
+ +Jun 23, 2021 @ 23:56:10.000
Women's Accessories, Women's Clothing
ZO0301903019, ZO0049800498
$43.98
2
+ +Jun 23, 2021 @ 23:41:46.000
Men's Clothing
ZO0128701287, ZO0577005770
$49.98
2
+ +Jun 23, 2021 @ 23:37:26.000
Men's Clothing
ZO0558005580, ZO0276502765
$18.98
2
+ +Jun 23, 2021 @ 23:33:07.000
Women's Accessories, Women's Clothing
ZO0358803588, ZO0179601796
$91.98
2
+ +Jun 23, 2021 @ 23:21:36.000
Men's Clothing
ZO0543605436, ZO0425604256
$89.98
2
+ +Jun 23, 2021 @ 23:21:36.000
Men's Clothing
ZO0629306293, ZO0578405784
$37.98
2
+ +Jun 23, 2021 @ 22:51:22.000
Men's Clothing
ZO0291602916, ZO0292302923
$221.98
2
+ +Jun 23, 2021 @ 22:48:29.000
Women's Clothing, Women's Accessories
ZO0262902629, ZO0358703587
$83.98
2
+ +Jun 23, 2021 @ 22:29:46.000
Men's Clothing
ZO0474604746, ZO0111701117
$36.98
2
+ +Jun 23, 2021 @ 22:12:29.000
Men's Accessories, Men's Shoes
ZO0598005980, ZO0681706817
$75.98
2
+ +Jun 23, 2021 @ 22:11:02.000
Women's Clothing, Women's Shoes
ZO0221402214, ZO0677006770
$103.98
2
+ +Jun 23, 2021 @ 21:59:31.000
Men's Clothing, Men's Shoes
ZO0579905799, ZO0386403864
$55.98
2
+ +Jun 23, 2021 @ 21:55:12.000
Men's Clothing
ZO0474204742, ZO0574005740
$31.98
2
+ +Jun 23, 2021 @ 21:48:00.000
Men's Clothing, Men's Shoes
ZO0580905809, ZO0507105071
$41.98
2
+ +Jun 23, 2021 @ 21:23:31.000
Women's Clothing
ZO0217002170, ZO0164201642
$27.98
2
+ +Jun 23, 2021 @ 21:17:46.000
Women's Shoes, Women's Clothing
ZO0368003680, ZO0173001730
$63.98
2
+ +Jun 23, 2021 @ 21:12:00.000
Men's Clothing
ZO0437404374, ZO0293102931
$24.98
2
+ +Jun 23, 2021 @ 21:09:07.000
Women's Accessories, Women's Shoes
ZO0085300853, ZO0678506785
$95.98
2
+ +Jun 23, 2021 @ 20:56:10.000
Women's Clothing, Women's Shoes
ZO0638706387, ZO0677206772
$92.98
2
+ +Jun 23, 2021 @ 20:41:46.000
Women's Shoes, Women's Clothing
ZO0678406784, ZO0712707127
$99.98
2
+ +Jun 23, 2021 @ 20:37:26.000
Women's Clothing
ZO0708107081, ZO0500905009
$42.98
2
+ +Jun 23, 2021 @ 20:33:07.000
Women's Shoes
ZO0250002500, ZO0675406754
$159.98
2
+ +Jun 23, 2021 @ 20:24:29.000
Women's Accessories
ZO0205602056, ZO0356903569
$46.98
2
+ +Jun 23, 2021 @ 20:00:00.000
Men's Clothing
ZO0441304413, ZO0561205612
$22.98
2
+ +Jun 23, 2021 @ 19:55:41.000
Men's Shoes, Men's Clothing
ZO0691306913, ZO0275502755
$139.98
2
+ +Jun 23, 2021 @ 19:32:38.000
Men's Clothing
ZO0295102951, ZO0453304533, ZO0588305883, ZO0411304113
$82.96
4
+ +Jun 23, 2021 @ 19:19:41.000
Men's Clothing, Men's Accessories
ZO0296402964, ZO0316203162
$42.98
2
+ +Jun 23, 2021 @ 19:18:14.000
Men's Clothing, Men's Shoes
ZO0588005880, ZO0571805718, ZO0403504035, ZO0457504575
$108.96
4
+ +Jun 23, 2021 @ 19:15:22.000
Women's Clothing
ZO0051800518, ZO0333303333
$92.98
2
+ +Jun 23, 2021 @ 19:06:43.000
Men's Clothing, Men's Shoes
ZO0431904319, ZO0683606836
$149.98
2
+ +Jun 23, 2021 @ 19:05:17.000
Women's Clothing, Women's Accessories
ZO0181701817, ZO0095300953
$45.98
2
+ +Jun 23, 2021 @ 18:58:05.000
Men's Clothing
ZO0620606206, ZO0454204542
$70.98
2
+ +Jun 23, 2021 @ 18:40:48.000
Women's Shoes, Women's Accessories
ZO0670906709, ZO0211302113
$128.98
2
+ +Jun 23, 2021 @ 18:30:43.000
Women's Clothing
ZO0263002630, ZO0497904979
$93.98
2
+ +Jun 23, 2021 @ 18:27:50.000
Men's Clothing, Men's Shoes
ZO0300603006, ZO0123501235, ZO0399803998, ZO0624206242
$160.96
4
+ +Jun 23, 2021 @ 18:13:26.000
Women's Accessories, Women's Shoes
ZO0696806968, ZO0020700207
$66.98
2
+ +Jun 23, 2021 @ 18:03:22.000
Women's Clothing, Women's Accessories
ZO0271302713, ZO0703207032
$74.98
2
+ +Jun 23, 2021 @ 18:00:29.000
Men's Shoes, Men's Accessories
ZO0403504035, ZO0608606086
$91.98
2
+ +Jun 23, 2021 @ 17:46:05.000
Men's Shoes, Men's Clothing
ZO0521405214, ZO0585905859
$64.98
2
+ +Jun 23, 2021 @ 17:31:41.000
Women's Clothing
ZO0171101711, ZO0048400484
$74.98
2
+ +Jun 23, 2021 @ 17:28:48.000
Women's Accessories
ZO0209302093, ZO0087400874
$47.98
2
+ +Jun 23, 2021 @ 17:27:22.000
Women's Accessories
ZO0096100961, ZO0091000910
$53.98
2
+ +Jun 23, 2021 @ 17:04:19.000
Men's Clothing
ZO0284802848, ZO0581605816
$49.98
2
+ +Jun 23, 2021 @ 17:02:53.000
Men's Shoes
ZO0401004010, ZO0257802578
$119.98
2
+ +Jun 23, 2021 @ 16:51:22.000
Men's Shoes, Men's Accessories
ZO0520705207, ZO0397603976, ZO0395003950, ZO0702307023
$207.96
4
+ +Jun 23, 2021 @ 16:49:55.000
Women's Shoes, Women's Clothing
ZO0364403644, ZO0150401504
$116.98
2
+ +Jun 23, 2021 @ 16:31:12.000
Men's Clothing, Women's Accessories
ZO0554505545, ZO0703407034
$96.98
2
+ +Jun 23, 2021 @ 16:25:26.000
Women's Clothing
ZO0263002630
$51.99
1
+ +Jun 23, 2021 @ 16:02:24.000
Women's Shoes
ZO0365203652, ZO0383303833
$126.98
2
+ +Jun 23, 2021 @ 16:00:58.000
Women's Clothing
ZO0179701797, ZO0496004960
$42.98
2
+
+ + +
+ + + +
+ +
+ + +
\ No newline at end of file diff --git a/dashboards-reports/server/routes/utils/__tests__/metricHelper.test.ts b/dashboards-reports/server/routes/utils/__tests__/metricHelper.test.ts index d598dc87..3953a724 100644 --- a/dashboards-reports/server/routes/utils/__tests__/metricHelper.test.ts +++ b/dashboards-reports/server/routes/utils/__tests__/metricHelper.test.ts @@ -1,27 +1,6 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT 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 { checkErrorType } from '../helpers'; diff --git a/dashboards-reports/server/routes/utils/__tests__/savedSearchReportHelper.test.ts b/dashboards-reports/server/routes/utils/__tests__/savedSearchReportHelper.test.ts index f1ca9b8a..7009dda3 100644 --- a/dashboards-reports/server/routes/utils/__tests__/savedSearchReportHelper.test.ts +++ b/dashboards-reports/server/routes/utils/__tests__/savedSearchReportHelper.test.ts @@ -1,32 +1,13 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT 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 'regenerator-runtime/runtime'; import { createSavedSearchReport } from '../savedSearchReportHelper'; import { reportSchema } from '../../../model'; +import { mockLogger } from '../../../../test/__mocks__/loggerMock'; +import _ from 'lodash'; /** * The mock and sample input for saved search export function. @@ -51,10 +32,10 @@ const input = { }, }, delivery: { - delivery_type: 'OpenSearch Dashboards user', - delivery_params: { - opensearch_dashboards_recipients: [], - }, + configIds: [], + title: 'title', + textDescription: 'text description', + htmlDescription: 'html description', }, trigger: { trigger_type: 'On demand', @@ -62,6 +43,8 @@ const input = { }, }; +const mockDateFormat = 'MM/DD/YYYY h:mm:ss.SSS a'; + /** * Max result window size in OpenSearch index settings. */ @@ -78,7 +61,11 @@ describe('test create saved search report', () => { const client = mockOpenSearchClient(hits); const { timeCreated, fileName } = await createSavedSearchReport( input, - client + client, + mockDateFormat, + ',', + undefined, + mockLogger ); expect(fileName).toContain(`test report table order_`); }, 20000); @@ -86,14 +73,22 @@ describe('test create saved search report', () => { test('create report with expected file name extension', async () => { const csvReport = await createSavedSearchReport( input, - mockOpenSearchClient([]) + mockOpenSearchClient([]), + mockDateFormat, + ',', + undefined, + mockLogger ); expect(csvReport.fileName).toContain('.csv'); input.report_definition.report_params.core_params.report_format = 'xlsx'; const xlsxReport = await createSavedSearchReport( input, - mockOpenSearchClient([]) + mockOpenSearchClient([]), + mockDateFormat, + ',', + undefined, + mockLogger ); expect(xlsxReport.fileName).toContain('.xlsx'); }, 20000); @@ -101,7 +96,14 @@ describe('test create saved search report', () => { test('create report for empty data set', async () => { const hits: Array<{ _source: any }> = []; const client = mockOpenSearchClient(hits); - const { dataUrl } = await createSavedSearchReport(input, client); + const { dataUrl } = await createSavedSearchReport( + input, + client, + mockDateFormat, + ',', + undefined, + mockLogger + ); expect(dataUrl).toEqual(''); }, 20000); @@ -114,7 +116,14 @@ describe('test create saved search report', () => { hit({ category: 'c5', customer_gender: 'Male' }), ]; const client = mockOpenSearchClient(hits); - const { dataUrl } = await createSavedSearchReport(input, client); + const { dataUrl } = await createSavedSearchReport( + input, + client, + mockDateFormat, + ',', + undefined, + mockLogger + ); expect(dataUrl).toEqual( 'category,customer_gender\n' + @@ -141,7 +150,14 @@ describe('test create saved search report', () => { hit({ category: 'c11', customer_gender: 'Male' }), ]; const client = mockOpenSearchClient(hits); - const { dataUrl } = await createSavedSearchReport(input, client); + const { dataUrl } = await createSavedSearchReport( + input, + client, + mockDateFormat, + ',', + undefined, + mockLogger + ); expect(dataUrl).toEqual( 'category,customer_gender\n' + @@ -171,7 +187,14 @@ describe('test create saved search report', () => { hit({ category: 'c5', customer_gender: 'Male' }), ]; const client = mockOpenSearchClient(hits); - const { dataUrl } = await createSavedSearchReport(input, client); + const { dataUrl } = await createSavedSearchReport( + input, + client, + mockDateFormat, + ',', + undefined, + mockLogger + ); expect(dataUrl).toEqual('category,customer_gender\n' + 'c1,Male'); }, 20000); @@ -193,7 +216,14 @@ describe('test create saved search report', () => { hit({ category: 'c10', customer_gender: 'Female' }), ]; const client = mockOpenSearchClient(hits); - const { dataUrl } = await createSavedSearchReport(input, client); + const { dataUrl } = await createSavedSearchReport( + input, + client, + mockDateFormat, + ',', + undefined, + mockLogger + ); expect(dataUrl).toEqual( 'category,customer_gender\n' + @@ -219,7 +249,14 @@ describe('test create saved search report', () => { hit({ category: 'c6', customer_gender: 'Female' }), ]; const client = mockOpenSearchClient(hits); - const { dataUrl } = await createSavedSearchReport(input, client); + const { dataUrl } = await createSavedSearchReport( + input, + client, + mockDateFormat, + ',', + undefined, + mockLogger + ); expect(dataUrl).toEqual( 'category,customer_gender\n' + @@ -239,7 +276,14 @@ describe('test create saved search report', () => { hit({ category: ',,c3', customer_gender: 'Male,,,' }), ]; const client = mockOpenSearchClient(hits); - const { dataUrl } = await createSavedSearchReport(input, client); + const { dataUrl } = await createSavedSearchReport( + input, + client, + mockDateFormat, + ',', + undefined, + mockLogger + ); expect(dataUrl).toEqual( 'category,customer_gender\n' + @@ -249,6 +293,30 @@ describe('test create saved search report', () => { ); }, 20000); + test('create report for data set with comma and custom separator', async () => { + const hits = [ + hit({ category: ',c1', customer_gender: 'Ma,le' }), + hit({ category: 'c2,', customer_gender: 'M,ale' }), + hit({ category: ',,c3', customer_gender: 'Male,,,' }), + ]; + const client = mockOpenSearchClient(hits); + const { dataUrl } = await createSavedSearchReport( + input, + client, + mockDateFormat, + '|', + undefined, + mockLogger + ); + + expect(dataUrl).toEqual( + 'category|customer_gender\n' + + ',c1|Ma,le\n' + + 'c2,|M,ale\n' + + ',,c3|Male,,,' + ); + }, 20000); + test('create report for data set with nested fields', async () => { const hits = [ hit({ @@ -265,7 +333,14 @@ describe('test create saved search report', () => { hits, '"geoip.country_iso_code", "geoip.city_name", "geoip.location"' ); - const { dataUrl } = await createSavedSearchReport(input, client); + const { dataUrl } = await createSavedSearchReport( + input, + client, + mockDateFormat, + ',', + undefined, + mockLogger + ); expect(dataUrl).toEqual( 'geoip.country_iso_code,geoip.location.lon,geoip.location.lat,geoip.city_name\n' + @@ -283,7 +358,14 @@ describe('test create saved search report', () => { hit({ category: ',,,@c5', customer_gender: 'Male' }), ]; const client = mockOpenSearchClient(hits); - const { dataUrl } = await createSavedSearchReport(input, client); + const { dataUrl } = await createSavedSearchReport( + input, + client, + mockDateFormat, + ',', + undefined, + mockLogger + ); expect(dataUrl).toEqual( 'category,customer_gender\n' + @@ -307,7 +389,14 @@ describe('test create saved search report', () => { hit({ category: ',,,@c5', customer_gender: 'Male' }), ]; const client = mockOpenSearchClient(hits); - const { dataUrl } = await createSavedSearchReport(input, client); + const { dataUrl } = await createSavedSearchReport( + input, + client, + mockDateFormat, + ',', + undefined, + mockLogger + ); expect(dataUrl).toEqual( 'category,customer_gender\n' + @@ -327,13 +416,109 @@ test('create report for data set contains null field value', async () => { hit({ category: 'c3', customer_gender: null }), ]; const client = mockOpenSearchClient(hits); - const { dataUrl } = await createSavedSearchReport(input, client); + const { dataUrl } = await createSavedSearchReport( + input, + client, + mockDateFormat, + ',', + undefined, + mockLogger + ); expect(dataUrl).toEqual( 'category,customer_gender\n' + 'c1,Ma\n' + 'c2,le\n' + 'c3, ' ); }, 20000); +test('create report for data set with metadata fields', async () => { + const metadataFields = { _index: 'nameofindex', _id: 'someid' }; + let hits = [ + hit({ category: 'c1', customer_gender: 'Male' }), + hit({ category: 'c2', customer_gender: 'Male' }), + hit({ category: 'c3', customer_gender: 'Male' }), + hit({ category: 'c4', customer_gender: 'Male' }), + hit({ category: 'c5', customer_gender: 'Male' }), + ]; + hits.forEach((i) => { + _.merge(i, metadataFields); + }); + + const client = mockOpenSearchClient( + hits, + '"category", "customer_gender","_index","_id"' + ); + const { dataUrl } = await createSavedSearchReport( + input, + client, + mockDateFormat, + ',', + undefined, + mockLogger + ); + + expect(dataUrl).toEqual( + 'category,customer_gender,_index,_id\n' + + 'c1,Male,nameofindex,someid\n' + + 'c2,Male,nameofindex,someid\n' + + 'c3,Male,nameofindex,someid\n' + + 'c4,Male,nameofindex,someid\n' + + 'c5,Male,nameofindex,someid' + ); +}, 20000); + +test('create report with empty/one/multiple(list) date values', async () => { + const hits = [ + hit( + { category: 'c1', customer_gender: 'Ma', order_date: [] }, + { order_date: [] } + ), + hit( + { + category: 'c2', + customer_gender: 'le', + order_date: ['2021-12-16T14:04:55'], + }, + { order_date: ['2021-12-16T14:04:55'] } + ), + hit( + { + category: 'c3', + customer_gender: 'he', + order_date: ['2021-12-17T14:04:55', '2021-12-18T14:04:55'], + }, + { order_date: ['2021-12-17T14:04:55', '2021-12-18T14:04:55'] } + ), + hit( + { + category: 'c4', + customer_gender: 'te', + order_date: '2021-12-19T14:04:55', + }, + { order_date: ['2021-12-19T14:04:55'] } + ), + ]; + const client = mockOpenSearchClient( + hits, + '"category", "customer_gender", "order_date"' + ); + const { dataUrl } = await createSavedSearchReport( + input, + client, + mockDateFormat, + ',', + undefined, + mockLogger + ); + + expect(dataUrl).toEqual( + 'category,customer_gender,order_date\n' + + 'c1,Ma,[]\n' + + 'c2,le,"[""12/16/2021 2:04:55.000 pm""]"\n' + + 'c3,he,"[""12/17/2021 2:04:55.000 pm"",""12/18/2021 2:04:55.000 pm""]"\n' + + 'c4,te,12/19/2021 2:04:55.000 pm' + ); +}, 20000); + /** * Mock Elasticsearch client and return different mock objects based on endpoint and parameters. */ @@ -399,13 +584,13 @@ function mockSavedSearch(columns = '"category", "customer_gender"') { "columns": [ ${columns} ], "sort": [], "version": 1, - "opensearchDashboardsSavedObjectMeta": { - "searchSourceJSON": "{\\"highlightAll\\":true,\\"version\\":true,\\"query\\":{\\"query\\":\\"\\",\\"language\\":\\"kuery\\"},\\"indexRefName\\":\\"opensearchDashboardsSavedObjectMeta.searchSourceJSON.index\\",\\"filter\\":[]}" + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\\"highlightAll\\":true,\\"version\\":true,\\"query\\":{\\"query\\":\\"\\",\\"language\\":\\"kuery\\"},\\"indexRefName\\":\\"kibanaSavedObjectMeta.searchSourceJSON.index\\",\\"filter\\":[]}" } }, "references": [ { - "name": "opensearchDashboardsSavedObjectMeta.searchSourceJSON.index", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", "type": "index-pattern", "id": "ff959d40-b880-11e8-a6d9-e546fe2bba5f" } @@ -456,8 +641,9 @@ function mockIndexSettings() { `); } -function hit(kv: any) { +function hit(source_kv: any, fields_kv = {}) { return { - _source: kv, + _source: source_kv, + fields: fields_kv, }; } diff --git a/dashboards-reports/server/routes/utils/__tests__/visualReportHelper.test.ts b/dashboards-reports/server/routes/utils/__tests__/visualReportHelper.test.ts index 6bf6dc6e..81595979 100644 --- a/dashboards-reports/server/routes/utils/__tests__/visualReportHelper.test.ts +++ b/dashboards-reports/server/routes/utils/__tests__/visualReportHelper.test.ts @@ -1,45 +1,15 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT 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 'regenerator-runtime/runtime'; import { createVisualReport } from '../visual_report/visualReportHelper'; import { Logger } from '../../../../../../src/core/server'; import { ReportParamsSchemaType, reportSchema } from '../../../model'; +import { mockLogger } from '../../../../test/__mocks__/loggerMock'; -const mockLogger: Logger = { - info: jest.fn(), - trace: jest.fn(), - warn: jest.fn(), - debug: jest.fn(), - error: jest.fn(), - fatal: jest.fn(), - log: jest.fn(), - get: jest.fn(), -}; - +const mockHeader = { mockKey: 'mockValue' }; const input = { query_url: '/app/dashboards#/view/7adfa750-4c81-11e8-b3d7-01146121b73d', time_from: 1343576635300, @@ -59,10 +29,10 @@ const input = { }, }, delivery: { - delivery_type: 'OpenSearch Dashboards user', - delivery_params: { - opensearch_dashboards_recipients: [], - }, + configIds: [], + title: 'title', + textDescription: 'text description', + htmlDescription: 'html description', }, trigger: { trigger_type: 'On demand', @@ -70,8 +40,7 @@ const input = { }, }; -const queryUrl = - 'https://demo.elastic.co/app/kibana#/dashboard/welcome_dashboard'; +const mockHtmlPath = `file://${__dirname}/demo_dashboard.html`; describe('test create visual report', () => { test('create report with valid input', async () => { @@ -84,8 +53,9 @@ describe('test create visual report', () => { const reportParams = input.report_definition.report_params; const { dataUrl, fileName } = await createVisualReport( reportParams as ReportParamsSchemaType, - queryUrl, - mockLogger + mockHtmlPath, + mockLogger, + mockHeader ); expect(fileName).toContain(`${reportParams.report_name}`); expect(fileName).toContain('.png'); @@ -99,8 +69,9 @@ describe('test create visual report', () => { const { dataUrl, fileName } = await createVisualReport( reportParams as ReportParamsSchemaType, - queryUrl, - mockLogger + mockHtmlPath, + mockLogger, + mockHeader ); expect(fileName).toContain(`${reportParams.report_name}`); expect(fileName).toContain('.pdf'); diff --git a/dashboards-reports/server/routes/utils/constants.ts b/dashboards-reports/server/routes/utils/constants.ts index 1b424f95..dffb0cd1 100644 --- a/dashboards-reports/server/routes/utils/constants.ts +++ b/dashboards-reports/server/routes/utils/constants.ts @@ -1,30 +1,10 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT 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 { CountersType } from './types'; +import Showdown from 'showdown'; export enum FORMAT { pdf = 'pdf', @@ -60,11 +40,11 @@ export enum REPORT_TYPE { savedSearch = 'Saved search', dashboard = 'Dashboard', visualization = 'Visualization', - notebook = 'Notebook' + notebook = 'Notebook', } export enum DATA_REPORT_CONFIG { - excelDateFormat = 'MM/DD/YYYY h:mm:ss a', + excelDateFormat = 'MM/DD/YYYY h:mm:ss.SSS a', } export enum TRIGGER_TYPE { @@ -80,7 +60,7 @@ export enum DELIVERY_TYPE { export enum SELECTOR { dashboard = '#dashboardViewport', visualization = '.visEditor__content', - notebook = '.euiPageBody' + notebook = '.euiPageBody', } // https://www.elastic.co/guide/en/elasticsearch/reference/6.8/search-request-from-size.html @@ -89,14 +69,44 @@ export const DEFAULT_MAX_SIZE = 10000; export const DEFAULT_REPORT_HEADER = '

OpenSearch Dashboards Reports

'; export const SECURITY_CONSTANTS = { - AUTH_COOKIE_NAME: 'security_authentication', TENANT_LOCAL_STORAGE_KEY: 'opendistro::security::tenant::show_popup', - PROXY_AUTH_USER_HEADER: 'x-proxy-user', - PROXY_AUTH_ROLES_HEADER: 'x-proxy-roles', - PROXY_AUTH_IP_HEADER: 'x-forwarded-for', }; +export const EXTRA_HEADERS = [ + 'cookie', + 'x-proxy-user', + 'x-proxy-roles', + 'x-forwarded-for', +]; + +export const converter = new Showdown.Converter({ + tables: true, + simplifiedAutoLink: true, + strikethrough: true, + tasklists: true, + noHeaderId: true, +}); + +const BLOCKED_KEYWORD = 'BLOCKED_KEYWORD'; +const ipv4Regex = /(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?):([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])/g +const ipv6Regex = /(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/g; +const localhostRegex = /localhost:([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])/g; +const iframeRegex = /iframe/g; + +export const replaceBlockedKeywords = (htmlString: string) => { + // replace : + htmlString = htmlString.replace(ipv4Regex, BLOCKED_KEYWORD); + // replace ipv6 addresses + htmlString = htmlString.replace(ipv6Regex, BLOCKED_KEYWORD); + // replace iframe keyword + htmlString = htmlString.replace(iframeRegex, BLOCKED_KEYWORD); + // replace localhost: + htmlString = htmlString.replace(localhostRegex, BLOCKED_KEYWORD); + return htmlString; +} + export const CHROMIUM_PATH = `${__dirname}/../../../.chromium/headless_shell`; + /** * Metric constants diff --git a/dashboards-reports/server/routes/utils/converters/__tests__/backendToUi.test.ts b/dashboards-reports/server/routes/utils/converters/__tests__/backendToUi.test.ts index 426777c2..4877f807 100644 --- a/dashboards-reports/server/routes/utils/converters/__tests__/backendToUi.test.ts +++ b/dashboards-reports/server/routes/utils/converters/__tests__/backendToUi.test.ts @@ -1,30 +1,9 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. */ -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT 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 { AccessInfoType } from 'server'; +import { ReportingConfig } from 'server/config/config'; import { BackendReportInstanceType, BACKEND_DELIVERY_FORMAT, @@ -72,32 +51,21 @@ const input: BackendReportInstanceType = { }, }, delivery: { - recipients: ['szhongna@amazon.com'], - deliveryFormat: BACKEND_DELIVERY_FORMAT.embedded, title: 'test email subject', textDescription: '- test\n- optional\n- message', htmlDescription: '
    \n
  • test
  • \n
  • optional
  • \n
  • message
  • \n
', - channelIds: [], + configIds: [], }, }, }, status: BACKEND_REPORT_STATE.success, }; -const testAccessInfo: AccessInfoType = { - basePath: '', - serverInfo: { - name: '', - hostname: 'localhost', - port: 5601, - protocol: 'http', - }, -}; +const sampleServerBasePath = '/test'; const output = { - query_url: - "/app/dashboards#/view/722b74f0-b882-11e8-a6d9-e546fe2bba5f?_g=(time:(from:'2020-11-11T00:32:00.000Z',to:'2020-11-11T01:02:00.000Z'))", + query_url: `${sampleServerBasePath}/app/dashboards#/view/722b74f0-b882-11e8-a6d9-e546fe2bba5f?_g=(time:(from:'2020-11-11T00:32:00.000Z',to:'2020-11-11T01:02:00.000Z'))`, time_from: 1605054720000, time_to: 1605056520000, last_updated: 1605056644321, @@ -109,7 +77,7 @@ const output = { report_source: 'Dashboard', description: 'some random', core_params: { - base_url: '/app/dashboards#/view/722b74f0-b882-11e8-a6d9-e546fe2bba5f', + base_url: `${sampleServerBasePath}/app/dashboards#/view/722b74f0-b882-11e8-a6d9-e546fe2bba5f`, report_format: 'pdf', header: '

test header

', footer: '

fake footer

', @@ -129,15 +97,11 @@ const output = { }, }, delivery: { - delivery_type: 'Channel', - delivery_params: { - recipients: ['szhongna@amazon.com'], - title: 'test email subject', - textDescription: '- test\n- optional\n- message', - htmlDescription: - '
    \n
  • test
  • \n
  • optional
  • \n
  • message
  • \n
', - channelIds: [], - }, + title: 'test email subject', + textDescription: '- test\n- optional\n- message', + htmlDescription: + '
    \n
  • test
  • \n
  • optional
  • \n
  • message
  • \n
', + configIds: [], }, time_created: 1605056426053, last_updated: 1605056426053, @@ -147,7 +111,7 @@ const output = { describe('test backend to ui model conversion', () => { test('convert backend to ui report', async () => { - const res = backendToUiReport(input, testAccessInfo.basePath); + const res = backendToUiReport(input, sampleServerBasePath); expect(res).toEqual(output); }, 20000); }); diff --git a/dashboards-reports/server/routes/utils/converters/__tests__/uiToBackend.test.ts b/dashboards-reports/server/routes/utils/converters/__tests__/uiToBackend.test.ts index d61fc3c8..a1fbe6d6 100644 --- a/dashboards-reports/server/routes/utils/converters/__tests__/uiToBackend.test.ts +++ b/dashboards-reports/server/routes/utils/converters/__tests__/uiToBackend.test.ts @@ -1,27 +1,6 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT 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 { ReportDefinitionSchemaType } from 'server/model'; @@ -53,12 +32,10 @@ const input: ReportDefinitionSchemaType = { }, }, delivery: { - delivery_type: DELIVERY_TYPE.channel, - delivery_params: { - recipients: ['szhongna@amazon.com'], - title: 'Email subject', - textDescription: 'This is a test email', - }, + configIds: [], + title: '', + textDescription: '', + htmlDescription: '' }, trigger: { trigger_type: TRIGGER_TYPE.schedule, @@ -94,10 +71,10 @@ const output = { }, }, delivery: { - recipients: ['szhongna@amazon.com'], - title: 'Email subject', - textDescription: 'This is a test email', - deliveryFormat: 'Embedded', + configIds: [], + title: '', + textDescription: '', + htmlDescription: '' }, }; diff --git a/dashboards-reports/server/routes/utils/converters/backendToUi.ts b/dashboards-reports/server/routes/utils/converters/backendToUi.ts index 1799dcce..0d9b9816 100644 --- a/dashboards-reports/server/routes/utils/converters/backendToUi.ts +++ b/dashboards-reports/server/routes/utils/converters/backendToUi.ts @@ -1,27 +1,6 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT 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 { @@ -139,7 +118,6 @@ export const backendToUiReportDefinition = ( const baseUrl = getBaseUrl(sourceType, sourceId); const reportSource = getUiReportSource(sourceType); - let uiReportDefinition: ReportDefinitionSchemaType = { report_params: { report_name: name, @@ -175,7 +153,6 @@ export const backendToUiReportDefinition = ( last_updated: lastUpdatedTimeMs, status: getUiReportDefinitionStatus(isEnabled), }; - // validate to assign default values to some fields for UI model uiReportDefinition = reportDefinitionSchema.validate(uiReportDefinition); uiReportDefinition.report_params.core_params.base_url = @@ -370,20 +347,17 @@ const getUiDeliveryParams = ( delivery: DeliveryType | undefined ): DeliverySchemaType => { const opensearchDashboardsUserDeliveryParams = { - delivery_type: DELIVERY_TYPE.opensearchDashboardsUser, - delivery_params: { - opensearch_dashboards_recipients: [], - }, + configIds: [], + title: '', + textDescription: '', + htmlDescription: '' }; let params: any; if (delivery) { - const { deliveryFormat, ...rest } = delivery; + const { ...rest } = delivery; params = { - delivery_type: DELIVERY_TYPE.channel, - delivery_params: { - ...rest, - }, + ...rest }; } else { params = opensearchDashboardsUserDeliveryParams; diff --git a/dashboards-reports/server/routes/utils/converters/uiToBackend.ts b/dashboards-reports/server/routes/utils/converters/uiToBackend.ts index 5bf2927e..4a2064cd 100644 --- a/dashboards-reports/server/routes/utils/converters/uiToBackend.ts +++ b/dashboards-reports/server/routes/utils/converters/uiToBackend.ts @@ -1,27 +1,6 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT 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 { @@ -109,20 +88,16 @@ const getBackendDelivery = ( delivery: DeliverySchemaType ): DeliveryType | undefined => { const { - delivery_type: deliveryType, - delivery_params: deliveryParams, + configIds: configIds, + title: title, + textDescription: textDescription, + htmlDescription: htmlDescription } = delivery; - let res: any; - switch (deliveryType) { - case DELIVERY_TYPE.opensearchDashboardsUser: - break; - - case DELIVERY_TYPE.channel: - res = { - ...(deliveryParams as ChannelSchemaType), - deliveryFormat: BACKEND_DELIVERY_FORMAT.embedded, //TODO: now we only support one delivery format - }; - break; + let res = { + configIds: configIds, + title: title, + textDescription: textDescription, + htmlDescription: htmlDescription } return res; }; diff --git a/dashboards-reports/server/routes/utils/dataReportHelpers.ts b/dashboards-reports/server/routes/utils/dataReportHelpers.ts index 3840121b..bc108832 100644 --- a/dashboards-reports/server/routes/utils/dataReportHelpers.ts +++ b/dashboards-reports/server/routes/utils/dataReportHelpers.ts @@ -1,35 +1,18 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. */ -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT 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 { DATA_REPORT_CONFIG } from './constants'; - -import esb from 'elastic-builder'; -import moment from 'moment'; +import esb, { Sort } from 'elastic-builder'; import converter from 'json-2-csv'; import _ from 'lodash'; +import moment from 'moment'; +import { DATA_REPORT_CONFIG } from './constants'; +import { + buildOpenSearchQuery, + Filter, + Query, +} from '../../../../../src/plugins/data/common'; export var metaData = { saved_search_id: null, @@ -43,97 +26,43 @@ export var metaData = { fields_exist: false, selectedFields: [], paternName: null, - filters: [], + searchSourceJSON: [], dateFields: [], }; // Get the selected columns by the user. export const getSelectedFields = async (columns) => { const selectedFields = []; + let fields_exist = false; for (let column of columns) { if (column !== '_source') { - metaData.fields_exist = true; + fields_exist = true; selectedFields.push(column); } else { + fields_exist = false; selectedFields.push('_source'); } } + metaData.fields_exist = fields_exist; metaData.selectedFields = selectedFields; }; -//Build the OpenSearch query from the meta data +// Build the OpenSearch query from the meta data // is_count is set to 1 if we building the count query but 0 if we building the fetch data query -export const buildQuery = (report, is_count) => { - let requestBody = esb.boolQuery(); - const filters = report._source.filters; - for (let item of JSON.parse(filters).filter) { - if (item.meta.disabled === false) { - switch (item.meta.negate) { - case false: - switch (item.meta.type) { - case 'phrase': - requestBody.must( - esb.matchPhraseQuery(item.meta.key, item.meta.params.query) - ); - break; - case 'exists': - requestBody.must(esb.existsQuery(item.meta.key)); - break; - case 'phrases': - if (item.meta.value.indexOf(',') > -1) { - const valueSplit = item.meta.value.split(', '); - for (const [key, incr] of valueSplit.entries()) { - requestBody.should(esb.matchPhraseQuery(item.meta.key, incr)); - } - } else { - requestBody.should( - esb.matchPhraseQuery(item.meta.key, item.meta.value) - ); - } - requestBody.minimumShouldMatch(1); - break; - } - break; - case true: - switch (item.meta.type) { - case 'phrase': - requestBody.mustNot( - esb.matchPhraseQuery(item.meta.key, item.meta.params.query) - ); - break; - case 'exists': - requestBody.mustNot(esb.existsQuery(item.meta.key)); - break; - case 'phrases': - let negatedBody = esb.boolQuery(); - if (item.meta.value.indexOf(',') > -1) { - const valueSplit = item.meta.value.split(', '); - for (const [key, incr] of valueSplit.entries()) { - negatedBody.should(esb.matchPhraseQuery(item.meta.key, incr)); - } - } else { - negatedBody.should( - esb.matchPhraseQuery(item.meta.key, item.meta.value) - ); - } - negatedBody.minimumShouldMatch(1); - requestBody.mustNot(negatedBody); - break; - } - break; - } - } - } - //search part - let searchQuery = JSON.parse(filters) - .query.query.replace(/ and /g, ' AND ') - .replace(/ or /g, ' OR ') - .replace(/ not /g, ' NOT '); - if (searchQuery) { - requestBody.must(esb.queryStringQuery(searchQuery)); - } +export const buildRequestBody = (report: any, is_count: number) => { + let esbBoolQuery = esb.boolQuery(); + const searchSourceJSON = report._source.searchSourceJSON; + + const savedObjectQuery: Query = JSON.parse(searchSourceJSON).query; + const savedObjectFilter: Filter = JSON.parse(searchSourceJSON).filter; + const QueryFromSavedObject = buildOpenSearchQuery( + undefined, + savedObjectQuery, + savedObjectFilter + ); + // Add time range if (report._source.timeFieldName && report._source.timeFieldName.length > 0) { - requestBody.must( + esbBoolQuery.must( esb .rangeQuery(report._source.timeFieldName) .format('epoch_millis') @@ -142,47 +71,75 @@ export const buildQuery = (report, is_count) => { ); } if (is_count) { - return esb.requestBodySearch().query(requestBody); + return esb.requestBodySearch().query(esbBoolQuery); } - //Add the Sort to the query - let reqBody = esb.requestBodySearch().query(requestBody).version(true); + // Add sorting to the query + let esbSearchQuery = esb + .requestBodySearch() + .query(esbBoolQuery) + .version(true); if (report._source.sorting.length > 0) { - if (report._source.sorting.length === 1) - reqBody.sort( - esb.sort(report._source.sorting[0][0], report._source.sorting[0][1]) - ); - else - reqBody.sort( - esb.sort(report._source.sorting[0], report._source.sorting[1]) - ); + const sortings: Sort[] = report._source.sorting.map((element: string[]) => { + return esb.sort(element[0], element[1]); + }); + esbSearchQuery.sorts(sortings); } - //get the selected fields only + // add selected fields to query if (report._source.fields_exist) { - reqBody.source({ includes: report._source.selectedFields }); + esbSearchQuery.source({ includes: report._source.selectedFields }); } - return reqBody; + // Add a customizer to merge queries to generate request body + let requestBody = _.mergeWith( + { query: QueryFromSavedObject }, + esbSearchQuery.toJSON(), + (objValue, srcValue) => { + if (_.isArray(objValue)) { + return objValue.concat(srcValue); + } + } + ); + + requestBody = addDocValueFields(report, requestBody); + return requestBody; }; // Fetch the data from OpenSearch -export const getOpenSearchData = (arrayHits, report, params) => { +export const getOpenSearchData = ( + arrayHits, + report, + params, + dateFormat: string +) => { let hits: any = []; for (let valueRes of arrayHits) { for (let data of valueRes.hits) { const fields = data.fields; - //get all the fields of type date and fromat them to excel format - for (let dateType of report._source.dateFields) { - if (data._source[dateType]) { - data._source[dateType] = moment(fields[dateType][0]).format( - DATA_REPORT_CONFIG.excelDateFormat - ); + // get all the fields of type date and format them to excel format + for (let dateField of report._source.dateFields) { + const dateValue = data._source[dateField]; + if (dateValue && dateValue.length !== 0) { + if (dateValue instanceof Array) { + // loop through array + dateValue.forEach((element, index) => { + data._source[dateField][index] = moment( + fields[dateField][index] + ).format(dateFormat); + }); + } else { + // The fields response always returns an array of values for each field + // https://www.elastic.co/guide/en/elasticsearch/reference/master/search-fields.html#search-fields-response + data._source[dateField] = moment(fields[dateField][0]).format( + dateFormat + ); + } } } delete data['fields']; if (report._source.fields_exist === true) { - let result = traverse(data._source, report._source.selectedFields); + let result = traverse(data, report._source.selectedFields); hits.push(params.excel ? sanitize(result) : result); } else { hits.push(params.excel ? sanitize(data) : data); @@ -197,10 +154,10 @@ export const getOpenSearchData = (arrayHits, report, params) => { }; //Convert the data to Csv format -export const convertToCSV = async (dataset) => { +export const convertToCSV = async (dataset, csvSeparator) => { let convertedData: any = []; const options = { - delimiter: { field: ',', eol: '\n' }, + delimiter: { field: csvSeparator, eol: '\n' }, emptyFieldValue: ' ', }; await converter.json2csvAsync(dataset[0], options).then((csv) => { @@ -220,7 +177,7 @@ function flattenHits(hits, result = {}, prefix = '') { ) { flattenHits(value, result, prefix + key + '.'); } else { - result[prefix + key] = value; + result[prefix.replace(/^_source\./, '') + key] = value; } } return result; @@ -249,11 +206,11 @@ function traverse(data, keys, result = {}) { */ function sanitize(doc: any) { for (const field in doc) { - if (doc[field] == null) - continue + if (doc[field] == null) continue; if ( doc[field].toString().startsWith('+') || - (doc[field].toString().startsWith('-') && typeof doc[field] !== "number") || + (doc[field].toString().startsWith('-') && + typeof doc[field] !== 'number') || doc[field].toString().startsWith('=') || doc[field].toString().startsWith('@') ) { @@ -262,3 +219,20 @@ function sanitize(doc: any) { } return doc; } + +const addDocValueFields = (report: any, requestBody: any) => { + const docValues = []; + for (const dateType of report._source.dateFields) { + docValues.push({ + field: dateType, + format: 'date_hour_minute_second_fraction', + }); + } + // elastic-builder doesn't provide function to build docvalue_fields with format, + // this is a workaround which appends docvalues field to the request body. + requestBody = { + ...requestBody, + docvalue_fields: docValues, + }; + return requestBody; +}; diff --git a/dashboards-reports/server/routes/utils/helpers.ts b/dashboards-reports/server/routes/utils/helpers.ts index ff83b93b..5bada6d0 100644 --- a/dashboards-reports/server/routes/utils/helpers.ts +++ b/dashboards-reports/server/routes/utils/helpers.ts @@ -1,27 +1,6 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT 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 { OpenSearchDashboardsResponseFactory } from '../../../../../src/core/server'; @@ -105,3 +84,11 @@ export const checkErrorType = (error: any) => { return 'system_error'; } }; + +export const joinRequestParams = ( + queryParams: string | string[] | undefined +) => { + if (Array.isArray(queryParams)) return queryParams.join(','); + if (typeof queryParams === 'string') return queryParams; + return ''; +}; \ No newline at end of file diff --git a/dashboards-reports/server/routes/utils/metricHelper.ts b/dashboards-reports/server/routes/utils/metricHelper.ts index ca21aa6e..d58876dc 100644 --- a/dashboards-reports/server/routes/utils/metricHelper.ts +++ b/dashboards-reports/server/routes/utils/metricHelper.ts @@ -1,27 +1,6 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT 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 { ReportSchemaType } from 'server/model'; diff --git a/dashboards-reports/server/routes/utils/notification/deliveryContentHelper.ts b/dashboards-reports/server/routes/utils/notification/deliveryContentHelper.ts deleted file mode 100644 index 1c1aa22f..00000000 --- a/dashboards-reports/server/routes/utils/notification/deliveryContentHelper.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. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT 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 fs from 'fs'; -import cheerio from 'cheerio'; - -export const composeEmbeddedHtml = ( - htmlDescription: string = '', - originalQueryUrl: string, - reportDetailUrl: string, - reportName: string -) => { - const logoAsBase64 = fs.readFileSync( - `${__dirname}/notification_content_template/logo.png`, - 'base64' - ); - - const $ = cheerio.load( - fs.readFileSync( - `${__dirname}/notification_content_template/email_content_template.html` - ), - { decodeEntities: false } - ); - // set each link and logo - $('.logo').attr('src', `data:image/png;base64,${logoAsBase64}`); - $('.report_name').attr('href', reportDetailUrl).text(reportName); - $('.report_snapshot').attr('href', originalQueryUrl); - $('.optional_message').html(htmlDescription); - //TODO: Add this once we have the actual link to download - // $('.report_download').attr('href', '') - - return $.root().html(); -}; diff --git a/dashboards-reports/server/routes/utils/notification/notification_content_template/logo.png b/dashboards-reports/server/routes/utils/notification/notification_content_template/logo.png deleted file mode 100644 index 46aa560a..00000000 Binary files a/dashboards-reports/server/routes/utils/notification/notification_content_template/logo.png and /dev/null differ diff --git a/dashboards-reports/server/routes/utils/savedSearchReportHelper.ts b/dashboards-reports/server/routes/utils/savedSearchReportHelper.ts index 6b839917..ce05b412 100644 --- a/dashboards-reports/server/routes/utils/savedSearchReportHelper.ts +++ b/dashboards-reports/server/routes/utils/savedSearchReportHelper.ts @@ -1,31 +1,10 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT 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 { - buildQuery, + buildRequestBody, convertToCSV, getOpenSearchData, getSelectedFields, @@ -34,9 +13,12 @@ import { import { ILegacyClusterClient, ILegacyScopedClusterClient, + Logger, } from '../../../../../src/core/server'; import { getFileName, callCluster } from './helpers'; import { CreateReportResultType } from './types'; +import { RequestParams } from '@elastic/elasticsearch'; +import esb from 'elastic-builder'; /** * Specify how long scroll context should be maintained for scrolled search @@ -46,17 +28,23 @@ const scrollTimeout = '1m'; export async function createSavedSearchReport( report: any, client: ILegacyClusterClient | ILegacyScopedClusterClient, - isScheduledTask: boolean = true + dateFormat: string, + csvSeparator: string, + isScheduledTask: boolean = true, + logger: Logger ): Promise { const params = report.report_definition.report_params; const reportFormat = params.core_params.report_format; const reportName = params.report_name; - await populateMetaData(client, report, isScheduledTask); + await populateMetaData(client, report, isScheduledTask, logger); const data = await generateReportData( client, params.core_params, - isScheduledTask + dateFormat, + csvSeparator, + isScheduledTask, + logger ); const curTime = new Date(); @@ -77,7 +65,8 @@ export async function createSavedSearchReport( async function populateMetaData( client: ILegacyClusterClient | ILegacyScopedClusterClient, report: any, - isScheduledTask: boolean + isScheduledTask: boolean, + logger: Logger ) { metaData.saved_search_id = report.report_definition.report_params.core_params.saved_search_id; @@ -89,28 +78,28 @@ async function populateMetaData( // Get saved search info let resIndexPattern: any = {}; const ssParams = { - index: '.opensearch_dashboards', + index: '.kibana', id: 'search:' + metaData.saved_search_id, }; const ssInfos = await callCluster(client, 'get', ssParams, isScheduledTask); metaData.sorting = ssInfos._source.search.sort; metaData.type = ssInfos._source.type; - metaData.filters = - ssInfos._source.search.opensearchDashboardsSavedObjectMeta.searchSourceJSON; + metaData.searchSourceJSON = + ssInfos._source.search.kibanaSavedObjectMeta.searchSourceJSON; // Get the list of selected columns in the saved search.Otherwise select all the fields under the _source await getSelectedFields(ssInfos._source.search.columns); // Get index name for (const item of ssInfos._source.references) { - if (item.name === JSON.parse(metaData.filters).indexRefName) { + if (item.name === JSON.parse(metaData.searchSourceJSON).indexRefName) { // Get index-pattern information const indexPattern = await callCluster( client, 'get', { - index: '.opensearch_dashboards', + index: '.kibana', id: 'index-pattern:' + item.id, }, isScheduledTask @@ -139,7 +128,10 @@ async function populateMetaData( async function generateReportData( client: ILegacyClusterClient | ILegacyScopedClusterClient, params: any, - isScheduledTask: boolean + dateFormat: string, + csvSeparator: string, + isScheduledTask: boolean, + logger: Logger ) { let opensearchData: any = {}; const arrayHits: any = []; @@ -153,7 +145,10 @@ async function generateReportData( return ''; } - const reqBody = buildRequestBody(buildQuery(report, 0)); + const reqBody = buildRequestBody(report, 0); + logger.info( + `[Reporting csv module] DSL request body: ${JSON.stringify(reqBody)}` + ); if (total > maxResultSize) { await getOpenSearchDataByScroll(); } else { @@ -187,29 +182,30 @@ async function generateReportData( // Build the OpenSearch Count query to count the size of result async function getOpenSearchDataSize() { - const countReq = buildQuery(report, 1); + const countReq = buildRequestBody(report, 1); return await callCluster( client, 'count', { index: indexPattern, - body: countReq.toJSON(), + body: countReq, }, isScheduledTask ); } async function getOpenSearchDataByScroll() { + const searchParams: RequestParams.Search = { + index: report._source.paternName, + scroll: scrollTimeout, + body: reqBody, + size: maxResultSize, + }; // Open scroll context by fetching first batch opensearchData = await callCluster( client, 'search', - { - index: report._source.paternName, - scroll: scrollTimeout, - body: reqBody, - size: maxResultSize, - }, + searchParams, isScheduledTask ); arrayHits.push(opensearchData.hits); @@ -243,38 +239,26 @@ async function generateReportData( } async function getOpenSearchDataBySearch() { + const searchParams: RequestParams.Search = { + index: report._source.paternName, + body: reqBody, + size: total, + }; + opensearchData = await callCluster( client, 'search', - { - index: report._source.paternName, - body: reqBody, - size: total, - }, + searchParams, isScheduledTask ); - arrayHits.push(opensearchData.hits); - } - function buildRequestBody(query: any) { - const docvalues = []; - for (const dateType of report._source.dateFields) { - docvalues.push({ - field: dateType, - format: 'date_hour_minute', - }); - } - - return { - query: query.toJSON().query, - docvalue_fields: docvalues, - }; + arrayHits.push(opensearchData.hits); } // Parse OpenSearch data and convert to CSV async function convertOpenSearchDataToCsv() { const dataset: any = []; - dataset.push(getOpenSearchData(arrayHits, report, params)); - return await convertToCSV(dataset); + dataset.push(getOpenSearchData(arrayHits, report, params, dateFormat)); + return await convertToCSV(dataset, csvSeparator); } } diff --git a/dashboards-reports/server/routes/utils/types.ts b/dashboards-reports/server/routes/utils/types.ts index 6ba79ebe..3c589466 100644 --- a/dashboards-reports/server/routes/utils/types.ts +++ b/dashboards-reports/server/routes/utils/types.ts @@ -1,27 +1,6 @@ /* + * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT 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 CreateReportResultType { diff --git a/dashboards-reports/server/routes/utils/visual_report/footer_template.html b/dashboards-reports/server/routes/utils/visual_report/footer_template.html new file mode 100644 index 00000000..6fc56f8c --- /dev/null +++ b/dashboards-reports/server/routes/utils/visual_report/footer_template.html @@ -0,0 +1,5 @@ +
+
+
+
+
diff --git a/dashboards-reports/server/routes/utils/visual_report/header_template.html b/dashboards-reports/server/routes/utils/visual_report/header_template.html new file mode 100644 index 00000000..9796c499 --- /dev/null +++ b/dashboards-reports/server/routes/utils/visual_report/header_template.html @@ -0,0 +1,5 @@ +
+
+
+
+
diff --git a/dashboards-reports/server/routes/utils/visual_report/report_template.html b/dashboards-reports/server/routes/utils/visual_report/report_template.html deleted file mode 100644 index 9a1e1536..00000000 --- a/dashboards-reports/server/routes/utils/visual_report/report_template.html +++ /dev/null @@ -1,244 +0,0 @@ - - - - - - OpenSearch Dashboards Reports - - - - -
-
-
-
-
-
-
- -
- - -
-
-
-
-
-
- - diff --git a/dashboards-reports/server/routes/utils/visual_report/style.css b/dashboards-reports/server/routes/utils/visual_report/style.css new file mode 100644 index 00000000..58628427 --- /dev/null +++ b/dashboards-reports/server/routes/utils/visual_report/style.css @@ -0,0 +1,211 @@ +html, +body { + margin: 0; + padding: 0; +} + +/* nice padding + matches Kibana default UI colors you could also set this to inherit if + the wrapper gets inserted inside a kibana section. I might also remove the manual text color here as well, potentially */ +.reportWrapper { + padding: 8px; + background-color: #fafbfd; +} + +/* Notice that I'm using an ID of #reportingHeader, and #reportingFooter, instead of a classname (.reportingHeader, .reportingFooter). This is + in order to force specificity here higher in case any other styles would conflict */ +#reportingHeader, +#reportingFooter { + font-family: 'Inter UI', -apple-system, BlinkMacSystemFont, 'Segoe UI', + Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', + 'Segoe UI Symbol'; + background-color: #fff; + border: 1px solid #d3dae6; + box-shadow: 0 2px 2px -1px rgba(152, 162, 179, 0.3), + 0 1px 5px -2px rgba(152, 162, 179, 0.3); + border-radius: 4px; + padding: 1em; + margin-bottom: 1em; +} + +#reportingFooter { + margin-top: 1em; +} + +#reportingHeader p, +#reportingFooter p { + max-width: 960px; +} + +/* Adjust the margin when the header is the first item */ +#reportingHeader h1:first-child, +#reportingFooter h1:first-child, +#reportingHeader h2:first-child, +#reportingFooter h2:first-child, +#reportingHeader h3:first-child, +#reportingFooter h3:first-child, +#reportingHeader h4:first-child, +#reportingFooter h4:first-child, +#reportingHeader h5:first-child, +#reportingFooter h5:first-child, +#reportingHeader h6:first-child, +#reportingFooter h6:first-child { + margin-top: 0.25em; +} + +/* nicer list styles */ +#reportingHeader ul, +#reportingFooter ul, +#reportingHeader ol, +#reportingFooter ol { + max-width: 70rem; + margin-bottom: 1em; +} + +#reportingHeader ul li, +#reportingFooter ul li, +#reportingHeader ol li, +#reportingFooter ol li { + margin-bottom: 0.25em; + margin-left: -0.5em; + padding-left: 0.25em; +} + +#reportingHeader ul, +#reportingFooter ul { + list-style-type: disc; +} + +/* here we explicitly set nested paragraphs inside lists to inherit their styles from the list, in case markdown does funky things */ +#reportingHeader ul p, +#reportingFooter ul p, +#reportingHeader ol p, +#reportingFooter ol p { + font-family: inherit; + font-size: inherit; + font-weight: inherit; + /* We only inherit vertical spacing, not horizontal */ + margin-top: inherit; + margin-bottom: inherit; +} + +