├── .dockerignore ├── .editorconfig ├── .eslintrc.js ├── .flowconfig ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── build.yml │ └── docker-image.yml ├── .gitignore ├── .gitmodules ├── .prettierrc.js ├── .vscode ├── extensions.json └── settings.json ├── .yarnrc ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE.md ├── README.md ├── SECURITY.md ├── docker-compose-v7.yml ├── docker-compose-v8.yml ├── docker-compose.yml ├── lerna.json ├── media ├── f1.gif ├── f2.gif ├── f3.gif ├── f4.gif └── f5.gif ├── package.json ├── packages ├── browser │ ├── .babelrc.js │ ├── .editorconfig │ ├── .eslintrc.js │ ├── .prettierrc.js │ ├── LICENSE.md │ ├── package.json │ └── src │ │ ├── actions │ │ ├── analyzers.js │ │ ├── app.js │ │ ├── applyQuery.js │ │ ├── cell.js │ │ ├── constants.js │ │ ├── currentIds.js │ │ ├── data.js │ │ ├── error.js │ │ ├── index.js │ │ ├── mappings.js │ │ ├── mode.js │ │ ├── nestedColumns.js │ │ ├── pageSize.js │ │ ├── query.js │ │ ├── selectAll.js │ │ ├── selectedRows.js │ │ ├── sort.js │ │ ├── stats.js │ │ ├── updatingRow.js │ │ └── version.js │ │ ├── apis │ │ ├── analyzers.js │ │ ├── app.js │ │ ├── cell.js │ │ ├── count.js │ │ ├── data.js │ │ ├── index.js │ │ ├── mappings.js │ │ ├── search.js │ │ └── version.js │ │ ├── components │ │ ├── Cell │ │ │ ├── ArrayCell.js │ │ │ ├── BooleanCell.js │ │ │ ├── Cell.js │ │ │ ├── Cell.styles.js │ │ │ ├── DateCell.js │ │ │ ├── NumberCell.js │ │ │ ├── ObjectCell.js │ │ │ ├── TextCell.js │ │ │ └── index.js │ │ ├── CommonStyles │ │ │ ├── filterIcons.js │ │ │ ├── label.js │ │ │ ├── linkButton.js │ │ │ ├── overflowText.js │ │ │ ├── popoverContent.js │ │ │ └── termAggregations.js │ │ ├── ConnectApp │ │ │ ├── ConnectApp.js │ │ │ └── index.js │ │ ├── DataBrowser │ │ │ ├── Actions.js │ │ │ ├── AddFieldModal.js │ │ │ ├── AddRowModal.js │ │ │ ├── ApplyQueryBanner.js │ │ │ ├── CloneApp.js │ │ │ ├── DataBrowser.js │ │ │ ├── DeleteRows.js │ │ │ ├── ExportData.js │ │ │ ├── GlobalSearch.js │ │ │ ├── Item.styles.js │ │ │ ├── ModeSwitch.js │ │ │ ├── MultipleUpdate.js │ │ │ ├── NestedColumnToggle.js │ │ │ ├── PageSize.js │ │ │ ├── ResultSet.js │ │ │ ├── ShowHideColumns.js │ │ │ ├── SortFilter.js │ │ │ ├── UpdateRow.js │ │ │ └── index.js │ │ ├── DataBrowserContainer │ │ │ ├── DataBrowserContainer.js │ │ │ └── index.js │ │ ├── DataTable │ │ │ ├── BooleanTermAggregation.js │ │ │ ├── ColumnHeader.js │ │ │ ├── DataGrid.js │ │ │ ├── DataTable.js │ │ │ ├── DataTableHeader.js │ │ │ ├── IdField.js │ │ │ ├── IdHeaderField.js │ │ │ ├── StyledCell.js │ │ │ ├── TermAggregation.js │ │ │ ├── index.js │ │ │ └── overflow.style.js │ │ ├── ErrorFlashMessage │ │ │ ├── ErrorMessage.js │ │ │ ├── FlashMessage.js │ │ │ └── index.js │ │ ├── Flex │ │ │ ├── Flex.js │ │ │ └── index.js │ │ ├── Icons │ │ │ ├── Hash.js │ │ │ └── Toggle.js │ │ ├── JsonView │ │ │ ├── JsonView.js │ │ │ └── index.js │ │ ├── Loader │ │ │ └── index.js │ │ ├── MappingsDropdown │ │ │ ├── MappingsDropdown.js │ │ │ └── index.js │ │ ├── MappingsIcon │ │ │ ├── MappingsIcon.js │ │ │ └── index.js │ │ └── theme │ │ │ └── colors.js │ │ ├── constants │ │ ├── config.js │ │ ├── es-languages.js │ │ ├── index.js │ │ ├── language.js │ │ └── props.js │ │ ├── images │ │ ├── dejavu-logo.svg │ │ └── loader.svg │ │ ├── index.js │ │ ├── reducers │ │ ├── analyzers.js │ │ ├── app.js │ │ ├── applyQuery.js │ │ ├── cell.js │ │ ├── currentIds.js │ │ ├── data.js │ │ ├── error.js │ │ ├── index.js │ │ ├── mappings.js │ │ ├── mode.js │ │ ├── nestedColumns.js │ │ ├── pageSize.js │ │ ├── query.js │ │ ├── selectAll.js │ │ ├── selectedRows.js │ │ ├── sort.js │ │ ├── stats.js │ │ ├── updatingRow.js │ │ └── version.js │ │ ├── sagas │ │ ├── app.js │ │ ├── cell.js │ │ ├── data.js │ │ ├── index.js │ │ └── mappings.js │ │ ├── store │ │ └── index.js │ │ └── utils │ │ ├── CustomError.js │ │ ├── date.js │ │ ├── exportData.js │ │ ├── index.js │ │ ├── mappings.js │ │ └── sampleData.js └── dejavu-main │ ├── .babelrc.js │ ├── .editorconfig │ ├── .eslintrc.js │ ├── .prettierrc.js │ ├── LICENSE.md │ ├── app │ ├── index.html │ └── src │ │ ├── App.js │ │ ├── _redirects │ │ ├── components │ │ ├── Navigation.js │ │ ├── NoMatch.js │ │ ├── OldDejavuBanner │ │ │ ├── OldDejavuBanner.js │ │ │ └── index.js │ │ ├── QueryExplorer.js │ │ └── SearchPreview.js │ │ ├── constants │ │ ├── config.js │ │ ├── es-languages.js │ │ ├── index.js │ │ ├── language.js │ │ └── props.js │ │ ├── favicon │ │ └── favicon.png │ │ ├── images │ │ └── dejavu-logo.svg │ │ └── index.js │ ├── chrome-specific │ ├── background.js │ ├── manifest.json │ ├── popup.html │ └── popup.js │ ├── package.json │ ├── server.js │ ├── site │ ├── index.html │ └── src │ │ ├── App.js │ │ ├── _redirects │ │ ├── components │ │ └── Footer.js │ │ ├── favicon │ │ └── favicon.png │ │ ├── images │ │ ├── airbed.png │ │ ├── chromestore.png │ │ ├── data-browser.png │ │ ├── data-importer.png │ │ ├── dataset.png │ │ ├── dejavu-logo.svg │ │ ├── docker.png │ │ ├── github.svg │ │ ├── jellyfish.svg │ │ ├── medium.svg │ │ ├── movie.png │ │ ├── search-sandbox.png │ │ ├── technews.png │ │ └── twitter.svg │ │ └── index.js │ ├── webpack.config.js │ └── webpack.config.site.js ├── vercel.json └── yarn.lock /.dockerignore: -------------------------------------------------------------------------------- 1 | .git/ 2 | node_modules/ 3 | dist/ 4 | site/ 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | indent_size = 4 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: 'babel-eslint', 3 | extends: [ 4 | 'airbnb', 5 | 'prettier', 6 | 'plugin:prettier/recommended', 7 | 'prettier/flowtype', 8 | 'prettier/react', 9 | 'prettier/standard', 10 | 'plugin:flowtype/recommended', 11 | 'plugin:jest/recommended', 12 | ], 13 | plugins: ['flowtype', 'prettier', 'jest'], 14 | env: { 15 | browser: true, 16 | }, 17 | rules: { 18 | 'react/jsx-filename-extension': [1, { extensions: ['.js'] }], 19 | 'react/forbid-prop-types': 0, 20 | 'react/destructuring-assignment': 0, 21 | 'react/require-default-props': 0, 22 | 'prettier/prettier': 'error', 23 | 'no-underscore-dangle': 0, 24 | 'jsx-a11y/click-events-have-key-events': 0, 25 | 'jsx-a11y/no-static-element-interactions': 0, 26 | }, 27 | }; 28 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | dist 3 | lib 4 | node_modules 5 | 6 | [include] 7 | packages/browser/src 8 | packages/dejavu-main/app 9 | 10 | [libs] 11 | 12 | [lints] 13 | 14 | [options] 15 | 16 | [strict] 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: lakhansamani 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **Dejavu Version** 14 | Your version of Dejavu and how you're running it: Web, Chrome Extension or as a Docker Image. 15 | 16 | **To Reproduce** 17 | Steps to reproduce the behavior: 18 | 1. Go to '...' 19 | 2. Click on '....' 20 | 3. Scroll down to '....' 21 | 4. See error 22 | 23 | **Expected behavior** 24 | A clear and concise description of what you expected to happen. 25 | 26 | **Screenshots** 27 | If applicable, add screenshots to help explain your problem. 28 | 29 | **Desktop (please complete the following information):** 30 | - OS: [e.g. iOS] 31 | - Browser [e.g. chrome, safari] 32 | - Version [e.g. 22] 33 | 34 | 35 | **Additional context** 36 | Add any other context about the problem here. 37 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea or enhancement for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Dejavu - Node.js Build and Release Workflow 2 | 3 | on: 4 | release: 5 | types: [published] 6 | repository_dispatch: 7 | types: [publish_binary] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: Checkout repository with submodules 16 | uses: actions/checkout@v3 17 | with: 18 | submodules: recursive 19 | fetch-depth: 0 20 | 21 | - name: Set up Node.js 20 LTS 22 | uses: actions/setup-node@v3 23 | with: 24 | node-version: '20' 25 | cache: 'yarn' 26 | 27 | - name: Install dependencies 28 | run: yarn 29 | 30 | - name: Extract version from payload 31 | id: get_version 32 | if: ${{ github.event_name == 'repository_dispatch' }} 33 | run: echo "version=${{ github.event.client_payload.version }}" >> $GITHUB_OUTPUT 34 | 35 | - name: Get Version For Release 36 | id: get_version_release 37 | if: ${{ github.event_name != 'repository_dispatch' }} 38 | uses: battila7/get-version-action@v2.2.1 39 | 40 | - name: Determine Version 41 | id: set_version 42 | run: | 43 | if [ -n "${{ steps.get_version.outputs.version }}" ]; then 44 | echo "version=${{ steps.get_version.outputs.version }}" >> $GITHUB_OUTPUT 45 | elif [ -n "${{ steps.get_version_release.outputs.version }}" ]; then 46 | echo "version=${{ steps.get_version_release.outputs.version }}" >> $GITHUB_OUTPUT 47 | else 48 | echo "Error: Version is not set." 49 | exit 1 50 | fi 51 | 52 | - name: Build Dejavu App 53 | run: yarn build:dejavu:app 54 | 55 | - name: Zip the dist folder 56 | run: | 57 | zip -r dist-${{ steps.set_version.outputs.version }}.zip ./packages/dejavu-main/dist 58 | 59 | # Fetch release by tag and get upload_url, stripping the placeholder 60 | - name: Get upload_url for release 61 | if: ${{ github.event_name == 'repository_dispatch' }} 62 | id: get_release 63 | run: | 64 | release_data=$(curl -s \ 65 | -H "Authorization: token ${{ secrets.PAT }}" \ 66 | "https://api.github.com/repos/${{ github.repository }}/releases/tags/${{ steps.set_version.outputs.version }}") 67 | upload_url=$(echo "$release_data" | jq -r '.upload_url' | sed -e "s/{?name,label}//") 68 | if [ "$upload_url" == "null" ]; then 69 | echo "Error: Release not found for tag ${steps.set_version.outputs.version}" 70 | exit 1 71 | fi 72 | echo "upload_url=$upload_url" >> $GITHUB_OUTPUT 73 | 74 | - name: Upload dist.zip to GitHub Release (using curl) 75 | run: | 76 | curl -X POST \ 77 | -H "Authorization: token ${{ secrets.PAT }}" \ 78 | -H "Content-Type: application/zip" \ 79 | --data-binary @./dist-${{ steps.set_version.outputs.version }}.zip \ 80 | "${{ steps.get_release.outputs.upload_url }}?name=dejavu-dist-${{ steps.set_version.outputs.version }}.zip" 81 | 82 | # For release events, use the upload_url from the event payload 83 | - name: Upload dist.zip to GitHub Release (release event) 84 | if: ${{ github.event_name == 'release' }} 85 | run: | 86 | upload_url="${{ github.event.release.upload_url }}" 87 | # Strip the placeholder 88 | upload_url="${upload_url%\{*}" 89 | curl -X POST \ 90 | -H "Authorization: token ${{ secrets.PAT }}" \ 91 | -H "Content-Type: application/zip" \ 92 | --data-binary @./dist-${{ steps.set_version.outputs.version }}.zip \ 93 | "${upload_url}?name=dejavu-dist-${{ steps.set_version.outputs.version }}.zip" -------------------------------------------------------------------------------- /.github/workflows/docker-image.yml: -------------------------------------------------------------------------------- 1 | name: Dejavu - Docker Publish Workflow 2 | 3 | on: 4 | release: 5 | types: [published] 6 | repository_dispatch: 7 | types: [publish_docker] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: Checkout repository with submodules 16 | uses: actions/checkout@v3 17 | with: 18 | submodules: recursive 19 | fetch-depth: 0 20 | 21 | - name: Extract version from payload 22 | id: get_version 23 | if: ${{ github.event_name == 'repository_dispatch' }} 24 | run: echo "version=${{ github.event.client_payload.version }}" >> $GITHUB_OUTPUT 25 | 26 | - name: Get Version For Release 27 | id: get_version_release 28 | if: ${{ github.event_name != 'repository_dispatch' }} 29 | uses: battila7/get-version-action@v2.2.1 30 | 31 | - name: Determine Version 32 | id: set_version 33 | run: | 34 | if [ -n "${{ steps.get_version.outputs.version }}" ]; then 35 | echo "version=${{ steps.get_version.outputs.version }}" >> $GITHUB_OUTPUT 36 | elif [ -n "${{ steps.get_version_release.outputs.version }}" ]; then 37 | echo "version=${{ steps.get_version_release.outputs.version }}" >> $GITHUB_OUTPUT 38 | else 39 | echo "Error: Version is not set." 40 | exit 1 41 | fi 42 | 43 | - name: Set up QEMU 44 | uses: docker/setup-qemu-action@v2 45 | 46 | - name: Set up Docker Buildx 47 | uses: docker/setup-buildx-action@v2 48 | 49 | - name: Login to DockerHub 50 | uses: docker/login-action@v2 51 | with: 52 | username: ${{ secrets.DOCKERHUB_USERNAME }} 53 | password: ${{ secrets.DOCKERHUB_TOKEN }} 54 | 55 | - name: Build and push latest image 56 | uses: docker/build-push-action@v4 57 | with: 58 | context: . 59 | push: true 60 | platforms: linux/amd64,linux/arm64 61 | tags: | 62 | appbaseio/dejavu:${{ steps.set_version.outputs.version }} 63 | appbaseio/dejavu:latest -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Project dependencies 2 | .cache 3 | .idea 4 | node_modules 5 | yarn-error.log 6 | lerna-debug.log 7 | 8 | # Build directory 9 | **/public 10 | **/.cache 11 | .DS_Store 12 | lib 13 | dist 14 | 15 | # Logs 16 | logs 17 | *.log 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | 22 | # Runtime data 23 | pids 24 | *.pid 25 | *.seed 26 | *.pid.lock 27 | 28 | # Directory for instrumented libs generated by jscoverage/JSCover 29 | lib-cov 30 | 31 | # Coverage directory used by tools like istanbul 32 | coverage 33 | 34 | # nyc test coverage 35 | .nyc_output 36 | 37 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 38 | .grunt 39 | 40 | # Bower dependency directory (https://bower.io/) 41 | bower_components 42 | 43 | # node-waf configuration 44 | .lock-wscript 45 | 46 | # Compiled binary addons (http://nodejs.org/api/addons.html) 47 | build/Release 48 | 49 | # Dependency directories 50 | node_modules/ 51 | jspm_packages/ 52 | 53 | # Typescript v1 declaration files 54 | typings/ 55 | 56 | # Optional npm cache directory 57 | .npm 58 | 59 | # Optional eslint cache 60 | .eslintcache 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | 74 | # macos 75 | .DS_Store 76 | 77 | # build 78 | /dist 79 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "packages/browser/src/batteries"] 2 | path = packages/browser/src/batteries 3 | url = https://github.com/appbaseio/batteries 4 | [submodule "packages/dejavu-main/app/src/batteries"] 5 | path = packages/dejavu-main/app/src/batteries 6 | url = https://github.com/appbaseio/batteries 7 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | tabWidth: 4, 3 | singleQuote: true, 4 | trailingComma: 'all', 5 | useTabs: true, 6 | }; 7 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "dbaeumer.vscode-eslint", 4 | "EditorConfig.EditorConfig", 5 | "esbenp.prettier-vscode", 6 | "Orta.vscode-jest", 7 | "msjsdiag.debugger-for-chrome", 8 | "jpoissonnier.vscode-styled-components", 9 | "lXSPandora.vscode-styled-components-snippets", 10 | "andys8.jest-snippets", 11 | "burkeholland.simple-react-snippets", 12 | "flowtype.flow-for-vscode" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "flow.pathToFlow": "${workspaceRoot}/node_modules/.bin/flow" 4 | } 5 | -------------------------------------------------------------------------------- /.yarnrc: -------------------------------------------------------------------------------- 1 | optional = false 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Dev Setup 2 | 3 | ## Starting dev server 4 | 5 | 1. Clone this repo or create your own fork and clone it if you want to send PRs 6 | 2. Do the following steps: 7 | `cd dejavu` 8 | `git submodule init` 9 | `git submodule sync` 10 | `git submodule update --recursive --remote` 11 | 3. Run `yarn` after cloning the repo to install dependencies 12 | 4. Run `yarn dev:browser` which starts the `watcher` under `packages/browser` package i.e. `@appbaseio/dejavu-browser`. 13 | 5. Run `yarn dev:dejavu` which starts the `webpack-dev-server` on port `1358` 14 | 15 | ## Code Formatting 16 | 17 | Run `yarn format` to run prettier on `*.js` files. 18 | 19 | > **Tip** 20 | > 21 | > Install the recommended extensions for your editor 22 | 23 | ## Troubleshooting 24 | 25 | While installing dependencies with `yarn` you might run into an issue with `libpng`. On Fedora linux you would need the `libpng-devel` installed for the dependency to compile: 26 | 27 | ```sh 28 | sudo dnf install libpng-devel pngquant gcc make libpng12 libpng12-devel 29 | ``` 30 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:lts-alpine3.20 2 | LABEL maintainer="reactivesearch.io" email="info@reactivesearch.io" 3 | 4 | WORKDIR /dejavu 5 | 6 | ADD package.json yarn.lock /dejavu/ 7 | 8 | RUN apk --no-cache update \ 9 | && apk --no-cache add git \ 10 | && rm -rf /var/cache/apk/* 11 | 12 | ADD . /dejavu 13 | 14 | RUN yarn \ 15 | && yarn cache clean && yarn build:dejavu:app \ 16 | && rm -rf /dejavu/node_modules \ 17 | && rm -rf /tmp/* 18 | 19 | RUN addgroup -S -g 201 dejavu && \ 20 | adduser -S -u 201 -G dejavu dejavu && \ 21 | chown -R dejavu:dejavu /dejavu 22 | 23 | USER dejavu 24 | 25 | EXPOSE 1358 26 | 27 | CMD node packages/dejavu-main/server.js 28 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2018 Appbase Inc. 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Dejavu's major releases have preserved backward compatibilities. The following versions are actively developed and will receive updates for security vulnerabilities. 6 | 7 | | Version | Supported | 8 | | ------- | ------------------ | 9 | | 3.x | :white_check_mark: | 10 | | < 3.0 | :x: | 11 | 12 | ## Reporting a Vulnerability 13 | 14 | You can report a security vulnerability by filing a Github issue, or writing to us at support@reactivesearch.io. 15 | -------------------------------------------------------------------------------- /docker-compose-v7.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | # search engine 4 | elasticsearch: 5 | image: docker.elastic.co/elasticsearch/elasticsearch-oss:7.10.2 6 | container_name: elasticsearch 7 | environment: 8 | - discovery.type=single-node 9 | - http.port=9200 10 | - http.cors.enabled=true 11 | - http.cors.allow-origin=http://localhost:1358,http://127.0.0.1:1358 12 | - http.cors.allow-headers=X-Requested-With,X-Auth-Token,Content-Type,Content-Length,Authorization 13 | - http.cors.allow-credentials=true 14 | - bootstrap.memory_lock=true 15 | - 'ES_JAVA_OPTS=-Xms512m -Xmx512m' 16 | ports: 17 | - '9200:9200' 18 | - '9300:9300' 19 | ulimits: 20 | memlock: 21 | soft: -1 22 | hard: -1 23 | volumes: 24 | - ./data:/usr/share/elasticsearch/data 25 | # elasticsearch browser 26 | dejavu: 27 | image: appbaseio/dejavu:3.6.0 28 | container_name: dejavu 29 | ports: 30 | - '1358:1358' 31 | links: 32 | - elasticsearch 33 | -------------------------------------------------------------------------------- /docker-compose-v8.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | # search engine 4 | elasticsearch: 5 | image: docker.elastic.co/elasticsearch/elasticsearch:8.15.1 6 | container_name: elasticsearch 7 | environment: 8 | - discovery.type=single-node 9 | - http.port=9200 10 | - http.cors.enabled=true 11 | - http.cors.allow-origin=http://localhost:1358,http://127.0.0.1:1358 12 | - http.cors.allow-headers=X-Requested-With,X-Auth-Token,Content-Type,Content-Length,Authorization 13 | - http.cors.allow-credentials=true 14 | - bootstrap.memory_lock=true 15 | - 'ES_JAVA_OPTS=-Xms512m -Xmx512m' 16 | - network.publish_host=_local_ 17 | - network.host=_local_,_site_ 18 | - transport.host=127.0.0.1 19 | - http.host=0.0.0.0 20 | - xpack.security.enabled=false 21 | ports: 22 | - '9200:9200' 23 | - '9300:9300' 24 | ulimits: 25 | memlock: 26 | soft: -1 27 | hard: -1 28 | volumes: 29 | - ./data:/usr/share/elasticsearch/data 30 | # elasticsearch browser 31 | dejavu: 32 | image: appbaseio/dejavu:latest 33 | container_name: dejavu 34 | ports: 35 | - '1358:1358' 36 | links: 37 | - elasticsearch 38 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | # search engine 4 | opensearch: 5 | image: opensearchproject/opensearch:2.17.0 6 | container_name: opensearch 7 | environment: 8 | - 'DISABLE_SECURITY_PLUGIN=true' 9 | - discovery.type=single-node 10 | - http.port=9200 11 | - http.cors.enabled=true 12 | - http.cors.allow-origin=http://localhost:1358,http://127.0.0.1:1358 13 | - http.cors.allow-headers=X-Requested-With,X-Auth-Token,Content-Type,Content-Length,Authorization 14 | - http.cors.allow-credentials=true 15 | - bootstrap.memory_lock=true 16 | - 'ES_JAVA_OPTS=-Xms512m -Xmx512m' 17 | ports: 18 | - '9200:9200' 19 | - '9300:9300' 20 | ulimits: 21 | memlock: 22 | soft: -1 23 | hard: -1 24 | volumes: 25 | - './data_os:/usr/share/opensearch/data' 26 | # elasticsearch browser 27 | dejavu: 28 | image: appbaseio/dejavu:latest 29 | container_name: dejavu 30 | ports: 31 | - '1358:1358' 32 | links: 33 | - opensearch 34 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [ 3 | "packages/*" 4 | ], 5 | "npmClient": "yarn", 6 | "useWorkspaces": true, 7 | "version": "3.5.3", 8 | "lerna": "2.11.0", 9 | "command": { 10 | "version": { 11 | "message": "chore(build): %v" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /media/f1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appbaseio/dejavu/73a3d6dd9843de1377722fb0a2c9443bccf5025c/media/f1.gif -------------------------------------------------------------------------------- /media/f2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appbaseio/dejavu/73a3d6dd9843de1377722fb0a2c9443bccf5025c/media/f2.gif -------------------------------------------------------------------------------- /media/f3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appbaseio/dejavu/73a3d6dd9843de1377722fb0a2c9443bccf5025c/media/f3.gif -------------------------------------------------------------------------------- /media/f4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appbaseio/dejavu/73a3d6dd9843de1377722fb0a2c9443bccf5025c/media/f4.gif -------------------------------------------------------------------------------- /media/f5.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appbaseio/dejavu/73a3d6dd9843de1377722fb0a2c9443bccf5025c/media/f5.gif -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dejavu-monorepo", 3 | "description": "Dejavu monorepo - https://dejavu.reactivesearch.io", 4 | "version": "3.8.3", 5 | "authors": [ 6 | "reactivesearch.io (https://github.com/appbaseio)" 7 | ], 8 | "dependencies": { 9 | "lerna": "^2.11.0", 10 | "react": "16.12.0" 11 | }, 12 | "devDependencies": { 13 | "@babel/cli": "^7.7.7", 14 | "@babel/core": "^7.1.0", 15 | "@babel/plugin-proposal-class-properties": "^7.1.0", 16 | "@babel/plugin-proposal-export-default-from": "^7.7.4", 17 | "@babel/plugin-proposal-object-rest-spread": "^7.0.0", 18 | "@babel/plugin-syntax-dynamic-import": "^7.0.0", 19 | "@babel/plugin-transform-spread": "^7.0.0", 20 | "@babel/preset-env": "^7.0.0", 21 | "@babel/preset-flow": "^7.0.0", 22 | "@babel/preset-react": "^7.0.0", 23 | "babel-core": "^7.0.0-bridge.0", 24 | "babel-eslint": "^10.0.3", 25 | "babel-jest": "^24.9.0", 26 | "babel-loader": "^8.0.2", 27 | "babel-plugin-direct-import": "^0.6.0-beta.1", 28 | "babel-plugin-emotion": "^9.2.10", 29 | "css-loader": "^1.0.0", 30 | "eslint": "^6.7.1", 31 | "eslint-config-airbnb": "^17.1.0", 32 | "eslint-config-prettier": "^3.0.1", 33 | "eslint-plugin-flowtype": "^2.50.1", 34 | "eslint-plugin-import": "^2.14.0", 35 | "eslint-plugin-jest": "^21.22.1", 36 | "eslint-plugin-jsx-a11y": "^6.1.1", 37 | "eslint-plugin-prettier": "^2.6.2", 38 | "eslint-plugin-react": "^7.11.1", 39 | "file-loader": "^2.0.0", 40 | "flow-bin": "^0.81.0", 41 | "fs-extra": "^7.0.1", 42 | "husky": "^1.3.1", 43 | "jest": "^24.9.0", 44 | "jest-emotion": "^9.2.7", 45 | "lint-staged": "^7.2.2", 46 | "prettier": "^1.14.2", 47 | "style-loader": "^0.23.0" 48 | }, 49 | "engines": { 50 | "node": ">=14.17.1 <21" 51 | }, 52 | "license": "MIT", 53 | "scripts": { 54 | "dev:browser": "lerna run watch --scope @appbaseio/dejavu-browser --stream", 55 | "start": "lerna run watch --scope dejavu-main --stream", 56 | "dev:dejavu": "NODE_OPTIONS=--openssl-legacy-provider lerna run watch --scope dejavu-main --stream", 57 | "dev:dejavu:site": "NODE_OPTIONS=--openssl-legacy-provider lerna run watch:site --scope dejavu-main --stream", 58 | "build:browser": "NODE_OPTIONS=--openssl-legacy-provider lerna run build --scope @appbaseio/dejavu-browser --stream", 59 | "build:dejavu": "NODE_OPTIONS=--openssl-legacy-provider yarn build:browser && lerna run build --scope dejavu-main --stream", 60 | "build:dejavu:app": "NODE_OPTIONS=--openssl-legacy-provider yarn build:browser && lerna run build:app --scope dejavu-main --stream", 61 | "build:dejavu:site": "NODE_OPTIONS=--openssl-legacy-provider yarn build:browser && lerna run build:site --scope dejavu-main --stream", 62 | "build:chrome-extension": "yarn build:browser && lerna run build:chrome-extension --scope dejavu-main --stream", 63 | "format": "lerna run format --stream" 64 | }, 65 | "husky": { 66 | "hooks": { 67 | "pre-commit": "lerna run --concurrency 1 --stream precommit" 68 | } 69 | }, 70 | "private": true, 71 | "workspaces": { 72 | "packages": [ 73 | "packages/*" 74 | ] 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /packages/browser/.babelrc.js: -------------------------------------------------------------------------------- 1 | const { NODE_ENV } = process.env; 2 | const isProduction = NODE_ENV === 'production'; 3 | 4 | const presets = [ 5 | '@babel/preset-react', 6 | '@babel/preset-flow', 7 | [ 8 | '@babel/env', 9 | { 10 | targets: { 11 | edge: '17', 12 | firefox: '60', 13 | chrome: '67', 14 | safari: '11.1', 15 | }, 16 | useBuiltIns: 'usage', 17 | }, 18 | ], 19 | ]; 20 | 21 | const plugins = [ 22 | [ 23 | 'emotion', 24 | isProduction ? { hoist: true } : { sourceMap: true, autoLabel: true }, 25 | ], 26 | '@babel/plugin-proposal-class-properties', 27 | '@babel/plugin-transform-spread', 28 | '@babel/plugin-proposal-object-rest-spread', 29 | '@babel/plugin-syntax-dynamic-import', 30 | '@babel/plugin-proposal-export-default-from', 31 | '@babel/plugin-proposal-nullish-coalescing-operator', 32 | '@babel/plugin-proposal-optional-chaining', 33 | ]; 34 | 35 | module.exports = { presets, plugins }; 36 | -------------------------------------------------------------------------------- /packages/browser/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | indent_size = 4 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /packages/browser/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: 'babel-eslint', 3 | extends: [ 4 | 'airbnb', 5 | 'prettier', 6 | 'plugin:prettier/recommended', 7 | 'prettier/flowtype', 8 | 'prettier/react', 9 | 'prettier/standard', 10 | 'plugin:flowtype/recommended', 11 | 'plugin:jest/recommended', 12 | ], 13 | plugins: ['flowtype', 'prettier', 'jest'], 14 | env: { 15 | browser: true, 16 | }, 17 | rules: { 18 | 'react/jsx-filename-extension': [1, { extensions: ['.js'] }], 19 | 'react/forbid-prop-types': 0, 20 | 'react/destructuring-assignment': 0, 21 | 'react/require-default-props': 0, 22 | 'prettier/prettier': 'error', 23 | 'no-underscore-dangle': 0, 24 | 'jsx-a11y/click-events-have-key-events': 0, 25 | 'jsx-a11y/no-static-element-interactions': 0, 26 | }, 27 | }; 28 | -------------------------------------------------------------------------------- /packages/browser/.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | tabWidth: 4, 3 | singleQuote: true, 4 | trailingComma: 'all', 5 | useTabs: true, 6 | }; 7 | -------------------------------------------------------------------------------- /packages/browser/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2018 Appbase Inc. 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /packages/browser/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@appbaseio/dejavu-browser", 3 | "description": "Dejavu monorepo data browser components", 4 | "version": "3.8.3", 5 | "authors": [ 6 | "reactivesearch.io (https://github.com/appbaseio)" 7 | ], 8 | "main": "lib/index.js", 9 | "module": "lib/index.es.js", 10 | "peerDependencies": { 11 | "@ant-design/icons": "^4.0.0", 12 | "@appbaseio/reactivesearch": "^3.23.1", 13 | "antd": "^5.0.0", 14 | "brace": "^0.11.1", 15 | "lodash": "^4.17.10", 16 | "prop-types": "^15.6.2", 17 | "react": "^16.5.1", 18 | "react-ace": "^6.5.0", 19 | "react-dom": "^16.5.1", 20 | "react-emotion": "^9.2.12", 21 | "react-redux": "^5.0.7", 22 | "react-router-dom": "^4.3.1", 23 | "redux": "^3.7.2", 24 | "redux-saga": "^0.16.0", 25 | "redux-thunk": "^2.3.0", 26 | "url-parser-lite": "^0.1.0", 27 | "urlsafe-base64": "https://github.com/farhan687/urlsafe-base64.git" 28 | }, 29 | "dependencies": { 30 | "@divyanshu013/media": "^1.0.0", 31 | "@fortawesome/fontawesome-svg-core": "^1.2.27", 32 | "@fortawesome/free-solid-svg-icons": "^5.12.1", 33 | "@fortawesome/react-fontawesome": "^0.1.8", 34 | "cross-storage": "^1.0.0", 35 | "dayjs": "^1.11.7", 36 | "dom-helpers": "^3.4.0", 37 | "dompurify": "^2.3.6", 38 | "emotion": "9.2.6", 39 | "file-saver": "^2.0.0-rc.4", 40 | "flat": "^5.0.1", 41 | "ismobilejs": "^0.5.1", 42 | "moment": "^2.29.4", 43 | "papaparse": "^5.2.0", 44 | "react-custom-scrollbars": "^4.2.1", 45 | "react-joyride": "2.0.0-15", 46 | "react-json-editor-ajrm": "^2.5.8", 47 | "react-virtualized": "^9.21.0" 48 | }, 49 | "devDependencies": { 50 | "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", 51 | "@babel/plugin-proposal-optional-chaining": "^7.20.7", 52 | "react": "^16.12.0", 53 | "react-dom": "^16.12.0" 54 | }, 55 | "files": [ 56 | "lib/" 57 | ], 58 | "scripts": { 59 | "watch": "yarn build:commonjs -w && yarn build:es -w", 60 | "build": "NODE_OPTIONS=--openssl-legacy-provider yarn build:commonjs && yarn build:es", 61 | "build:commonjs": "babel --ignore __tests__ src --out-dir lib --copy-files src/images", 62 | "build:es": "babel src/index.js --out-file lib/index.es.js", 63 | "format": "prettier --write --use-tabs 'src/!(batteries*)**/**/*.js'", 64 | "precommit": "lint-staged" 65 | }, 66 | "lint-staged": { 67 | "./src/!(batteries*)**/*.js": [ 68 | "prettier --write --use-tabs", 69 | "git add" 70 | ] 71 | }, 72 | "repository": { 73 | "type": "git", 74 | "url": "https://github.com/appbaseio/dejavu" 75 | }, 76 | "gitHead": "c07862f02b67828a2fb196749a7396c3755fe3f7" 77 | } 78 | -------------------------------------------------------------------------------- /packages/browser/src/actions/analyzers.js: -------------------------------------------------------------------------------- 1 | import { ANALYZERS } from './constants'; 2 | 3 | const setAnalyzers = analyzers => ({ 4 | type: ANALYZERS.SET_ANALYZERS, 5 | analyzers, 6 | }); 7 | 8 | export default setAnalyzers; 9 | -------------------------------------------------------------------------------- /packages/browser/src/actions/app.js: -------------------------------------------------------------------------------- 1 | import { APP } from './constants'; 2 | 3 | const connectApp = (appname, url, headers) => ({ 4 | type: APP.CONNECT_REQUEST, 5 | appname, 6 | url, 7 | headers, 8 | }); 9 | 10 | const connectAppSuccess = (appname, url) => ({ 11 | type: APP.CONNECT_SUCCESS, 12 | appname, 13 | url, 14 | }); 15 | 16 | const connectAppFailure = () => ({ 17 | type: APP.CONNECT_FAILURE, 18 | }); 19 | 20 | const disconnectApp = () => ({ 21 | type: APP.DISCONNECT, 22 | }); 23 | 24 | const setHeaders = headers => ({ 25 | type: APP.SET_HEADERS, 26 | headers, 27 | }); 28 | 29 | const reloadApp = () => ({ 30 | type: APP.RELOAD_APP, 31 | }); 32 | 33 | export { 34 | connectApp, 35 | disconnectApp, 36 | connectAppSuccess, 37 | connectAppFailure, 38 | setHeaders, 39 | reloadApp, 40 | }; 41 | -------------------------------------------------------------------------------- /packages/browser/src/actions/applyQuery.js: -------------------------------------------------------------------------------- 1 | import { APPLY_QUERY } from './constants'; 2 | 3 | const setApplyQuery = applyQuery => ({ 4 | type: APPLY_QUERY.SET_APPLY_QUERY, 5 | applyQuery, 6 | }); 7 | 8 | export default setApplyQuery; 9 | -------------------------------------------------------------------------------- /packages/browser/src/actions/cell.js: -------------------------------------------------------------------------------- 1 | import { CELL } from './constants'; 2 | 3 | const setCellActive = (row, column) => ({ 4 | type: CELL.CELL_ACTIVE, 5 | row, 6 | column, 7 | }); 8 | 9 | const setCellHighlight = (row, column) => ({ 10 | type: CELL.CELL_HIGHLIGHT, 11 | row, 12 | column, 13 | }); 14 | 15 | const setCellValueRequest = (id, property, value, index, esType) => ({ 16 | type: CELL.CELL_SETVALUE_REQUEST, 17 | id, 18 | property, 19 | value, 20 | index, 21 | esType, 22 | }); 23 | 24 | const setCellValueSuccess = () => ({ type: CELL.CELL_SETVALUE_SUCCESS }); // update data 25 | 26 | const setCellValueFailure = () => ({ 27 | type: CELL.CELL_SETVALUE_FAILURE, 28 | }); 29 | 30 | export { 31 | setCellActive, 32 | setCellHighlight, 33 | setCellValueRequest, 34 | setCellValueSuccess, 35 | setCellValueFailure, 36 | }; 37 | -------------------------------------------------------------------------------- /packages/browser/src/actions/constants.js: -------------------------------------------------------------------------------- 1 | const APP = { 2 | CONNECT_REQUEST: 'CONNECT_REQUEST', 3 | CONNECT_SUCCESS: 'CONNECT_SUCCESS', 4 | CONNECT_FAILURE: 'CONNECT_FAILURE', 5 | DISCONNECT: 'DISCONNECT', 6 | SET_HEADERS: 'SET_HEADERS', 7 | RELOAD_APP: 'RELOAD_APP', 8 | }; 9 | 10 | const MAPPINGS = { 11 | MAPPINGS_FETCH_REQUEST: 'MAPPINGS_FETCH_REQUEST', 12 | MAPPINGS_FETCH_SUCCESS: 'MAPPINGS_FETCH_SUCCESS', 13 | MAPPINGS_FETCH_FAILURE: 'MAPPINGS_FETCH_FAILURE', 14 | ADD_MAPPING_REQUEST: 'ADD_MAPPING_REQUEST', 15 | ADD_MAPPING_SUCCESS: 'ADD_MAPPING_SUCCESS', 16 | ADD_MAPPING_FAILURE: 'ADD_MAPPING_FAILURE', 17 | SET_VISIBLE_COLUMNS: 'SET_VISIBLE_COLUMNS', 18 | SET_NESTED_VISIBLE_COLUMNS: 'SET_NESTED_VISIBLE_COLUMNS', 19 | SET_ARRAY_FIELDS: 'SET_ARRAY_FIELDS', 20 | }; 21 | 22 | const CELL = { 23 | CELL_ACTIVE: 'CELL_ACTIVE', 24 | CELL_HIGHLIGHT: 'CELL_HIGHLIGHT', 25 | CELL_SETVALUE_REQUEST: 'CELL_SETVALUE_REQUEST', 26 | CELL_SETVALUE_SUCCESS: 'CELL_SETVALUE_SUCCESS', 27 | CELL_SETVALUE_FAILURE: 'CELL_SETVALUE_FAILURE', 28 | }; 29 | 30 | const DATA = { 31 | ADD_DATA_REQUEST: 'ADD_DATA_REQUEST', 32 | ADD_DATA_SUCCESS: 'ADD_DATA_SUCCESS', 33 | ADD_DATA_FAILUREL: 'ADD_DATA_FAILURE', 34 | UPDATE_REACTIVE_LIST: 'UPDATE_REACTIVE_LIST', 35 | }; 36 | 37 | const MODE = { 38 | SET_MODE: 'SET_MODE', 39 | }; 40 | 41 | const ERROR = { 42 | SET_ERROR: 'SET_ERROR', 43 | CLEAR_ERROR: 'CLEAR_ERROR', 44 | }; 45 | 46 | const SELECTED_ROWS = { 47 | SET_SELECTED_ROWS: 'SET_SELECTED_ROWS', 48 | }; 49 | 50 | const UPDATING_ROW = { 51 | SET_UPDATING_ROW: 'SET_UPDATING_ROW', 52 | }; 53 | 54 | const CURRENT_IDS = { 55 | SET_CURRENT_IDS: 'SET_CURRENT_IDS', 56 | }; 57 | 58 | const SORT = { 59 | SET_SORT: 'SET_SORT', 60 | RESET_SORT: 'RESET_SORT', 61 | }; 62 | 63 | const PAGE_SIZE = { 64 | SET_PAGE_SIZE: 'SET_PAGE_SIZE', 65 | }; 66 | 67 | const NESTED_COLUMNS = { 68 | SET_IS_SHOWING_NESTED_COLUMNS: 'SET_IS_SHOWING_NESTED_COLUMNS', 69 | }; 70 | 71 | const VERSION = { 72 | SET_VERSION: 'SET_VERSION', 73 | }; 74 | 75 | const QUERY = { 76 | SET_QUERY: 'SET_QUERY', 77 | }; 78 | 79 | const SELECT_ALL = { 80 | SET_SELECT_ALL: 'SET_SELECT_ALL', 81 | }; 82 | 83 | const APPLY_QUERY = { 84 | SET_APPLY_QUERY: 'SET_APPLY_QUERY', 85 | }; 86 | 87 | const STATS = { 88 | SET_STATS: 'SET_STATS', 89 | }; 90 | 91 | const ANALYZERS = { 92 | SET_ANALYZERS: 'SET_ANALYZERS', 93 | }; 94 | 95 | export { 96 | APP, 97 | CELL, 98 | DATA, 99 | MAPPINGS, 100 | MODE, 101 | ERROR, 102 | SELECTED_ROWS, 103 | UPDATING_ROW, 104 | CURRENT_IDS, 105 | SORT, 106 | PAGE_SIZE, 107 | NESTED_COLUMNS, 108 | VERSION, 109 | QUERY, 110 | SELECT_ALL, 111 | APPLY_QUERY, 112 | STATS, 113 | ANALYZERS, 114 | }; 115 | -------------------------------------------------------------------------------- /packages/browser/src/actions/currentIds.js: -------------------------------------------------------------------------------- 1 | // action to list current rendered ids 2 | import { CURRENT_IDS } from './constants'; 3 | 4 | const setCurrentIds = ids => ({ 5 | type: CURRENT_IDS.SET_CURRENT_IDS, 6 | ids, 7 | }); 8 | 9 | export default setCurrentIds; 10 | -------------------------------------------------------------------------------- /packages/browser/src/actions/data.js: -------------------------------------------------------------------------------- 1 | import { DATA } from './constants'; 2 | 3 | const addDataRequest = (indexName, typeName, docId, data, tab, version) => ({ 4 | type: DATA.ADD_DATA_REQUEST, 5 | indexName, 6 | typeName, 7 | docId, 8 | data, 9 | tab, 10 | version, 11 | }); 12 | 13 | const addDataSuccess = () => ({ 14 | type: DATA.ADD_DATA_SUCCESS, 15 | }); 16 | 17 | const addDataFailure = () => ({ 18 | type: DATA.ADD_DATA_FAILURE, 19 | }); 20 | 21 | const updateReactiveList = () => ({ 22 | type: DATA.UPDATE_REACTIVE_LIST, 23 | }); 24 | 25 | export { addDataRequest, addDataSuccess, addDataFailure, updateReactiveList }; 26 | -------------------------------------------------------------------------------- /packages/browser/src/actions/error.js: -------------------------------------------------------------------------------- 1 | import { ERROR } from './constants'; 2 | 3 | export const setError = error => ({ 4 | type: ERROR.SET_ERROR, 5 | error, 6 | }); 7 | 8 | export const clearError = () => ({ 9 | type: ERROR.CLEAR_ERROR, 10 | }); 11 | -------------------------------------------------------------------------------- /packages/browser/src/actions/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | connectApp, 3 | disconnectApp, 4 | connectAppSuccess, 5 | connectAppFailure, 6 | setHeaders, 7 | } from './app'; 8 | import { 9 | fetchMappings, 10 | fetchMappingsSuccess, 11 | fetchMappingsFailure, 12 | addMappingRequest, 13 | addMappingSuccess, 14 | addMappingFailure, 15 | setArrayFields, 16 | } from './mappings'; 17 | import { 18 | setCellActive, 19 | setCellHighlight, 20 | setCellValueRequest, 21 | setCellValueSuccess, 22 | setCellValueFailure, 23 | } from './cell'; 24 | import setMode from './mode'; 25 | import { setError, clearError } from './error'; 26 | import { 27 | addDataRequest, 28 | addDataSuccess, 29 | addDataFailure, 30 | updateReactiveList, 31 | } from './data'; 32 | import setSelectedRows from './selectedRows'; 33 | import setUpdatingRow from './updatingRow'; 34 | import setCurrentIds from './currentIds'; 35 | import { setSort, resetSort } from './sort'; 36 | import setPageSize from './pageSize'; 37 | import setIsShwoingNestedColumn from './nestedColumns'; 38 | import setVersion from './version'; 39 | import setQuery from './query'; 40 | import setSelectAll from './selectAll'; 41 | import setApplyQuery from './applyQuery'; 42 | import setStats from './stats'; 43 | import setAnalyzers from './analyzers'; 44 | 45 | export { 46 | // app 47 | connectApp, 48 | disconnectApp, 49 | connectAppSuccess, 50 | connectAppFailure, 51 | setHeaders, 52 | // mappings 53 | fetchMappings, 54 | fetchMappingsSuccess, 55 | fetchMappingsFailure, 56 | addMappingRequest, 57 | addMappingSuccess, 58 | addMappingFailure, 59 | setArrayFields, 60 | // cell 61 | setCellActive, 62 | setCellHighlight, 63 | setCellValueRequest, 64 | setCellValueSuccess, 65 | setCellValueFailure, 66 | // data 67 | addDataRequest, 68 | addDataSuccess, 69 | addDataFailure, 70 | updateReactiveList, 71 | // mode 72 | setMode, 73 | // error 74 | setError, 75 | clearError, 76 | // selectedRows, 77 | setSelectedRows, 78 | // updatingRow, 79 | setUpdatingRow, 80 | // setting current ids 81 | setCurrentIds, 82 | // sort 83 | setSort, 84 | resetSort, 85 | // page size 86 | setPageSize, 87 | // nested columns, 88 | setIsShwoingNestedColumn, 89 | // version 90 | setVersion, 91 | // query 92 | setQuery, 93 | // select all 94 | setSelectAll, 95 | // apply query 96 | setApplyQuery, 97 | // stats 98 | setStats, 99 | // analyzers 100 | setAnalyzers, 101 | }; 102 | -------------------------------------------------------------------------------- /packages/browser/src/actions/mappings.js: -------------------------------------------------------------------------------- 1 | import { MAPPINGS } from './constants'; 2 | 3 | const fetchMappings = () => ({ 4 | type: MAPPINGS.MAPPINGS_FETCH_REQUEST, 5 | }); 6 | 7 | const fetchMappingsSuccess = ( 8 | data, 9 | indexes, 10 | types, 11 | indexTypeMap, 12 | columns, 13 | visibleColumns, 14 | searchableColumns, 15 | typePropertyMapping, 16 | nestedVisibleColumns, 17 | nestedSearchableColumns, 18 | nestedColumns, 19 | termsAggregationColumns, 20 | sortableColumns, 21 | shouldShowNestedSwitch, 22 | searchableColumnsWeights, 23 | nestedSearchableColumnsWeights, 24 | ) => ({ 25 | type: MAPPINGS.MAPPINGS_FETCH_SUCCESS, 26 | data, 27 | indexes, 28 | types, 29 | indexTypeMap, 30 | columns, 31 | visibleColumns, 32 | searchableColumns, 33 | typePropertyMapping, 34 | nestedVisibleColumns, 35 | nestedSearchableColumns, 36 | nestedColumns, 37 | termsAggregationColumns, 38 | sortableColumns, 39 | shouldShowNestedSwitch, 40 | searchableColumnsWeights, 41 | nestedSearchableColumnsWeights, 42 | }); 43 | 44 | const fetchMappingsFailure = () => ({ 45 | type: MAPPINGS.MAPPINGS_FETCH_FAILURE, 46 | }); 47 | 48 | const addMappingRequest = (indexName, typeName, field, mapping, version) => ({ 49 | type: MAPPINGS.ADD_MAPPING_REQUEST, 50 | field, 51 | mapping, 52 | indexName, 53 | typeName, 54 | version, 55 | }); 56 | 57 | const addMappingSuccess = () => ({ 58 | type: MAPPINGS.ADD_MAPPING_SUCCESS, 59 | }); 60 | 61 | const addMappingFailure = () => ({ 62 | type: MAPPINGS.ADD_MAPPING_FAILURE, 63 | }); 64 | 65 | const setVisibleColumns = visibleColumns => ({ 66 | type: MAPPINGS.SET_VISIBLE_COLUMNS, 67 | visibleColumns, 68 | }); 69 | 70 | const setNestedVisibleColumns = nestedVisibleColumns => ({ 71 | type: MAPPINGS.SET_NESTED_VISIBLE_COLUMNS, 72 | nestedVisibleColumns, 73 | }); 74 | 75 | const setArrayFields = ( 76 | nestedColumns, 77 | nestedVisibleColumns, 78 | nestedMappings, 79 | appName, 80 | typePropertyMapping, 81 | ) => ({ 82 | type: MAPPINGS.SET_ARRAY_FIELDS, 83 | nestedColumns, 84 | nestedVisibleColumns, 85 | nestedMappings, 86 | appName, 87 | typePropertyMapping, 88 | }); 89 | 90 | export { 91 | fetchMappings, 92 | fetchMappingsSuccess, 93 | fetchMappingsFailure, 94 | addMappingRequest, 95 | addMappingSuccess, 96 | addMappingFailure, 97 | setVisibleColumns, 98 | setNestedVisibleColumns, 99 | setArrayFields, 100 | }; 101 | -------------------------------------------------------------------------------- /packages/browser/src/actions/mode.js: -------------------------------------------------------------------------------- 1 | import { MODE } from './constants'; 2 | 3 | const setMode = mode => ({ 4 | type: MODE.SET_MODE, 5 | mode, 6 | }); 7 | 8 | export default setMode; 9 | -------------------------------------------------------------------------------- /packages/browser/src/actions/nestedColumns.js: -------------------------------------------------------------------------------- 1 | import { NESTED_COLUMNS } from './constants'; 2 | 3 | const setIsShwoingNestedColumn = isShowingNestedColumns => ({ 4 | type: NESTED_COLUMNS.SET_IS_SHOWING_NESTED_COLUMNS, 5 | isShowingNestedColumns, 6 | }); 7 | 8 | export default setIsShwoingNestedColumn; 9 | -------------------------------------------------------------------------------- /packages/browser/src/actions/pageSize.js: -------------------------------------------------------------------------------- 1 | import { PAGE_SIZE } from './constants'; 2 | 3 | const setPageSize = pageSize => ({ 4 | type: PAGE_SIZE.SET_PAGE_SIZE, 5 | pageSize, 6 | }); 7 | 8 | export default setPageSize; 9 | -------------------------------------------------------------------------------- /packages/browser/src/actions/query.js: -------------------------------------------------------------------------------- 1 | import { QUERY } from './constants'; 2 | 3 | const setQuery = query => ({ 4 | type: QUERY.SET_QUERY, 5 | query, 6 | }); 7 | 8 | export default setQuery; 9 | -------------------------------------------------------------------------------- /packages/browser/src/actions/selectAll.js: -------------------------------------------------------------------------------- 1 | import { SELECT_ALL } from './constants'; 2 | 3 | const setSelectAll = selectAll => ({ 4 | type: SELECT_ALL.SET_SELECT_ALL, 5 | selectAll, 6 | }); 7 | 8 | export default setSelectAll; 9 | -------------------------------------------------------------------------------- /packages/browser/src/actions/selectedRows.js: -------------------------------------------------------------------------------- 1 | import { SELECTED_ROWS } from './constants'; 2 | 3 | const setSelectedRows = selectedRows => ({ 4 | type: SELECTED_ROWS.SET_SELECTED_ROWS, 5 | selectedRows, 6 | }); 7 | 8 | export default setSelectedRows; 9 | -------------------------------------------------------------------------------- /packages/browser/src/actions/sort.js: -------------------------------------------------------------------------------- 1 | import { SORT } from './constants'; 2 | 3 | export const setSort = (order, field) => ({ 4 | type: SORT.SET_SORT, 5 | order, 6 | field, 7 | }); 8 | 9 | export const resetSort = () => ({ 10 | type: SORT.RESET_SORT, 11 | }); 12 | -------------------------------------------------------------------------------- /packages/browser/src/actions/stats.js: -------------------------------------------------------------------------------- 1 | import { STATS } from './constants'; 2 | 3 | const setStats = stats => ({ 4 | type: STATS.SET_STATS, 5 | stats, 6 | }); 7 | 8 | export default setStats; 9 | -------------------------------------------------------------------------------- /packages/browser/src/actions/updatingRow.js: -------------------------------------------------------------------------------- 1 | import { UPDATING_ROW } from './constants'; 2 | 3 | const setUpdatingRow = rowData => ({ 4 | type: UPDATING_ROW.SET_UPDATING_ROW, 5 | rowData, 6 | }); 7 | 8 | export default setUpdatingRow; 9 | -------------------------------------------------------------------------------- /packages/browser/src/actions/version.js: -------------------------------------------------------------------------------- 1 | import { VERSION } from './constants'; 2 | 3 | const setVersion = version => ({ 4 | type: VERSION.SET_VERSION, 5 | version, 6 | }); 7 | 8 | export default setVersion; 9 | -------------------------------------------------------------------------------- /packages/browser/src/apis/app.js: -------------------------------------------------------------------------------- 1 | import { 2 | parseUrl, 3 | getHeaders, 4 | isEmptyObject, 5 | getCustomHeaders, 6 | convertArrayToHeaders, 7 | } from '../utils'; 8 | import CustomError from '../utils/CustomError'; 9 | 10 | const testConnection = async (appname, rawUrl, reqHeaders = []) => { 11 | const defaultError = 'Unable to connect'; 12 | try { 13 | const { url } = parseUrl(rawUrl); 14 | const headers = getHeaders(rawUrl); 15 | const storageHeaders = getCustomHeaders(appname); 16 | const customHeaders = reqHeaders.length ? reqHeaders : storageHeaders; 17 | 18 | const res = await fetch(`${url}/${appname}`, { 19 | 'Content-Type': 'application/json', 20 | headers: { ...headers, ...convertArrayToHeaders(customHeaders) }, 21 | }).then(response => response.json()); 22 | 23 | if (res.status >= 400 || (res.error && res.error.code >= 400)) { 24 | throw new CustomError( 25 | JSON.stringify(res.error, null, 2), 26 | `HTTP STATUS: ${res.status >= 400 || 27 | (res.error && res.error.code 28 | ? res.error.code 29 | : 400)} - ${defaultError}`, 30 | ); 31 | } 32 | 33 | if (isEmptyObject(res)) { 34 | throw new CustomError( 35 | JSON.stringify( 36 | { 37 | error: `Unable to find ${appname}`, 38 | }, 39 | null, 40 | 2, 41 | ), 42 | `Error: Index not found`, 43 | ); 44 | } 45 | return res; 46 | } catch (error) { 47 | const err = error; 48 | let description = ` Possible Errors 49 | `; 51 | 52 | if (err.message === 'Failed to fetch') { 53 | err.message = defaultError; 54 | } 55 | 56 | if (err.message === 'NetworkError when attempting to fetch resource.') { 57 | description = `You are trying to load http content over https. 58 | You might have to enable mixed content of your browser 59 | https://kb.iu.edu/d/bdny`; 60 | } 61 | 62 | throw new CustomError( 63 | err.description && !isEmptyObject(JSON.parse(err.description)) 64 | ? err.description 65 | : description, 66 | err.message || defaultError, 67 | err.stack, 68 | ); 69 | } 70 | }; 71 | 72 | export default testConnection; 73 | -------------------------------------------------------------------------------- /packages/browser/src/apis/cell.js: -------------------------------------------------------------------------------- 1 | import { unflatten } from 'flat'; 2 | 3 | import CustomError from '../utils/CustomError'; 4 | import { updateDocument } from './data'; 5 | 6 | const setCellValue = async (app, type, rawUrl, id, property, value) => { 7 | const doc = unflatten({ [property]: value }); 8 | const formattedId = encodeURIComponent(id); 9 | try { 10 | return updateDocument({ 11 | index: app, 12 | id: formattedId, 13 | url: rawUrl, 14 | document: doc, 15 | }); 16 | } catch (error) { 17 | const errorMessage = error.message || 'Unable to update data'; 18 | 19 | throw new CustomError( 20 | error.description || errorMessage, 21 | errorMessage, 22 | error.stack, 23 | ); 24 | } 25 | }; 26 | 27 | export default setCellValue; 28 | -------------------------------------------------------------------------------- /packages/browser/src/apis/count.js: -------------------------------------------------------------------------------- 1 | import { 2 | parseUrl, 3 | getCustomHeaders, 4 | convertArrayToHeaders, 5 | getHeaders, 6 | } from '../utils'; 7 | import CustomError from '../utils/CustomError'; 8 | 9 | const getCount = async (app, type, rawUrl, version) => { 10 | const defaultError = 'Unable to get count'; 11 | try { 12 | const { url } = parseUrl(rawUrl); 13 | const headers = getHeaders(rawUrl); 14 | const customHeaders = getCustomHeaders(app); 15 | 16 | let updatedUrl = `${url}/${app}/_count`; 17 | 18 | // types were removed in ES 7 19 | if (version === 5 || version === 6) { 20 | updatedUrl = `${url}/${app}/${type}/_count`; 21 | } 22 | 23 | const res = await fetch(updatedUrl, { 24 | headers: { 25 | ...headers, 26 | ...convertArrayToHeaders(customHeaders), 27 | }, 28 | method: 'GET', 29 | }).then(response => response.json()); 30 | 31 | if (res.status >= 400) { 32 | throw new CustomError( 33 | JSON.stringify(res.error || res, null, 2), 34 | `HTTP STATUS: ${res.status} - ${res.message || defaultError}`, 35 | ); 36 | } 37 | return res; 38 | } catch (error) { 39 | const errorMessage = error.message || defaultError; 40 | 41 | throw new CustomError( 42 | error.description || errorMessage, 43 | errorMessage, 44 | error.stack, 45 | ); 46 | } 47 | }; 48 | 49 | export default getCount; 50 | -------------------------------------------------------------------------------- /packages/browser/src/apis/index.js: -------------------------------------------------------------------------------- 1 | import testConnection from './app'; 2 | import { fetchMappings, addMapping } from './mappings'; 3 | import setCellValue from './cell'; 4 | import { updateDocument, deleteData, addData } from './data'; 5 | import getVersion from './version'; 6 | import getCount from './count'; 7 | import search from './search'; 8 | import { getAnalyzersApi, closeApp, openApp, putSettings } from './analyzers'; 9 | 10 | export { 11 | testConnection, 12 | fetchMappings, 13 | addMapping, 14 | setCellValue, 15 | updateDocument, 16 | deleteData, 17 | addData, 18 | getVersion, 19 | getCount, 20 | search, 21 | getAnalyzersApi, 22 | closeApp, 23 | openApp, 24 | putSettings, 25 | }; 26 | -------------------------------------------------------------------------------- /packages/browser/src/apis/mappings.js: -------------------------------------------------------------------------------- 1 | import { 2 | parseUrl, 3 | getHeaders, 4 | convertArrayToHeaders, 5 | getCustomHeaders, 6 | } from '../utils'; 7 | import CustomError from '../utils/CustomError'; 8 | 9 | const fetchMappings = async (appname, rawUrl) => { 10 | const defaultError = 'Unable to fetch mappings'; 11 | try { 12 | const { url } = parseUrl(rawUrl); 13 | const headers = getHeaders(rawUrl); 14 | const customHeaders = getCustomHeaders(appname); 15 | 16 | const res = await fetch(`${url}/${appname}/_mapping`, { 17 | headers: { 18 | ...headers, 19 | ...convertArrayToHeaders(customHeaders), 20 | }, 21 | }).then(response => response.json()); 22 | if (res.status >= 400 || (res.error && res.error.code >= 400)) { 23 | throw new CustomError( 24 | JSON.stringify(res.error, null, 2), 25 | `HTTP STATUS: ${res.status >= 400 || 26 | (res.error && res.error.code 27 | ? res.error.code 28 | : 400)} - ${defaultError}`, 29 | ); 30 | } 31 | return res; 32 | } catch (error) { 33 | throw new CustomError( 34 | error.description || defaultError, 35 | error.message, 36 | error.stack, 37 | ); 38 | } 39 | }; 40 | 41 | const addMapping = async ( 42 | indexName, 43 | typeName, 44 | rawUrl, 45 | field, 46 | mapping, 47 | version, 48 | ) => { 49 | const defaultError = 'Unable to add mapping'; 50 | try { 51 | const { url } = parseUrl(rawUrl); 52 | const headers = getHeaders(rawUrl); 53 | const customHeaders = getCustomHeaders(indexName); 54 | const apiUrl = `${url}/${indexName}/_mapping${ 55 | version === 5 || version === 6 ? `/${typeName}` : '' 56 | }`; 57 | const res = await fetch(apiUrl, { 58 | headers: { 59 | ...headers, 60 | ...convertArrayToHeaders(customHeaders), 61 | }, 62 | method: 'PUT', 63 | body: JSON.stringify({ 64 | properties: { 65 | [field]: mapping, 66 | }, 67 | }), 68 | }).then(response => response.json()); 69 | if (res.status >= 400 || (res.error && res.error.code >= 400)) { 70 | throw new CustomError( 71 | JSON.stringify(res.error, null, 2), 72 | `HTTP STATUS: ${res.status >= 400 || 73 | (res.error && res.error.code 74 | ? res.error.code 75 | : 400)} - ${defaultError}`, 76 | ); 77 | } 78 | return res; 79 | } catch (error) { 80 | throw new CustomError( 81 | error.description || defaultError, 82 | error.message, 83 | error.stack, 84 | ); 85 | } 86 | }; 87 | 88 | export { fetchMappings, addMapping }; 89 | -------------------------------------------------------------------------------- /packages/browser/src/apis/search.js: -------------------------------------------------------------------------------- 1 | import { 2 | parseUrl, 3 | getCustomHeaders, 4 | convertArrayToHeaders, 5 | getHeaders, 6 | } from '../utils'; 7 | import CustomError from '../utils/CustomError'; 8 | 9 | const search = async (app, type, rawUrl, version, fetchData) => { 10 | const defaultError = 'Unable to get count'; 11 | try { 12 | const { url } = parseUrl(rawUrl); 13 | const headers = getHeaders(rawUrl); 14 | const customHeaders = getCustomHeaders(app); 15 | 16 | let updatedUrl = `${url}/${app}/_search`; 17 | 18 | if (version === 5 || version === 6) { 19 | updatedUrl = `${url}/${app}/${type ? `${type}/` : ''}_search`; 20 | } 21 | 22 | const res = await fetch(updatedUrl, { 23 | headers: { 24 | ...headers, 25 | ...convertArrayToHeaders(customHeaders), 26 | }, 27 | method: 'POST', 28 | body: JSON.stringify(fetchData), 29 | }).then(response => response.json()); 30 | 31 | if (res.status >= 400) { 32 | throw new CustomError( 33 | JSON.stringify(res.error || res, null, 2), 34 | `HTTP STATUS: ${res.status} - ${res.message || defaultError}`, 35 | ); 36 | } 37 | return res; 38 | } catch (error) { 39 | const errorMessage = error.message || defaultError; 40 | 41 | throw new CustomError( 42 | error.description || errorMessage, 43 | errorMessage, 44 | error.stack, 45 | ); 46 | } 47 | }; 48 | 49 | export default search; 50 | -------------------------------------------------------------------------------- /packages/browser/src/apis/version.js: -------------------------------------------------------------------------------- 1 | import CustomError from '../utils/CustomError'; 2 | import { 3 | parseUrl, 4 | getHeaders, 5 | convertArrayToHeaders, 6 | getCustomHeaders, 7 | } from '../utils'; 8 | 9 | export default async (rawUrl, indexName) => { 10 | const defaultError = 'Unable to get version'; 11 | let version = ''; 12 | 13 | try { 14 | const { url } = parseUrl(rawUrl); 15 | const headers = getHeaders(rawUrl); 16 | 17 | // Main logic: Fetch `${url}/` and read `version.number` 18 | const res = await fetch(url, { 19 | headers: { 20 | ...headers, 21 | }, 22 | method: 'GET', 23 | }).then(response => response.json()); 24 | 25 | if (res.status >= 400 || (res.error && res.error.code >= 400)) { 26 | // If error, use fallback logic 27 | throw new Error('Error fetching version from root endpoint'); 28 | } 29 | 30 | version = res.version && res.version.number; 31 | 32 | if (!version) { 33 | // If version is not obtained, use fallback logic 34 | throw new Error('Version not found in root endpoint response'); 35 | } 36 | } catch (error) { 37 | // Fallback logic 38 | try { 39 | const { url } = parseUrl(rawUrl); 40 | const headers = getHeaders(rawUrl); 41 | 42 | let fetchUrl = url; 43 | let fetchHeaders = {}; 44 | 45 | if (indexName) { 46 | fetchUrl = `${url}/${indexName}/_settings`; 47 | fetchHeaders = convertArrayToHeaders( 48 | getCustomHeaders(indexName), 49 | ); 50 | } 51 | 52 | const res = await fetch(fetchUrl, { 53 | headers: { 54 | ...headers, 55 | ...fetchHeaders, 56 | }, 57 | method: 'GET', 58 | }).then(response => response.json()); 59 | 60 | if (res.status >= 400 || (res.error && res.error.code >= 400)) { 61 | throw new CustomError( 62 | JSON.stringify(res.error, null, 2), 63 | `HTTP STATUS: ${res.status >= 400 || 64 | (res.error && res.error.code 65 | ? res.error.code 66 | : 400)} - ${defaultError}`, 67 | ); 68 | } 69 | 70 | if (indexName) { 71 | const defaultIndex = Object.keys(res)[0]; 72 | if (defaultIndex) { 73 | version = 74 | res[defaultIndex].settings.index.version.upgraded || 75 | res[defaultIndex].settings.index.version.created; 76 | } else { 77 | version = '7'; 78 | } 79 | } else { 80 | version = res.version && res.version.number; 81 | } 82 | 83 | if (!version) { 84 | version = '7'; 85 | } 86 | } catch (fallbackError) { 87 | throw new CustomError( 88 | fallbackError.description || defaultError, 89 | fallbackError.message, 90 | fallbackError.stack, 91 | ); 92 | } 93 | } 94 | 95 | // Process version to get major version number 96 | let majorVersion = version.split('.')[0]; 97 | 98 | if (!majorVersion) { 99 | majorVersion = '7'; 100 | } 101 | 102 | return majorVersion; 103 | }; 104 | -------------------------------------------------------------------------------- /packages/browser/src/components/Cell/ArrayCell.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React, { Component, Fragment } from 'react'; 4 | import { Select, Popover } from 'antd'; 5 | import { func, string, any } from 'prop-types'; 6 | 7 | import { MODES } from '../../constants'; 8 | import JsonView from '../JsonView'; 9 | 10 | import popoverContent from '../CommonStyles/popoverContent'; 11 | import { NUMERIC_DATATYPES } from '../../utils/mappings'; 12 | 13 | type Props = { 14 | children: [], 15 | onChange: func, 16 | mode: string, 17 | mappingType: string, 18 | }; 19 | 20 | type State = { 21 | data: any, 22 | }; 23 | 24 | const { Option } = Select; 25 | 26 | const parseData = (value = [], dataType) => { 27 | if (NUMERIC_DATATYPES.includes(dataType)) 28 | return value.map(val => Number(val)); 29 | return value; 30 | }; 31 | 32 | class ArrayCell extends Component { 33 | state = { 34 | data: this.props.children || [], 35 | }; 36 | 37 | handleChange = (value: any) => { 38 | const { mappingType } = this.props; 39 | this.setState({ data: value }); 40 | this.props.onChange(parseData(value, mappingType)); 41 | }; 42 | 43 | render() { 44 | const { data } = this.state; 45 | const { mode } = this.props; 46 | 47 | return ( 48 | 49 | {mode === MODES.EDIT ? ( 50 | 84 | ) : ( 85 | Boolean(data.length) && ( 86 | 89 | 90 | 91 | } 92 | trigger="click" 93 | > 94 | [...] 95 | 96 | ) 97 | )} 98 | 99 | ); 100 | } 101 | } 102 | 103 | ArrayCell.propTypes = { 104 | onChange: func.isRequired, 105 | children: any, 106 | mode: string, 107 | mappingType: string, 108 | }; 109 | 110 | export default ArrayCell; 111 | -------------------------------------------------------------------------------- /packages/browser/src/components/Cell/BooleanCell.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react'; 4 | import { Select } from 'antd'; 5 | import { func, string, any } from 'prop-types'; 6 | 7 | import CellStyled from './Cell.styles'; 8 | import { MODES } from '../../constants'; 9 | 10 | const { Option } = Select; 11 | 12 | type Props = { 13 | children: boolean, 14 | onChange: func, 15 | mode: string, 16 | }; 17 | 18 | const BooleanCell = ({ children, onChange, mode }: Props) => ( 19 | 20 | {mode === MODES.EDIT ? ( 21 | 34 | ) : ( 35 | children !== 'undefined' && {String(children)} 36 | )} 37 | 38 | ); 39 | 40 | BooleanCell.propTypes = { 41 | onChange: func.isRequired, 42 | children: any, 43 | mode: string, 44 | }; 45 | 46 | export default BooleanCell; 47 | -------------------------------------------------------------------------------- /packages/browser/src/components/Cell/Cell.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react'; 4 | import { func, bool, any, object } from 'prop-types'; 5 | 6 | import { isObject } from '../../utils'; 7 | import { dateFormatMap } from '../../utils/date'; 8 | 9 | import BooleanCell from './BooleanCell'; 10 | import TextCell from './TextCell'; 11 | import NumberCell from './NumberCell'; 12 | import ArrayCell from './ArrayCell'; 13 | import DateCell from './DateCell'; 14 | import ObjectCell from './ObjectCell'; 15 | 16 | type Props = { 17 | mapping: object, 18 | children: any, 19 | active?: boolean, 20 | onChange: func, 21 | mode: string, 22 | column: string, 23 | row: string, 24 | }; 25 | 26 | const Cell = ({ mapping, column, row, ...props }: Props) => { 27 | if (mapping && (mapping.type || mapping.properties)) { 28 | switch (mapping.type) { 29 | case 'boolean': 30 | return ; 31 | case 'integer': 32 | case 'float': 33 | case 'long': 34 | case 'double': 35 | if (Array.isArray(props.children)) { 36 | return ( 37 | 42 | ); 43 | } 44 | return ; 45 | case 'date': 46 | return ( 47 | 51 | ); 52 | case 'object': 53 | case 'geo_point': 54 | case 'geo_shape': 55 | return ; 56 | case 'string': 57 | case 'text': 58 | case 'keyword': 59 | if (Array.isArray(props.children) && props) { 60 | return ( 61 | 66 | ); 67 | } 68 | if (isObject(mapping.properties)) { 69 | return ; 70 | } 71 | return ; 72 | default: 73 | return ; 74 | } 75 | } else { 76 | return null; 77 | } 78 | }; 79 | 80 | Cell.propTypes = { 81 | children: any, 82 | active: bool, 83 | onChange: func.isRequired, 84 | }; 85 | 86 | export default Cell; 87 | -------------------------------------------------------------------------------- /packages/browser/src/components/Cell/Cell.styles.js: -------------------------------------------------------------------------------- 1 | import styled from 'react-emotion'; 2 | 3 | const Cell = styled('div')( 4 | { 5 | height: '100%', 6 | width: '100%', 7 | position: 'relative', 8 | display: 'flex', 9 | justifyContent: 'left', 10 | alignItems: 'center', 11 | }, 12 | ({ overflow, padding }) => 13 | Object.assign({}, overflow && { overflow }, padding && { padding }), 14 | ); 15 | 16 | export default Cell; 17 | -------------------------------------------------------------------------------- /packages/browser/src/components/Cell/DateCell.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react'; 4 | import { DatePicker } from 'antd'; 5 | import moment from 'dayjs'; 6 | 7 | import CellStyled from './Cell.styles'; 8 | import { getDateFormat } from '../../utils'; 9 | import { MODES } from '../../constants'; 10 | 11 | type Props = { 12 | children: any, 13 | onChange: string => void, 14 | format?: string, 15 | mode: string, 16 | }; 17 | 18 | const DateCell = ({ children, onChange, format, mode }: Props) => ( 19 | 20 | {mode === MODES.EDIT ? ( 21 | { 34 | onChange(dateString || null); 35 | }} 36 | /> 37 | ) : ( 38 | children && 39 | moment(children, getDateFormat(format)).format( 40 | getDateFormat(format), 41 | ) 42 | )} 43 | 44 | ); 45 | 46 | export default DateCell; 47 | -------------------------------------------------------------------------------- /packages/browser/src/components/Cell/NumberCell.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React, { Component, Fragment } from 'react'; 4 | import { Input } from 'antd'; 5 | import { func, any, bool } from 'prop-types'; 6 | 7 | import CellStyled from './Cell.styles'; 8 | import { MODES } from '../../constants'; 9 | 10 | type Props = { 11 | children: any, 12 | onChange: func, 13 | mode?: string, 14 | editable?: boolean, 15 | shouldAutoFocus?: boolean, 16 | }; 17 | 18 | type State = { 19 | value: any, 20 | }; 21 | class NumberCell extends Component { 22 | state = { 23 | value: this.props.children, 24 | }; 25 | 26 | // $FlowFixMe 27 | handleChange = e => { 28 | const { 29 | target: { value: nextValue }, 30 | } = e; 31 | this.setState(({ value }) => { 32 | // eslint-disable-next-line 33 | if (!isNaN(nextValue)) { 34 | return { value: nextValue }; 35 | } 36 | return { value }; 37 | }); 38 | }; 39 | 40 | saveChange = () => { 41 | const { onChange } = this.props; 42 | const { value } = this.state; 43 | let nextValue = value; 44 | if (value === '' || value === '-') { 45 | nextValue = 0; 46 | this.setState({ value: nextValue }); 47 | } 48 | 49 | onChange(Number(nextValue)); 50 | }; 51 | 52 | render() { 53 | const { children, mode, editable, shouldAutoFocus } = this.props; 54 | const { value } = this.state; 55 | return ( 56 | 57 | {editable || mode === MODES.EDIT ? ( 58 | 72 | ) : ( 73 | {children} 74 | )} 75 | 76 | ); 77 | } 78 | } 79 | 80 | NumberCell.propTypes = { 81 | onChange: func.isRequired, 82 | children: any, 83 | shouldAutoFocus: bool, 84 | }; 85 | 86 | export default NumberCell; 87 | -------------------------------------------------------------------------------- /packages/browser/src/components/Cell/ObjectCell.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React, { Component, Fragment } from 'react'; 4 | import { func, any, string } from 'prop-types'; 5 | import { EditOutlined } from '@ant-design/icons'; 6 | import { Popover, Modal } from 'antd'; 7 | import AceEditor from 'react-ace'; 8 | 9 | import 'brace/mode/json'; 10 | import 'brace/theme/github'; 11 | 12 | import CellStyled from './Cell.styles'; 13 | import JsonView from '../JsonView'; 14 | import { isVaildJSON } from '../../utils'; 15 | import { MODES } from '../../constants'; 16 | import popoverContent from '../CommonStyles/popoverContent'; 17 | 18 | type Props = { 19 | children: any, 20 | onChange: any => void, 21 | mode: string, 22 | }; 23 | 24 | type State = { 25 | showModal: boolean, 26 | error: boolean, 27 | value: string, 28 | }; 29 | class ObjectCell extends Component { 30 | state = { 31 | showModal: false, 32 | error: false, 33 | value: JSON.stringify(this.props.children, null, 2), 34 | }; 35 | 36 | handleAfterClose = () => { 37 | this.setState({ 38 | showModal: false, 39 | error: false, 40 | value: JSON.stringify(this.props.children, null, 2), 41 | }); 42 | }; 43 | 44 | toggleModal = () => { 45 | this.setState(({ showModal }) => ({ 46 | showModal: !showModal, 47 | })); 48 | }; 49 | 50 | handleJsonInput = (value: string) => { 51 | this.setState({ 52 | error: !isVaildJSON(value), 53 | value, 54 | }); 55 | }; 56 | 57 | saveValue = () => { 58 | const { error, value } = this.state; 59 | const { onChange } = this.props; 60 | const valueToSave = isVaildJSON(value) ? JSON.parse(value) : {}; 61 | 62 | if (!error) { 63 | onChange(valueToSave); 64 | this.toggleModal(); 65 | } 66 | }; 67 | 68 | render() { 69 | const { children, mode } = this.props; 70 | const { showModal, error, value } = this.state; 71 | return ( 72 | 73 | 80 | 83 | 84 | 85 | } 86 | trigger="click" 87 | > 88 | {` {...} `} 89 | 90 | {mode === MODES.EDIT && ( 91 | 95 | )} 96 | 97 | 105 |
106 | 120 |
121 |
122 | ); 123 | } 124 | } 125 | 126 | ObjectCell.propTypes = { 127 | onChange: func.isRequired, 128 | children: any, 129 | mode: string, 130 | }; 131 | 132 | export default ObjectCell; 133 | -------------------------------------------------------------------------------- /packages/browser/src/components/Cell/TextCell.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React, { Fragment } from 'react'; 4 | import { Input, Popover } from 'antd'; 5 | import { func, any, bool, string } from 'prop-types'; 6 | 7 | import createDOMPurify from 'dompurify'; 8 | import CellStyled from './Cell.styles'; 9 | import overflowText from '../DataTable/overflow.style'; 10 | import { MODES } from '../../constants'; 11 | import popoverContent from '../CommonStyles/popoverContent'; 12 | 13 | const DOMPurify = createDOMPurify(window); 14 | 15 | type Props = { 16 | children: [], 17 | onChange: any => void, 18 | shouldAutoFocus?: boolean, 19 | mode?: string, 20 | editable?: boolean, 21 | }; 22 | 23 | const TextCell = ({ 24 | children, 25 | onChange, 26 | mode, 27 | shouldAutoFocus, 28 | editable, 29 | }: Props) => ( 30 | 31 | {editable || mode === MODES.EDIT ? ( 32 | { 41 | const { value } = e.target; 42 | onChange(value); 43 | }} 44 | css={{ 45 | height: '100% important', 46 | width: '100% !important', 47 | border: `${shouldAutoFocus ? 'none' : 'auto'} !important`, 48 | }} 49 | /> 50 | ) : ( 51 | 52 | 57 |
64 |
65 | } 66 | > 67 | {children && ( 68 |
81 | )} 82 | 83 | 84 | )} 85 | 86 | ); 87 | TextCell.propTypes = { 88 | onChange: func.isRequired, 89 | children: any, 90 | shouldAutoFocus: bool, 91 | mode: string, 92 | editable: bool, 93 | }; 94 | 95 | export default TextCell; 96 | -------------------------------------------------------------------------------- /packages/browser/src/components/Cell/index.js: -------------------------------------------------------------------------------- 1 | import Cell from './Cell'; 2 | 3 | export default Cell; 4 | -------------------------------------------------------------------------------- /packages/browser/src/components/CommonStyles/filterIcons.js: -------------------------------------------------------------------------------- 1 | import { css } from 'react-emotion'; 2 | 3 | const styles = css` 4 | font-size: 13px; 5 | cursor: pointer; 6 | color: #bcbaba; 7 | &:hover { 8 | color: #333; 9 | } 10 | `; 11 | export default styles; 12 | -------------------------------------------------------------------------------- /packages/browser/src/components/CommonStyles/label.js: -------------------------------------------------------------------------------- 1 | import { css } from 'react-emotion'; 2 | 3 | export default css` 4 | .dejavu-browser-form-item-label label::after { 5 | content: none !important; 6 | } 7 | `; 8 | -------------------------------------------------------------------------------- /packages/browser/src/components/CommonStyles/linkButton.js: -------------------------------------------------------------------------------- 1 | import { css } from 'react-emotion'; 2 | import colors from '../theme/colors'; 3 | 4 | const styles = css` 5 | background: none; 6 | border: 0; 7 | outline: 0; 8 | color: ${colors.hoverLink}; 9 | cursor: pointer; 10 | `; 11 | export default styles; 12 | -------------------------------------------------------------------------------- /packages/browser/src/components/CommonStyles/overflowText.js: -------------------------------------------------------------------------------- 1 | import { css } from 'react-emotion'; 2 | 3 | export default css` 4 | text-overflow: ellipsis; 5 | white-space: nowrap; 6 | overflow: hidden; 7 | `; 8 | -------------------------------------------------------------------------------- /packages/browser/src/components/CommonStyles/popoverContent.js: -------------------------------------------------------------------------------- 1 | import { css } from 'react-emotion'; 2 | 3 | export default css` 4 | overflow-y: auto; 5 | overflow-x: auto; 6 | word-wrap: break-word; 7 | max-width: 200px; 8 | max-height: 300px; 9 | `; 10 | -------------------------------------------------------------------------------- /packages/browser/src/components/CommonStyles/termAggregations.js: -------------------------------------------------------------------------------- 1 | import colors from '../theme/colors'; 2 | 3 | export default { 4 | maxWidth: 200, 5 | '.ant-input': { 6 | boxSizing: 'border-box !important', 7 | }, 8 | '.dejavu-browser-checkbox-input:checked + label::before': { 9 | borderColor: `${colors.primary} !important`, 10 | }, 11 | '.dejavu-browser-checkbox-input:checked + label::after': { 12 | left: 'calc(1px + 12px/5) !important', 13 | width: 'calc(12px / 2) !important', 14 | height: 'calc(12px / 5) !important', 15 | marginTop: 'calc(12px / -2 / 2 * 0.8) !important', 16 | top: '11px !important', 17 | }, 18 | '.dejavu-browser-checkbox-input + label::before': { 19 | borderRadius: '3px', 20 | color: `${colors.primary} !important`, 21 | borderWidth: '1px !important', 22 | height: '12px !important', 23 | width: '12px !important', 24 | }, 25 | '.dejavu-browser-checkbox-input:hover + label::before': { 26 | borderColor: `${colors.primary} !important`, 27 | }, 28 | label: { 29 | alignItems: 'center !important', 30 | }, 31 | }; 32 | -------------------------------------------------------------------------------- /packages/browser/src/components/ConnectApp/index.js: -------------------------------------------------------------------------------- 1 | import ConnectApp from './ConnectApp'; 2 | 3 | export default ConnectApp; 4 | -------------------------------------------------------------------------------- /packages/browser/src/components/DataBrowser/Actions.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React, { Fragment } from 'react'; 4 | import { ReloadOutlined } from '@ant-design/icons'; 5 | import { Button } from 'antd'; 6 | import { connect } from 'react-redux'; 7 | import { mediaMin } from '@divyanshu013/media'; 8 | import { SelectedFilters } from '@appbaseio/reactivesearch'; 9 | 10 | import Flex from '../Flex'; 11 | import ShowHideColumn from './ShowHideColumns'; 12 | import ModeSwitch from './ModeSwitch'; 13 | import ExportData from './ExportData'; 14 | import DeleteRows from './DeleteRows'; 15 | import UpdateRow from './UpdateRow'; 16 | import PageSize from './PageSize'; 17 | import SortFilter from './SortFilter'; 18 | import MultipleUpdate from './MultipleUpdate'; 19 | 20 | import { getSelectedRows } from '../../reducers/selectedRows'; 21 | import { getUpdatingRow } from '../../reducers/updatingRow'; 22 | 23 | type Props = { 24 | onReload: () => void, 25 | selectedRows: string[], 26 | updatingRow?: any, 27 | }; 28 | 29 | const Actions = ({ onReload, selectedRows, updatingRow }: Props) => ( 30 |
39 | 40 |
41 | {selectedRows.length > 0 ? ( 42 | 43 | 44 | {updatingRow ? : } 45 | 46 | ) : ( 47 | 48 | 49 | 56 | 57 | )} 58 | 59 |
60 | 61 | 66 | 67 | 68 | 69 | 70 |
71 |
72 | ); 73 | 74 | const mapStateToProps = state => ({ 75 | selectedRows: getSelectedRows(state), 76 | updatingRow: getUpdatingRow(state), 77 | }); 78 | 79 | export default connect(mapStateToProps)(Actions); 80 | -------------------------------------------------------------------------------- /packages/browser/src/components/DataBrowser/ApplyQueryBanner.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react'; 4 | import { Alert } from 'antd'; 5 | import { connect } from 'react-redux'; 6 | 7 | import { setApplyQuery, setSelectedRows, setSelectAll } from '../../actions'; 8 | import { getApplyQuery } from '../../reducers/applyQuery'; 9 | import { getSelectAll } from '../../reducers/selectAll'; 10 | import { getSelectedRows } from '../../reducers/selectedRows'; 11 | import { getStats } from '../../reducers/stats'; 12 | import { numberWithCommas } from '../../utils'; 13 | import linkButton from '../CommonStyles/linkButton'; 14 | 15 | type Props = { 16 | applyQuery: boolean, 17 | selectAll: boolean, 18 | selectedRows: string[], 19 | onSetApplyQuery: boolean => void, 20 | onSetSelectAll: boolean => void, 21 | onSetSelectedRows: any => void, 22 | stats: any, 23 | }; 24 | 25 | const ApplyQueryBanner = ({ 26 | applyQuery, 27 | selectAll, 28 | selectedRows, 29 | onSetApplyQuery, 30 | onSetSelectAll, 31 | onSetSelectedRows, 32 | stats, 33 | }: Props) => ( 34 | <> 35 | {applyQuery && ( 36 | 40 | All {numberWithCommas(stats.totalResults)} docs are 41 | selected 42 | 53 |
54 | } 55 | type="info" 56 | /> 57 | )} 58 | 59 | {selectAll && ( 60 | 64 | All {selectedRows.length} docs from current page are 65 | selected.{' '} 66 | 77 | 78 | } 79 | type="info" 80 | /> 81 | )} 82 | 83 | ); 84 | 85 | const mapStateToProps = state => ({ 86 | applyQuery: getApplyQuery(state), 87 | selectAll: getSelectAll(state), 88 | selectedRows: getSelectedRows(state), 89 | stats: getStats(state), 90 | }); 91 | 92 | const mapDispatchToProps = { 93 | onSetApplyQuery: setApplyQuery, 94 | onSetSelectedRows: setSelectedRows, 95 | onSetSelectAll: setSelectAll, 96 | }; 97 | 98 | export default connect(mapStateToProps, mapDispatchToProps)(ApplyQueryBanner); 99 | -------------------------------------------------------------------------------- /packages/browser/src/components/DataBrowser/CloneApp.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import React from 'react'; 3 | import { connect } from 'react-redux'; 4 | import { ForkOutlined } from '@ant-design/icons'; 5 | import { Button } from 'antd'; 6 | 7 | import { getIsConnected, getAppname, getUrl } from '../../reducers/app'; 8 | 9 | type Props = { 10 | isConnected: boolean, 11 | rawUrl?: string, 12 | appname?: string, 13 | }; 14 | 15 | const cloneHandler = (appname, rawUrl) => { 16 | window.open( 17 | `https://importer.reactivesearch.io/?appname=${appname}&url=${rawUrl}&origin=dejavu`, 18 | '_blank', 19 | ); 20 | }; 21 | 22 | const CloneApp = ({ appname, rawUrl, isConnected }: Props) => { 23 | return ( 24 | isConnected && ( 25 | 34 | ) 35 | ); 36 | }; 37 | const mapStateToProps = state => ({ 38 | appname: getAppname(state), 39 | rawUrl: getUrl(state), 40 | isConnected: getIsConnected(state), 41 | }); 42 | 43 | export default connect(mapStateToProps)(CloneApp); 44 | -------------------------------------------------------------------------------- /packages/browser/src/components/DataBrowser/DeleteRows.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React, { Component } from 'react'; 4 | import { Popconfirm, Button } from 'antd'; 5 | import { DeleteOutlined } from '@ant-design/icons'; 6 | import { connect } from 'react-redux'; 7 | 8 | import { getUrl } from '../../reducers/app'; 9 | import { getSelectedRows } from '../../reducers/selectedRows'; 10 | import { getIndexes, getTypes } from '../../reducers/mappings'; 11 | import { getApplyQuery } from '../../reducers/applyQuery'; 12 | import { getQuery } from '../../reducers/query'; 13 | import { getVersion } from '../../reducers/version'; 14 | import { 15 | setError, 16 | clearError, 17 | updateReactiveList, 18 | setSelectedRows, 19 | setSelectAll, 20 | setApplyQuery, 21 | } from '../../actions'; 22 | import { deleteData } from '../../apis/data'; 23 | 24 | type Props = { 25 | selectedRows: string[], 26 | appUrl: string, 27 | indexes: string[], 28 | types: string[], 29 | version: number, 30 | setError: any => void, 31 | clearError: () => void, 32 | updateReactiveList: () => void, 33 | setSelectedRows: any => void, 34 | applyQuery: boolean, 35 | query: any, 36 | onSetApplyQuery: boolean => void, 37 | onSetSelectAll: boolean => void, 38 | }; 39 | 40 | class DeleteRows extends Component { 41 | handleConfirm = async () => { 42 | const { 43 | appUrl, 44 | indexes, 45 | types, 46 | selectedRows, 47 | setError: onSetError, 48 | clearError: onClearError, 49 | updateReactiveList: onUpdateReactiveList, 50 | setSelectedRows: onSetSelectedRows, 51 | onSetApplyQuery, 52 | onSetSelectAll, 53 | applyQuery, 54 | query, 55 | version, 56 | } = this.props; 57 | const queryData = applyQuery ? query.query : selectedRows; 58 | try { 59 | onClearError(); 60 | await deleteData( 61 | appUrl, 62 | indexes.join(','), 63 | types.join(','), 64 | queryData, 65 | version, 66 | ); 67 | setTimeout(() => { 68 | onSetSelectedRows([]); 69 | onSetSelectAll(false); 70 | onSetApplyQuery(false); 71 | onUpdateReactiveList(); 72 | }, 100); 73 | } catch (error) { 74 | onSetError(error); 75 | } 76 | }; 77 | 78 | render() { 79 | return ( 80 | 87 | 94 | 95 | ); 96 | } 97 | } 98 | 99 | const mpaStateToProps = state => ({ 100 | appUrl: getUrl(state), 101 | selectedRows: getSelectedRows(state), 102 | indexes: getIndexes(state), 103 | types: getTypes(state), 104 | applyQuery: getApplyQuery(state), 105 | query: getQuery(state), 106 | version: getVersion(state), 107 | }); 108 | 109 | const mapDispatchToProps = { 110 | setError, 111 | clearError, 112 | updateReactiveList, 113 | setSelectedRows, 114 | onSetApplyQuery: setApplyQuery, 115 | onSetSelectAll: setSelectAll, 116 | }; 117 | export default connect(mpaStateToProps, mapDispatchToProps)(DeleteRows); 118 | -------------------------------------------------------------------------------- /packages/browser/src/components/DataBrowser/GlobalSearch.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React, { Component } from 'react'; 4 | import { DataSearch } from '@appbaseio/reactivesearch'; 5 | import { css } from 'react-emotion'; 6 | import { SearchOutlined } from '@ant-design/icons'; 7 | import { connect } from 'react-redux'; 8 | 9 | import { 10 | getNestedSearchableColumns, 11 | getSearchableColumns, 12 | getSearchableColumnsWeights, 13 | getNesetedSearchableColumnsWeights, 14 | } from '../../reducers/mappings'; 15 | import { getIsShowingNestedColumns } from '../../reducers/nestedColumns'; 16 | import { getMode } from '../../reducers/mode'; 17 | import { MODES } from '../../constants'; 18 | 19 | type Props = { 20 | isShowingNestedColumns: boolean, 21 | nestedSearchableColumns: string[], 22 | searchableColumns: string[], 23 | mode: string, 24 | searchableColumnsWeights: number[], 25 | nestedSearchableColumnsWeights: number[], 26 | }; 27 | 28 | type State = { 29 | searchValue: string, 30 | hasMounted: boolean, 31 | }; 32 | 33 | class GlobalSearch extends Component { 34 | state = { 35 | searchValue: '', 36 | hasMounted: true, 37 | }; 38 | 39 | shouldComponentUpdate(nextProps) { 40 | const { searchValue } = this.state; 41 | if (nextProps.mode !== this.props.mode && searchValue.trim()) { 42 | this.setMountState(false); 43 | setTimeout(() => { 44 | this.setMountState(true); 45 | }); 46 | 47 | return false; 48 | } 49 | return true; 50 | } 51 | 52 | setMountState = hasMounted => { 53 | this.setState({ 54 | hasMounted, 55 | }); 56 | }; 57 | 58 | handleSearchValueChange = (searchValue, triggerQuery) => { 59 | this.setState( 60 | { 61 | searchValue, 62 | }, 63 | () => triggerQuery(), 64 | ); 65 | }; 66 | 67 | render() { 68 | const { 69 | isShowingNestedColumns, 70 | nestedSearchableColumns, 71 | searchableColumns: searchCols, 72 | searchableColumnsWeights, 73 | nestedSearchableColumnsWeights, 74 | mode, 75 | } = this.props; 76 | const searchableColumns = isShowingNestedColumns 77 | ? nestedSearchableColumns 78 | : searchCols; 79 | const weights = isShowingNestedColumns 80 | ? nestedSearchableColumnsWeights 81 | : searchableColumnsWeights; 82 | const { searchValue, hasMounted } = this.state; 83 | 84 | return ( 85 |
86 | {hasMounted && ( 87 | 105 | )} 106 |
107 | ); 108 | } 109 | } 110 | 111 | const mapStateToProps = state => ({ 112 | searchableColumns: getSearchableColumns(state), 113 | searchableColumnsWeights: getSearchableColumnsWeights(state), 114 | nestedSearchableColumns: getNestedSearchableColumns(state), 115 | nestedSearchableColumnsWeights: getNesetedSearchableColumnsWeights(state), 116 | isShowingNestedColumns: getIsShowingNestedColumns(state), 117 | mode: getMode(state), 118 | }); 119 | 120 | export default connect(mapStateToProps)(GlobalSearch); 121 | -------------------------------------------------------------------------------- /packages/browser/src/components/DataBrowser/Item.styles.js: -------------------------------------------------------------------------------- 1 | import { Form } from 'antd'; 2 | import styled from 'react-emotion'; 3 | 4 | const { Item } = Form; 5 | 6 | export default styled(Item)` 7 | margin-bottom: 5px; 8 | `; 9 | -------------------------------------------------------------------------------- /packages/browser/src/components/DataBrowser/ModeSwitch.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React, { Component } from 'react'; 4 | import { func, string, object } from 'prop-types'; 5 | import { Select } from 'antd'; 6 | import { EyeOutlined, EditOutlined } from '@ant-design/icons'; 7 | import { connect } from 'react-redux'; 8 | import { withRouter } from 'react-router-dom'; 9 | 10 | import { getMode } from '../../reducers/mode'; 11 | import setMode from '../../actions/mode'; 12 | import { updateQueryStringParameter } from '../../utils'; 13 | import colors from '../theme/colors'; 14 | import { MODES } from '../../constants'; 15 | 16 | const { Option } = Select; 17 | 18 | type Props = { 19 | mode: string, 20 | setMode: string => void, 21 | history: object, 22 | location: object, 23 | }; 24 | 25 | class ModeSwitch extends Component { 26 | handleModeChange = value => { 27 | this.props.setMode(value); 28 | // this.setSearchQuery(value); 29 | }; 30 | 31 | setSearchQuery = mode => { 32 | const searchQuery = this.props.location.search; 33 | this.props.history.push({ 34 | search: updateQueryStringParameter(searchQuery, 'mode', mode), 35 | }); 36 | }; 37 | 38 | render() { 39 | const { mode } = this.props; 40 | 41 | return ( 42 | 66 | ); 67 | } 68 | } 69 | 70 | ModeSwitch.propTypes = { 71 | mode: string.isRequired, 72 | setMode: func.isRequired, 73 | history: object, 74 | location: object, 75 | }; 76 | 77 | const mapStateToProps = state => ({ 78 | mode: getMode(state), 79 | }); 80 | 81 | const mapDispatchToProps = { 82 | setMode, 83 | }; 84 | 85 | export default withRouter( 86 | connect(mapStateToProps, mapDispatchToProps)(ModeSwitch), 87 | ); 88 | -------------------------------------------------------------------------------- /packages/browser/src/components/DataBrowser/NestedColumnToggle.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import React, { PureComponent } from 'react'; 3 | import { connect } from 'react-redux'; 4 | import { Checkbox } from 'antd'; 5 | 6 | import Flex from '../Flex'; 7 | 8 | import { setIsShwoingNestedColumn } from '../../actions'; 9 | import { getIsShowingNestedColumns } from '../../reducers/nestedColumns'; 10 | import { getShouldShowNestedSwitch } from '../../reducers/mappings'; 11 | 12 | type Props = { 13 | isShowingNestedColumns: boolean, 14 | shouldShowNestedSwitch: boolean, 15 | setIsShwoingNestedColumn: boolean => void, 16 | }; 17 | 18 | class NestedColumnToggle extends PureComponent { 19 | handleNestedColumnToggle = e => { 20 | const { 21 | target: { checked }, 22 | } = e; 23 | 24 | this.props.setIsShwoingNestedColumn(checked); 25 | }; 26 | 27 | render() { 28 | const { isShowingNestedColumns, shouldShowNestedSwitch } = this.props; 29 | return ( 30 | shouldShowNestedSwitch && ( 31 | 38 | 45 | Show object data as columns 46 | 47 | 48 | ) 49 | ); 50 | } 51 | } 52 | 53 | const mapStateToProps = state => ({ 54 | isShowingNestedColumns: getIsShowingNestedColumns(state), 55 | shouldShowNestedSwitch: getShouldShowNestedSwitch(state), 56 | }); 57 | 58 | const mapDispatchToProps = { 59 | setIsShwoingNestedColumn, 60 | }; 61 | 62 | export default connect(mapStateToProps, mapDispatchToProps)(NestedColumnToggle); 63 | -------------------------------------------------------------------------------- /packages/browser/src/components/DataBrowser/PageSize.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React, { Fragment, PureComponent } from 'react'; 4 | import { Select } from 'antd'; 5 | import { connect } from 'react-redux'; 6 | 7 | import { getPageSize } from '../../reducers/pageSize'; 8 | import { setPageSize, updateReactiveList } from '../../actions'; 9 | 10 | const { Option } = Select; 11 | 12 | type Props = { 13 | pageSize: number, 14 | onPageSizeChange: any => void, 15 | onUpdateReactiveList: () => void, 16 | }; 17 | 18 | class PageSize extends PureComponent { 19 | handlePageSizeChange = pageSize => { 20 | const { onPageSizeChange, onUpdateReactiveList } = this.props; 21 | onPageSizeChange(pageSize); 22 | onUpdateReactiveList(); 23 | }; 24 | 25 | render() { 26 | const { pageSize } = this.props; 27 | 28 | return ( 29 | 30 | 37 | 38 | ); 39 | } 40 | } 41 | 42 | const mpaStateToProps = state => ({ 43 | pageSize: getPageSize(state), 44 | }); 45 | 46 | const mapDispatchToProps = { 47 | onPageSizeChange: setPageSize, 48 | onUpdateReactiveList: updateReactiveList, 49 | }; 50 | 51 | export default connect(mpaStateToProps, mapDispatchToProps)(PageSize); 52 | -------------------------------------------------------------------------------- /packages/browser/src/components/DataBrowser/SortFilter.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React, { Fragment } from 'react'; 4 | import { connect } from 'react-redux'; 5 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 6 | import { 7 | faSortAlphaDown, 8 | faSortAlphaUp, 9 | } from '@fortawesome/free-solid-svg-icons'; 10 | 11 | import Flex from '../Flex'; 12 | 13 | import { getSort } from '../../reducers/sort'; 14 | import { resetSort } from '../../actions'; 15 | import colors from '../theme/colors'; 16 | import overflowStyles from '../CommonStyles/overflowText'; 17 | 18 | type Props = { 19 | onResetSort: () => void, 20 | field: string, 21 | order: string, 22 | }; 23 | 24 | const SortFiler = ({ field, order, onResetSort }: Props) => ( 25 | 26 | {field !== '_score' && ( 27 | 48 | 51 | 58 | {field} 59 | 60 | 72 | 73 | )} 74 | 75 | ); 76 | 77 | const mapStateToProps = state => { 78 | const { field, order } = getSort(state); 79 | return { 80 | field, 81 | order, 82 | }; 83 | }; 84 | 85 | const mapDispatchToProps = { 86 | onResetSort: resetSort, 87 | }; 88 | 89 | export default connect(mapStateToProps, mapDispatchToProps)(SortFiler); 90 | -------------------------------------------------------------------------------- /packages/browser/src/components/DataBrowser/index.js: -------------------------------------------------------------------------------- 1 | import DataBrowser from './DataBrowser'; 2 | 3 | export default DataBrowser; 4 | -------------------------------------------------------------------------------- /packages/browser/src/components/DataBrowserContainer/DataBrowserContainer.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react'; 4 | import { Skeleton } from 'antd'; 5 | import { connect } from 'react-redux'; 6 | import { bool, string } from 'prop-types'; 7 | 8 | import DataBrowser from '../DataBrowser'; 9 | 10 | import { getIsLoading, getIsConnected, getKey } from '../../reducers/app'; 11 | 12 | type Props = { 13 | isConnected: boolean, 14 | isLoading: boolean, 15 | hasCloneApp: boolean, 16 | key: string, 17 | enableReactivesearch: boolean, 18 | }; 19 | 20 | const DataBrowserContainer = ({ 21 | isConnected, 22 | isLoading, 23 | hasCloneApp, 24 | key, 25 | enableReactivesearch, 26 | }: Props) => ( 27 | 28 | {isConnected && ( 29 | 34 | )} 35 | 36 | ); 37 | 38 | const mapStateToProps = state => ({ 39 | isConnected: getIsConnected(state), 40 | isLoading: getIsLoading(state), 41 | key: getKey(state), 42 | }); 43 | 44 | DataBrowserContainer.propTypes = { 45 | isLoading: bool.isRequired, 46 | isConnected: bool.isRequired, 47 | key: string.isRequired, 48 | }; 49 | 50 | export default connect(mapStateToProps)(DataBrowserContainer); 51 | -------------------------------------------------------------------------------- /packages/browser/src/components/DataBrowserContainer/index.js: -------------------------------------------------------------------------------- 1 | import DataBrowserContainer from './DataBrowserContainer'; 2 | 3 | export default DataBrowserContainer; 4 | -------------------------------------------------------------------------------- /packages/browser/src/components/DataTable/BooleanTermAggregation.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react'; 4 | import { Popover } from 'antd'; 5 | import { MultiDataList } from '@appbaseio/reactivesearch'; 6 | import { css } from 'react-emotion'; 7 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 8 | import { faFilter } from '@fortawesome/free-solid-svg-icons'; 9 | 10 | import Flex from '../Flex'; 11 | 12 | import filterIconStyles from '../CommonStyles/filterIcons'; 13 | import overflowStyles from '../CommonStyles/overflowText'; 14 | import colors from '../theme/colors'; 15 | import termAggregationStyles from '../CommonStyles/termAggregations'; 16 | 17 | type Props = { 18 | field: string, 19 | }; 20 | 21 | const BooleanTermAggregation = ({ field }: Props) => { 22 | let componentId = field; 23 | if (field === '_type') { 24 | componentId = 'typeField'; 25 | } 26 | 27 | if (field === '_index') { 28 | componentId = 'indexField'; 29 | } 30 | return ( 31 | ( 56 | 65 | 69 | {label} 70 | 71 | 72 | )} 73 | /> 74 | } 75 | title="Filter" 76 | trigger="click" 77 | placement="bottomRight" 78 | > 79 | 80 | 81 | ); 82 | }; 83 | 84 | export default BooleanTermAggregation; 85 | -------------------------------------------------------------------------------- /packages/browser/src/components/DataTable/DataTableHeader.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | // $FlowFixMe 4 | import React, { forwardRef } from 'react'; 5 | import { connect } from 'react-redux'; 6 | 7 | import IdHeaderField from './IdHeaderField'; 8 | import ColumnHeader from './ColumnHeader'; 9 | import Flex from '../Flex'; 10 | 11 | import { setSelectedRows, setUpdatingRow } from '../../actions'; 12 | import { 13 | getVisibleColumns, 14 | getNestedVisibleColumns, 15 | } from '../../reducers/mappings'; 16 | import { getIsShowingNestedColumns } from '../../reducers/nestedColumns'; 17 | 18 | type Props = { 19 | visibleColumns: string[], 20 | nestedVisibleColumns: string[], 21 | isShowingNestedColumns: boolean, 22 | headerRef: any, 23 | }; 24 | 25 | const DataTableHeader = ({ 26 | visibleColumns, 27 | isShowingNestedColumns, 28 | nestedVisibleColumns, 29 | headerRef, 30 | }: Props) => { 31 | const mappingCols = isShowingNestedColumns 32 | ? nestedVisibleColumns 33 | : visibleColumns; 34 | const columns = ['_id', ...mappingCols]; 35 | 36 | if (columns.length >= 1) { 37 | return ( 38 | 47 | {columns.map(col => 48 | col === '_id' ? ( 49 | 50 | ) : ( 51 | 52 | ), 53 | )} 54 | 55 | ); 56 | } 57 | return null; 58 | }; 59 | 60 | const mapStateToProps = state => ({ 61 | visibleColumns: getVisibleColumns(state), 62 | nestedVisibleColumns: getNestedVisibleColumns(state), 63 | isShowingNestedColumns: getIsShowingNestedColumns(state), 64 | }); 65 | 66 | const mapDispatchToProps = { 67 | onSelectedRows: setSelectedRows, 68 | onSetUpdatingRow: setUpdatingRow, 69 | }; 70 | 71 | const DataTableContainer = connect( 72 | mapStateToProps, 73 | mapDispatchToProps, 74 | )(DataTableHeader); 75 | 76 | export default forwardRef((props, ref) => ( 77 | 78 | )); 79 | -------------------------------------------------------------------------------- /packages/browser/src/components/DataTable/IdField.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React, { Component } from 'react'; 4 | import { connect } from 'react-redux'; 5 | import { Popover, Checkbox } from 'antd'; 6 | 7 | import { setSelectedRows, setUpdatingRow } from '../../actions'; 8 | import { getSelectedRows } from '../../reducers/selectedRows'; 9 | import { getMode } from '../../reducers/mode'; 10 | import { getPageSize } from '../../reducers/pageSize'; 11 | import { MODES } from '../../constants'; 12 | import { getOnlySource, getUrlParams, convertToMax } from '../../utils'; 13 | 14 | import Flex from '../Flex'; 15 | import JsonView from '../JsonView'; 16 | import overflowText from './overflow.style'; 17 | import popoverContent from '../CommonStyles/popoverContent'; 18 | 19 | type Props = { 20 | value: any, 21 | mode: string, 22 | selectedRows: any[], 23 | setSelectedRows: (string[]) => void, 24 | setUpdatingRow: any => void, 25 | data: any, 26 | rowIndex: number, 27 | pageSize: number, 28 | }; 29 | class IdField extends Component { 30 | handleRowSelectChange = e => { 31 | const { 32 | target: { checked, value }, 33 | } = e; 34 | const currentSelectedRows = [...this.props.selectedRows]; 35 | const currentValueIndex = currentSelectedRows.indexOf(value); 36 | let newSelectedRows = []; 37 | if (checked && currentValueIndex === -1) { 38 | newSelectedRows = [...currentSelectedRows, value]; 39 | } else { 40 | newSelectedRows = [ 41 | ...currentSelectedRows.slice(0, currentValueIndex), 42 | ...currentSelectedRows.slice(currentValueIndex + 1), 43 | ]; 44 | } 45 | 46 | if (newSelectedRows.length === 1) { 47 | const data = this.props.data.find( 48 | item => item._id === newSelectedRows[0], 49 | ); 50 | this.props.setUpdatingRow(data); 51 | } else { 52 | this.props.setUpdatingRow(null); 53 | } 54 | 55 | this.props.setSelectedRows(newSelectedRows); 56 | }; 57 | 58 | render() { 59 | const { 60 | value, 61 | mode, 62 | selectedRows, 63 | data, 64 | rowIndex, 65 | pageSize, 66 | } = this.props; 67 | const { results } = getUrlParams(window.location.search); 68 | const currentPage = parseInt(results || 1, 10); 69 | 70 | return ( 71 | 72 |
73 | {convertToMax( 74 | pageSize * (currentPage - 1) + (rowIndex + 1), 75 | pageSize * (currentPage - 1) + pageSize, 76 | )} 77 |
78 | {mode === MODES.EDIT && ( 79 | -1} 83 | css={{ 84 | marginLeft: 8, 85 | }} 86 | /> 87 | )} 88 | 91 | 92 | 93 | } 94 | trigger="click" 95 | > 96 | {` {...} `} 102 | 103 | {value}} 105 | placement="topLeft" 106 | trigger="click" 107 | > 108 |
114 | {value} 115 |
116 |
117 |
118 | ); 119 | } 120 | } 121 | 122 | const mapStateToProps = state => ({ 123 | mode: getMode(state), 124 | selectedRows: getSelectedRows(state), 125 | pageSize: getPageSize(state), 126 | }); 127 | 128 | const mapDispatchToProps = { 129 | setSelectedRows, 130 | setUpdatingRow, 131 | }; 132 | 133 | export default connect(mapStateToProps, mapDispatchToProps)(IdField); 134 | -------------------------------------------------------------------------------- /packages/browser/src/components/DataTable/StyledCell.js: -------------------------------------------------------------------------------- 1 | import styled, { css } from 'react-emotion'; 2 | import { MODES } from '../../constants'; 3 | import colors from '../theme/colors'; 4 | 5 | export default styled('div')` 6 | display: flex; 7 | align-items: center; 8 | white-space: nowrap; 9 | overflow: hidden; 10 | text-overflow: ellipsis; 11 | width: 200px; 12 | font-size: 12px; 13 | padding: 10px; 14 | border-bottom: 1px solid ${colors.tableBorderColor}; 15 | border-right: 1px solid ${colors.tableBorderColor}; 16 | flex: 0 0 auto; 17 | ${props => 18 | props.mode === MODES.EDIT 19 | ? css` 20 | min-height: 45px; 21 | max-height: 45px; 22 | ` 23 | : css` 24 | min-height: 30px; 25 | max-height: 30px; 26 | `}; 27 | `; 28 | -------------------------------------------------------------------------------- /packages/browser/src/components/DataTable/TermAggregation.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react'; 4 | import { SyncOutlined } from '@ant-design/icons'; 5 | import { Popover } from 'antd'; 6 | import { MultiList } from '@appbaseio/reactivesearch'; 7 | import { css } from 'react-emotion'; 8 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 9 | import { faFilter } from '@fortawesome/free-solid-svg-icons'; 10 | 11 | import Flex from '../Flex'; 12 | 13 | import filterIconStyles from '../CommonStyles/filterIcons'; 14 | import overflowStyles from '../CommonStyles/overflowText'; 15 | import termAggregationStyles from '../CommonStyles/termAggregations'; 16 | import colors from '../theme/colors'; 17 | 18 | type Props = { 19 | field: string, 20 | }; 21 | 22 | type State = { 23 | hasMounted: boolean, 24 | }; 25 | 26 | class TermAggregation extends React.Component { 27 | state = { 28 | hasMounted: true, 29 | }; 30 | 31 | onUpdate = () => { 32 | this.setState( 33 | () => ({ 34 | hasMounted: false, 35 | }), 36 | () => 37 | setTimeout(() => 38 | this.setState({ 39 | hasMounted: true, 40 | }), 41 | ), 42 | ); 43 | }; 44 | 45 | render() { 46 | const { field } = this.props; 47 | const { hasMounted } = this.state; 48 | let componentId = field; 49 | if (field === '_type') { 50 | componentId = 'typeField'; 51 | } 52 | 53 | if (field === '_index') { 54 | componentId = 'indexField'; 55 | } 56 | 57 | return ( 58 | ( 74 | 82 | 86 | {label} 87 | 88 | {count} 89 | 90 | )} 91 | loader="Loading..." 92 | renderNoResults={() =>

No Results Found!

} 93 | /> 94 | ) 95 | } 96 | title={ 97 | 98 | Filter 99 | 104 | 105 | } 106 | trigger="click" 107 | placement="bottomRight" 108 | > 109 | 110 |
111 | ); 112 | } 113 | } 114 | 115 | export default TermAggregation; 116 | -------------------------------------------------------------------------------- /packages/browser/src/components/DataTable/index.js: -------------------------------------------------------------------------------- 1 | import DataTable from './DataTable'; 2 | 3 | export default DataTable; 4 | -------------------------------------------------------------------------------- /packages/browser/src/components/DataTable/overflow.style.js: -------------------------------------------------------------------------------- 1 | export default { 2 | whiteSpace: 'nowrap', 3 | overflow: 'hidden', 4 | textOverflow: 'ellipsis', 5 | }; 6 | -------------------------------------------------------------------------------- /packages/browser/src/components/ErrorFlashMessage/ErrorMessage.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React, { Component, Fragment } from 'react'; 4 | import { string, func } from 'prop-types'; 5 | import { ReloadOutlined } from '@ant-design/icons'; 6 | import { Button } from 'antd'; 7 | import { connect } from 'react-redux'; 8 | import createDOMPurify from 'dompurify'; 9 | import { reloadApp } from '../../actions/app'; 10 | 11 | const DOMPurify = createDOMPurify(window); 12 | 13 | type State = { 14 | isShowingDetails: boolean, 15 | }; 16 | 17 | type Props = { 18 | description?: string, 19 | handleReload: func, 20 | }; 21 | 22 | class ErrorMessage extends Component { 23 | state = { 24 | isShowingDetails: false, 25 | }; 26 | 27 | toggleDetails = () => { 28 | this.setState(prevState => ({ 29 | isShowingDetails: !prevState.isShowingDetails, 30 | })); 31 | }; 32 | 33 | render() { 34 | const { isShowingDetails } = this.state; 35 | const { description, handleReload } = this.props; 36 | return ( 37 | 38 | 47 | 51 |
52 | {isShowingDetails && ( 53 |
59 | )} 60 | 61 | ); 62 | } 63 | } 64 | 65 | ErrorMessage.propTypes = { 66 | description: string, 67 | handleReload: func.isRequired, 68 | }; 69 | 70 | const mapDispatchToProps = dispatch => ({ 71 | handleReload: () => dispatch(reloadApp()), 72 | }); 73 | 74 | export default connect(null, mapDispatchToProps)(ErrorMessage); 75 | -------------------------------------------------------------------------------- /packages/browser/src/components/ErrorFlashMessage/FlashMessage.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React, { Fragment } from 'react'; 4 | import { Alert } from 'antd'; 5 | import { connect } from 'react-redux'; 6 | 7 | import ErrorMessage from './ErrorMessage'; 8 | 9 | import { getError } from '../../reducers/error'; 10 | import { clearError } from '../../actions'; 11 | 12 | type Props = { 13 | error?: any, 14 | onDismissError: () => void, 15 | }; 16 | 17 | const FlashMessage = ({ error, onDismissError }: Props) => ( 18 | 19 | {error && ( 20 | } 27 | /> 28 | )} 29 | 30 | ); 31 | 32 | const mapStateToProps = state => ({ 33 | error: getError(state), 34 | }); 35 | 36 | const mapDispatchToProps = { 37 | onDismissError: clearError, 38 | }; 39 | 40 | export default connect(mapStateToProps, mapDispatchToProps)(FlashMessage); 41 | -------------------------------------------------------------------------------- /packages/browser/src/components/ErrorFlashMessage/index.js: -------------------------------------------------------------------------------- 1 | import FlashMessage from './FlashMessage'; 2 | 3 | export default FlashMessage; 4 | -------------------------------------------------------------------------------- /packages/browser/src/components/Flex/Flex.js: -------------------------------------------------------------------------------- 1 | import styled, { css } from 'react-emotion'; 2 | 3 | const Flex = styled.div` 4 | display: flex; 5 | flex-direction: ${({ flexDirection }) => flexDirection || 'row'}; 6 | flex-wrap: ${({ wrap }) => wrap || 'wrap'}; 7 | ${({ alignItems }) => 8 | alignItems && 9 | css` 10 | align-items: ${alignItems}; 11 | `}; 12 | ${({ justifyContent }) => 13 | justifyContent && 14 | css` 15 | justify-content: ${justifyContent}; 16 | `}; 17 | `; 18 | 19 | export default Flex; 20 | -------------------------------------------------------------------------------- /packages/browser/src/components/Flex/index.js: -------------------------------------------------------------------------------- 1 | import Flex from './Flex'; 2 | 3 | export default Flex; 4 | -------------------------------------------------------------------------------- /packages/browser/src/components/Icons/Hash.js: -------------------------------------------------------------------------------- 1 | import React, { forwardRef } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const Hash = forwardRef( 5 | ({ color = 'currentColor', size = 24, ...rest }, ref) => { 6 | return ( 7 | 20 | 21 | 22 | 23 | 24 | 25 | ); 26 | }, 27 | ); 28 | 29 | Hash.propTypes = { 30 | color: PropTypes.string, 31 | size: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 32 | }; 33 | 34 | Hash.displayName = 'Hash'; 35 | 36 | export default Hash; 37 | -------------------------------------------------------------------------------- /packages/browser/src/components/Icons/Toggle.js: -------------------------------------------------------------------------------- 1 | import React, { forwardRef } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const ToggleRight = forwardRef( 5 | ({ color = 'currentColor', size = 24, ...rest }, ref) => { 6 | return ( 7 | 20 | 21 | 22 | 23 | ); 24 | }, 25 | ); 26 | 27 | ToggleRight.propTypes = { 28 | color: PropTypes.string, 29 | size: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), 30 | }; 31 | 32 | ToggleRight.displayName = 'ToggleRight'; 33 | 34 | export default ToggleRight; 35 | -------------------------------------------------------------------------------- /packages/browser/src/components/JsonView/JsonView.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import React from 'react'; 3 | import { object, oneOfType, array } from 'prop-types'; 4 | 5 | type Props = { 6 | json?: object | array, 7 | }; 8 | 9 | const JsonView = ({ json }: Props) => ( 10 |
{JSON.stringify(json, null, 2)}
11 | ); 12 | 13 | JsonView.defaultProps = { 14 | json: {}, 15 | }; 16 | 17 | JsonView.propTypes = { 18 | json: oneOfType([object, array]), 19 | }; 20 | 21 | export default JsonView; 22 | -------------------------------------------------------------------------------- /packages/browser/src/components/JsonView/index.js: -------------------------------------------------------------------------------- 1 | import JsonView from './JsonView'; 2 | 3 | export default JsonView; 4 | -------------------------------------------------------------------------------- /packages/browser/src/components/Loader/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import loaderImage from '../../images/loader.svg'; 3 | 4 | const Loader = () => loader; 5 | 6 | export default Loader; 7 | -------------------------------------------------------------------------------- /packages/browser/src/components/MappingsDropdown/MappingsDropdown.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react'; 4 | import { object } from 'prop-types'; 5 | import { Popover } from 'antd'; 6 | 7 | import JsonView from '../JsonView'; 8 | import MappingsIcon from '../MappingsIcon'; 9 | import popoverContent from '../CommonStyles/popoverContent'; 10 | 11 | type Props = { 12 | mapping: object, 13 | }; 14 | 15 | const MappingsDropdown = ({ mapping }: Props) => ( 16 | 19 | 20 |
21 | } 22 | trigger="click" 23 | > 24 | 25 |
26 | ); 27 | 28 | MappingsDropdown.propTypes = { 29 | mapping: object.isRequired, 30 | }; 31 | 32 | export default MappingsDropdown; 33 | -------------------------------------------------------------------------------- /packages/browser/src/components/MappingsDropdown/index.js: -------------------------------------------------------------------------------- 1 | import MappingsDropdown from './MappingsDropdown'; 2 | 3 | export default MappingsDropdown; 4 | -------------------------------------------------------------------------------- /packages/browser/src/components/MappingsIcon/MappingsIcon.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React from 'react'; 4 | import { object } from 'prop-types'; 5 | import { 6 | MessageOutlined, 7 | EnvironmentOutlined, 8 | CalendarOutlined, 9 | } from '@ant-design/icons'; 10 | import Hash from '../Icons/Hash'; 11 | import Toggle from '../Icons/Toggle'; 12 | 13 | type Props = { 14 | mapping: object, 15 | }; 16 | 17 | const MappingsIcon = ({ mapping, ...props }: Props) => { 18 | const { type } = mapping; 19 | switch (type) { 20 | case 'text': 21 | case 'keyword': 22 | case 'string': 23 | return ; 24 | case 'integer': 25 | case 'long': 26 | return ; 27 | case 'geo_point': 28 | case 'geo_shape': 29 | return ; 30 | case 'boolean': 31 | return ; 32 | case 'date': 33 | return ; 34 | case 'float': 35 | case 'double': 36 | return ( 37 | 38 | π 39 | 40 | ); 41 | default: 42 | return ( 43 | {`{...}`} 44 | ); 45 | } 46 | }; 47 | 48 | export default MappingsIcon; 49 | -------------------------------------------------------------------------------- /packages/browser/src/components/MappingsIcon/index.js: -------------------------------------------------------------------------------- 1 | import MappingsIcon from './MappingsIcon'; 2 | 3 | export default MappingsIcon; 4 | -------------------------------------------------------------------------------- /packages/browser/src/components/theme/colors.js: -------------------------------------------------------------------------------- 1 | export default { 2 | // border-colors 3 | tableBorderColor: '#e8e8e8', 4 | 5 | // background-colors 6 | tableHead: '#fafafa', 7 | 8 | // solid-colors 9 | white: '#fff', 10 | 11 | // theme-colors 12 | primary: '#1890ff', 13 | editing: '#f0ad4e', 14 | viewing: '#5cb85c', 15 | background: '#eee', 16 | text: '#333', 17 | hoverBackground: '#ccc', 18 | hoverLink: '#096dd9', 19 | }; 20 | -------------------------------------------------------------------------------- /packages/browser/src/constants/config.js: -------------------------------------------------------------------------------- 1 | export const getURL = () => { 2 | let url = localStorage.getItem('url') || sessionStorage.getItem('url'); 3 | const params = new URLSearchParams(window.location.search); 4 | if (!url || url === 'undefined') { 5 | url = params.has('url') ? params.get('url') : 'null'; 6 | 7 | if (url) localStorage.setItem('url', url.replace(/\/+$/, '')); 8 | } 9 | return url === 'null' ? 'http://localhost:8000' : url; 10 | }; 11 | 12 | export const getVersion = () => { 13 | const version = localStorage.getItem('version'); 14 | return version; 15 | }; 16 | export const isUsingOpenSearch = () => 17 | localStorage.getItem('isUsingOpenSearch') === 'true'; 18 | export const SCALR_URL = 'scalr.api.appbase.io'; 19 | export const ACC_API = 'https://accapi.appbase.io'; 20 | export const SCALR_API = 'https://scalr.api.appbase.io'; 21 | export const IMPORTER_LINK = 'https://importer.reactivesearch.io/'; 22 | 23 | export const exampleConfig = [ 24 | { 25 | title: 'Import Data', 26 | description: 'Learn how to bring your data to reactivesearch.io.', 27 | image: { 28 | alt: 'Import Data', 29 | src: '/static/images/explainer/import_data.png', 30 | }, 31 | href: 'https://docs.reactivesearch.io/docs/data/import/', 32 | }, 33 | { 34 | title: 'Manage Mappings', 35 | description: 36 | 'View and edit field mappings (aka data types), set synonyms.', 37 | image: { 38 | alt: 'Manage Mappings', 39 | src: '/static/images/explainer/manage_mappings.png', 40 | }, 41 | href: 'https://docs.reactivesearch.io/docs/data/mappings/', 42 | }, 43 | { 44 | title: 'Browse Data', 45 | description: 'View, filter, and edit your indexed data.', 46 | image: { 47 | alt: 'Browse Data', 48 | src: '/static/images/explainer/browse_data.png', 49 | }, 50 | href: 'https://docs.reactivesearch.io/docs/data/Browser/', 51 | }, 52 | { 53 | title: 'Search Preview', 54 | description: 'Visually build your search and test search relevance.', 55 | image: { 56 | alt: 'Search Preview', 57 | src: '/static/images/explainer/search_preview.png', 58 | }, 59 | href: 60 | 'https://docs.reactivesearch.io/docs/search/relevancy/#test-search-relevancy', 61 | }, 62 | { 63 | title: 'Understand your Search ROI', 64 | description: 65 | 'Use analytics to see how search is impacting your business.', 66 | image: { 67 | alt: 'Analytics', 68 | src: '/static/images/explainer/search_ROI.png', 69 | }, 70 | href: 'https://docs.reactivesearch.io/docs/analytics/Overview/', 71 | }, 72 | { 73 | title: 'Setup Analytics', 74 | description: 75 | 'Learn how to start tracking your search and click analytics.', 76 | image: { 77 | alt: 'Setup Analytics', 78 | src: '/static/images/explainer/setup_analytics.png', 79 | }, 80 | href: 'https://docs.reactivesearch.io/docs/analytics/Implement/', 81 | }, 82 | { 83 | title: 'Access Analytics via APIs', 84 | description: 'Access search analytics via APIs for your custom needs.', 85 | image: { 86 | alt: 'Analytics via APIs', 87 | src: '/static/images/explainer/analytics_API.png', 88 | }, 89 | href: 'https://docs.reactivesearch.io/docs/analytics/Implement/', 90 | }, 91 | { 92 | title: 'Secure your Search App', 93 | description: 94 | 'Use advanced security controls to secure your search app.', 95 | image: { 96 | alt: 'Analytics via APIs', 97 | src: '/static/images/explainer/security.png', 98 | }, 99 | href: 'https://docs.reactivesearch.io/docs/security/Credentials/', 100 | }, 101 | ]; 102 | -------------------------------------------------------------------------------- /packages/browser/src/constants/es-languages.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line import/prefer-default-export 2 | export const languages = [ 3 | { 4 | value: 'universal', 5 | label: 'Universal', 6 | }, 7 | { 8 | value: 'arabic', 9 | label: 'Arabic', 10 | }, 11 | { 12 | value: 'armenian', 13 | label: 'Armenian', 14 | }, 15 | { 16 | value: 'basque', 17 | label: 'Basque', 18 | }, 19 | { 20 | value: 'bengali', 21 | label: 'Bengali', 22 | }, 23 | { 24 | value: 'brazilian', 25 | label: 'Brazilian Portuguese', 26 | }, 27 | { 28 | value: 'bulgarian', 29 | label: 'Bulgarian', 30 | }, 31 | { 32 | value: 'catalan', 33 | label: 'Catalan', 34 | }, 35 | { 36 | value: 'chinese', 37 | label: 'Chinese', 38 | }, 39 | { 40 | value: 'cjk', 41 | label: 'Cjk', 42 | }, 43 | { 44 | value: 'czech', 45 | label: 'Czech', 46 | }, 47 | { 48 | value: 'danish', 49 | label: 'Danish', 50 | }, 51 | { 52 | value: 'dutch', 53 | label: 'Dutch', 54 | }, 55 | { 56 | value: 'english', 57 | label: 'English', 58 | }, 59 | { 60 | value: 'estonian', 61 | label: 'Estonian', 62 | }, 63 | { 64 | value: 'finnish', 65 | label: 'Finnish', 66 | }, 67 | { 68 | value: 'french', 69 | label: 'French', 70 | }, 71 | { 72 | value: 'galician', 73 | label: 'Galician', 74 | }, 75 | { 76 | value: 'german', 77 | label: 'German', 78 | }, 79 | { 80 | value: 'greek', 81 | label: 'Greek', 82 | }, 83 | { 84 | value: 'hindi', 85 | label: 'Hindi', 86 | }, 87 | { 88 | value: 'hungarian', 89 | label: 'Hungarian', 90 | }, 91 | { 92 | value: 'indonesian', 93 | label: 'Indonesian', 94 | }, 95 | { 96 | value: 'irish', 97 | label: 'Irish', 98 | }, 99 | { 100 | value: 'italian', 101 | label: 'Italian', 102 | }, 103 | { 104 | value: 'japanese', 105 | label: 'Japanese', 106 | }, 107 | { 108 | value: 'korean', 109 | label: 'Korean', 110 | }, 111 | { 112 | value: 'latvian', 113 | label: 'Latvian', 114 | }, 115 | { 116 | value: 'lithuanian', 117 | label: 'Lithuanian', 118 | }, 119 | { 120 | value: 'norwegian', 121 | label: 'Norwegian', 122 | }, 123 | { 124 | value: 'persian', 125 | label: 'Persian', 126 | }, 127 | { 128 | value: 'polish', 129 | label: 'Polish', 130 | }, 131 | { 132 | value: 'portuguese', 133 | label: 'Portuguese', 134 | }, 135 | { 136 | value: 'romanian', 137 | label: 'Romanian', 138 | }, 139 | { 140 | value: 'russian', 141 | label: 'Russian', 142 | }, 143 | { 144 | value: 'sorani', 145 | label: 'Sorani', 146 | }, 147 | { 148 | value: 'spanish', 149 | label: 'Spanish', 150 | }, 151 | { 152 | value: 'swedish', 153 | label: 'Swedish', 154 | }, 155 | { 156 | value: 'thai', 157 | label: 'Thai', 158 | }, 159 | { 160 | value: 'turkish', 161 | label: 'Turkish', 162 | }, 163 | { 164 | value: 'ukranian', 165 | label: 'Ukranian', 166 | }, 167 | ]; 168 | -------------------------------------------------------------------------------- /packages/browser/src/constants/props.js: -------------------------------------------------------------------------------- 1 | // Remove eslint setting when another const is introduced 2 | 3 | // eslint-disable-next-line 4 | export const VIEWS = { 5 | AGGREGATION: 'aggregation', 6 | SEARCH: 'search', 7 | SCHEMA: 'schema', 8 | RANK_FEATURE: 'rank feature', 9 | }; 10 | -------------------------------------------------------------------------------- /packages/browser/src/images/dejavu-logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/browser/src/images/loader.svg: -------------------------------------------------------------------------------- 1 | 2 | 16 | 17 | 26 | 31 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /packages/browser/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Provider } from 'react-redux'; 3 | import { BrowserRouter } from 'react-router-dom'; 4 | 5 | import { ConfigProvider } from 'antd'; 6 | import DataBrowserContainer from './components/DataBrowserContainer/DataBrowserContainer'; 7 | import configureStore from './store'; 8 | 9 | // shared components 10 | import DefaultFlex from './components/Flex'; 11 | import DefaultFlashMessage from './components/ErrorFlashMessage/FlashMessage'; 12 | import DefaultConnectApp from './components/ConnectApp/ConnectApp'; 13 | 14 | // shared reducers 15 | import * as appReducers from './reducers/app'; 16 | import * as mappingsReducers from './reducers/mappings'; 17 | 18 | // shared utils 19 | import * as utils from './utils'; 20 | 21 | // shared constants 22 | import * as constants from './constants'; 23 | 24 | // shared theme 25 | import colors from './components/theme/colors'; 26 | 27 | // shared store 28 | const store = configureStore(); 29 | 30 | function WithConfigProvider(props) { 31 | return ( 32 | 33 | {/* eslint-disable-next-line react/prop-types */} 34 | {props.children} 35 | 36 | ); 37 | } 38 | 39 | const Flex = props => ( 40 | 41 | 42 | 43 | ); 44 | 45 | const FlashMessage = props => ( 46 | 47 | 48 | 49 | ); 50 | 51 | const ConnectApp = props => ( 52 | 53 | 54 | 55 | ); 56 | 57 | const DataBrowserWrapper = props => ( 58 | 59 | 60 | 61 |
62 | 63 | 64 | 70 |
71 |
72 |
73 |
74 | ); 75 | 76 | export { 77 | Flex, 78 | FlashMessage, 79 | ConnectApp, 80 | appReducers, 81 | mappingsReducers, 82 | utils, 83 | store, 84 | constants, 85 | colors, 86 | }; 87 | 88 | // main data browser module 89 | export default DataBrowserWrapper; 90 | -------------------------------------------------------------------------------- /packages/browser/src/reducers/analyzers.js: -------------------------------------------------------------------------------- 1 | import { ANALYZERS } from '../actions/constants'; 2 | 3 | const analyzer = (state = [], action) => { 4 | switch (action.type) { 5 | case ANALYZERS.SET_ANALYZERS: 6 | return action.analyzers; 7 | default: 8 | return state; 9 | } 10 | }; 11 | 12 | const getAnalyzers = state => state.analyzers; 13 | 14 | export { getAnalyzers }; 15 | 16 | export default analyzer; 17 | -------------------------------------------------------------------------------- /packages/browser/src/reducers/app.js: -------------------------------------------------------------------------------- 1 | import { APP } from '../actions/constants'; 2 | 3 | const initialState = { 4 | appname: null, 5 | url: null, 6 | isConnected: false, 7 | isLoading: false, 8 | headers: [], 9 | key: Date.now(), 10 | }; 11 | 12 | const app = (state = initialState, action) => { 13 | const { appname, url, type, headers } = action; 14 | switch (type) { 15 | case APP.CONNECT_REQUEST: 16 | return { 17 | ...state, 18 | isLoading: true, 19 | }; 20 | case APP.CONNECT_SUCCESS: 21 | return { 22 | ...state, 23 | appname, 24 | url, 25 | isConnected: true, 26 | isLoading: false, 27 | }; 28 | case APP.CONNECT_FAILURE: 29 | return { 30 | ...state, 31 | isLoading: false, 32 | }; 33 | case APP.SET_HEADERS: 34 | return { ...state, headers }; 35 | case APP.RELOAD_APP: 36 | return { ...state, key: Date.now() }; 37 | default: 38 | return state; 39 | } 40 | }; 41 | 42 | // selectors 43 | const getAppname = state => state.app.appname; 44 | const getUrl = state => state.app.url; 45 | const getIsConnected = state => state.app.isConnected; 46 | const getIsLoading = state => state.app.isLoading; 47 | const getHeaders = state => state.app.headers; 48 | const getKey = state => state.app.key; 49 | 50 | export { getAppname, getUrl, getIsLoading, getIsConnected, getHeaders, getKey }; 51 | 52 | export default app; 53 | -------------------------------------------------------------------------------- /packages/browser/src/reducers/applyQuery.js: -------------------------------------------------------------------------------- 1 | import { APPLY_QUERY } from '../actions/constants'; 2 | 3 | const applyQueryReducer = (state = false, action) => { 4 | const { applyQuery } = action; 5 | switch (action.type) { 6 | case APPLY_QUERY.SET_APPLY_QUERY: 7 | return applyQuery; 8 | default: 9 | return state; 10 | } 11 | }; 12 | 13 | const getApplyQuery = state => state.applyQuery; 14 | 15 | export { getApplyQuery }; 16 | 17 | export default applyQueryReducer; 18 | -------------------------------------------------------------------------------- /packages/browser/src/reducers/cell.js: -------------------------------------------------------------------------------- 1 | import { CELL } from '../actions/constants'; 2 | 3 | const initialState = { 4 | activeCell: { 5 | row: null, 6 | column: null, 7 | }, 8 | highlightCell: { 9 | row: null, 10 | column: null, 11 | }, 12 | }; 13 | 14 | const cell = (state = initialState, action) => { 15 | const { type, row, column } = action; 16 | switch (type) { 17 | case CELL.CELL_ACTIVE: 18 | return { 19 | ...state, 20 | activeCell: { 21 | row, 22 | column, 23 | }, 24 | }; 25 | case CELL.CELL_HIGHLIGHT: 26 | return { 27 | ...state, 28 | highlightCell: { row, column }, 29 | }; 30 | case CELL.CELL_SETVALUE_FAILURE: 31 | return state; 32 | default: 33 | return state; 34 | } 35 | }; 36 | 37 | // selectors 38 | const getActiveCell = state => state.cell.activeCell; 39 | const getHighlightCell = state => state.cell.highlightCell; 40 | 41 | export { getActiveCell, getHighlightCell }; 42 | 43 | export default cell; 44 | -------------------------------------------------------------------------------- /packages/browser/src/reducers/currentIds.js: -------------------------------------------------------------------------------- 1 | import { CURRENT_IDS } from '../actions/constants'; 2 | 3 | const currentIds = (state = [], action) => { 4 | switch (action.type) { 5 | case CURRENT_IDS.SET_CURRENT_IDS: 6 | return action.ids; 7 | default: 8 | return state; 9 | } 10 | }; 11 | 12 | const getCurrentIds = state => state.currentIds; 13 | 14 | export { getCurrentIds }; 15 | 16 | export default currentIds; 17 | -------------------------------------------------------------------------------- /packages/browser/src/reducers/data.js: -------------------------------------------------------------------------------- 1 | import { DATA, MAPPINGS } from '../actions/constants'; 2 | 3 | const initialState = { 4 | reactiveListKey: 0, // to remount ReactiveList and get fresh data 5 | isLoading: false, 6 | }; 7 | 8 | // a single state for data addition in the app currently, might need separation of concern later 9 | const data = (state = initialState, action) => { 10 | const { type } = action; 11 | const { reactiveListKey } = state; 12 | switch (type) { 13 | case DATA.ADD_DATA_REQUEST: 14 | case MAPPINGS.ADD_MAPPING_REQUEST: 15 | return { 16 | ...state, 17 | isLoading: true, 18 | }; 19 | case DATA.ADD_DATA_SUCCESS: 20 | return { 21 | ...state, 22 | isLoading: false, 23 | }; 24 | case MAPPINGS.ADD_MAPPING_SUCCESS: 25 | return { 26 | ...state, 27 | isLoading: false, 28 | }; 29 | case DATA.ADD_DATA_FAILURE: 30 | case MAPPINGS.ADD_MAPPING_FAILURE: 31 | return { 32 | ...state, 33 | isLoading: false, 34 | }; 35 | case DATA.UPDATE_REACTIVE_LIST: 36 | return { 37 | ...state, 38 | reactiveListKey: reactiveListKey + 1, 39 | }; 40 | default: 41 | return state; 42 | } 43 | }; 44 | 45 | // selectors 46 | const getReactiveListKey = state => state.data.reactiveListKey; 47 | const getIsLoading = state => state.data.isLoading; 48 | 49 | export { getReactiveListKey, getIsLoading }; 50 | 51 | export default data; 52 | -------------------------------------------------------------------------------- /packages/browser/src/reducers/error.js: -------------------------------------------------------------------------------- 1 | import { ERROR } from '../actions/constants'; 2 | 3 | const error = (state = null, action) => { 4 | switch (action.type) { 5 | case ERROR.SET_ERROR: 6 | return action.error; 7 | case ERROR.CLEAR_ERROR: 8 | return null; 9 | default: 10 | return state; 11 | } 12 | }; 13 | 14 | const getError = state => state.error; 15 | 16 | export { getError }; 17 | 18 | export default error; 19 | -------------------------------------------------------------------------------- /packages/browser/src/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | 3 | import { APP as APP_CONSTANTS } from '../actions/constants'; 4 | import app from './app'; 5 | import mappings from './mappings'; 6 | import cell from './cell'; 7 | import data from './data'; 8 | import mode from './mode'; 9 | import error from './error'; 10 | import selectedRows from './selectedRows'; 11 | import updatingRow from './updatingRow'; 12 | import currentIds from './currentIds'; 13 | import sort from './sort'; 14 | import pageSize from './pageSize'; 15 | import nestedColumns from './nestedColumns'; 16 | import version from './version'; 17 | import query from './query'; 18 | import applyQuery from './applyQuery'; 19 | import selectAll from './selectAll'; 20 | import stats from './stats'; 21 | import analyzers from './analyzers'; 22 | import batteriesReducers from '../batteries/modules/reducers'; 23 | 24 | const appReducer = combineReducers({ 25 | app, 26 | mappings, 27 | cell, 28 | data, 29 | mode, 30 | error, 31 | selectedRows, 32 | updatingRow, 33 | currentIds, 34 | sort, 35 | pageSize, 36 | nestedColumns, 37 | version, 38 | query, 39 | applyQuery, 40 | selectAll, 41 | stats, 42 | analyzers, 43 | ...batteriesReducers, 44 | }); 45 | 46 | const initialState = appReducer({}, {}); 47 | 48 | const rootReducer = (state, action) => { 49 | let newState = { ...state }; 50 | if (action.type === APP_CONSTANTS.DISCONNECT) { 51 | newState = { ...initialState }; 52 | } 53 | 54 | return appReducer(newState, action); 55 | }; 56 | 57 | export default rootReducer; 58 | -------------------------------------------------------------------------------- /packages/browser/src/reducers/mappings.js: -------------------------------------------------------------------------------- 1 | import { MAPPINGS } from '../actions/constants'; 2 | 3 | const initialState = { 4 | data: null, 5 | isLoading: false, 6 | indexes: [], 7 | types: [], 8 | indexTypeMap: {}, 9 | columns: [], 10 | visibleColumns: [], 11 | searchableColumns: [], 12 | typePropertyMapping: {}, 13 | nestedVisibleColumns: [], 14 | nestedSearchableColumns: [], 15 | nestedColumns: [], 16 | termsAggregationColumns: [], 17 | sortableColumns: [], 18 | shouldShowNestedSwitch: false, 19 | }; 20 | 21 | const mappings = (state = initialState, action) => { 22 | const { 23 | data, 24 | type, 25 | indexes, 26 | types, 27 | indexTypeMap, 28 | columns, 29 | visibleColumns, 30 | searchableColumns, 31 | typePropertyMapping, 32 | nestedVisibleColumns, 33 | nestedSearchableColumns, 34 | nestedColumns, 35 | appName, 36 | termsAggregationColumns, 37 | sortableColumns, 38 | shouldShowNestedSwitch, 39 | searchableColumnsWeights, 40 | nestedSearchableColumnsWeights, 41 | } = action; 42 | switch (type) { 43 | case MAPPINGS.MAPPINGS_FETCH_REQUEST: 44 | return { 45 | ...state, 46 | isLoading: true, 47 | }; 48 | case MAPPINGS.MAPPINGS_FETCH_SUCCESS: 49 | return { 50 | ...state, 51 | data, 52 | indexes, 53 | types, 54 | indexTypeMap, 55 | isLoading: false, 56 | columns, 57 | visibleColumns, 58 | searchableColumns, 59 | typePropertyMapping, 60 | nestedVisibleColumns, 61 | nestedSearchableColumns, 62 | nestedColumns, 63 | termsAggregationColumns, 64 | sortableColumns, 65 | shouldShowNestedSwitch, 66 | searchableColumnsWeights, 67 | nestedSearchableColumnsWeights, 68 | }; 69 | case MAPPINGS.MAPPINGS_FETCH_FAILURE: 70 | return { 71 | ...state, 72 | isLoading: false, 73 | }; 74 | case MAPPINGS.SET_VISIBLE_COLUMNS: 75 | return { 76 | ...state, 77 | visibleColumns, 78 | }; 79 | case MAPPINGS.SET_NESTED_VISIBLE_COLUMNS: 80 | return { 81 | ...state, 82 | nestedVisibleColumns, 83 | }; 84 | case MAPPINGS.SET_ARRAY_FIELDS: 85 | return { 86 | ...state, 87 | nestedColumns, 88 | nestedVisibleColumns, 89 | typePropertyMapping, 90 | data: { 91 | [appName]: { 92 | ...state.data[appName], 93 | nestedProperties: { 94 | ...state.data[appName].nestedProperties, 95 | ...action.nestedMappings, 96 | }, 97 | }, 98 | }, 99 | }; 100 | default: 101 | return state; 102 | } 103 | }; 104 | 105 | // selectors 106 | const getMappings = state => state.mappings.data; 107 | const getIsLoading = state => state.mappings.isLoading; 108 | const getIndexes = state => state.mappings.indexes; 109 | const getTypes = state => state.mappings.types; 110 | const getIndexTypeMap = state => state.mappings.indexTypeMap; 111 | const getColumns = state => state.mappings.columns; 112 | const getVisibleColumns = state => state.mappings.visibleColumns; 113 | const getSearchableColumns = state => state.mappings.searchableColumns; 114 | const getTypePropertyMapping = state => state.mappings.typePropertyMapping; 115 | const getNestedColumns = state => state.mappings.nestedColumns; 116 | const getNestedVisibleColumns = state => state.mappings.nestedVisibleColumns; 117 | const getNestedSearchableColumns = state => 118 | state.mappings.nestedSearchableColumns; 119 | const getTermsAggregationColumns = state => 120 | state.mappings.termsAggregationColumns; 121 | const getSortableColumns = state => state.mappings.sortableColumns; 122 | const getShouldShowNestedSwitch = state => 123 | state.mappings.shouldShowNestedSwitch; 124 | const getSearchableColumnsWeights = state => 125 | state.mappings.searchableColumnsWeights; 126 | const getNesetedSearchableColumnsWeights = state => 127 | state.mappings.nestedSearchableColumnsWeights; 128 | 129 | export { 130 | getMappings, 131 | getIsLoading, 132 | getIndexes, 133 | getTypes, 134 | getIndexTypeMap, 135 | getColumns, 136 | getVisibleColumns, 137 | getSearchableColumns, 138 | getTypePropertyMapping, 139 | getNestedColumns, 140 | getNestedVisibleColumns, 141 | getNestedSearchableColumns, 142 | getTermsAggregationColumns, 143 | getSortableColumns, 144 | getShouldShowNestedSwitch, 145 | getSearchableColumnsWeights, 146 | getNesetedSearchableColumnsWeights, 147 | }; 148 | 149 | export default mappings; 150 | -------------------------------------------------------------------------------- /packages/browser/src/reducers/mode.js: -------------------------------------------------------------------------------- 1 | import { MODE } from '../actions/constants'; 2 | import { MODES } from '../constants'; 3 | 4 | const mode = (state = MODES.EDIT, action) => { 5 | switch (action.type) { 6 | case MODE.SET_MODE: 7 | return action.mode; 8 | default: 9 | return state; 10 | } 11 | }; 12 | 13 | const getMode = state => state.mode; 14 | 15 | export { getMode }; 16 | 17 | export default mode; 18 | -------------------------------------------------------------------------------- /packages/browser/src/reducers/nestedColumns.js: -------------------------------------------------------------------------------- 1 | import { NESTED_COLUMNS } from '../actions/constants'; 2 | 3 | const nestedColumns = (state = false, action) => { 4 | const { isShowingNestedColumns } = action; 5 | switch (action.type) { 6 | case NESTED_COLUMNS.SET_IS_SHOWING_NESTED_COLUMNS: 7 | return isShowingNestedColumns; 8 | default: 9 | return state; 10 | } 11 | }; 12 | 13 | const getIsShowingNestedColumns = state => state.nestedColumns; 14 | 15 | export { getIsShowingNestedColumns }; 16 | 17 | export default nestedColumns; 18 | -------------------------------------------------------------------------------- /packages/browser/src/reducers/pageSize.js: -------------------------------------------------------------------------------- 1 | import { PAGE_SIZE } from '../actions/constants'; 2 | 3 | const pagination = (state = 15, action) => { 4 | const { pageSize } = action; 5 | switch (action.type) { 6 | case PAGE_SIZE.SET_PAGE_SIZE: 7 | return pageSize; 8 | default: 9 | return state; 10 | } 11 | }; 12 | 13 | const getPageSize = state => state.pageSize; 14 | export { getPageSize }; 15 | 16 | export default pagination; 17 | -------------------------------------------------------------------------------- /packages/browser/src/reducers/query.js: -------------------------------------------------------------------------------- 1 | import { QUERY } from '../actions/constants'; 2 | 3 | const queryReducer = (state = {}, action) => { 4 | const { query } = action; 5 | switch (action.type) { 6 | case QUERY.SET_QUERY: 7 | return query; 8 | default: 9 | return state; 10 | } 11 | }; 12 | 13 | const getQuery = state => state.query; 14 | 15 | export { getQuery }; 16 | 17 | export default queryReducer; 18 | -------------------------------------------------------------------------------- /packages/browser/src/reducers/selectAll.js: -------------------------------------------------------------------------------- 1 | import { SELECT_ALL } from '../actions/constants'; 2 | 3 | const selectAllReducer = (state = false, action) => { 4 | const { selectAll } = action; 5 | switch (action.type) { 6 | case SELECT_ALL.SET_SELECT_ALL: 7 | return selectAll; 8 | default: 9 | return state; 10 | } 11 | }; 12 | 13 | const getSelectAll = state => state.selectAll; 14 | 15 | export { getSelectAll }; 16 | 17 | export default selectAllReducer; 18 | -------------------------------------------------------------------------------- /packages/browser/src/reducers/selectedRows.js: -------------------------------------------------------------------------------- 1 | import { SELECTED_ROWS } from '../actions/constants'; 2 | 3 | const selectedRows = (state = [], action) => { 4 | switch (action.type) { 5 | case SELECTED_ROWS.SET_SELECTED_ROWS: 6 | return action.selectedRows; 7 | default: 8 | return state; 9 | } 10 | }; 11 | 12 | const getSelectedRows = state => state.selectedRows; 13 | 14 | export { getSelectedRows }; 15 | 16 | export default selectedRows; 17 | -------------------------------------------------------------------------------- /packages/browser/src/reducers/sort.js: -------------------------------------------------------------------------------- 1 | import { SORT } from '../actions/constants'; 2 | 3 | const defaultState = { 4 | order: 'desc', 5 | field: '_score', 6 | }; 7 | const sort = (state = defaultState, action) => { 8 | const { order, field } = action; 9 | switch (action.type) { 10 | case SORT.SET_SORT: 11 | return { 12 | ...state, 13 | order, 14 | field, 15 | }; 16 | case SORT.RESET_SORT: 17 | return defaultState; 18 | default: 19 | return state; 20 | } 21 | }; 22 | 23 | const getSort = state => state.sort; 24 | 25 | export { getSort }; 26 | 27 | export default sort; 28 | -------------------------------------------------------------------------------- /packages/browser/src/reducers/stats.js: -------------------------------------------------------------------------------- 1 | import { STATS } from '../actions/constants'; 2 | 3 | const statsReducers = (state = {}, action) => { 4 | const { stats, type } = action; 5 | switch (type) { 6 | case STATS.SET_STATS: 7 | return stats; 8 | default: 9 | return state; 10 | } 11 | }; 12 | 13 | const getStats = state => state.stats; 14 | 15 | export { getStats }; 16 | export default statsReducers; 17 | -------------------------------------------------------------------------------- /packages/browser/src/reducers/updatingRow.js: -------------------------------------------------------------------------------- 1 | import { UPDATING_ROW } from '../actions/constants'; 2 | 3 | const updatingRow = (state = null, action) => { 4 | switch (action.type) { 5 | case UPDATING_ROW.SET_UPDATING_ROW: 6 | return action.rowData; 7 | default: 8 | return state; 9 | } 10 | }; 11 | 12 | const getUpdatingRow = state => state.updatingRow; 13 | 14 | export { getUpdatingRow }; 15 | 16 | export default updatingRow; 17 | -------------------------------------------------------------------------------- /packages/browser/src/reducers/version.js: -------------------------------------------------------------------------------- 1 | import { VERSION } from '../actions/constants'; 2 | 3 | const versionReducer = (state = 5, action) => { 4 | const { version } = action; 5 | switch (action.type) { 6 | case VERSION.SET_VERSION: 7 | return version; 8 | default: 9 | return state; 10 | } 11 | }; 12 | 13 | const getVersion = state => state.version; 14 | 15 | export { getVersion }; 16 | 17 | export default versionReducer; 18 | -------------------------------------------------------------------------------- /packages/browser/src/sagas/app.js: -------------------------------------------------------------------------------- 1 | import { put, call, takeEvery } from 'redux-saga/effects'; 2 | 3 | import { APP } from '../actions/constants'; 4 | import { testConnection } from '../apis'; 5 | import { 6 | connectAppSuccess, 7 | connectAppFailure, 8 | setError, 9 | clearError, 10 | } from '../actions'; 11 | import { trimUrl } from '../utils'; 12 | 13 | function* handleConnectApp({ appname, url, headers }) { 14 | const appUrl = trimUrl(url); 15 | try { 16 | yield put(clearError()); 17 | const newHeaders = 18 | headers && 19 | headers.filter(item => item.key.trim() && item.value.trim()); 20 | yield call(testConnection, appname, appUrl, newHeaders); 21 | yield put(connectAppSuccess(appname, appUrl)); 22 | } catch (error) { 23 | yield put(connectAppFailure()); 24 | yield put( 25 | setError({ 26 | message: error.message, 27 | description: error.description, 28 | }), 29 | ); 30 | } 31 | } 32 | 33 | export default function* watchConnectApp() { 34 | yield takeEvery(APP.CONNECT_REQUEST, handleConnectApp); 35 | } 36 | -------------------------------------------------------------------------------- /packages/browser/src/sagas/cell.js: -------------------------------------------------------------------------------- 1 | import { put, call, takeEvery, select } from 'redux-saga/effects'; 2 | 3 | import { CELL } from '../actions/constants'; 4 | import { setCellValue } from '../apis'; 5 | import { 6 | setCellValueSuccess, 7 | setCellValueFailure, 8 | setError, 9 | clearError, 10 | } from '../actions'; 11 | import { getUrl } from '../reducers/app'; 12 | 13 | function* handleSetValue({ id, property, value, index, esType }) { 14 | try { 15 | yield put(clearError()); 16 | const url = yield select(getUrl); 17 | const data = yield call( 18 | setCellValue, 19 | index, 20 | esType, 21 | url, 22 | id, 23 | property, 24 | value, 25 | ); 26 | yield put(setCellValueSuccess(data)); 27 | } catch (error) { 28 | yield put(setCellValueFailure()); 29 | yield put(setError(error)); 30 | } 31 | } 32 | 33 | export default function* watchSetCellValueRequest() { 34 | yield takeEvery(CELL.CELL_SETVALUE_REQUEST, handleSetValue); 35 | } 36 | -------------------------------------------------------------------------------- /packages/browser/src/sagas/data.js: -------------------------------------------------------------------------------- 1 | import { put, call, takeEvery, select } from 'redux-saga/effects'; 2 | 3 | import { DATA } from '../actions/constants'; 4 | import { addData } from '../apis'; 5 | import { 6 | addDataSuccess, 7 | addDataFailure, 8 | setError, 9 | clearError, 10 | } from '../actions'; 11 | import { getUrl } from '../reducers/app'; 12 | import { handleFetchMappings } from './mappings'; 13 | 14 | function* handleAddData({ indexName, typeName, docId, data, tab, version }) { 15 | try { 16 | yield put(clearError()); 17 | const url = yield select(getUrl); 18 | yield call(addData, indexName, typeName, docId, url, data, version); 19 | if (tab === 'json') { 20 | yield call(handleFetchMappings); 21 | } 22 | yield put(addDataSuccess()); 23 | } catch (error) { 24 | yield put(addDataFailure()); 25 | yield put(setError(error)); 26 | } 27 | } 28 | 29 | export default function* watchAddDataRequest() { 30 | yield takeEvery(DATA.ADD_DATA_REQUEST, handleAddData); 31 | } 32 | -------------------------------------------------------------------------------- /packages/browser/src/sagas/index.js: -------------------------------------------------------------------------------- 1 | import { all } from 'redux-saga/effects'; 2 | 3 | import app from './app'; 4 | import mappings from './mappings'; 5 | import cell from './cell'; 6 | import data from './data'; 7 | 8 | export default function* rootSaga() { 9 | yield all([app(), mappings(), cell(), data()]); 10 | } 11 | -------------------------------------------------------------------------------- /packages/browser/src/store/index.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware, compose } from 'redux'; 2 | import createSagaMiddleware from 'redux-saga'; 3 | import thunk from 'redux-thunk'; 4 | 5 | import rootReducer from '../reducers'; 6 | import rootSaga from '../sagas'; 7 | 8 | const configureStore = () => { 9 | const sagaMiddleware = createSagaMiddleware(); 10 | const middlewares = applyMiddleware(...[sagaMiddleware, thunk]); 11 | const store = createStore( 12 | rootReducer, 13 | window.__REDUX_DEVTOOLS_EXTENSION__ 14 | ? compose(middlewares, window.__REDUX_DEVTOOLS_EXTENSION__()) 15 | : middlewares, 16 | ); 17 | sagaMiddleware.run(rootSaga); 18 | return store; 19 | }; 20 | 21 | export default configureStore; 22 | -------------------------------------------------------------------------------- /packages/browser/src/utils/CustomError.js: -------------------------------------------------------------------------------- 1 | class CustomError extends Error { 2 | constructor(description, ...params) { 3 | super(...params); 4 | 5 | if (Error.captureStackTrace) { 6 | Error.captureStackTrace(this, CustomError); 7 | } 8 | 9 | this.description = description; 10 | this.date = new Date(); 11 | } 12 | } 13 | 14 | export default CustomError; 15 | -------------------------------------------------------------------------------- /packages/browser/src/utils/date.js: -------------------------------------------------------------------------------- 1 | // converts elasticsearch date format to moment format 2 | const dateFormatMap = { 3 | 'YYYY/MM/DD': 'YYYY/MM/DD', 4 | basic_date: 'YYYYMMDD', 5 | date: 'YYYY-MM-DD', 6 | epoch_millis: 'x', 7 | epoch_second: 'X', 8 | basic_date_time_no_millis: 'YYYYMMDDTHHmmssZ', 9 | date_time_no_millis: 'YYYY-MM-DDTHH:mm:ss[Z]', 10 | basic_date_time: 'YYYYMMDDTHHmmss.SSSZ', 11 | date_time: 'YYYY-MM-DDTHH:mm:ss.SSSZ', 12 | basic_time: 'HHmmss.SSSZ', 13 | basic_time_no_millis: 'HHmmssZ', 14 | strict_date_optional_time: 'YYYYMMDD', 15 | date_hour_minute_second: 'YYYY-MM-DDTHH:mm:ss', 16 | strict_date_hour_minute_second: 'YYYY-MM-DDTHH:mm:ss', 17 | }; 18 | 19 | const getDateFormat = format => { 20 | let momentFormat = format; 21 | if (format.indexOf('||') > -1) { 22 | const formats = format.split('||'); 23 | const availableFormat = formats.find( 24 | dateFormat => dateFormatMap[dateFormat], 25 | ); 26 | if (availableFormat) { 27 | momentFormat = dateFormatMap[availableFormat]; 28 | } 29 | } else { 30 | momentFormat = dateFormatMap[format] || format; 31 | } 32 | 33 | return momentFormat; 34 | }; 35 | 36 | export { dateFormatMap }; 37 | export default getDateFormat; 38 | -------------------------------------------------------------------------------- /packages/browser/src/utils/exportData.js: -------------------------------------------------------------------------------- 1 | import { search } from '../apis'; 2 | 3 | let jsonData = []; 4 | 5 | export const MAX_DATA = 100000; 6 | 7 | const defaultQuery = { 8 | query: { 9 | match_all: {}, 10 | }, 11 | }; 12 | 13 | /** 14 | * A function to convert multilevel object to single level object and use key value pairs as Column and row pairs using recursion 15 | */ 16 | export const flatten = data => { 17 | const result = {}; 18 | 19 | function recurse(cur, prop = '') { 20 | if (Object(cur) !== cur) { 21 | result[prop] = cur; 22 | } else if (Array.isArray(cur)) { 23 | result[prop] = JSON.stringify(cur); 24 | } else { 25 | let isEmpty = true; 26 | Object.keys(cur).forEach(p => { 27 | isEmpty = false; 28 | recurse(cur[p], prop ? `${prop}.${p}` : p); 29 | }); 30 | if (isEmpty && prop) { 31 | result[prop] = {}; 32 | } 33 | } 34 | } 35 | 36 | recurse(data); 37 | return result; 38 | }; 39 | 40 | export const searchAfter = async ( 41 | app, 42 | types, 43 | url, 44 | version, 45 | query, 46 | chunkInfo, 47 | searchAfterData, 48 | ) => { 49 | try { 50 | const others = {}; 51 | if (searchAfterData) { 52 | others.search_after = searchAfterData; 53 | } 54 | let sort; 55 | 56 | if (version === 5) { 57 | sort = [{ _uid: 'desc' }]; // For version 5, use _uid with desc 58 | } else if (version === 8) { 59 | sort = ['_doc']; // For version 8, use _doc without desc 60 | } else { 61 | // For OpenSearch 1.x, 2.x, ES 6.x, 7.x 62 | sort = [{ _id: 'desc' }]; // For versions 1, 2, 6, 7 use _id with desc 63 | } 64 | 65 | const data = await search(app, types, url, version, { 66 | ...query, 67 | size: 1000, 68 | sort, 69 | ...others, 70 | }); 71 | 72 | // eslint-disable-next-line 73 | const res = await getSearchAfterData( 74 | app, 75 | types, 76 | url, 77 | version, 78 | query, 79 | chunkInfo, 80 | searchAfterData, 81 | data, 82 | ); 83 | 84 | if (typeof res === 'string') { 85 | let exportData = JSON.parse(res); 86 | const lastObject = exportData[exportData.length - 1]; 87 | exportData = exportData.map(value => { 88 | const item = Object.assign(value._source); 89 | item._id = value._id; 90 | return item; 91 | }); 92 | 93 | return { 94 | data: exportData, 95 | searchAfter: 96 | version === 5 97 | ? `${lastObject._type}#${lastObject._id}` 98 | : lastObject.sort, 99 | }; 100 | } 101 | 102 | return res; 103 | } catch (e) { 104 | console.error('SEARCH ERROR', e); 105 | return e; 106 | } 107 | }; 108 | 109 | const getSearchAfterData = async ( 110 | app, 111 | types, 112 | url, 113 | version, 114 | query, 115 | chunkInfo, 116 | searchAfterData, 117 | data, 118 | ) => { 119 | const { hits } = data; 120 | let str = null; 121 | /** 122 | * Checking if the current length is less than chunk total, recursive call searchAfter 123 | */ 124 | if (hits && jsonData.length < chunkInfo.total) { 125 | const { hits: totalhits, total } = hits; 126 | jsonData = jsonData.concat(totalhits); 127 | const lastObject = totalhits[totalhits.length - 1]; 128 | let nextSearchData; 129 | if (version === 5) { 130 | nextSearchData = `${lastObject._type}#${lastObject._id}`; 131 | } else { 132 | nextSearchData = lastObject.sort; 133 | } 134 | return searchAfter( 135 | app, 136 | types, 137 | url, 138 | version, 139 | query, 140 | chunkInfo, 141 | totalhits.length === total ? '' : nextSearchData, 142 | ); 143 | } 144 | 145 | str = JSON.stringify(jsonData, null, 4); 146 | jsonData = []; 147 | return str; 148 | }; 149 | 150 | /** 151 | * Main function for getting data to be exported; 152 | * @param {*} app 153 | * @param {*} types 154 | * @param {*} url 155 | * @param {*} query 156 | * @param {*} searchAfter 157 | */ 158 | const exportData = async ( 159 | app, 160 | types, 161 | url, 162 | version, 163 | query, 164 | chunkInfo, 165 | searchAfterData, 166 | ) => { 167 | try { 168 | const finalQuery = query || defaultQuery; 169 | const res = await searchAfter( 170 | app, 171 | types, 172 | url, 173 | version, 174 | finalQuery, 175 | chunkInfo, 176 | searchAfterData, 177 | ); 178 | 179 | return res; 180 | } catch (e) { 181 | return e; 182 | } 183 | }; 184 | 185 | export default exportData; 186 | -------------------------------------------------------------------------------- /packages/browser/src/utils/sampleData.js: -------------------------------------------------------------------------------- 1 | import moment from 'dayjs'; 2 | import { META_FIELDS } from './mappings'; 3 | import getDateFormat from './date'; 4 | 5 | export const extractSource = data => { 6 | const source = { ...data }; 7 | META_FIELDS.forEach(item => { 8 | delete source[item]; 9 | }); 10 | 11 | return source; 12 | }; 13 | 14 | const getSampleData = properties => { 15 | const data = {}; 16 | Object.keys(properties).forEach(item => { 17 | if (META_FIELDS.indexOf(item) === -1) { 18 | switch (properties[item].type) { 19 | case 'boolean': 20 | data[item] = false; 21 | break; 22 | case 'integer': 23 | case 'float': 24 | case 'long': 25 | case 'double': 26 | data[item] = 0; 27 | break; 28 | case 'date': 29 | data[item] = properties[item].format 30 | ? moment().format( 31 | getDateFormat(properties[item].format), 32 | ) 33 | : moment().format('x'); 34 | break; 35 | case 'object': 36 | case 'geo_point': 37 | case 'geo_shape': 38 | data[item] = { 39 | lat: '1.34', 40 | long: '2.4', 41 | }; 42 | break; 43 | case 'string': 44 | case 'text': 45 | case 'keyword': 46 | data[item] = ''; 47 | break; 48 | default: 49 | data[item] = {}; 50 | } 51 | } 52 | }); 53 | 54 | return data; 55 | }; 56 | 57 | export default getSampleData; 58 | -------------------------------------------------------------------------------- /packages/dejavu-main/.babelrc.js: -------------------------------------------------------------------------------- 1 | const { NODE_ENV } = process.env; 2 | const isProduction = NODE_ENV === 'production'; 3 | 4 | const presets = [ 5 | '@babel/preset-react', 6 | '@babel/preset-flow', 7 | [ 8 | '@babel/env', 9 | { 10 | targets: { 11 | edge: '17', 12 | firefox: '60', 13 | chrome: '67', 14 | safari: '11.1', 15 | }, 16 | useBuiltIns: 'usage', 17 | }, 18 | ], 19 | ]; 20 | 21 | const plugins = [ 22 | [ 23 | 'emotion', 24 | isProduction ? { hoist: true } : { sourceMap: true, autoLabel: true }, 25 | ], 26 | '@babel/plugin-proposal-class-properties', 27 | '@babel/plugin-transform-spread', 28 | '@babel/plugin-proposal-object-rest-spread', 29 | '@babel/plugin-syntax-dynamic-import', 30 | '@babel/plugin-proposal-export-default-from', 31 | '@babel/plugin-proposal-nullish-coalescing-operator', 32 | '@babel/plugin-proposal-optional-chaining', 33 | ]; 34 | 35 | module.exports = { presets, plugins }; 36 | -------------------------------------------------------------------------------- /packages/dejavu-main/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | indent_size = 4 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /packages/dejavu-main/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: 'babel-eslint', 3 | extends: [ 4 | 'airbnb', 5 | 'prettier', 6 | 'plugin:prettier/recommended', 7 | 'prettier/flowtype', 8 | 'prettier/react', 9 | 'prettier/standard', 10 | 'plugin:flowtype/recommended', 11 | 'plugin:jest/recommended', 12 | ], 13 | plugins: ['flowtype', 'prettier', 'jest'], 14 | env: { 15 | browser: true, 16 | }, 17 | rules: { 18 | 'react/jsx-filename-extension': [1, { extensions: ['.js'] }], 19 | 'react/forbid-prop-types': 0, 20 | 'react/destructuring-assignment': 0, 21 | 'react/require-default-props': 0, 22 | 'prettier/prettier': 'error', 23 | 'no-underscore-dangle': 0, 24 | 'jsx-a11y/click-events-have-key-events': 0, 25 | 'jsx-a11y/no-static-element-interactions': 0, 26 | }, 27 | }; 28 | -------------------------------------------------------------------------------- /packages/dejavu-main/.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | tabWidth: 4, 3 | singleQuote: true, 4 | trailingComma: 'all', 5 | useTabs: true, 6 | }; 7 | -------------------------------------------------------------------------------- /packages/dejavu-main/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2018 Appbase Inc. 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /packages/dejavu-main/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | Dejavu, a web UI for OpenSearch and Elasticsearch 12 | 13 | 14 | 15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /packages/dejavu-main/app/src/_redirects: -------------------------------------------------------------------------------- 1 | /* /index.html 200 2 | -------------------------------------------------------------------------------- /packages/dejavu-main/app/src/components/Navigation.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React, { useState, useEffect } from 'react'; 4 | import { 5 | ExperimentOutlined, 6 | TableOutlined, 7 | UploadOutlined, 8 | } from '@ant-design/icons'; 9 | import { Menu } from 'antd'; 10 | import { connect } from 'react-redux'; 11 | import { withRouter } from 'react-router-dom'; 12 | import { 13 | mappingsReducers, 14 | appReducers, 15 | utils, 16 | } from '@appbaseio/dejavu-browser'; 17 | 18 | const { getIndexes } = mappingsReducers; 19 | const { getIsConnected } = appReducers; 20 | const { getUrlParams, isExtension } = utils; 21 | 22 | type Props = { 23 | indexes: string[], 24 | isConnected: boolean, 25 | history: any, 26 | }; 27 | 28 | const { Item } = Menu; 29 | 30 | const Navigation = ({ indexes, isConnected, history }: Props) => { 31 | const [selectedKey, setSelectedKey] = useState('browse'); 32 | 33 | useEffect(() => { 34 | const routeName = window.location.pathname.substring(1); 35 | setSelectedKey(routeName); 36 | }, []); 37 | 38 | const navHandler = key => { 39 | switch (key) { 40 | case 'import': 41 | window.open('https://importer.reactivesearch.io/', '_blank'); 42 | break; 43 | case 'browse': 44 | setSelectedKey('browse'); 45 | history.push('/'); 46 | break; 47 | default: 48 | setSelectedKey(key); 49 | history.push(key); 50 | break; 51 | } 52 | }; 53 | 54 | // special case for chrome extension 55 | if (isExtension()) { 56 | const { route } = getUrlParams(window.location.search); 57 | if (route) { 58 | setSelectedKey(route); 59 | } else { 60 | setSelectedKey('browse'); 61 | } 62 | } 63 | return ( 64 | navHandler(key)} 68 | > 69 | 70 | 71 | Data Browser 72 | 73 | {(indexes.length <= 1 || !isConnected) && ( 74 | 75 | 76 | Search Preview 77 | 78 | )} 79 | 80 | 81 | Import Data ↗️ 82 | 83 | 84 | ); 85 | }; 86 | 87 | const mapStateToProps = state => ({ 88 | indexes: getIndexes(state), 89 | isConnected: getIsConnected(state), 90 | }); 91 | 92 | export default connect(mapStateToProps)(withRouter(Navigation)); 93 | -------------------------------------------------------------------------------- /packages/dejavu-main/app/src/components/NoMatch.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { FrownOutlined } from '@ant-design/icons'; 3 | import { Button } from 'antd'; 4 | import { Link } from 'react-router-dom'; 5 | 6 | const NoMatch = () => ( 7 |
15 | 16 |

Page not found

17 |

18 | Sorry, we couldn 19 | {"'"}t find what you are looking for 20 |

21 | 22 | 25 | 26 |
27 | ); 28 | 29 | export default NoMatch; 30 | -------------------------------------------------------------------------------- /packages/dejavu-main/app/src/components/OldDejavuBanner/OldDejavuBanner.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import React from 'react'; 3 | import { connect } from 'react-redux'; 4 | import { LinkOutlined } from '@ant-design/icons'; 5 | import { Alert } from 'antd'; 6 | import { appReducers, utils } from '@appbaseio/dejavu-browser'; 7 | 8 | const { getIsConnected, getAppname, getUrl } = appReducers; 9 | const { getUrlParams, getLocalStorageItem, setLocalStorageData } = utils; 10 | 11 | const LOCAL_STORAGE_ITEM = 'showOldBanner'; 12 | 13 | type Props = { 14 | isConnected: boolean, 15 | rawUrl: string, 16 | appname: string, 17 | }; 18 | 19 | const OldDejavuBanner = ({ appname, rawUrl, isConnected }: Props) => { 20 | const { oldBanner } = getUrlParams(window.location.search); 21 | const showOldBanner = getLocalStorageItem(LOCAL_STORAGE_ITEM); 22 | let isVisible = true; 23 | 24 | if (oldBanner && oldBanner === 'false') { 25 | isVisible = false; 26 | } 27 | 28 | if (showOldBanner && showOldBanner === 'false') { 29 | isVisible = false; 30 | } 31 | 32 | return ( 33 | isConnected && 34 | isVisible && ( 35 | 40 | Open app in old dejavu UI 41 | 44 | 45 | 46 | 47 | } 48 | onClose={() => { 49 | setLocalStorageData(LOCAL_STORAGE_ITEM, 'false'); 50 | }} 51 | /> 52 | ) 53 | ); 54 | }; 55 | const mapStateToProps = state => ({ 56 | isConnected: getIsConnected(state), 57 | appname: getAppname(state), 58 | rawUrl: getUrl(state), 59 | }); 60 | 61 | export default connect(mapStateToProps)(OldDejavuBanner); 62 | -------------------------------------------------------------------------------- /packages/dejavu-main/app/src/components/OldDejavuBanner/index.js: -------------------------------------------------------------------------------- 1 | import OldDejavuBanner from './OldDejavuBanner'; 2 | 3 | export default OldDejavuBanner; 4 | -------------------------------------------------------------------------------- /packages/dejavu-main/app/src/components/QueryExplorer.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import React, { Component } from 'react'; 4 | import { connect } from 'react-redux'; 5 | import { 6 | FlashMessage as ErrorFlashMessage, 7 | ConnectApp, 8 | appReducers, 9 | } from '@appbaseio/dejavu-browser'; 10 | 11 | const { getIsConnected, getAppname, getUrl } = appReducers; 12 | 13 | const LZMA = require('lzma/src/lzma-c'); 14 | require('urlsafe-base64/app.js'); 15 | 16 | type Props = { 17 | isConnected: boolean, 18 | appname?: string, 19 | rawUrl?: string, 20 | }; 21 | 22 | type State = { 23 | isProcessingUrl: boolean, 24 | url: string, 25 | }; 26 | 27 | class QueryExplorer extends Component { 28 | state = { 29 | isProcessingUrl: false, 30 | url: '', 31 | }; 32 | 33 | componentDidMount() { 34 | this.processUrl(); 35 | } 36 | 37 | componentDidUpdate(nextProps) { 38 | if (nextProps.rawUrl !== this.props.rawUrl) { 39 | this.processUrl(); 40 | } 41 | } 42 | 43 | processUrl = () => { 44 | const { rawUrl, appname } = this.props; 45 | if (rawUrl) { 46 | this.setState({ 47 | isProcessingUrl: true, 48 | }); 49 | const connectionString = JSON.stringify({ 50 | url: rawUrl, 51 | appname, 52 | }); 53 | LZMA.LZMA_WORKER.compress(connectionString, 9, url => { 54 | const res = window.SafeEncode.encode( 55 | window.SafeEncode.buffer(url), 56 | ); 57 | this.setState({ 58 | isProcessingUrl: false, 59 | url: res, 60 | }); 61 | }); 62 | } 63 | }; 64 | 65 | render() { 66 | const { isConnected } = this.props; 67 | const { url, isProcessingUrl } = this.state; 68 | 69 | return ( 70 |
78 | 79 | 80 |
81 | {isConnected && !isProcessingUrl && url && ( 82 |