fix(chalice): fixed EE sessions search for mobile projects (#3010)
refactor(chalice): enhanced sessions search payload validation
This commit is contained in:
parent
500d70aa67
commit
afb08cfe6d
13 changed files with 1547 additions and 45 deletions
|
|
@ -80,7 +80,7 @@ def __get_sort_key(key):
|
|||
}.get(key, 'max_datetime')
|
||||
|
||||
|
||||
def search(data: schemas.SearchErrorsSchema, project_id, user_id):
|
||||
def search(data: schemas.SearchErrorsSchema, project: schemas.ProjectContext, user_id):
|
||||
empty_response = {
|
||||
'total': 0,
|
||||
'errors': []
|
||||
|
|
@ -107,7 +107,7 @@ def search(data: schemas.SearchErrorsSchema, project_id, user_id):
|
|||
data.endTimestamp = TimeUTC.now(1)
|
||||
if len(data.events) > 0 or len(data.filters) > 0:
|
||||
print("-- searching for sessions before errors")
|
||||
statuses = sessions_search.search_sessions(data=data, project_id=project_id, user_id=user_id, errors_only=True,
|
||||
statuses = sessions_search.search_sessions(data=data, project=project, user_id=user_id, errors_only=True,
|
||||
error_status=data.status)
|
||||
if len(statuses) == 0:
|
||||
return empty_response
|
||||
|
|
@ -125,7 +125,7 @@ def search(data: schemas.SearchErrorsSchema, project_id, user_id):
|
|||
params = {
|
||||
"startDate": data.startTimestamp,
|
||||
"endDate": data.endTimestamp,
|
||||
"project_id": project_id,
|
||||
"project_id": project.project_id,
|
||||
"userId": user_id,
|
||||
"step_size": step_size}
|
||||
if data.status != schemas.ErrorStatus.ALL:
|
||||
|
|
@ -207,7 +207,7 @@ def search(data: schemas.SearchErrorsSchema, project_id, user_id):
|
|||
"""SELECT error_id
|
||||
FROM public.errors
|
||||
WHERE project_id = %(project_id)s AND error_id IN %(error_ids)s;""",
|
||||
{"project_id": project_id, "error_ids": tuple([r["error_id"] for r in rows]),
|
||||
{"project_id": project.project_id, "error_ids": tuple([r["error_id"] for r in rows]),
|
||||
"user_id": user_id})
|
||||
cur.execute(query=query)
|
||||
statuses = helper.list_to_camel_case(cur.fetchall())
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ def __get_errors_list(project: schemas.ProjectContext, user_id, data: schemas.Ca
|
|||
"total": 0,
|
||||
"errors": []
|
||||
}
|
||||
return errors.search(data.series[0].filter, project_id=project.project_id, user_id=user_id)
|
||||
return errors.search(data.series[0].filter, project=project, user_id=user_id)
|
||||
|
||||
|
||||
def __get_sessions_list(project: schemas.ProjectContext, user_id, data: schemas.CardSchema):
|
||||
|
|
@ -52,7 +52,7 @@ def __get_sessions_list(project: schemas.ProjectContext, user_id, data: schemas.
|
|||
"total": 0,
|
||||
"sessions": []
|
||||
}
|
||||
return sessions_search.search_sessions(data=data.series[0].filter, project_id=project.project_id, user_id=user_id)
|
||||
return sessions_search.search_sessions(data=data.series[0].filter, project=project, user_id=user_id)
|
||||
|
||||
|
||||
def __get_heat_map_chart(project: schemas.ProjectContext, user_id, data: schemas.CardHeatMap,
|
||||
|
|
@ -168,18 +168,18 @@ def get_chart(project: schemas.ProjectContext, data: schemas.CardSchema, user_id
|
|||
return supported.get(data.metric_type, not_supported)(project=project, data=data, user_id=user_id)
|
||||
|
||||
|
||||
def get_sessions_by_card_id(project_id, user_id, metric_id, data: schemas.CardSessionsSchema):
|
||||
if not card_exists(metric_id=metric_id, project_id=project_id, user_id=user_id):
|
||||
def get_sessions_by_card_id(project: schemas.ProjectContext, user_id, metric_id, data: schemas.CardSessionsSchema):
|
||||
if not card_exists(metric_id=metric_id, project_id=project.project_id, user_id=user_id):
|
||||
return None
|
||||
results = []
|
||||
for s in data.series:
|
||||
results.append({"seriesId": s.series_id, "seriesName": s.name,
|
||||
**sessions_search.search_sessions(data=s.filter, project_id=project_id, user_id=user_id)})
|
||||
**sessions_search.search_sessions(data=s.filter, project=project, user_id=user_id)})
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def get_sessions(project_id, user_id, data: schemas.CardSessionsSchema):
|
||||
def get_sessions(project: schemas.ProjectContext, user_id, data: schemas.CardSessionsSchema):
|
||||
results = []
|
||||
if len(data.series) == 0:
|
||||
return results
|
||||
|
|
@ -189,7 +189,7 @@ def get_sessions(project_id, user_id, data: schemas.CardSessionsSchema):
|
|||
s.filter = schemas.SessionsSearchPayloadSchema(**s.filter.model_dump(by_alias=True))
|
||||
|
||||
results.append({"seriesId": None, "seriesName": s.name,
|
||||
**sessions_search.search_sessions(data=s.filter, project_id=project_id, user_id=user_id)})
|
||||
**sessions_search.search_sessions(data=s.filter, project=project, user_id=user_id)})
|
||||
|
||||
return results
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
import ast
|
||||
import logging
|
||||
from typing import List, Union
|
||||
|
||||
import logging
|
||||
from typing import List, Union
|
||||
|
||||
import schemas
|
||||
from chalicelib.core import events, metadata, projects
|
||||
from chalicelib.core import events, metadata
|
||||
from chalicelib.core.metrics import metrics
|
||||
from chalicelib.core.sessions import sessions_favorite, performance_event, sessions_legacy
|
||||
from chalicelib.core.sessions import performance_event, sessions_legacy
|
||||
from chalicelib.utils import pg_client, helper, metrics_helper, ch_client, exp_ch_helper
|
||||
from chalicelib.utils import sql_helper as sh
|
||||
|
||||
|
|
|
|||
|
|
@ -40,16 +40,16 @@ COALESCE((SELECT TRUE
|
|||
|
||||
|
||||
# This function executes the query and return result
|
||||
def search_sessions(data: schemas.SessionsSearchPayloadSchema, project_id, user_id, errors_only=False,
|
||||
error_status=schemas.ErrorStatus.ALL, count_only=False, issue=None, ids_only=False,
|
||||
platform="web"):
|
||||
def search_sessions(data: schemas.SessionsSearchPayloadSchema, project: schemas.ProjectContext,
|
||||
user_id, errors_only=False, error_status=schemas.ErrorStatus.ALL,
|
||||
count_only=False, issue=None, ids_only=False, platform="web"):
|
||||
if data.bookmarked:
|
||||
data.startTimestamp, data.endTimestamp = sessions_favorite.get_start_end_timestamp(project_id, user_id)
|
||||
data.startTimestamp, data.endTimestamp = sessions_favorite.get_start_end_timestamp(project.project_id, user_id)
|
||||
|
||||
full_args, query_part = sessions_legacy.search_query_parts(data=data, error_status=error_status,
|
||||
errors_only=errors_only,
|
||||
favorite_only=data.bookmarked, issue=issue,
|
||||
project_id=project_id,
|
||||
project_id=project.project_id,
|
||||
user_id=user_id, platform=platform)
|
||||
if data.limit is not None and data.page is not None:
|
||||
full_args["sessions_limit"] = data.limit
|
||||
|
|
@ -86,7 +86,7 @@ def search_sessions(data: schemas.SessionsSearchPayloadSchema, project_id, user_
|
|||
else:
|
||||
sort = 'start_ts'
|
||||
|
||||
meta_keys = metadata.get(project_id=project_id)
|
||||
meta_keys = metadata.get(project_id=project.project_id)
|
||||
main_query = cur.mogrify(f"""SELECT COUNT(*) AS count,
|
||||
COALESCE(JSONB_AGG(users_sessions)
|
||||
FILTER (WHERE rn>%(sessions_limit_s)s AND rn<=%(sessions_limit_e)s), '[]'::JSONB) AS sessions
|
||||
|
|
@ -120,7 +120,7 @@ def search_sessions(data: schemas.SessionsSearchPayloadSchema, project_id, user_
|
|||
# sort += " " + data.order + "," + helper.key_to_snake_case(data.sort)
|
||||
sort = helper.key_to_snake_case(data.sort)
|
||||
|
||||
meta_keys = metadata.get(project_id=project_id)
|
||||
meta_keys = metadata.get(project_id=project.project_id)
|
||||
main_query = cur.mogrify(f"""SELECT COUNT(full_sessions) AS count,
|
||||
COALESCE(JSONB_AGG(full_sessions)
|
||||
FILTER (WHERE rn>%(sessions_limit_s)s AND rn<=%(sessions_limit_e)s), '[]'::JSONB) AS sessions
|
||||
|
|
|
|||
36
api/chalicelib/utils/contextual_validators.py
Normal file
36
api/chalicelib/utils/contextual_validators.py
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import schemas
|
||||
from fastapi import HTTPException, Depends
|
||||
from or_dependencies import OR_context
|
||||
|
||||
|
||||
def validate_contextual_payload(
|
||||
item: schemas.SessionsSearchPayloadSchema,
|
||||
context: schemas.CurrentContext = Depends(OR_context)
|
||||
) -> schemas.SessionsSearchPayloadSchema:
|
||||
if context.project.platform == "web":
|
||||
for e in item.events:
|
||||
if e.type in [schemas.EventType.CLICK_MOBILE,
|
||||
schemas.EventType.INPUT_MOBILE,
|
||||
schemas.EventType.VIEW_MOBILE,
|
||||
schemas.EventType.CUSTOM_MOBILE,
|
||||
schemas.EventType.REQUEST_MOBILE,
|
||||
schemas.EventType.ERROR_MOBILE,
|
||||
schemas.EventType.SWIPE_MOBILE]:
|
||||
raise HTTPException(status_code=422,
|
||||
detail=f"Mobile event '{e.type}' not supported for web project")
|
||||
else:
|
||||
for e in item.events:
|
||||
if e.type in [schemas.EventType.CLICK,
|
||||
schemas.EventType.INPUT,
|
||||
schemas.EventType.LOCATION,
|
||||
schemas.EventType.CUSTOM,
|
||||
schemas.EventType.REQUEST,
|
||||
schemas.EventType.REQUEST_DETAILS,
|
||||
schemas.EventType.GRAPHQL,
|
||||
schemas.EventType.STATE_ACTION,
|
||||
schemas.EventType.ERROR,
|
||||
schemas.EventType.TAG]:
|
||||
raise HTTPException(status_code=422,
|
||||
detail=f"Web event '{e.type}' not supported for mobile project")
|
||||
|
||||
return item
|
||||
|
|
@ -22,6 +22,7 @@ from chalicelib.utils.TimeUTC import TimeUTC
|
|||
from or_dependencies import OR_context, OR_role
|
||||
from routers.base import get_routers
|
||||
from routers.subs import spot
|
||||
from chalicelib.utils import contextual_validators
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
public_app, app, app_apikey = get_routers()
|
||||
|
|
@ -252,17 +253,19 @@ def get_projects(context: schemas.CurrentContext = Depends(OR_context)):
|
|||
|
||||
|
||||
@app.post('/{projectId}/sessions/search', tags=["sessions"])
|
||||
def search_sessions(projectId: int, data: schemas.SessionsSearchPayloadSchema = Body(...),
|
||||
def search_sessions(projectId: int, data: schemas.SessionsSearchPayloadSchema = \
|
||||
Depends(contextual_validators.validate_contextual_payload),
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = sessions_search.search_sessions(data=data, project_id=projectId, user_id=context.user_id,
|
||||
data = sessions_search.search_sessions(data=data, project=context.project, user_id=context.user_id,
|
||||
platform=context.project.platform)
|
||||
return {'data': data}
|
||||
|
||||
|
||||
@app.post('/{projectId}/sessions/search/ids', tags=["sessions"])
|
||||
def session_ids_search(projectId: int, data: schemas.SessionsSearchPayloadSchema = Body(...),
|
||||
def session_ids_search(projectId: int, data: schemas.SessionsSearchPayloadSchema = \
|
||||
Depends(contextual_validators.validate_contextual_payload),
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = sessions_search.search_sessions(data=data, project_id=projectId, user_id=context.user_id, ids_only=True,
|
||||
data = sessions_search.search_sessions(data=data, project=context.project, user_id=context.user_id, ids_only=True,
|
||||
platform=context.project.platform)
|
||||
return {'data': data}
|
||||
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ def try_card(projectId: int, data: schemas.CardSchema = Body(...),
|
|||
@app.post('/{projectId}/cards/try/sessions', tags=["cards"])
|
||||
def try_card_sessions(projectId: int, data: schemas.CardSessionsSchema = Body(...),
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = custom_metrics.get_sessions(project_id=projectId, user_id=context.user_id, data=data)
|
||||
data = custom_metrics.get_sessions(project=context.project, user_id=context.user_id, data=data)
|
||||
return {"data": data}
|
||||
|
||||
|
||||
|
|
@ -130,7 +130,7 @@ def get_card(projectId: int, metric_id: Union[int, str], context: schemas.Curren
|
|||
def get_card_sessions(projectId: int, metric_id: int,
|
||||
data: schemas.CardSessionsSchema = Body(...),
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = custom_metrics.get_sessions_by_card_id(project_id=projectId, user_id=context.user_id, metric_id=metric_id,
|
||||
data = custom_metrics.get_sessions_by_card_id(project=context.project, user_id=context.user_id, metric_id=metric_id,
|
||||
data=data)
|
||||
if data is None:
|
||||
return {"errors": ["custom metric not found"]}
|
||||
|
|
|
|||
1
ee/api/.gitignore
vendored
1
ee/api/.gitignore
vendored
|
|
@ -292,3 +292,4 @@ Pipfile.lock
|
|||
/chalicelib/core/errors/errors.py
|
||||
/chalicelib/core/errors/errors_ch.py
|
||||
/chalicelib/core/errors/errors_details.py
|
||||
/chalicelib/utils/contextual_validators.py
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ def _get_current_auth_context(request: Request, jwt_payload: dict) -> schemas.Cu
|
|||
role=user["role"],
|
||||
permissions=user["permissions"],
|
||||
serviceAccount=user["serviceAccount"])
|
||||
print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>VC")
|
||||
return request.state.currentContext
|
||||
|
||||
|
||||
|
|
|
|||
1447
ee/api/chalicelib/core/sessions/sessions_legacy_mobil.py
Normal file
1447
ee/api/chalicelib/core/sessions/sessions_legacy_mobil.py
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -3,7 +3,7 @@ import logging
|
|||
|
||||
import schemas
|
||||
from chalicelib.core import metadata, projects
|
||||
from chalicelib.core.sessions import sessions_favorite, sessions_legacy, sessions
|
||||
from chalicelib.core.sessions import sessions_favorite, sessions_legacy, sessions, sessions_legacy_mobil
|
||||
from chalicelib.utils import pg_client, helper, ch_client, exp_ch_helper
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
@ -58,16 +58,24 @@ SESSION_PROJECTION_COLS_CH_MAP = """\
|
|||
|
||||
|
||||
# This function executes the query and return result
|
||||
def search_sessions(data: schemas.SessionsSearchPayloadSchema, project_id, user_id, errors_only=False,
|
||||
def search_sessions(data: schemas.SessionsSearchPayloadSchema, project: schemas.ProjectContext,
|
||||
user_id, errors_only=False,
|
||||
error_status=schemas.ErrorStatus.ALL, count_only=False, issue=None, ids_only=False,
|
||||
platform="web"):
|
||||
if data.bookmarked:
|
||||
data.startTimestamp, data.endTimestamp = sessions_favorite.get_start_end_timestamp(project_id, user_id)
|
||||
full_args, query_part = sessions.search_query_parts_ch(data=data, error_status=error_status,
|
||||
errors_only=errors_only,
|
||||
favorite_only=data.bookmarked, issue=issue,
|
||||
project_id=project_id,
|
||||
user_id=user_id, platform=platform)
|
||||
data.startTimestamp, data.endTimestamp = sessions_favorite.get_start_end_timestamp(project.project_id, user_id)
|
||||
if project.platform == "web":
|
||||
full_args, query_part = sessions.search_query_parts_ch(data=data, error_status=error_status,
|
||||
errors_only=errors_only,
|
||||
favorite_only=data.bookmarked, issue=issue,
|
||||
project_id=project.project_id,
|
||||
user_id=user_id, platform=platform)
|
||||
else:
|
||||
full_args, query_part = sessions_legacy_mobil.search_query_parts_ch(data=data, error_status=error_status,
|
||||
errors_only=errors_only,
|
||||
favorite_only=data.bookmarked, issue=issue,
|
||||
project_id=project.project_id,
|
||||
user_id=user_id, platform=platform)
|
||||
if data.sort == "startTs":
|
||||
data.sort = "datetime"
|
||||
if data.limit is not None and data.page is not None:
|
||||
|
|
@ -106,7 +114,7 @@ def search_sessions(data: schemas.SessionsSearchPayloadSchema, project_id, user_
|
|||
else:
|
||||
sort = 'start_ts'
|
||||
|
||||
meta_keys = metadata.get(project_id=project_id)
|
||||
meta_keys = metadata.get(project_id=project.project_id)
|
||||
meta_map = ",map(%s) AS 'metadata'" \
|
||||
% ','.join([f"'{m['key']}',coalesce(metadata_{m['index']},'None')" for m in meta_keys])
|
||||
main_query = cur.mogrify(f"""SELECT COUNT(*) AS count,
|
||||
|
|
@ -141,7 +149,7 @@ def search_sessions(data: schemas.SessionsSearchPayloadSchema, project_id, user_
|
|||
# sort += " " + data.order + "," + helper.key_to_snake_case(data.sort)
|
||||
sort = helper.key_to_snake_case(data.sort)
|
||||
|
||||
meta_keys = metadata.get(project_id=project_id)
|
||||
meta_keys = metadata.get(project_id=project.project_id)
|
||||
meta_map = ",'metadata',toString(map(%s))" \
|
||||
% ','.join([f"'{m['key']}',coalesce(metadata_{m['index']},'None')" for m in meta_keys])
|
||||
main_query = cur.format(query=f"""SELECT any(total) AS count,
|
||||
|
|
|
|||
|
|
@ -112,3 +112,4 @@ rm -rf ./chalicelib/core/errors/modules
|
|||
rm -rf ./chalicelib/core/errors/errors.py
|
||||
rm -rf ./chalicelib/core/errors/errors_ch.py
|
||||
rm -rf ./chalicelib/core/errors/errors_details.py
|
||||
rm -rf ./chalicelib/utils/contextual_validators.py
|
||||
|
|
|
|||
|
|
@ -7,17 +7,18 @@ from fastapi import HTTPException, status
|
|||
from starlette.responses import RedirectResponse, FileResponse, JSONResponse, Response
|
||||
|
||||
import schemas
|
||||
from chalicelib.core import scope
|
||||
from chalicelib.core import assist, signup, feature_flags
|
||||
from chalicelib.core import scope
|
||||
from chalicelib.core import tenants, users, projects, license
|
||||
from chalicelib.core import webhook
|
||||
from chalicelib.core.collaborations.collaboration_slack import Slack
|
||||
from chalicelib.core.errors import errors
|
||||
from chalicelib.core.metrics import heatmaps
|
||||
from chalicelib.core.sessions import sessions, sessions_notes, sessions_replay, sessions_favorite, sessions_assignments, \
|
||||
sessions_viewed, unprocessed_sessions, sessions_search
|
||||
from chalicelib.core import tenants, users, projects, license
|
||||
from chalicelib.core import webhook
|
||||
from chalicelib.core.collaborations.collaboration_slack import Slack
|
||||
from chalicelib.utils import SAML2_helper, smtp
|
||||
from chalicelib.utils import captcha
|
||||
from chalicelib.utils import contextual_validators
|
||||
from chalicelib.utils import helper
|
||||
from chalicelib.utils.TimeUTC import TimeUTC
|
||||
from or_dependencies import OR_context, OR_scope, OR_role
|
||||
|
|
@ -266,18 +267,20 @@ def get_projects(context: schemas.CurrentContext = Depends(OR_context)):
|
|||
|
||||
@app.post('/{projectId}/sessions/search', tags=["sessions"],
|
||||
dependencies=[OR_scope(Permissions.SESSION_REPLAY)])
|
||||
def search_sessions(projectId: int, data: schemas.SessionsSearchPayloadSchema = Body(...),
|
||||
def search_sessions(projectId: int, data: schemas.SessionsSearchPayloadSchema = \
|
||||
Depends(contextual_validators.validate_contextual_payload),
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = sessions_search.search_sessions(data=data, project_id=projectId, user_id=context.user_id,
|
||||
data = sessions_search.search_sessions(data=data, project=context.project, user_id=context.user_id,
|
||||
platform=context.project.platform)
|
||||
return {'data': data}
|
||||
|
||||
|
||||
@app.post('/{projectId}/sessions/search/ids', tags=["sessions"],
|
||||
dependencies=[OR_scope(Permissions.SESSION_REPLAY)])
|
||||
def session_ids_search(projectId: int, data: schemas.SessionsSearchPayloadSchema = Body(...),
|
||||
def session_ids_search(projectId: int, data: schemas.SessionsSearchPayloadSchema = \
|
||||
Depends(contextual_validators.validate_contextual_payload),
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = sessions_search.search_sessions(data=data, project_id=projectId, user_id=context.user_id, ids_only=True,
|
||||
data = sessions_search.search_sessions(data=data, project=context.project, user_id=context.user_id, ids_only=True,
|
||||
platform=context.project.platform)
|
||||
return {'data': data}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue