diff --git a/backend/__init__.py b/backend/__init__.py index 455a8ec..8d914e2 100644 --- a/backend/__init__.py +++ b/backend/__init__.py @@ -3,6 +3,8 @@ from flask_cors import CORS from flask_restful import Api from requests_oauthlib import OAuth2Session +import sentry_sdk +from sentry_sdk.integrations.flask import FlaskIntegration from backend.config import EnvironmentConfig @@ -15,6 +17,18 @@ def create_app(config=EnvironmentConfig): + + if config.SENTRY_BACKEND_DSN: + sentry_sdk.init( + dsn=EnvironmentConfig.SENTRY_BACKEND_DSN, + environment=EnvironmentConfig.SENTRY_ENVIRONMENT, + integrations=[FlaskIntegration()], + traces_sample_rate=1.0, + _experiments={ + "profiles_sample_rate": 1.0, + }, + ) + app = Flask(__name__) app.config.from_object(config) db.init_app(app) diff --git a/backend/config.py b/backend/config.py index e66e3aa..f76bd82 100644 --- a/backend/config.py +++ b/backend/config.py @@ -44,3 +44,7 @@ class EnvironmentConfig: OAUTH2_USER_INFO_URL = os.getenv("OAUTH2_USER_INFO_URL", None) APP_SECRET_KEY = os.getenv("APP_SECRET_KEY", None) + + # Sentry configuration + SENTRY_BACKEND_DSN = os.getenv("SENTRY_BACKEND_DSN", None) + SENTRY_ENVIRONMENT = os.getenv("SENTRY_ENVIRONMENT", "development") diff --git a/example.env b/example.env index 4004ebf..2528820 100644 --- a/example.env +++ b/example.env @@ -1,10 +1,9 @@ # Backend App config API_DEBUG=1 -API_BASE_URL= -MAPBOX_ACCESS_TOKEN= # Frontend App config API_BASE_URL=http://127.0.0.1:5000 +MAPBOX_ACCESS_TOKEN= # DATABASE CONNECTION PARAMETRS POSTGRES_DB=osm-localizer @@ -25,3 +24,7 @@ OAUTH2_SCOPE=read_prefs write_api # Create a strong secret key APP_SECRET_KEY=secret + +# Sentry config +# SENTRY_BACKEND_DSN="https://" +# SENTRY_FRONTEND_DSN="https://" diff --git a/frontend/.env.expand b/frontend/.env.expand index 06c5869..c57bc25 100644 --- a/frontend/.env.expand +++ b/frontend/.env.expand @@ -3,3 +3,6 @@ REACT_APP_OAUTH_CLIENT_ID=$OAUTH2_CLIENT_ID REACT_APP_OAUTH_CLIENT_SECRET=$OAUTH2_CLIENT_SECRET REACT_APP_DEFAULT_CHANGESET_COMMENT=$DEFAULT_CHANGESET_COMMENT REACT_APP_API_BASE_URL=$API_BASE_URL + +REACT_APP_SENTRY_FRONTEND_DSN=$SENTRY_FRONTEND_DSN +REACT_APP_SENTRY_ENVIRONMENT=$SENTRY_ENVIRONMENT diff --git a/frontend/package.json b/frontend/package.json index 0ff9356..1032556 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -7,6 +7,8 @@ "@emotion/styled": "^11.10.5", "@mapbox/mapbox-gl-draw": "^1.4.0", "@reduxjs/toolkit": "^1.9.3", + "@sentry/react": "^7.43.0", + "@sentry/tracing": "^7.43.0", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", diff --git a/frontend/src/components/tagEditor/editForm.js b/frontend/src/components/tagEditor/editForm.js index ef5a6cf..8620240 100644 --- a/frontend/src/components/tagEditor/editForm.js +++ b/frontend/src/components/tagEditor/editForm.js @@ -5,22 +5,45 @@ import { useDetectClickOutside } from "react-detect-click-outside"; import InputToolForm from "./inputToolForm"; import TranslateComponent from "./translate"; -export const inputComponnent = (key, value) => { +export const inputComponnent = (key, value, index, props, editTags) => { return ( -
- - {key} - - +
+
+ + {key} + + +
); }; +const range = (a, b, step)=>{ + var A = []; + A[0] = a; + step = step || 1; + while(a+step <= b){ + A[A.length]= a+= step; + } + return A +} + +const ExchangeButton = ({topKey,buttomKey})=>{ + return( + { }} + > + + + ) +} + const SkipDropdown = (props) => { const [isDropdownOpen, setIsDropdownOpen] = useState(false); const ref = useDetectClickOutside({ @@ -102,6 +125,8 @@ export function TagEditorForm(props) { return changedKeys; }; + console.log(range(1, (editTags.length)-1, 1)) + const onSubmitChange = (values) => { async function updateElement() { const changedKeys = detectChange(values); @@ -137,10 +162,27 @@ export function TagEditorForm(props) { form.reset(props.element["tags"]); }} > -
- {editTags.map((key) => { - return inputComponnent(key, props.element["tags"][key]); - })} +
+
+ {editTags.map((tag, index) => { + return inputComponnent( + tag, + props.element["tags"][tag], + index, + props, + editTags + ); + })} +
+ {/* Add button between two input fields to exchange value between them */} +
+ {range(1, (editTags.length)-1, 1).map((index)=>{ + return( + + ) + }) + } +
{props.translateEngine ? ( diff --git a/frontend/src/config.js b/frontend/src/config.js index a5a0ba6..7e42ebc 100644 --- a/frontend/src/config.js +++ b/frontend/src/config.js @@ -8,3 +8,6 @@ export const DEFAULT_CHANGESET_COMMENT = export const API_BASE_URL = process.env.REACT_APP_API_BASE_URL ? new URL("/api/", process.env.REACT_APP_API_BASE_URL) : "http://127.0.0.1:5000/api/"; + +export const SENTRY_FRONTEND_DSN = process.env.REACT_APP_SENTRY_FRONTEND_DSN; +export const SENTRY_ENVIRONMENT = process.env.REACT_APP_SENTRY_ENVIRONMENT; diff --git a/frontend/src/index.js b/frontend/src/index.js index 631f712..28b40c3 100644 --- a/frontend/src/index.js +++ b/frontend/src/index.js @@ -1,12 +1,24 @@ import React from "react"; import ReactDOM from "react-dom/client"; import { Provider } from "react-redux"; +import * as Sentry from "@sentry/react"; +import { BrowserTracing } from "@sentry/tracing"; import "./index.css"; import App from "./App"; import store from "./store/store"; import reportWebVitals from "./reportWebVitals"; import "bootstrap/dist/css/bootstrap.min.css"; +import { SENTRY_FRONTEND_DSN, SENTRY_ENVIRONMENT } from "./config"; + +if (SENTRY_FRONTEND_DSN) { + Sentry.init({ + dsn: SENTRY_FRONTEND_DSN, + environment: SENTRY_ENVIRONMENT, + integrations: [new BrowserTracing()], + tracesSampleRate: 0.1, + }); +} const root = ReactDOM.createRoot(document.getElementById("root")); root.render( diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 4743ffa..32d6de8 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1852,6 +1852,69 @@ resolved "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz" integrity sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg== +"@sentry/browser@7.43.0": + version "7.43.0" + resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.43.0.tgz#335f23ae020fc5be9aec2a89f65022e0e93d915f" + integrity sha512-NlRkBYKb9o5IQdGY8Ktps19Hz9RdSuqS1tlLC7Sjr+MqZqSHmhKq8MWJKciRynxBeMbeGt0smExi9BqpVQdCEg== + dependencies: + "@sentry/core" "7.43.0" + "@sentry/replay" "7.43.0" + "@sentry/types" "7.43.0" + "@sentry/utils" "7.43.0" + tslib "^1.9.3" + +"@sentry/core@7.43.0": + version "7.43.0" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.43.0.tgz#c78e79399172738c96e3b388244258153c49215f" + integrity sha512-zvMZgEi7ptLBwDnd+xR/u4zdSe5UzS4S3ZhoemdQrn1PxsaVySD/ptyzLoGSZEABqlRxGHnQrZ78MU1hUDvKuQ== + dependencies: + "@sentry/types" "7.43.0" + "@sentry/utils" "7.43.0" + tslib "^1.9.3" + +"@sentry/react@^7.43.0": + version "7.43.0" + resolved "https://registry.yarnpkg.com/@sentry/react/-/react-7.43.0.tgz#a81d57aae7bf6d02238c69b508861a52714ae141" + integrity sha512-HWt0Eh+Y+Z/g+PWgeYWT6+5B+J82gauQ0GydjGeHeeSpoZRPRwWAoRFh+NKM/pe3neVr59VCyn4ghyoE3kODGA== + dependencies: + "@sentry/browser" "7.43.0" + "@sentry/types" "7.43.0" + "@sentry/utils" "7.43.0" + hoist-non-react-statics "^3.3.2" + tslib "^1.9.3" + +"@sentry/replay@7.43.0": + version "7.43.0" + resolved "https://registry.yarnpkg.com/@sentry/replay/-/replay-7.43.0.tgz#c77d5c6c4a3921cf67a58b69302ec4ea5eaa7fbe" + integrity sha512-2dGJS6p8uG1JZ7x/A3FyqnILTkXarbvfR+o1lC7z9lu34Wx0ZBeU2in/S2YHNGAE6XvfsePq3ya/s7LaNkk4qQ== + dependencies: + "@sentry/core" "7.43.0" + "@sentry/types" "7.43.0" + "@sentry/utils" "7.43.0" + +"@sentry/tracing@^7.43.0": + version "7.43.0" + resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-7.43.0.tgz#0164e2754736976469cfbe6adcb8919ec8adbe41" + integrity sha512-Mld2AyV8xYnRLYbDWvDy8PlGcln3h5JsUx6ScQGOxnFTmCQR50Tldtzq50VDs2fv6xH0+YrL/UIyjxCDc7EXzQ== + dependencies: + "@sentry/core" "7.43.0" + "@sentry/types" "7.43.0" + "@sentry/utils" "7.43.0" + tslib "^1.9.3" + +"@sentry/types@7.43.0": + version "7.43.0" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.43.0.tgz#e621257601e9db2a39cdd3bd75e67fe338ed51eb" + integrity sha512-5XxCWqYWJNoS+P6Ie2ZpUDxLRCt7FTEzmlQkCdjW6MFWOX26hAbF/wEuOTYAFKZXMIXOz0Egofik1e8v1Cg6/A== + +"@sentry/utils@7.43.0": + version "7.43.0" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.43.0.tgz#ad16efb86b94ffe6dca2ed2b299d5230ba6d815b" + integrity sha512-f78YfMLcgNU7+suyWFCuQhQlneXXMS+egb0EFZh7iU7kANUPRX5T4b+0C+fwaPm5gA6XfGYskr4ZnzQJLOlSqg== + dependencies: + "@sentry/types" "7.43.0" + tslib "^1.9.3" + "@sinclair/typebox@^0.24.1": version "0.24.51" resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz" @@ -9091,7 +9154,7 @@ tsconfig-paths@^3.14.1: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@^1.8.1: +tslib@^1.8.1, tslib@^1.9.3: version "1.14.1" resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== diff --git a/pdm.lock b/pdm.lock index 080c5bd..1bc20ba 100644 --- a/pdm.lock +++ b/pdm.lock @@ -26,6 +26,12 @@ dependencies = [ "tomli>=1.1.0; python_full_version < \"3.11.0a7\"", ] +[[package]] +name = "blinker" +version = "1.5" +requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +summary = "Fast, simple object-to-object and broadcast signaling" + [[package]] name = "certifi" version = "2022.12.7" @@ -305,6 +311,26 @@ dependencies = [ "requests>=2.0.0", ] +[[package]] +name = "sentry-sdk" +version = "1.17.0" +summary = "Python client for Sentry (https://sentry.io)" +dependencies = [ + "certifi", + "urllib3>=1.26.11; python_version >= \"3.6\"", +] + +[[package]] +name = "sentry-sdk" +version = "1.17.0" +extras = ["flask"] +summary = "Python client for Sentry (https://sentry.io)" +dependencies = [ + "blinker>=1.1", + "flask>=0.11", + "sentry-sdk==1.17.0", +] + [[package]] name = "setuptools" version = "67.6.0" @@ -367,7 +393,7 @@ summary = "The comprehensive WSGI web application library." [metadata] lock_version = "4.1" -content_hash = "sha256:f5d1cf6d99306fa994c6fd153bcd888c351aac663c0a0cd308961c0e82859ce4" +content_hash = "sha256:a16749354e84881c82c217de13f9cd59aa4c0d5746af4df64f244f8dd3755a39" [metadata.files] "alembic 1.8.1" = [ @@ -392,6 +418,10 @@ content_hash = "sha256:f5d1cf6d99306fa994c6fd153bcd888c351aac663c0a0cd308961c0e8 {url = "https://files.pythonhosted.org/packages/f1/b7/6de002378cfe0b83beba72f0a7875dfb6005b2a214ac9f9ca689583069ef/black-22.12.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2"}, {url = "https://files.pythonhosted.org/packages/f2/b9/06fe2dd83a2104d83c2b737f41aa5679f5a4395630005443ba4fa6fece8b/black-22.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148"}, ] +"blinker 1.5" = [ + {url = "https://files.pythonhosted.org/packages/2b/12/82786486cefb68685bb1c151730f510b0f4e5d621d77f245bc0daf9a6c64/blinker-1.5.tar.gz", hash = "sha256:923e5e2f69c155f2cc42dafbbd70e16e3fde24d2d4aa2ab72fbe386238892462"}, + {url = "https://files.pythonhosted.org/packages/30/41/caa5da2dbe6d26029dfe11d31dfa8132b4d6d30b6d6b61a24824075a5f06/blinker-1.5-py2.py3-none-any.whl", hash = "sha256:1eb563df6fdbc39eeddc177d953203f99f097e9bf0e2b8f9f3cf18b6ca425e36"}, +] "certifi 2022.12.7" = [ {url = "https://files.pythonhosted.org/packages/37/f7/2b1b0ec44fdc30a3d31dfebe52226be9ddc40cd6c0f34ffc8923ba423b69/certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, {url = "https://files.pythonhosted.org/packages/71/4c/3db2b8021bd6f2f0ceb0e088d6b2d49147671f25832fb17970e9b583d742/certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, @@ -877,6 +907,10 @@ content_hash = "sha256:f5d1cf6d99306fa994c6fd153bcd888c351aac663c0a0cd308961c0e8 {url = "https://files.pythonhosted.org/packages/6f/bb/5deac77a9af870143c684ab46a7934038a53eb4aa975bc0687ed6ca2c610/requests_oauthlib-1.3.1-py2.py3-none-any.whl", hash = "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5"}, {url = "https://files.pythonhosted.org/packages/95/52/531ef197b426646f26b53815a7d2a67cb7a331ef098bb276db26a68ac49f/requests-oauthlib-1.3.1.tar.gz", hash = "sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a"}, ] +"sentry-sdk 1.17.0" = [ + {url = "https://files.pythonhosted.org/packages/3e/f1/4bfd190f07f49f823eb9fe55b23c8be8a21aa9b9190df6d27721052fd046/sentry-sdk-1.17.0.tar.gz", hash = "sha256:ad40860325c94d1a656da70fba5a7c4dbb2f6809d3cc2d00f74ca0b608330f14"}, + {url = "https://files.pythonhosted.org/packages/e5/c5/669e528a5ccb16c22ef239a8dc9e51c855a2bcea548e8d71808ce1438045/sentry_sdk-1.17.0-py2.py3-none-any.whl", hash = "sha256:3c4e898f7a3edf5a2042cd0dcab6ee124e2112189228c272c08ad15d3850c201"}, +] "setuptools 67.6.0" = [ {url = "https://files.pythonhosted.org/packages/25/f3/d68c20919bc774c6cb127f1762f2f2f999d700a58198556e883dd3700e58/setuptools-67.6.0.tar.gz", hash = "sha256:2ee892cd5f29f3373097f5a814697e397cf3ce313616df0af11231e2ad118077"}, {url = "https://files.pythonhosted.org/packages/c3/9e/8a7ba2c9984a060daa6c6f9fff4d576b7ace3936239d6b771541eab972ed/setuptools-67.6.0-py3-none-any.whl", hash = "sha256:b78aaa36f6b90a074c1fa651168723acbf45d14cb1196b6f02c0fd07f17623b2"}, diff --git a/pyproject.toml b/pyproject.toml index 22e688d..9a2d2f6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,6 +28,7 @@ dependencies = [ "flask-restful>=0.3.9", "gunicorn>=20.1.0", "tornado>=6.2", + "sentry-sdk[flask]>=1.17.0", ] requires-python = ">=3.10" license = {text = "MIT"}