Skip to content

Commit

Permalink
Release 10.2.0 (#335)
Browse files Browse the repository at this point in the history
## What's changed

* chore(deps-dev): update black requirement from ~=24.3.0 to ~=24.4.2
(#314)
* chore(deps-dev): bump ruff from 0.3.5 to 0.4.7
(#322)
* chore(deps): bump python-dateutil from 2.8.2 to 2.9.0.post0
(#282)
* chore(deps): bump react-highlight-words from 0.17.0 to 0.20.0
(#215)
* feat: initialize chromatic
(#334)
* fix(vis-heatmap): disable deselection of aggregation type
(#333)
* feat: Sentry integration
(#319)
  • Loading branch information
puehringer authored Jun 10, 2024
2 parents 4d7d70a + df816d4 commit 1a66533
Show file tree
Hide file tree
Showing 15 changed files with 158 additions and 8 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,18 @@ on:
required: false
type: boolean
default: false
chromatic:
description: 'Run chromatic'
required: false
type: boolean
default: false
push:

jobs:
build:
uses: datavisyn/github-workflows/.github/workflows/build-node-python.yml@main
secrets: inherit
with:
chromatic_enable: ${{ inputs.chromatic == true }}
cypress_enable: true
cypress_run_because_flag: ${{ inputs.cypress == true }}
14 changes: 14 additions & 0 deletions .github/workflows/chromatic.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name: Chromatic UI test

on:
push:
paths:
- 'src/vis/**/*' # trigger whenever something changed in the VIS (?)
- '**.stories.*' # trigger whenever any story has changed

jobs:
build:
uses: datavisyn/github-workflows/.github/workflows/build-node-python.yml@main
secrets: inherit
with:
chromatic_enable: true
12 changes: 11 additions & 1 deletion .storybook/main.js
Original file line number Diff line number Diff line change
@@ -1 +1,11 @@
module.exports = require('visyn_scripts/config/storybook.main.template');
const c = require('visyn_scripts/config/storybook.main.template');

const config = {
...c,
addons: [
// Other Storybook addons
...c.addons,
'@chromatic-com/storybook',
],
};
export default config;
5 changes: 5 additions & 0 deletions chromatic.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"projectId": "Project:640e9afaf15e817c63e10f42",
"zip": true,
"buildScriptName": "storybook:build"
}
10 changes: 6 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "visyn_core",
"description": "Core repository for datavisyn applications.",
"version": "10.1.0",
"version": "10.2.0",
"author": {
"name": "datavisyn GmbH",
"email": "[email protected]",
Expand Down Expand Up @@ -72,7 +72,7 @@
"test": "visyn_scripts test",
"bundle:dev": "visyn_scripts bundle --mode development --env workspace_mode=single",
"bundle:prod": "visyn_scripts bundle --mode production --env workspace_mode=single",
"chromatic": "npx chromatic --build-script-name storybook:build"
"chromatic": "yarn run chromatic"
},
"dependencies": {
"@emotion/css": "^11.11.2",
Expand All @@ -93,6 +93,7 @@
"@mantine/styles": "~6.0.21",
"@mantine/tiptap": "~7.8.1",
"@mantine6/core": "npm:@mantine/core@~6.0.21",
"@sentry/react": "^8.5.0",
"@types/d3-hexbin": "^0.2.3",
"@types/d3v7": "npm:@types/d3@^7.4.0",
"@types/plotly.js-dist-min": "^2.3.0",
Expand All @@ -112,14 +113,15 @@
"plotly.js-dist-min": "~2.12.0",
"react": "~18.2.0",
"react-dom": "~18.2.0",
"react-highlight-words": "^0.17.0",
"react-highlight-words": "^0.20.0",
"react-plotly.js": "^2.5.1",
"react-spring": "^9.7.1",
"use-deep-compare-effect": "^1.8.0",
"visyn_scripts": "^9.0.0"
},
"devDependencies": {
"chromatic": "^6.19.9",
"@chromatic-com/storybook": "^1.3.2",
"chromatic": "^11.3.0",
"cypress": "^13.2.0"
},
"visyn": {
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ psycopg2==2.9.9
pydantic==1.10.14
pyjwt[crypto]==2.8.0
pytest-postgresql==6.0.0
python-dateutil==2.8.2
python-dateutil==2.9.0.post0
python-multipart==0.0.9
requests==2.32.0
SQLAlchemy>=1.4.40,<=1.4.49
Expand Down
4 changes: 2 additions & 2 deletions requirements_dev.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
black~=24.3.0
black~=24.4.2
pyright==1.1.356
pytest-runner~=6.0.1
pytest~=8.2.1
ruff==0.3.5
ruff==0.4.7
55 changes: 55 additions & 0 deletions src/app/VisynAppProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ModalsProvider, ModalsProviderProps } from '@mantine/modals';
import { Notifications, NotificationsProps } from '@mantine/notifications';
import merge from 'lodash/merge';
import * as React from 'react';
import { BrowserOptions } from '@sentry/react';
import { loadClientConfig } from '../base/clientConfig';
import { useAsync, useInitVisynApp, useVisynUser } from '../hooks';
import { VisProvider } from '../vis/Provider';
Expand All @@ -25,6 +26,8 @@ export function VisynAppProvider({
mantineProviderProps,
mantineModalsProviderProps,
mantineNotificationsProviderProps,
sentryInitOptions = {},
sentryOptions = {},
}: {
/**
* Set this to true to disable the MantineProvider of Mantine 6. Use only if no Mantine 6 components are used.
Expand All @@ -38,6 +41,19 @@ export function VisynAppProvider({
mantineProviderProps?: Omit<MantineProviderProps, 'children'>;
mantineModalsProviderProps?: Omit<ModalsProviderProps, 'children'>;
mantineNotificationsProviderProps?: Omit<NotificationsProps, 'children'>;
/**
* Options to pass to Sentry.init. The DSN is automatically set from the client config.
*/
sentryInitOptions?: Omit<BrowserOptions, 'dsn'>;
/**
* Additional options for the Sentry integration.
*/
sentryOptions?: {
/**
* Set the user in Sentry. Defaults to true.
*/
setUser?: boolean;
};
}) {
const user = useVisynUser();
const { status: initStatus } = useInitVisynApp();
Expand Down Expand Up @@ -67,6 +83,45 @@ export function VisynAppProvider({
[user, appName, clientConfig],
);

React.useEffect(() => {
// Hook to initialize Sentry if a DSN is provided.
if (clientConfig?.sentry_dsn) {
import('@sentry/react').then((Sentry) => {
if (!Sentry.isInitialized()) {
Sentry.init({
dsn: clientConfig.sentry_dsn,
tunnel: clientConfig.sentry_proxy_to,
integrations: [Sentry.browserTracingIntegration(), Sentry.replayIntegration()],

// Set tracesSampleRate to 1.0 to capture 100%
// of transactions for performance monitoring.
tracesSampleRate: 1.0,

// Capture Replay for 10% of all sessions,
// plus for 100% of sessions with an error
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0,

...(sentryInitOptions || {}),
});
}
});
}
}, [clientConfig?.sentry_dsn, clientConfig?.sentry_proxy_to, sentryInitOptions]);

React.useEffect(() => {
// Hook to set the user in Sentry if a DSN is provided and the user is set.
if (clientConfig?.sentry_dsn && user && sentryOptions?.setUser !== false) {
import('@sentry/react').then((Sentry) => {
if (Sentry.isInitialized()) {
Sentry.setUser({
username: user.name,
});
}
});
}
}, [clientConfig?.sentry_dsn, sentryOptions?.setUser, user]);

const mergedMantineProviderProps = React.useMemo(() => merge(merge({}, DEFAULT_MANTINE_PROVIDER_PROPS), mantineProviderProps || {}), [mantineProviderProps]);
const mergedMantine6ProviderProps = React.useMemo(
() => merge(merge({}, DEFAULT_MANTINE6_PROVIDER_PROPS), mantineProviderProps || {}),
Expand Down
2 changes: 2 additions & 0 deletions src/base/clientConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import { Ajax } from './ajax';
*/
export interface IClientConfig {
env?: 'development' | 'production';
sentry_dsn?: string;
sentry_proxy_to?: string;
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/vis/sidebar/AggregateTypeSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export function AggregateTypeSelect({
<>
<Select
label="Aggregate type"
allowDeselect={false}
onChange={(option) => aggregateTypeSelectCallback(option as EAggregateTypes)}
name="numColumns"
data={selectOptions || []}
Expand Down
4 changes: 4 additions & 0 deletions visyn_core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ def init_app(self, app: FastAPI):

app.include_router(create_settings_router())

from .sentry.sentry_proxy_router import sentry_router

app.include_router(sentry_router)

def register(self, registry: RegHelper):
# phovea_server
registry.append_router("caleydo-idtype", "visyn_core.id_mapping.idtype_api", {})
Expand Down
Empty file added visyn_core/sentry/__init__.py
Empty file.
34 changes: 34 additions & 0 deletions visyn_core/sentry/sentry_proxy_router.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import json
import logging
from urllib.parse import urlparse

import httpx
from fastapi import APIRouter, Request, Response

from .. import manager

sentry_router = APIRouter(prefix="/api/sentry", tags=["Sentry"])

_log = logging.getLogger(__name__)


@sentry_router.post("/")
async def proxy_sentry_envelope(request: Request): # pyright: ignore[reportUnusedFunction]
dsn = manager.settings.visyn_core.sentry.dsn
if dsn:
async with httpx.AsyncClient(timeout=10) as client:
# Example to parse a sentry envelope: https://github.com/getsentry/examples/blob/66da5f8c9559f64f1bfa57f8dd9b0731f75cd0e9/tunneling/python/app.py
envelope = await request.body()
piece = envelope.split(b"\n")[0].decode("utf-8")
header = json.loads(piece)
dsn = urlparse(header.get("dsn"))
proxy_to = manager.settings.visyn_core.sentry.proxy_to or f"{dsn.scheme}://{dsn.hostname}"
project_id = dsn.path.strip("/")
res = await client.post(
url=f"{proxy_to}/api/{project_id}/envelope/",
content=envelope,
headers={"Content-Type": "application/x-sentry-envelope"},
)
return Response(status_code=res.status_code, content=res.content)
else:
return Response(status_code=500, content="Sentry is not configured")
2 changes: 2 additions & 0 deletions visyn_core/settings/client_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ def _get_model():
@visyn_client_config
class VisynCoreClientConfigModel(BaseModel):
env: Literal["development", "production"] = Field(default_factory=lambda: manager.settings.env)
sentry_dsn: str | None = Field(default_factory=lambda: manager.settings.visyn_core.sentry.dsn)
sentry_proxy_to: str | None = Field(default_factory=lambda: "/api/sentry/" if manager.settings.visyn_core.sentry.proxy_to else None)


def init_client_config(app: FastAPI):
Expand Down
15 changes: 15 additions & 0 deletions visyn_core/settings/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,17 @@ class TelemetrySettings(BaseModel):
metrics_middleware: BaseTelemetrySettings = BaseTelemetrySettings()


class SentrySettings(BaseModel):
dsn: str | None = None
"""
Public DSN of the Sentry project.
"""
proxy_to: str | None = None
"""
Proxy Sentry envelopes to this URL. Used if an internal Sentry server is used, otherwise the original DSN is used.
"""


class VisynCoreSettings(BaseModel):
total_anyio_tokens: int = 100
"""
Expand All @@ -163,6 +174,10 @@ class VisynCoreSettings(BaseModel):
"""
Settings for telemetry using OpenTelemetry, prometheus, ...
"""
sentry: SentrySettings = SentrySettings()
"""
Settings for Sentry. DSN will be shared via the client config.
"""
cypress: bool = False
"""
True if the application is running in Cypress testing environment. Enables application to return special responses for example.
Expand Down

0 comments on commit 1a66533

Please sign in to comment.