feat(api): dashboards 3/5

This commit is contained in:
Taha Yassine Kraiem 2022-03-29 18:33:41 +02:00
parent 8e1f93630c
commit fe53ee07d7
6 changed files with 129 additions and 65 deletions

View file

@ -4,6 +4,24 @@ import schemas
from chalicelib.utils import helper
from chalicelib.utils import pg_client
CATEGORY_DESCRIPTION = {
'categ1': 'lorem',
}
def get_templates(project_id, user_id):
with pg_client.PostgresClient() as cur:
pg_query = cur.mogrify(f"""SELECT category, jsonb_agg(metrics ORDER BY name) AS widgets
FROM metrics
WHERE deleted_at IS NULL AND (project_id ISNULL OR (project_id = %(project_id)s AND (is_public OR user_id= %(userId)s)))
GROUP BY category
ORDER BY category;""", {"project_id": project_id, "userId": user_id})
cur.execute(pg_query)
rows = cur.fetchall()
for r in rows:
r["description"] = CATEGORY_DESCRIPTION.get(r["category"], "")
return helper.list_to_camel_case(rows)
def create_dashboard(project_id, user_id, data: schemas.CreateDashboardSchema):
with pg_client.PostgresClient() as cur:
@ -31,15 +49,10 @@ def get_dashboards(project_id, user_id):
def get_dashboard(project_id, user_id, dashboard_id):
with pg_client.PostgresClient() as cur:
pg_query = """SELECT dashboards.*, all_template_widgets.widgets AS template_widgets, all_metric_widgets.widgets AS metric_widgets
pg_query = """SELECT dashboards.*, all_metric_widgets.widgets AS widgets
FROM dashboards
LEFT JOIN LATERAL (SELECT COALESCE(JSONB_AGG(templates), '[]'::jsonb) AS widgets
FROM templates
INNER JOIN dashboard_widgets USING (template_id)
WHERE dashboard_widgets.dashboard_id = dashboards.dashboard_id
) AS all_template_widgets ON (TRUE)
LEFT JOIN LATERAL (SELECT COALESCE(JSONB_AGG(raw_metrics), '[]') AS widgets
FROM (SELECT metrics.*, metric_series.series
FROM (SELECT dashboard_widgets.*, metrics.*, metric_series.series
FROM metrics
INNER JOIN dashboard_widgets USING (metric_id)
LEFT JOIN LATERAL (SELECT JSONB_AGG(metric_series.* ORDER BY index) AS series
@ -59,35 +72,84 @@ def get_dashboard(project_id, user_id, dashboard_id):
print(cur.mogrify(pg_query, params))
cur.execute(cur.mogrify(pg_query, params))
row = cur.fetchone()
row["widgets"] = row.pop("template_widgets") + row.pop("metric_widgets")
return helper.dict_to_camel_case(row)
def delete_dashboard(project_id, user_id, dashboard_id):
with pg_client.PostgresClient() as cur:
pg_query = """UPDATE dashboards
SET deleted_at = timezone('utc'::text, now())
WHERE dashboards.project_id = %(projectId)s
AND dashboard_id = %(dashboard_id)s
AND (dashboards.user_id = %(userId)s OR is_public);"""
params = {"userId": user_id, "projectId": project_id, "dashboard_id": dashboard_id}
cur.execute(cur.mogrify(pg_query, params))
return {"data": {"success": True}}
def update_dashboard(project_id, user_id, dashboard_id, data: schemas.CreateDashboardSchema):
with pg_client.PostgresClient() as cur:
pg_query = """UPDATE dashboards
SET name = %(name)s, is_pinned = %(is_pinned)s, is_public = %(is_public)s
WHERE dashboards.project_id = %(projectId)s
AND dashboard_id = %(dashboard_id)s
AND (dashboards.user_id = %(userId)s OR is_public)
RETURNING *;"""
params = {"userId": user_id, "projectId": project_id, "dashboard_id": dashboard_id, **data.dict()}
cur.execute(cur.mogrify(pg_query, params))
row = cur.fetchone()
return helper.dict_to_camel_case(row)
def add_widget(project_id, user_id, dashboard_id, data: schemas.AddWidgetToDashboardPayloadSchema):
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)
pg_query = """INSERT INTO dashboard_widgets(dashboard_id, metric_id, user_id, config, name)
SELECT %(dashboard_id)s AS dashboard_id, %(metric_id)s AS metric_id,
%(userId)s AS user_id, %(config)s::jsonb AS config, %(name)s AS name
WHERE EXISTS(SELECT 1 FROM dashboards
WHERE dashboards.deleted_at ISNULL AND dashboards.project_id = %(projectId)s
AND dashboard_id = %(dashboard_id)s
AND (dashboards.user_id = %(userId)s OR is_public))
RETURNING *;"""
params = {"userId": user_id, "projectId": project_id, "dashboard_id": dashboard_id, **data.dict()}
params["configuration"] = json.dumps(params.get("configuration", {}))
params["config"] = json.dumps(data.config)
cur.execute(cur.mogrify(pg_query, params))
row = cur.fetchone()
return helper.dict_to_camel_case(row)
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"
def update_widget(project_id, user_id, dashboard_id, widget_id, data: schemas.AddWidgetToDashboardPayloadSchema):
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", {}))
pg_query = """UPDATE dashboard_widgets
SET name= %(name)s, config= %(config)s
WHERE dashboard_id=%(dashboard_id)s AND widget_id=%(widget_id)s
RETURNINIG *;"""
params = {"userId": user_id, "projectId": project_id, "dashboard_id": dashboard_id,
"widget_id": widget_id, **data.dict()}
cur.execute(cur.mogrify(pg_query, params))
row = cur.fetchone()
return helper.dict_to_camel_case(row)
def remove_widget(project_id, user_id, dashboard_id, widget_id):
with pg_client.PostgresClient() as cur:
pg_query = """DELETE FROM dashboard_widgets
WHERE dashboard_id=%(dashboard_id)s AND widget_id=%(widget_id)s;"""
params = {"userId": user_id, "projectId": project_id, "dashboard_id": dashboard_id, "widget_id": widget_id}
cur.execute(cur.mogrify(pg_query, params))
return {"data": {"success": True}}
def pin_dashboard(project_id, user_id, dashboard_id):
with pg_client.PostgresClient() as cur:
pg_query = """UPDATE dashboards
SET is_pinned = FALSE
WHERE dashboard_id=%(dashboard_id)s AND project_id=%(project_id)s;
UPDATE dashboards
SET is_pinned = True
WHERE dashboard_id=%(dashboard_id)s AND project_id=%(project_id)s AND deleted_at ISNULL
RETURNING *;"""
params = {"userId": user_id, "project_id": project_id, "dashboard_id": dashboard_id}
cur.execute(cur.mogrify(pg_query, params))
row = cur.fetchone()
return helper.dict_to_camel_case(row)

View file

@ -1,20 +0,0 @@
from chalicelib.utils import helper
from chalicelib.utils import pg_client
CATEGORY_DESCRIPTION = {
'categ1': 'lorem',
}
def get_templates(project_id, user_id):
with pg_client.PostgresClient() as cur:
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;""", {"project_id": project_id, "userId": user_id})
cur.execute(pg_query)
rows = cur.fetchall()
for r in rows:
r["description"] = CATEGORY_DESCRIPTION.get(r["category"], "")
return helper.list_to_camel_case(rows)

View file

@ -1,32 +1,50 @@
from fastapi import Body, Depends
import schemas
from chalicelib.core import dashboards2, templates, custom_metrics
from chalicelib.core import dashboards2, custom_metrics
from or_dependencies import OR_context
from routers.base import get_routers
public_app, app, app_apikey = get_routers()
@app.post('/{projectId}/dashboards', tags=["dashboard", "metrics"])
@app.put('/{projectId}/dashboards', tags=["dashboard", "metrics"])
@app.post('/{projectId}/dashboards', tags=["dashboard"])
@app.put('/{projectId}/dashboards', tags=["dashboard"])
def create_dashboards(projectId: int, data: schemas.CreateDashboardSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return {"data": dashboards2.create_dashboard(project_id=projectId, user_id=context.user_id, data=data)}
@app.get('/{projectId}/dashboards', tags=["dashboard", "metrics"])
@app.get('/{projectId}/dashboards', tags=["dashboard"])
def get_dashboards(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
return {"data": dashboards2.get_dashboards(project_id=projectId, user_id=context.user_id)}
@app.get('/{projectId}/dashboards/{dashboardId}', tags=["dashboard", "metrics"])
@app.get('/{projectId}/dashboards/{dashboardId}', tags=["dashboard"])
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"])
@app.post('/{projectId}/dashboards/{dashboardId}', tags=["dashboard"])
@app.put('/{projectId}/dashboards/{dashboardId}', tags=["dashboard"])
def update_dashboard(projectId: int, dashboardId: int, data: schemas.CreateDashboardSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return {"data": dashboards2.update_dashboard(project_id=projectId, user_id=context.user_id,
dashboard_id=dashboardId, data=data)}
@app.delete('/{projectId}/dashboards/{dashboardId}', tags=["dashboard"])
def delete_dashboard(projectId: int, dashboardId: int, context: schemas.CurrentContext = Depends(OR_context)):
return dashboards2.delete_dashboard(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId)
@app.get('/{projectId}/dashboards/{dashboardId}/pin', tags=["dashboard"])
def pin_dashboard(projectId: int, dashboardId: int, context: schemas.CurrentContext = Depends(OR_context)):
return {"data": dashboards2.pin_dashboard(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId)}
@app.post('/{projectId}/dashboards/{dashboardId}/widgets', tags=["dashboard"])
@app.put('/{projectId}/dashboards/{dashboardId}/widgets', tags=["dashboard"])
def add_widget_to_dashboard(projectId: int, dashboardId: int,
data: schemas.AddWidgetToDashboardPayloadSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
@ -34,22 +52,25 @@ def add_widget_to_dashboard(projectId: int, dashboardId: int,
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(...),
@app.post('/{projectId}/dashboards/{dashboardId}/widgets/{widgetId}', tags=["dashboard"])
@app.put('/{projectId}/dashboards/{dashboardId}/widgets/{widgetId}', tags=["dashboard"])
def update_widget_in_dashboard(projectId: int, dashboardId: int, widgetId: int,
data: schemas.AddWidgetToDashboardPayloadSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return dashboards2.update_widget(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId,
widget_id=widgetId, data=data)
@app.delete('/{projectId}/dashboards/{dashboardId}/widgets/{widgetId}', tags=["dashboard"])
def remove_widget_from_dashboard(projectId: int, dashboardId: int, widgetId: int,
context: schemas.CurrentContext = Depends(OR_context)):
return {"data": dashboards2.add_widget(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId,
data=data)}
return dashboards2.remove_widget(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId,
widget_id=widgetId)
# @app.get('/{projectId}/widgets', tags=["dashboard", "metrics"])
# def get_dashboards(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
# return {"data": dashboards2.get_widgets(project_id=projectId)}
@app.get('/{projectId}/metrics/templates', tags=["dashboard", "metrics"])
@app.get('/{projectId}/metrics/templates', tags=["dashboard"])
def get_templates(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
return {"data": templates.get_templates(project_id=projectId, user_id=context.user_id)}
return {"data": dashboards2.get_templates(project_id=projectId, user_id=context.user_id)}
@app.post('/{projectId}/metrics/try', tags=["dashboard"])

View file

@ -878,7 +878,7 @@ class SavedSearchSchema(FunnelSchema):
class CreateDashboardSchema(BaseModel):
name: str = Field(...)
name: str = Field(..., min_length=1)
is_public: bool = Field(default=False)
is_pinned: bool = Field(default=False)
@ -887,10 +887,9 @@ class CreateDashboardSchema(BaseModel):
class AddWidgetToDashboardPayloadSchema(BaseModel):
template_id: Optional[int] = Field(default=None)
metric_id: Optional[int] = Field(default=None)
name: Optional[str] = Field(default=None)
configuration: dict = Field(default={})
config: dict = Field(default={})
@root_validator
def validator(cls, values):

View file

@ -53,6 +53,7 @@ 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 is_pinned boolean NOT NULL DEFAULT FALSE,
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,

View file

@ -953,6 +953,7 @@ $$
metric_value text[] NOT NULL DEFAULT '{}'::text[],
metric_format text,
category text NULL DEFAULT 'custom',
is_pinned boolean NOT NULL DEFAULT FALSE,
is_predefined boolean NOT NULL DEFAULT FALSE,
is_template boolean NOT NULL DEFAULT FALSE,
key text NULL DEFAULT NULL,