From 8e1f93630ce44c0f7606761e47860ee0212824df Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Mon, 28 Mar 2022 19:55:06 +0200 Subject: [PATCH] feat(api): dashboard re-think 1/5 --- api/app.py | 3 +- api/chalicelib/core/dashboards2.py | 24 +++-- api/chalicelib/core/templates.py | 9 +- api/routers/app/__init__.py | 0 api/routers/subs/metrics.py | 18 +++- api/routers/{app => subs}/v1_api.py | 0 .../db/init_dbs/postgresql/1.5.5/1.5.5.sql | 102 +++++++++++++----- .../db/init_dbs/postgresql/init_schema.sql | 14 ++- 8 files changed, 119 insertions(+), 51 deletions(-) delete mode 100644 api/routers/app/__init__.py rename api/routers/{app => subs}/v1_api.py (100%) diff --git a/api/app.py b/api/app.py index b1ddca682..ae7f051bf 100644 --- a/api/app.py +++ b/api/app.py @@ -9,10 +9,9 @@ from starlette.responses import StreamingResponse from chalicelib.utils import helper from chalicelib.utils import pg_client from routers import core, core_dynamic -from routers.app import v1_api from routers.crons import core_crons from routers.crons import core_dynamic_crons -from routers.subs import dashboard, insights, metrics +from routers.subs import dashboard, insights, metrics, v1_api app = FastAPI() diff --git a/api/chalicelib/core/dashboards2.py b/api/chalicelib/core/dashboards2.py index 782532718..c36293059 100644 --- a/api/chalicelib/core/dashboards2.py +++ b/api/chalicelib/core/dashboards2.py @@ -77,13 +77,17 @@ def add_widget(project_id, user_id, dashboard_id, data: schemas.AddWidgetToDashb row = cur.fetchone() return helper.dict_to_camel_case(row) -# def get_widgets(project_id): -# with pg_client.PostgresClient() as cur: -# pg_query = f"""SELECT * -# FROM widgets -# WHERE deleted_at ISNULL -# AND project_id = %(projectId)s;""" -# params = {"projectId": project_id} -# cur.execute(cur.mogrify(pg_query, params)) -# rows = cur.fetchall() -# return helper.list_to_camel_case(rows) +def remove_widget(project_id, user_id, dashboard_id,widget_id): + ref_key = "metric_id" + if data.template_id is not None: + ref_key = "template_id" + with pg_client.PostgresClient() as cur: + pg_query = f"""INSERT INTO dashboard_widgets(dashboard_id, {ref_key}, user_id, configuration, name) + VALUES (%(dashboard_id)s, %({ref_key})s, %(userId)s, %(configuration)s::jsonb, %(name)s) + RETURNING *;""" + params = {"userId": user_id, "projectId": project_id, "dashboard_id": dashboard_id, **data.dict()} + params["configuration"] = json.dumps(params.get("configuration", {})) + cur.execute(cur.mogrify(pg_query, params)) + row = cur.fetchone() + return helper.dict_to_camel_case(row) + diff --git a/api/chalicelib/core/templates.py b/api/chalicelib/core/templates.py index 8141bdd35..8e42668d3 100644 --- a/api/chalicelib/core/templates.py +++ b/api/chalicelib/core/templates.py @@ -6,12 +6,13 @@ CATEGORY_DESCRIPTION = { } -def get_templates(): +def get_templates(project_id, user_id): with pg_client.PostgresClient() as cur: - pg_query = f"""SELECT category, jsonb_agg(templates ORDER BY name) AS widgets - FROM templates + pg_query = cur.mogrify(f"""SELECT category, jsonb_agg(metrics ORDER BY name) AS widgets + FROM metrics + WHERE project_id ISNULL OR (project_id = %(project_id)s AND (is_public OR user_id= %(userId)s)) GROUP BY category - ORDER BY category;""" + ORDER BY category;""", {"project_id": project_id, "userId": user_id}) cur.execute(pg_query) rows = cur.fetchall() for r in rows: diff --git a/api/routers/app/__init__.py b/api/routers/app/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/api/routers/subs/metrics.py b/api/routers/subs/metrics.py index 9c0bb64ba..56b486977 100644 --- a/api/routers/subs/metrics.py +++ b/api/routers/subs/metrics.py @@ -21,15 +21,23 @@ def get_dashboards(projectId: int, context: schemas.CurrentContext = Depends(OR_ @app.get('/{projectId}/dashboards/{dashboardId}', tags=["dashboard", "metrics"]) -def get_dashboards(projectId: int, dashboardId: int, context: schemas.CurrentContext = Depends(OR_context)): +def get_dashboard(projectId: int, dashboardId: int, context: schemas.CurrentContext = Depends(OR_context)): return {"data": dashboards2.get_dashboard(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId)} @app.post('/{projectId}/dashboards/{dashboardId}/metrics', tags=["dashboard", "metrics"]) @app.put('/{projectId}/dashboards/{dashboardId}/metrics', tags=["dashboard", "metrics"]) -def add_widget_to_dashboards(projectId: int, dashboardId: int, - data: schemas.AddWidgetToDashboardPayloadSchema = Body(...), - context: schemas.CurrentContext = Depends(OR_context)): +def add_widget_to_dashboard(projectId: int, dashboardId: int, + data: schemas.AddWidgetToDashboardPayloadSchema = Body(...), + context: schemas.CurrentContext = Depends(OR_context)): + return {"data": dashboards2.add_widget(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId, + data=data)} + + +@app.delete('/{projectId}/dashboards/{dashboardId}/metrics/{metricId}', tags=["dashboard", "metrics"]) +def remove_widget_from_dashboard(projectId: int, dashboardId: int, metricId: int, + data: schemas.AddWidgetToDashboardPayloadSchema = Body(...), + context: schemas.CurrentContext = Depends(OR_context)): return {"data": dashboards2.add_widget(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId, data=data)} @@ -41,7 +49,7 @@ def add_widget_to_dashboards(projectId: int, dashboardId: int, @app.get('/{projectId}/metrics/templates', tags=["dashboard", "metrics"]) def get_templates(projectId: int, context: schemas.CurrentContext = Depends(OR_context)): - return {"data": templates.get_templates()} + return {"data": templates.get_templates(project_id=projectId, user_id=context.user_id)} @app.post('/{projectId}/metrics/try', tags=["dashboard"]) diff --git a/api/routers/app/v1_api.py b/api/routers/subs/v1_api.py similarity index 100% rename from api/routers/app/v1_api.py rename to api/routers/subs/v1_api.py diff --git a/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql b/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql index a8bb8aafd..d0d31ecce 100644 --- a/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql +++ b/scripts/helm/db/init_dbs/postgresql/1.5.5/1.5.5.sql @@ -6,38 +6,86 @@ SELECT 'v1.5.5' $$ LANGUAGE sql IMMUTABLE; -CREATE TABLE dashboards +-- CREATE TABLE dashboards +-- ( +-- dashboard_id integer generated BY DEFAULT AS IDENTITY PRIMARY KEY, +-- project_id integer NOT NULL REFERENCES projects (project_id) ON DELETE CASCADE, +-- user_id integer NOT NULL REFERENCES users (user_id) ON DELETE SET NULL, +-- name text NOT NULL, +-- is_public boolean NOT NULL DEFAULT TRUE, +-- is_pinned boolean NOT NULL DEFAULT FALSE, +-- created_at timestamp NOT NULL DEFAULT timezone('utc'::text, now()), +-- deleted_at timestamp NULL DEFAULT NULL +-- ); + +-- CREATE TABLE templates +-- ( +-- template_id integer generated BY DEFAULT AS IDENTITY PRIMARY KEY, +-- template_key text, +-- name text NOT NULL, +-- category text NOT NULL, +-- series jsonb NOT NULL, +-- config jsonb NOT NULL, +-- predefined boolean DEFAULT TRUE +-- ); + +-- CREATE TABLE dashboard_widgets +-- ( +-- widget_id integer generated BY DEFAULT AS IDENTITY PRIMARY KEY, +-- dashboard_id integer NOT NULL REFERENCES dashboards (dashboard_id) ON DELETE CASCADE, +-- metric_id integer NOT NULL REFERENCES metrics (metric_id) ON DELETE CASCADE, +-- -- template_id integer NOT NULL REFERENCES templates (template_id) ON DELETE CASCADE, +-- user_id integer NOT NULL REFERENCES users (user_id) ON DELETE SET NULL, +-- created_at timestamp NOT NULL DEFAULT timezone('utc'::text, now()), +-- configuration jsonb NOT NULL DEFAULT '{}'::jsonb, +-- name text +-- ); + +-- INSERT INTO public.templates (name, category, series, config, predefined, template_key) +-- VALUES ('captured sessions', 'overview', '[]', '{}', true, 'count_sessions'), +-- ('request load time', 'overview', '[]', '{}', true, 'avg_request_load_time'), +-- ('page load time', 'overview', '[]', '{}', true, 'avg_page_load_time'), +-- ('image load time', 'overview', '[]', '{}', true, 'avg_image_load_time'); + +ALTER TYPE metric_view_type ADD VALUE IF NOT EXISTS 'areaChart'; + +ALTER TABLE IF EXISTS metrics + DROP CONSTRAINT IF EXISTS null_project_id_for_template_only; + +ALTER TABLE IF EXISTS metrics + ADD COLUMN IF NOT EXISTS category text NULL DEFAULT 'custom', + ADD COLUMN IF NOT EXISTS is_predefined boolean NOT NULL DEFAULT FALSE, + ADD COLUMN IF NOT EXISTS is_template boolean NOT NULL DEFAULT FALSE, + ADD COLUMN IF NOT EXISTS key text NULL DEFAULT NULL, + ADD COLUMN IF NOT EXISTS config jsonb NOT NULL DEFAULT '{}'::jsonb, + ALTER COLUMN project_id DROP NOT NULL, + ADD CONSTRAINT null_project_id_for_template_only + CHECK ( (metrics.category != 'custom') != (metrics.project_id IS NOT NULL) ); + + + +CREATE TABLE IF NOT EXISTS dashboard_widgets ( - dashboard_id integer generated BY DEFAULT AS IDENTITY PRIMARY KEY, - project_id integer NOT NULL REFERENCES projects (project_id) ON DELETE CASCADE, + widget_id integer generated BY DEFAULT AS IDENTITY PRIMARY KEY, + dashboard_id integer NOT NULL REFERENCES dashboards (dashboard_id) ON DELETE CASCADE, + metric_id integer NOT NULL REFERENCES metrics (metric_id) ON DELETE CASCADE, user_id integer NOT NULL REFERENCES users (user_id) ON DELETE SET NULL, - name text NOT NULL, - is_public boolean NOT NULL DEFAULT TRUE, - is_pinned boolean NOT NULL DEFAULT FALSE, created_at timestamp NOT NULL DEFAULT timezone('utc'::text, now()), - deleted_at timestamp NULL DEFAULT NULL + config jsonb NOT NULL DEFAULT '{}'::jsonb, + name text ); +-- INSERT INTO public.templates (name, category, series, config, predefined, template_key) +-- VALUES ('captured sessions', 'overview', '[]', '{}', true, 'count_sessions'), +-- ('request load time', 'overview', '[]', '{}', true, 'avg_request_load_time'), +-- ('page load time', 'overview', '[]', '{}', true, 'avg_page_load_time'), +-- ('image load time', 'overview', '[]', '{}', true, 'avg_image_load_time'); -CREATE TABLE widgets -( - widget_id integer generated BY DEFAULT AS IDENTITY PRIMARY KEY, - project_id integer NOT NULL REFERENCES projects (project_id) ON DELETE CASCADE, - user_id integer NOT NULL REFERENCES users (user_id) ON DELETE SET NULL, - name text NOT NULL, - is_template boolean NOT NULL DEFAULT FALSE, - predefined boolean NOT NULL DEFAULT FALSE, - created_at timestamp NOT NULL DEFAULT timezone('utc'::text, now()), - deleted_at timestamp NULL DEFAULT NULL -); +INSERT INTO metrics (name, category, config, is_predefined, is_template, is_public, key) +VALUES ('captured sessions', 'overview', '{}', true, true, true, 'count_sessions'), + ('request load time', 'overview', '{}', true, true, true, 'avg_request_load_time'), + ('page load time', 'overview', '{}', true, true, true, 'avg_page_load_time'), + ('image load time', 'overview', '{}', true, true, true, 'avg_image_load_time'); -CREATE TABLE dashboard_widgets -( - dashboard_id integer NOT NULL REFERENCES dashboards (dashboard_id) ON DELETE CASCADE, - widget_id integer NOT NULL REFERENCES widgets (widget_id) ON DELETE CASCADE, - user_id integer NOT NULL REFERENCES users (user_id) ON DELETE SET NULL, - created_at timestamp NOT NULL DEFAULT timezone('utc'::text, now()), - configuration jsonb NOT NULL DEFAULT '{}'::jsonb -); -COMMIT; \ No newline at end of file +COMMIT \ No newline at end of file diff --git a/scripts/helm/db/init_dbs/postgresql/init_schema.sql b/scripts/helm/db/init_dbs/postgresql/init_schema.sql index 834a79968..52f367e8e 100644 --- a/scripts/helm/db/init_dbs/postgresql/init_schema.sql +++ b/scripts/helm/db/init_dbs/postgresql/init_schema.sql @@ -936,11 +936,11 @@ $$ CREATE INDEX jobs_project_id_idx ON jobs (project_id); CREATE TYPE metric_type AS ENUM ('timeseries','table'); - CREATE TYPE metric_view_type AS ENUM ('lineChart','progress','table','pieChart'); + CREATE TYPE metric_view_type AS ENUM ('lineChart','progress','table','pieChart','areaChart'); CREATE TABLE metrics ( metric_id integer generated BY DEFAULT AS IDENTITY PRIMARY KEY, - project_id integer NOT NULL REFERENCES projects (project_id) ON DELETE CASCADE, + project_id integer NULL REFERENCES projects (project_id) ON DELETE CASCADE, user_id integer REFERENCES users (user_id) ON DELETE SET NULL, name text NOT NULL, is_public boolean NOT NULL DEFAULT FALSE, @@ -951,8 +951,16 @@ $$ view_type metric_view_type NOT NULL DEFAULT 'lineChart', metric_of text NOT NULL DEFAULT 'sessionCount', metric_value text[] NOT NULL DEFAULT '{}'::text[], - metric_format text + metric_format text, + category text NULL DEFAULT 'custom', + is_predefined boolean NOT NULL DEFAULT FALSE, + is_template boolean NOT NULL DEFAULT FALSE, + key text NULL DEFAULT NULL, + config jsonb NOT NULL DEFAULT '{}'::jsonb, + CONSTRAINT null_project_id_for_template_only + CHECK ( (metrics.category != 'custom') != (metrics.project_id IS NOT NULL) ) ); + CREATE INDEX metrics_user_id_is_public_idx ON public.metrics (user_id, is_public); CREATE TABLE metric_series (