commit
8d885c7041
30 changed files with 2719 additions and 323 deletions
|
|
@ -6,14 +6,14 @@ from starlette import status
|
|||
from starlette.exceptions import HTTPException
|
||||
|
||||
from chalicelib.core import authorizers, users
|
||||
from schemas import CurrentContext
|
||||
import schemas
|
||||
|
||||
|
||||
class JWTAuth(HTTPBearer):
|
||||
def __init__(self, auto_error: bool = True):
|
||||
super(JWTAuth, self).__init__(auto_error=auto_error)
|
||||
|
||||
async def __call__(self, request: Request) -> Optional[CurrentContext]:
|
||||
async def __call__(self, request: Request) -> Optional[schemas.CurrentContext]:
|
||||
credentials: HTTPAuthorizationCredentials = await super(JWTAuth, self).__call__(request)
|
||||
if credentials:
|
||||
if not credentials.scheme == "Bearer":
|
||||
|
|
@ -49,9 +49,9 @@ class JWTAuth(HTTPBearer):
|
|||
jwt_payload["authorizer_identity"] = "jwt"
|
||||
print(jwt_payload)
|
||||
request.state.authorizer_identity = "jwt"
|
||||
request.state.currentContext = CurrentContext(tenant_id=jwt_payload.get("tenantId", -1),
|
||||
user_id=jwt_payload.get("userId", -1),
|
||||
email=user["email"])
|
||||
request.state.currentContext = schemas.CurrentContext(tenant_id=jwt_payload.get("tenantId", -1),
|
||||
user_id=jwt_payload.get("userId", -1),
|
||||
email=user["email"])
|
||||
return request.state.currentContext
|
||||
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
from os import access, R_OK
|
||||
from os.path import exists
|
||||
|
||||
import requests
|
||||
from decouple import config
|
||||
from os.path import exists
|
||||
from starlette.exceptions import HTTPException
|
||||
|
||||
import schemas
|
||||
from chalicelib.core import projects
|
||||
from starlette.exceptions import HTTPException
|
||||
from os import access, R_OK
|
||||
|
||||
ASSIST_KEY = config("ASSIST_KEY")
|
||||
ASSIST_URL = config("ASSIST_URL") % ASSIST_KEY
|
||||
|
|
@ -165,16 +167,24 @@ def get_ice_servers():
|
|||
and len(config("iceServers")) > 0 else None
|
||||
|
||||
|
||||
def get_raw_mob_by_id(project_id, session_id):
|
||||
def __get_efs_path():
|
||||
efs_path = config("FS_DIR")
|
||||
if not exists(efs_path):
|
||||
raise HTTPException(400, f"EFS not found in path: {efs_path}")
|
||||
|
||||
if not access(efs_path, R_OK):
|
||||
raise HTTPException(400, f"EFS found under: {efs_path}; but it is not readable, please check permissions")
|
||||
return efs_path
|
||||
|
||||
path_to_file = efs_path + "/" + str(session_id)
|
||||
|
||||
def __get_mob_path(project_id, session_id):
|
||||
params = {"projectId": project_id, "sessionId": session_id}
|
||||
return config("EFS_SESSION_MOB_PATTERN", default="%(sessionId)s") % params
|
||||
|
||||
|
||||
def get_raw_mob_by_id(project_id, session_id):
|
||||
efs_path = __get_efs_path()
|
||||
path_to_file = efs_path + "/" + __get_mob_path(project_id=project_id, session_id=session_id)
|
||||
if exists(path_to_file):
|
||||
if not access(path_to_file, R_OK):
|
||||
raise HTTPException(400, f"Replay file found under: {efs_path};"
|
||||
|
|
@ -183,3 +193,21 @@ def get_raw_mob_by_id(project_id, session_id):
|
|||
return path_to_file
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def __get_devtools_path(project_id, session_id):
|
||||
params = {"projectId": project_id, "sessionId": session_id}
|
||||
return config("EFS_DEVTOOLS_MOB_PATTERN", default="%(sessionId)s") % params
|
||||
|
||||
|
||||
def get_raw_devtools_by_id(project_id, session_id):
|
||||
efs_path = __get_efs_path()
|
||||
path_to_file = efs_path + "/" + __get_devtools_path(project_id=project_id, session_id=session_id)
|
||||
if exists(path_to_file):
|
||||
if not access(path_to_file, R_OK):
|
||||
raise HTTPException(400, f"Devtools file found under: {efs_path};"
|
||||
f" but it is not readable, please check permissions")
|
||||
|
||||
return path_to_file
|
||||
|
||||
return None
|
||||
|
|
|
|||
|
|
@ -709,36 +709,6 @@ def __status_rank(status):
|
|||
}.get(status)
|
||||
|
||||
|
||||
def merge(error_ids):
|
||||
error_ids = list(set(error_ids))
|
||||
errors = get_batch(error_ids)
|
||||
if len(error_ids) <= 1 or len(error_ids) > len(errors):
|
||||
return {"errors": ["invalid list of ids"]}
|
||||
error_ids = [e["errorId"] for e in errors]
|
||||
parent_error_id = error_ids[0]
|
||||
status = "unresolved"
|
||||
for e in errors:
|
||||
if __status_rank(status) < __status_rank(e["status"]):
|
||||
status = e["status"]
|
||||
if __status_rank(status) == MAX_RANK:
|
||||
break
|
||||
params = {
|
||||
"error_ids": tuple(error_ids),
|
||||
"parent_error_id": parent_error_id,
|
||||
"status": status
|
||||
}
|
||||
with pg_client.PostgresClient() as cur:
|
||||
query = cur.mogrify(
|
||||
"""UPDATE public.errors
|
||||
SET parent_error_id = %(parent_error_id)s, status = %(status)s
|
||||
WHERE error_id IN %(error_ids)s OR parent_error_id IN %(error_ids)s;""",
|
||||
params)
|
||||
cur.execute(query=query)
|
||||
# row = cur.fetchone()
|
||||
|
||||
return {"data": "success"}
|
||||
|
||||
|
||||
def format_first_stack_frame(error):
|
||||
error["stack"] = sourcemaps.format_payload(error.pop("payload"), truncate_to_first=True)
|
||||
for s in error["stack"]:
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ def execute_jobs():
|
|||
)
|
||||
|
||||
sessions.delete_sessions_by_session_ids(session_ids)
|
||||
sessions_mobs.delete_mobs(session_ids)
|
||||
sessions_mobs.delete_mobs(session_ids=session_ids, project_id=job["projectId"])
|
||||
else:
|
||||
raise Exception(f"The action {job['action']} not supported.")
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@ from typing import List
|
|||
|
||||
import schemas
|
||||
from chalicelib.core import events, metadata, events_ios, \
|
||||
sessions_mobs, issues, projects, errors, resources, assist, performance_event, sessions_viewed, sessions_favorite
|
||||
sessions_mobs, issues, projects, errors, resources, assist, performance_event, sessions_viewed, sessions_favorite, \
|
||||
sessions_devtool
|
||||
from chalicelib.utils import pg_client, helper, metrics_helper
|
||||
|
||||
SESSION_PROJECTION_COLS = """s.project_id,
|
||||
|
|
@ -81,7 +82,7 @@ def get_by_id2_pg(project_id, session_id, user_id, full_data=False, include_fav_
|
|||
data['crashes'] = events_ios.get_crashes_by_session_id(session_id=session_id)
|
||||
data['userEvents'] = events_ios.get_customs_by_sessionId(project_id=project_id,
|
||||
session_id=session_id)
|
||||
data['mobsUrl'] = sessions_mobs.get_ios(sessionId=session_id)
|
||||
data['mobsUrl'] = sessions_mobs.get_ios(session_id=session_id)
|
||||
else:
|
||||
data['events'] = events.get_by_sessionId2_pg(project_id=project_id, session_id=session_id,
|
||||
group_clickrage=True)
|
||||
|
|
@ -89,14 +90,14 @@ def get_by_id2_pg(project_id, session_id, user_id, full_data=False, include_fav_
|
|||
data['stackEvents'] = [e for e in all_errors if e['source'] != "js_exception"]
|
||||
# to keep only the first stack
|
||||
data['errors'] = [errors.format_first_stack_frame(e) for e in all_errors if
|
||||
e['source'] == "js_exception"][
|
||||
:500] # limit the number of errors to reduce the response-body size
|
||||
# limit the number of errors to reduce the response-body size
|
||||
e['source'] == "js_exception"][:500]
|
||||
data['userEvents'] = events.get_customs_by_sessionId2_pg(project_id=project_id,
|
||||
session_id=session_id)
|
||||
data['mobsUrl'] = sessions_mobs.get_web(sessionId=session_id)
|
||||
data['domURL'] = sessions_mobs.get_urls(session_id=session_id, project_id=project_id)
|
||||
data['devtoolsURL'] = sessions_devtool.get_urls(session_id=session_id, project_id=project_id)
|
||||
data['resources'] = resources.get_by_session_id(session_id=session_id, project_id=project_id,
|
||||
start_ts=data["startTs"],
|
||||
duration=data["duration"])
|
||||
start_ts=data["startTs"], duration=data["duration"])
|
||||
|
||||
data['metadata'] = __group_metadata(project_metadata=data.pop("projectMetadata"), session=data)
|
||||
data['issues'] = issues.get_by_session_id(session_id=session_id, project_id=project_id)
|
||||
|
|
@ -1237,3 +1238,15 @@ def count_all():
|
|||
with pg_client.PostgresClient(unlimited_query=True) as cur:
|
||||
row = cur.execute(query="SELECT COUNT(session_id) AS count FROM public.sessions")
|
||||
return row.get("count", 0)
|
||||
|
||||
|
||||
def session_exists(project_id, session_id):
|
||||
with pg_client.PostgresClient() as cur:
|
||||
query = cur.mogrify("""SELECT 1
|
||||
FROM public.sessions
|
||||
WHERE session_id=%(session_id)s
|
||||
AND project_id=%(project_id)s""",
|
||||
{"project_id": project_id, "session_id": session_id})
|
||||
cur.execute(query)
|
||||
row = cur.fetchone()
|
||||
return row is not None
|
||||
|
|
|
|||
24
api/chalicelib/core/sessions_devtool.py
Normal file
24
api/chalicelib/core/sessions_devtool.py
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
from decouple import config
|
||||
|
||||
from chalicelib.utils.s3 import client
|
||||
|
||||
|
||||
def __get_devtools_keys(project_id, session_id):
|
||||
params = {
|
||||
"sessionId": session_id,
|
||||
"projectId": project_id
|
||||
}
|
||||
return [
|
||||
config("DEVTOOLS_MOB_PATTERN", default="%(sessionId)sdevtools") % params
|
||||
]
|
||||
|
||||
|
||||
def get_urls(session_id, project_id):
|
||||
results = []
|
||||
for k in __get_devtools_keys(project_id=project_id, session_id=session_id):
|
||||
results.append(client.generate_presigned_url(
|
||||
'get_object',
|
||||
Params={'Bucket': config("sessions_bucket"), 'Key': k},
|
||||
ExpiresIn=config("PRESIGNED_URL_EXPIRATION", cast=int, default=900)
|
||||
))
|
||||
return results
|
||||
|
|
@ -7,8 +7,8 @@ def add_favorite_session(project_id, user_id, session_id):
|
|||
cur.execute(
|
||||
cur.mogrify(f"""\
|
||||
INSERT INTO public.user_favorite_sessions(user_id, session_id)
|
||||
VALUES (%(userId)s,%(sessionId)s);""",
|
||||
{"userId": user_id, "sessionId": session_id})
|
||||
VALUES (%(userId)s,%(session_id)s);""",
|
||||
{"userId": user_id, "session_id": session_id})
|
||||
)
|
||||
return sessions.get_by_id2_pg(project_id=project_id, session_id=session_id, user_id=user_id, full_data=False,
|
||||
include_fav_viewed=True)
|
||||
|
|
@ -20,8 +20,8 @@ def remove_favorite_session(project_id, user_id, session_id):
|
|||
cur.mogrify(f"""\
|
||||
DELETE FROM public.user_favorite_sessions
|
||||
WHERE user_id = %(userId)s
|
||||
AND session_id = %(sessionId)s;""",
|
||||
{"userId": user_id, "sessionId": session_id})
|
||||
AND session_id = %(session_id)s;""",
|
||||
{"userId": user_id, "session_id": session_id})
|
||||
)
|
||||
return sessions.get_by_id2_pg(project_id=project_id, session_id=session_id, user_id=user_id, full_data=False,
|
||||
include_fav_viewed=True)
|
||||
|
|
@ -42,8 +42,8 @@ def favorite_session_exists(user_id, session_id):
|
|||
FROM public.user_favorite_sessions
|
||||
WHERE
|
||||
user_id = %(userId)s
|
||||
AND session_id = %(sessionId)s""",
|
||||
{"userId": user_id, "sessionId": session_id})
|
||||
AND session_id = %(session_id)s""",
|
||||
{"userId": user_id, "session_id": session_id})
|
||||
)
|
||||
r = cur.fetchone()
|
||||
return r is not None
|
||||
|
|
|
|||
|
|
@ -4,37 +4,40 @@ from chalicelib.utils import s3
|
|||
from chalicelib.utils.s3 import client
|
||||
|
||||
|
||||
def get_web(sessionId):
|
||||
def __get_mob_keys(project_id, session_id):
|
||||
params = {
|
||||
"sessionId": session_id,
|
||||
"projectId": project_id
|
||||
}
|
||||
return [
|
||||
client.generate_presigned_url(
|
||||
'get_object',
|
||||
Params={
|
||||
'Bucket': config("sessions_bucket"),
|
||||
'Key': str(sessionId)
|
||||
},
|
||||
ExpiresIn=100000
|
||||
),
|
||||
client.generate_presigned_url(
|
||||
'get_object',
|
||||
Params={
|
||||
'Bucket': config("sessions_bucket"),
|
||||
'Key': str(sessionId) + "e"
|
||||
},
|
||||
ExpiresIn=100000
|
||||
)]
|
||||
config("SESSION_MOB_PATTERN_S", default="%(sessionId)s") % params,
|
||||
config("SESSION_MOB_PATTERN_E", default="%(sessionId)se") % params
|
||||
]
|
||||
|
||||
|
||||
def get_ios(sessionId):
|
||||
def get_urls(project_id, session_id):
|
||||
results = []
|
||||
for k in __get_mob_keys(project_id=project_id, session_id=session_id):
|
||||
results.append(client.generate_presigned_url(
|
||||
'get_object',
|
||||
Params={'Bucket': config("sessions_bucket"), 'Key': k},
|
||||
ExpiresIn=config("PRESIGNED_URL_EXPIRATION", cast=int, default=900)
|
||||
))
|
||||
return results
|
||||
|
||||
|
||||
def get_ios(session_id):
|
||||
return client.generate_presigned_url(
|
||||
'get_object',
|
||||
Params={
|
||||
'Bucket': config("ios_bucket"),
|
||||
'Key': str(sessionId)
|
||||
'Key': str(session_id)
|
||||
},
|
||||
ExpiresIn=100000
|
||||
ExpiresIn=config("PRESIGNED_URL_EXPIRATION", cast=int, default=900)
|
||||
)
|
||||
|
||||
|
||||
def delete_mobs(session_ids):
|
||||
def delete_mobs(project_id, session_ids):
|
||||
for session_id in session_ids:
|
||||
s3.schedule_for_deletion(config("sessions_bucket"), session_id)
|
||||
for k in __get_mob_keys(project_id=project_id, session_id=session_id):
|
||||
s3.schedule_for_deletion(config("sessions_bucket"), k)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ def view_session(project_id, user_id, session_id):
|
|||
with pg_client.PostgresClient() as cur:
|
||||
cur.execute(
|
||||
cur.mogrify("""INSERT INTO public.user_viewed_sessions(user_id, session_id)
|
||||
VALUES (%(userId)s,%(sessionId)s)
|
||||
VALUES (%(userId)s,%(session_id)s)
|
||||
ON CONFLICT DO NOTHING;""",
|
||||
{"userId": user_id, "sessionId": session_id})
|
||||
{"userId": user_id, "session_id": session_id})
|
||||
)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ def start_replay(project_id, session_id, device, os_version, mob_url):
|
|||
r = requests.post(config("IOS_MIDDLEWARE") + "/replay", json={
|
||||
"projectId": project_id,
|
||||
"projectKey": projects.get_project_key(project_id),
|
||||
"sessionId": session_id,
|
||||
"session_id": session_id,
|
||||
"device": device,
|
||||
"osVersion": os_version,
|
||||
"mobUrl": mob_url
|
||||
|
|
|
|||
|
|
@ -48,4 +48,10 @@ sourcemaps_bucket=sourcemaps
|
|||
sourcemaps_reader=http://127.0.0.1:9000/sourcemaps/%s/sourcemaps
|
||||
stage=default-foss
|
||||
version_number=1.4.0
|
||||
FS_DIR=/mnt/efs
|
||||
FS_DIR=/mnt/efs
|
||||
EFS_SESSION_MOB_PATTERN=%(sessionId)s/dom.mob
|
||||
EFS_DEVTOOLS_MOB_PATTERN=%(sessionId)s/devtools.mob
|
||||
SESSION_MOB_PATTERN_S=%(sessionId)s/dom.mobs
|
||||
SESSION_MOB_PATTERN_E=%(sessionId)s/dom.mobe
|
||||
DEVTOOLS_MOB_PATTERN=%(sessionId)s/devtools.mob
|
||||
PRESIGNED_URL_EXPIRATION=3600
|
||||
|
|
@ -6,9 +6,11 @@ from auth.auth_project import ProjectAuthorizer
|
|||
from or_dependencies import ORRoute
|
||||
|
||||
|
||||
def get_routers() -> (APIRouter, APIRouter, APIRouter):
|
||||
def get_routers(extra_dependencies=[]) -> (APIRouter, APIRouter, APIRouter):
|
||||
public_app = APIRouter(route_class=ORRoute)
|
||||
app = APIRouter(dependencies=[Depends(JWTAuth()), Depends(ProjectAuthorizer("projectId"))], route_class=ORRoute)
|
||||
app_apikey = APIRouter(dependencies=[Depends(APIKeyAuth()), Depends(ProjectAuthorizer("projectKey"))],
|
||||
route_class=ORRoute)
|
||||
app = APIRouter(dependencies=[Depends(JWTAuth()), Depends(ProjectAuthorizer("projectId"))] + extra_dependencies,
|
||||
route_class=ORRoute)
|
||||
app_apikey = APIRouter(
|
||||
dependencies=[Depends(APIKeyAuth()), Depends(ProjectAuthorizer("projectKey"))] + extra_dependencies,
|
||||
route_class=ORRoute)
|
||||
return public_app, app, app_apikey
|
||||
|
|
|
|||
|
|
@ -59,87 +59,6 @@ def sessions_search(projectId: int, data: schemas.FlatSessionsSearchPayloadSchem
|
|||
return {'data': data}
|
||||
|
||||
|
||||
@app.get('/{projectId}/sessions/{sessionId}', tags=["sessions"])
|
||||
@app.get('/{projectId}/sessions2/{sessionId}', tags=["sessions"])
|
||||
def get_session2(projectId: int, sessionId: Union[int, str], background_tasks: BackgroundTasks,
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
if isinstance(sessionId, str):
|
||||
return {"errors": ["session not found"]}
|
||||
data = sessions.get_by_id2_pg(project_id=projectId, session_id=sessionId, full_data=True, user_id=context.user_id,
|
||||
include_fav_viewed=True, group_metadata=True)
|
||||
if data is None:
|
||||
return {"errors": ["session not found"]}
|
||||
if data.get("inDB"):
|
||||
background_tasks.add_task(sessions_viewed.view_session, project_id=projectId, user_id=context.user_id,
|
||||
session_id=sessionId)
|
||||
return {
|
||||
'data': data
|
||||
}
|
||||
|
||||
|
||||
@app.get('/{projectId}/sessions/{sessionId}/favorite', tags=["sessions"])
|
||||
@app.get('/{projectId}/sessions2/{sessionId}/favorite', tags=["sessions"])
|
||||
def add_remove_favorite_session2(projectId: int, sessionId: int,
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
return {
|
||||
"data": sessions_favorite.favorite_session(project_id=projectId, user_id=context.user_id,
|
||||
session_id=sessionId)}
|
||||
|
||||
|
||||
@app.get('/{projectId}/sessions/{sessionId}/assign', tags=["sessions"])
|
||||
@app.get('/{projectId}/sessions2/{sessionId}/assign', tags=["sessions"])
|
||||
def assign_session(projectId: int, sessionId, context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = sessions_assignments.get_by_session(project_id=projectId, session_id=sessionId,
|
||||
tenant_id=context.tenant_id,
|
||||
user_id=context.user_id)
|
||||
if "errors" in data:
|
||||
return data
|
||||
return {
|
||||
'data': data
|
||||
}
|
||||
|
||||
|
||||
@app.get('/{projectId}/sessions/{sessionId}/errors/{errorId}/sourcemaps', tags=["sessions", "sourcemaps"])
|
||||
@app.get('/{projectId}/sessions2/{sessionId}/errors/{errorId}/sourcemaps', tags=["sessions", "sourcemaps"])
|
||||
def get_error_trace(projectId: int, sessionId: int, errorId: str,
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = errors.get_trace(project_id=projectId, error_id=errorId)
|
||||
if "errors" in data:
|
||||
return data
|
||||
return {
|
||||
'data': data
|
||||
}
|
||||
|
||||
|
||||
@app.get('/{projectId}/sessions/{sessionId}/assign/{issueId}', tags=["sessions", "issueTracking"])
|
||||
@app.get('/{projectId}/sessions2/{sessionId}/assign/{issueId}', tags=["sessions", "issueTracking"])
|
||||
def assign_session(projectId: int, sessionId: int, issueId: str,
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = sessions_assignments.get(project_id=projectId, session_id=sessionId, assignment_id=issueId,
|
||||
tenant_id=context.tenant_id, user_id=context.user_id)
|
||||
if "errors" in data:
|
||||
return data
|
||||
return {
|
||||
'data': data
|
||||
}
|
||||
|
||||
|
||||
@app.post('/{projectId}/sessions/{sessionId}/assign/{issueId}/comment', tags=["sessions", "issueTracking"])
|
||||
@app.put('/{projectId}/sessions/{sessionId}/assign/{issueId}/comment', tags=["sessions", "issueTracking"])
|
||||
@app.post('/{projectId}/sessions2/{sessionId}/assign/{issueId}/comment', tags=["sessions", "issueTracking"])
|
||||
@app.put('/{projectId}/sessions2/{sessionId}/assign/{issueId}/comment', tags=["sessions", "issueTracking"])
|
||||
def comment_assignment(projectId: int, sessionId: int, issueId: str, data: schemas.CommentAssignmentSchema = Body(...),
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = sessions_assignments.comment(tenant_id=context.tenant_id, project_id=projectId,
|
||||
session_id=sessionId, assignment_id=issueId,
|
||||
user_id=context.user_id, message=data.message)
|
||||
if "errors" in data.keys():
|
||||
return data
|
||||
return {
|
||||
'data': data
|
||||
}
|
||||
|
||||
|
||||
@app.get('/{projectId}/events/search', tags=["events"])
|
||||
def events_search(projectId: int, q: str,
|
||||
type: Union[schemas.FilterType, schemas.EventType,
|
||||
|
|
@ -664,13 +583,6 @@ def get_all_announcements(context: schemas.CurrentContext = Depends(OR_context))
|
|||
return {"data": announcements.view(user_id=context.user_id)}
|
||||
|
||||
|
||||
@app.post('/{projectId}/errors/merge', tags=["errors"])
|
||||
def errors_merge(projectId: int, data: schemas.ErrorIdsPayloadSchema = Body(...),
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = errors.merge(error_ids=data.errors)
|
||||
return data
|
||||
|
||||
|
||||
@app.get('/show_banner', tags=["banner"])
|
||||
def errors_merge(context: schemas.CurrentContext = Depends(OR_context)):
|
||||
return {"data": False}
|
||||
|
|
@ -894,38 +806,6 @@ def sessions_live(projectId: int, data: schemas.LiveSessionsSearchPayloadSchema
|
|||
return {'data': data}
|
||||
|
||||
|
||||
@app.get('/{projectId}/assist/sessions/{sessionId}', tags=["assist"])
|
||||
def get_live_session(projectId: int, sessionId: str, background_tasks: BackgroundTasks,
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = assist.get_live_session_by_id(project_id=projectId, session_id=sessionId)
|
||||
if data is None:
|
||||
data = sessions.get_by_id2_pg(project_id=projectId, session_id=sessionId, full_data=True,
|
||||
user_id=context.user_id, include_fav_viewed=True, group_metadata=True, live=False)
|
||||
if data is None:
|
||||
return {"errors": ["session not found"]}
|
||||
if data.get("inDB"):
|
||||
background_tasks.add_task(sessions_viewed.view_session, project_id=projectId,
|
||||
user_id=context.user_id, session_id=sessionId)
|
||||
return {'data': data}
|
||||
|
||||
|
||||
@app.get('/{projectId}/unprocessed/{sessionId}', tags=["assist"])
|
||||
@app.get('/{projectId}/assist/sessions/{sessionId}/replay', tags=["assist"])
|
||||
def get_live_session_replay_file(projectId: int, sessionId: str,
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
path = assist.get_raw_mob_by_id(project_id=projectId, session_id=sessionId)
|
||||
if path is None:
|
||||
return {"errors": ["Replay file not found"]}
|
||||
|
||||
return FileResponse(path=path, media_type="application/octet-stream")
|
||||
|
||||
|
||||
@app.post('/{projectId}/heatmaps/url', tags=["heatmaps"])
|
||||
def get_heatmaps_by_url(projectId: int, data: schemas.GetHeatmapPayloadSchema = Body(...),
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
return {"data": heatmaps.get_by_url(project_id=projectId, data=data.dict())}
|
||||
|
||||
|
||||
@app.post('/{projectId}/mobile/{sessionId}/urls', tags=['mobile'])
|
||||
def mobile_signe(projectId: int, sessionId: int, data: schemas.MobileSignPayloadSchema = Body(...),
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
|
|
@ -971,67 +851,6 @@ def edit_client(data: schemas.UpdateTenantSchema = Body(...),
|
|||
return tenants.update(tenant_id=context.tenant_id, user_id=context.user_id, data=data)
|
||||
|
||||
|
||||
@app.post('/{projectId}/errors/search', tags=['errors'])
|
||||
def errors_search(projectId: int, data: schemas.SearchErrorsSchema = Body(...),
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
return {"data": errors.search(data, projectId, user_id=context.user_id)}
|
||||
|
||||
|
||||
@app.get('/{projectId}/errors/stats', tags=['errors'])
|
||||
def errors_stats(projectId: int, startTimestamp: int, endTimestamp: int,
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
return errors.stats(projectId, user_id=context.user_id, startTimestamp=startTimestamp, endTimestamp=endTimestamp)
|
||||
|
||||
|
||||
@app.get('/{projectId}/errors/{errorId}', tags=['errors'])
|
||||
def errors_get_details(projectId: int, errorId: str, background_tasks: BackgroundTasks, density24: int = 24,
|
||||
density30: int = 30,
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = errors.get_details(project_id=projectId, user_id=context.user_id, error_id=errorId,
|
||||
**{"density24": density24, "density30": density30})
|
||||
if data.get("data") is not None:
|
||||
background_tasks.add_task(errors_viewed.viewed_error, project_id=projectId, user_id=context.user_id,
|
||||
error_id=errorId)
|
||||
return data
|
||||
|
||||
|
||||
@app.get('/{projectId}/errors/{errorId}/stats', tags=['errors'])
|
||||
def errors_get_details_right_column(projectId: int, errorId: str, startDate: int = TimeUTC.now(-7),
|
||||
endDate: int = TimeUTC.now(), density: int = 7,
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = errors.get_details_chart(project_id=projectId, user_id=context.user_id, error_id=errorId,
|
||||
**{"startDate": startDate, "endDate": endDate, "density": density})
|
||||
return data
|
||||
|
||||
|
||||
@app.get('/{projectId}/errors/{errorId}/sourcemaps', tags=['errors'])
|
||||
def errors_get_details_sourcemaps(projectId: int, errorId: str,
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = errors.get_trace(project_id=projectId, error_id=errorId)
|
||||
if "errors" in data:
|
||||
return data
|
||||
return {
|
||||
'data': data
|
||||
}
|
||||
|
||||
|
||||
@app.get('/{projectId}/errors/{errorId}/{action}', tags=["errors"])
|
||||
def add_remove_favorite_error(projectId: int, errorId: str, action: str, startDate: int = TimeUTC.now(-7),
|
||||
endDate: int = TimeUTC.now(), context: schemas.CurrentContext = Depends(OR_context)):
|
||||
if action == "favorite":
|
||||
return errors_favorite.favorite_error(project_id=projectId, user_id=context.user_id, error_id=errorId)
|
||||
elif action == "sessions":
|
||||
start_date = startDate
|
||||
end_date = endDate
|
||||
return {
|
||||
"data": errors.get_sessions(project_id=projectId, user_id=context.user_id, error_id=errorId,
|
||||
start_date=start_date, end_date=end_date)}
|
||||
elif action in list(errors.ACTION_STATE.keys()):
|
||||
return errors.change_state(project_id=projectId, user_id=context.user_id, error_id=errorId, action=action)
|
||||
else:
|
||||
return {"errors": ["undefined action"]}
|
||||
|
||||
|
||||
@app.get('/notifications', tags=['notifications'])
|
||||
def get_notifications(context: schemas.CurrentContext = Depends(OR_context)):
|
||||
return {"data": notifications.get_all(tenant_id=context.tenant_id, user_id=context.user_id)}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,18 @@
|
|||
from typing import Optional
|
||||
from typing import Optional, Union
|
||||
|
||||
from decouple import config
|
||||
from fastapi import Body, Depends, BackgroundTasks
|
||||
from starlette.responses import RedirectResponse
|
||||
from starlette.responses import RedirectResponse, FileResponse
|
||||
|
||||
import schemas
|
||||
from chalicelib.core import integrations_manager
|
||||
from chalicelib.core import sessions
|
||||
from chalicelib.core import sessions, errors, errors_viewed, errors_favorite, sessions_assignments, heatmaps, \
|
||||
sessions_favorite, assist
|
||||
from chalicelib.core import sessions_viewed
|
||||
from chalicelib.core import tenants, users, projects, license
|
||||
from chalicelib.core import webhook
|
||||
from chalicelib.core.collaboration_slack import Slack
|
||||
from chalicelib.utils import helper
|
||||
from chalicelib.utils.TimeUTC import TimeUTC
|
||||
from or_dependencies import OR_context
|
||||
from routers.base import get_routers
|
||||
|
||||
|
|
@ -165,3 +167,202 @@ def get_general_stats():
|
|||
def get_projects(context: schemas.CurrentContext = Depends(OR_context)):
|
||||
return {"data": projects.get_projects(tenant_id=context.tenant_id, recording_state=True, gdpr=True, recorded=True,
|
||||
stack_integrations=True)}
|
||||
|
||||
|
||||
@app.get('/{projectId}/sessions/{sessionId}', tags=["sessions"])
|
||||
@app.get('/{projectId}/sessions2/{sessionId}', tags=["sessions"])
|
||||
def get_session(projectId: int, sessionId: Union[int, str], background_tasks: BackgroundTasks,
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
if isinstance(sessionId, str):
|
||||
return {"errors": ["session not found"]}
|
||||
data = sessions.get_by_id2_pg(project_id=projectId, session_id=sessionId, full_data=True, user_id=context.user_id,
|
||||
include_fav_viewed=True, group_metadata=True)
|
||||
if data is None:
|
||||
return {"errors": ["session not found"]}
|
||||
if data.get("inDB"):
|
||||
background_tasks.add_task(sessions_viewed.view_session, project_id=projectId, user_id=context.user_id,
|
||||
session_id=sessionId)
|
||||
return {
|
||||
'data': data
|
||||
}
|
||||
|
||||
|
||||
@app.get('/{projectId}/sessions/{sessionId}/errors/{errorId}/sourcemaps', tags=["sessions", "sourcemaps"])
|
||||
@app.get('/{projectId}/sessions2/{sessionId}/errors/{errorId}/sourcemaps', tags=["sessions", "sourcemaps"])
|
||||
def get_error_trace(projectId: int, sessionId: int, errorId: str,
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = errors.get_trace(project_id=projectId, error_id=errorId)
|
||||
if "errors" in data:
|
||||
return data
|
||||
return {
|
||||
'data': data
|
||||
}
|
||||
|
||||
|
||||
@app.post('/{projectId}/errors/search', tags=['errors'])
|
||||
def errors_search(projectId: int, data: schemas.SearchErrorsSchema = Body(...),
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
return {"data": errors.search(data, projectId, user_id=context.user_id)}
|
||||
|
||||
|
||||
@app.get('/{projectId}/errors/stats', tags=['errors'])
|
||||
def errors_stats(projectId: int, startTimestamp: int, endTimestamp: int,
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
return errors.stats(projectId, user_id=context.user_id, startTimestamp=startTimestamp, endTimestamp=endTimestamp)
|
||||
|
||||
|
||||
@app.get('/{projectId}/errors/{errorId}', tags=['errors'])
|
||||
def errors_get_details(projectId: int, errorId: str, background_tasks: BackgroundTasks, density24: int = 24,
|
||||
density30: int = 30,
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = errors.get_details(project_id=projectId, user_id=context.user_id, error_id=errorId,
|
||||
**{"density24": density24, "density30": density30})
|
||||
if data.get("data") is not None:
|
||||
background_tasks.add_task(errors_viewed.viewed_error, project_id=projectId, user_id=context.user_id,
|
||||
error_id=errorId)
|
||||
return data
|
||||
|
||||
|
||||
@app.get('/{projectId}/errors/{errorId}/stats', tags=['errors'])
|
||||
def errors_get_details_right_column(projectId: int, errorId: str, startDate: int = TimeUTC.now(-7),
|
||||
endDate: int = TimeUTC.now(), density: int = 7,
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = errors.get_details_chart(project_id=projectId, user_id=context.user_id, error_id=errorId,
|
||||
**{"startDate": startDate, "endDate": endDate, "density": density})
|
||||
return data
|
||||
|
||||
|
||||
@app.get('/{projectId}/errors/{errorId}/sourcemaps', tags=['errors'])
|
||||
def errors_get_details_sourcemaps(projectId: int, errorId: str,
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = errors.get_trace(project_id=projectId, error_id=errorId)
|
||||
if "errors" in data:
|
||||
return data
|
||||
return {
|
||||
'data': data
|
||||
}
|
||||
|
||||
|
||||
@app.get('/{projectId}/errors/{errorId}/{action}', tags=["errors"])
|
||||
def add_remove_favorite_error(projectId: int, errorId: str, action: str, startDate: int = TimeUTC.now(-7),
|
||||
endDate: int = TimeUTC.now(), context: schemas.CurrentContext = Depends(OR_context)):
|
||||
if action == "favorite":
|
||||
return errors_favorite.favorite_error(project_id=projectId, user_id=context.user_id, error_id=errorId)
|
||||
elif action == "sessions":
|
||||
start_date = startDate
|
||||
end_date = endDate
|
||||
return {
|
||||
"data": errors.get_sessions(project_id=projectId, user_id=context.user_id, error_id=errorId,
|
||||
start_date=start_date, end_date=end_date)}
|
||||
elif action in list(errors.ACTION_STATE.keys()):
|
||||
return errors.change_state(project_id=projectId, user_id=context.user_id, error_id=errorId, action=action)
|
||||
else:
|
||||
return {"errors": ["undefined action"]}
|
||||
|
||||
|
||||
@app.get('/{projectId}/assist/sessions/{sessionId}', tags=["assist"])
|
||||
def get_live_session(projectId: int, sessionId: str, background_tasks: BackgroundTasks,
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = assist.get_live_session_by_id(project_id=projectId, session_id=sessionId)
|
||||
if data is None:
|
||||
data = sessions.get_by_id2_pg(project_id=projectId, session_id=sessionId, full_data=True,
|
||||
user_id=context.user_id, include_fav_viewed=True, group_metadata=True, live=False)
|
||||
if data is None:
|
||||
return {"errors": ["session not found"]}
|
||||
if data.get("inDB"):
|
||||
background_tasks.add_task(sessions_viewed.view_session, project_id=projectId,
|
||||
user_id=context.user_id, session_id=sessionId)
|
||||
return {'data': data}
|
||||
|
||||
|
||||
@app.get('/{projectId}/unprocessed/{sessionId}', tags=["assist"])
|
||||
@app.get('/{projectId}/assist/sessions/{sessionId}/replay', tags=["assist"])
|
||||
def get_live_session_replay_file(projectId: int, sessionId: Union[int, str],
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
if isinstance(sessionId, str) or not sessions.session_exists(project_id=projectId, session_id=sessionId):
|
||||
if isinstance(sessionId, str):
|
||||
print(f"{sessionId} not a valid number.")
|
||||
else:
|
||||
print(f"{projectId}/{sessionId} not found in DB.")
|
||||
|
||||
return {"errors": ["Replay file not found"]}
|
||||
path = assist.get_raw_mob_by_id(project_id=projectId, session_id=sessionId)
|
||||
if path is None:
|
||||
return {"errors": ["Replay file not found"]}
|
||||
|
||||
return FileResponse(path=path, media_type="application/octet-stream")
|
||||
|
||||
|
||||
@app.get('/{projectId}/unprocessed/{sessionId}/devtools', tags=["assist"])
|
||||
@app.get('/{projectId}/assist/sessions/{sessionId}/devtools', tags=["assist"])
|
||||
def get_live_session_devtools_file(projectId: int, sessionId: Union[int, str],
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
if isinstance(sessionId, str) or not sessions.session_exists(project_id=projectId, session_id=sessionId):
|
||||
if isinstance(sessionId, str):
|
||||
print(f"{sessionId} not a valid number.")
|
||||
else:
|
||||
print(f"{projectId}/{sessionId} not found in DB.")
|
||||
|
||||
return {"errors": ["Devtools file not found"]}
|
||||
path = assist.get_raw_devtools_by_id(project_id=projectId, session_id=sessionId)
|
||||
if path is None:
|
||||
return {"errors": ["Devtools file not found"]}
|
||||
|
||||
return FileResponse(path=path, media_type="application/octet-stream")
|
||||
|
||||
|
||||
@app.post('/{projectId}/heatmaps/url', tags=["heatmaps"])
|
||||
def get_heatmaps_by_url(projectId: int, data: schemas.GetHeatmapPayloadSchema = Body(...),
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
return {"data": heatmaps.get_by_url(project_id=projectId, data=data.dict())}
|
||||
|
||||
|
||||
@app.get('/{projectId}/sessions/{sessionId}/favorite', tags=["sessions"])
|
||||
@app.get('/{projectId}/sessions2/{sessionId}/favorite', tags=["sessions"])
|
||||
def add_remove_favorite_session2(projectId: int, sessionId: int,
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
return {
|
||||
"data": sessions_favorite.favorite_session(project_id=projectId, user_id=context.user_id,
|
||||
session_id=sessionId)}
|
||||
|
||||
|
||||
@app.get('/{projectId}/sessions/{sessionId}/assign', tags=["sessions"])
|
||||
@app.get('/{projectId}/sessions2/{sessionId}/assign', tags=["sessions"])
|
||||
def assign_session(projectId: int, sessionId, context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = sessions_assignments.get_by_session(project_id=projectId, session_id=sessionId,
|
||||
tenant_id=context.tenant_id,
|
||||
user_id=context.user_id)
|
||||
if "errors" in data:
|
||||
return data
|
||||
return {
|
||||
'data': data
|
||||
}
|
||||
|
||||
|
||||
@app.get('/{projectId}/sessions/{sessionId}/assign/{issueId}', tags=["sessions", "issueTracking"])
|
||||
@app.get('/{projectId}/sessions2/{sessionId}/assign/{issueId}', tags=["sessions", "issueTracking"])
|
||||
def assign_session(projectId: int, sessionId: int, issueId: str,
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = sessions_assignments.get(project_id=projectId, session_id=sessionId, assignment_id=issueId,
|
||||
tenant_id=context.tenant_id, user_id=context.user_id)
|
||||
if "errors" in data:
|
||||
return data
|
||||
return {
|
||||
'data': data
|
||||
}
|
||||
|
||||
|
||||
@app.post('/{projectId}/sessions/{sessionId}/assign/{issueId}/comment', tags=["sessions", "issueTracking"])
|
||||
@app.put('/{projectId}/sessions/{sessionId}/assign/{issueId}/comment', tags=["sessions", "issueTracking"])
|
||||
@app.post('/{projectId}/sessions2/{sessionId}/assign/{issueId}/comment', tags=["sessions", "issueTracking"])
|
||||
@app.put('/{projectId}/sessions2/{sessionId}/assign/{issueId}/comment', tags=["sessions", "issueTracking"])
|
||||
def comment_assignment(projectId: int, sessionId: int, issueId: str, data: schemas.CommentAssignmentSchema = Body(...),
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = sessions_assignments.comment(tenant_id=context.tenant_id, project_id=projectId,
|
||||
session_id=sessionId, assignment_id=issueId,
|
||||
user_id=context.user_id, message=data.message)
|
||||
if "errors" in data.keys():
|
||||
return data
|
||||
return {
|
||||
'data': data
|
||||
}
|
||||
|
|
|
|||
5
ee/api/.gitignore
vendored
5
ee/api/.gitignore
vendored
|
|
@ -210,7 +210,6 @@ Pipfile
|
|||
/chalicelib/core/log_tool_sumologic.py
|
||||
/chalicelib/core/metadata.py
|
||||
/chalicelib/core/mobile.py
|
||||
/chalicelib/core/sessions.py
|
||||
/chalicelib/core/sessions_assignments.py
|
||||
/chalicelib/core/sessions_metas.py
|
||||
/chalicelib/core/sessions_mobs.py
|
||||
|
|
@ -243,23 +242,19 @@ Pipfile
|
|||
/routers/__init__.py
|
||||
/chalicelib/core/assist.py
|
||||
/auth/auth_apikey.py
|
||||
/auth/auth_jwt.py
|
||||
/build.sh
|
||||
/routers/base.py
|
||||
/routers/core.py
|
||||
/routers/crons/core_crons.py
|
||||
/routers/subs/dashboard.py
|
||||
/db_changes.sql
|
||||
/Dockerfile.bundle
|
||||
/entrypoint.bundle.sh
|
||||
/chalicelib/core/heatmaps.py
|
||||
/routers/subs/insights.py
|
||||
/schemas.py
|
||||
#exp /chalicelib/core/custom_metrics.py
|
||||
/chalicelib/core/performance_event.py
|
||||
/chalicelib/core/saved_search.py
|
||||
/app_alerts.py
|
||||
/build_alerts.sh
|
||||
/routers/subs/metrics.py
|
||||
/routers/subs/v1_api.py
|
||||
#exp /chalicelib/core/dashboards.py
|
||||
|
|
|
|||
60
ee/api/auth/auth_jwt.py
Normal file
60
ee/api/auth/auth_jwt.py
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
from typing import Optional
|
||||
|
||||
from fastapi import Request
|
||||
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
||||
from starlette import status
|
||||
from starlette.exceptions import HTTPException
|
||||
|
||||
from chalicelib.core import authorizers, users
|
||||
import schemas_ee
|
||||
|
||||
|
||||
class JWTAuth(HTTPBearer):
|
||||
def __init__(self, auto_error: bool = True):
|
||||
super(JWTAuth, self).__init__(auto_error=auto_error)
|
||||
|
||||
async def __call__(self, request: Request) -> Optional[schemas_ee.CurrentContext]:
|
||||
credentials: HTTPAuthorizationCredentials = await super(JWTAuth, self).__call__(request)
|
||||
if credentials:
|
||||
if not credentials.scheme == "Bearer":
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid authentication scheme.")
|
||||
jwt_payload = authorizers.jwt_authorizer(credentials.scheme + " " + credentials.credentials)
|
||||
auth_exists = jwt_payload is not None \
|
||||
and users.auth_exists(user_id=jwt_payload.get("userId", -1),
|
||||
tenant_id=jwt_payload.get("tenantId", -1),
|
||||
jwt_iat=jwt_payload.get("iat", 100),
|
||||
jwt_aud=jwt_payload.get("aud", ""))
|
||||
if jwt_payload is None \
|
||||
or jwt_payload.get("iat") is None or jwt_payload.get("aud") is None \
|
||||
or not auth_exists:
|
||||
print("JWTAuth: Token issue")
|
||||
if jwt_payload is not None:
|
||||
print(jwt_payload)
|
||||
print(f"JWTAuth: user_id={jwt_payload.get('userId')} tenant_id={jwt_payload.get('tenantId')}")
|
||||
if jwt_payload is None:
|
||||
print("JWTAuth: jwt_payload is None")
|
||||
print(credentials.scheme + " " + credentials.credentials)
|
||||
if jwt_payload is not None and jwt_payload.get("iat") is None:
|
||||
print("JWTAuth: iat is None")
|
||||
if jwt_payload is not None and jwt_payload.get("aud") is None:
|
||||
print("JWTAuth: aud is None")
|
||||
if jwt_payload is not None and not auth_exists:
|
||||
print("JWTAuth: not users.auth_exists")
|
||||
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Invalid token or expired token.")
|
||||
user = users.get(user_id=jwt_payload.get("userId", -1), tenant_id=jwt_payload.get("tenantId", -1))
|
||||
if user is None:
|
||||
print("JWTAuth: User not found.")
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="User not found.")
|
||||
jwt_payload["authorizer_identity"] = "jwt"
|
||||
print(jwt_payload)
|
||||
request.state.authorizer_identity = "jwt"
|
||||
request.state.currentContext = schemas_ee.CurrentContext(tenant_id=jwt_payload.get("tenantId", -1),
|
||||
user_id=jwt_payload.get("userId", -1),
|
||||
email=user["email"],
|
||||
permissions=user["permissions"])
|
||||
return request.state.currentContext
|
||||
|
||||
else:
|
||||
print("JWTAuth: Invalid authorization code.")
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid authorization code.")
|
||||
|
|
@ -716,36 +716,6 @@ def __status_rank(status):
|
|||
}.get(status)
|
||||
|
||||
|
||||
def merge(error_ids):
|
||||
error_ids = list(set(error_ids))
|
||||
errors = get_batch(error_ids)
|
||||
if len(error_ids) <= 1 or len(error_ids) > len(errors):
|
||||
return {"errors": ["invalid list of ids"]}
|
||||
error_ids = [e["errorId"] for e in errors]
|
||||
parent_error_id = error_ids[0]
|
||||
status = "unresolved"
|
||||
for e in errors:
|
||||
if __status_rank(status) < __status_rank(e["status"]):
|
||||
status = e["status"]
|
||||
if __status_rank(status) == MAX_RANK:
|
||||
break
|
||||
params = {
|
||||
"error_ids": tuple(error_ids),
|
||||
"parent_error_id": parent_error_id,
|
||||
"status": status
|
||||
}
|
||||
with pg_client.PostgresClient() as cur:
|
||||
query = cur.mogrify(
|
||||
"""UPDATE public.errors
|
||||
SET parent_error_id = %(parent_error_id)s, status = %(status)s
|
||||
WHERE error_id IN %(error_ids)s OR parent_error_id IN %(error_ids)s;""",
|
||||
params)
|
||||
cur.execute(query=query)
|
||||
# row = cur.fetchone()
|
||||
|
||||
return {"data": "success"}
|
||||
|
||||
|
||||
def format_first_stack_frame(error):
|
||||
error["stack"] = sourcemaps.format_payload(error.pop("payload"), truncate_to_first=True)
|
||||
for s in error["stack"]:
|
||||
|
|
|
|||
10
ee/api/chalicelib/core/permissions.py
Normal file
10
ee/api/chalicelib/core/permissions.py
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
from fastapi.security import SecurityScopes
|
||||
|
||||
import schemas_ee
|
||||
|
||||
|
||||
def check(security_scopes: SecurityScopes, context: schemas_ee.CurrentContext):
|
||||
for scope in security_scopes.scopes:
|
||||
if scope not in context.permissions:
|
||||
return False
|
||||
return True
|
||||
1254
ee/api/chalicelib/core/sessions.py
Normal file
1254
ee/api/chalicelib/core/sessions.py
Normal file
File diff suppressed because it is too large
Load diff
31
ee/api/chalicelib/core/sessions_devtool.py
Normal file
31
ee/api/chalicelib/core/sessions_devtool.py
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
from decouple import config
|
||||
from fastapi.security import SecurityScopes
|
||||
|
||||
import schemas_ee
|
||||
from chalicelib.core import permissions
|
||||
from chalicelib.utils.s3 import client
|
||||
|
||||
SCOPES = SecurityScopes([schemas_ee.Permissions.dev_tools])
|
||||
|
||||
|
||||
def __get_devtools_keys(project_id, session_id):
|
||||
params = {
|
||||
"sessionId": session_id,
|
||||
"projectId": project_id
|
||||
}
|
||||
return [
|
||||
config("DEVTOOLS_MOB_PATTERN", default="%(sessionId)sdevtools") % params
|
||||
]
|
||||
|
||||
|
||||
def get_urls(session_id, project_id, context: schemas_ee.CurrentContext):
|
||||
if not permissions.check(security_scopes=SCOPES, context=context):
|
||||
return []
|
||||
results = []
|
||||
for k in __get_devtools_keys(project_id=project_id, session_id=session_id):
|
||||
results.append(client.generate_presigned_url(
|
||||
'get_object',
|
||||
Params={'Bucket': config("sessions_bucket"), 'Key': k},
|
||||
ExpiresIn=config("PRESIGNED_URL_EXPIRATION", cast=int, default=900)
|
||||
))
|
||||
return results
|
||||
|
|
@ -1542,3 +1542,14 @@ def count_all():
|
|||
with pg_client.PostgresClient(unlimited_query=True) as cur:
|
||||
row = cur.execute(query="SELECT COUNT(session_id) AS count FROM public.sessions")
|
||||
return row.get("count", 0)
|
||||
|
||||
|
||||
def session_exists(project_id, session_id):
|
||||
with ch_client.ClickHouseClient() as cur:
|
||||
query = cur.format("""SELECT 1
|
||||
FROM public.sessions
|
||||
WHERE session_id=%(session_id)s
|
||||
AND project_id=%(project_id)s""",
|
||||
{"project_id": project_id, "session_id": session_id})
|
||||
row = cur.execute(query)
|
||||
return row is not None
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
from decouple import config
|
||||
|
||||
import schemas_ee
|
||||
from chalicelib.core import sessions, sessions_favorite_exp
|
||||
from chalicelib.utils import pg_client, s3_extra
|
||||
|
||||
|
||||
def add_favorite_session(project_id, user_id, session_id):
|
||||
def add_favorite_session(project_id, user_id, session_id, context: schemas_ee.CurrentContext):
|
||||
with pg_client.PostgresClient() as cur:
|
||||
cur.execute(
|
||||
cur.mogrify(f"""\
|
||||
|
|
@ -15,10 +16,10 @@ def add_favorite_session(project_id, user_id, session_id):
|
|||
|
||||
sessions_favorite_exp.add_favorite_session(project_id=project_id, user_id=user_id, session_id=session_id)
|
||||
return sessions.get_by_id2_pg(project_id=project_id, session_id=session_id, user_id=user_id, full_data=False,
|
||||
include_fav_viewed=True)
|
||||
include_fav_viewed=True, context=context)
|
||||
|
||||
|
||||
def remove_favorite_session(project_id, user_id, session_id):
|
||||
def remove_favorite_session(project_id, user_id, session_id, context: schemas_ee.CurrentContext):
|
||||
with pg_client.PostgresClient() as cur:
|
||||
cur.execute(
|
||||
cur.mogrify(f"""\
|
||||
|
|
@ -29,10 +30,10 @@ def remove_favorite_session(project_id, user_id, session_id):
|
|||
)
|
||||
sessions_favorite_exp.remove_favorite_session(project_id=project_id, user_id=user_id, session_id=session_id)
|
||||
return sessions.get_by_id2_pg(project_id=project_id, session_id=session_id, user_id=user_id, full_data=False,
|
||||
include_fav_viewed=True)
|
||||
include_fav_viewed=True, context=context)
|
||||
|
||||
|
||||
def favorite_session(project_id, user_id, session_id):
|
||||
def favorite_session(project_id, user_id, session_id, context: schemas_ee.CurrentContext):
|
||||
if favorite_session_exists(user_id=user_id, session_id=session_id):
|
||||
key = str(session_id)
|
||||
try:
|
||||
|
|
@ -59,7 +60,7 @@ def favorite_session(project_id, user_id, session_id):
|
|||
except Exception as e:
|
||||
print(f"!!!Error while tagging: {key} to vault")
|
||||
print(str(e))
|
||||
return add_favorite_session(project_id=project_id, user_id=user_id, session_id=session_id)
|
||||
return add_favorite_session(project_id=project_id, user_id=user_id, session_id=session_id, context=context)
|
||||
|
||||
|
||||
def favorite_session_exists(user_id, session_id):
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ rm -rf ./chalicelib/core/log_tool_stackdriver.py
|
|||
rm -rf ./chalicelib/core/log_tool_sumologic.py
|
||||
rm -rf ./chalicelib/core/metadata.py
|
||||
rm -rf ./chalicelib/core/mobile.py
|
||||
rm -rf ./chalicelib/core/sessions.py
|
||||
rm -rf ./chalicelib/core/sessions_assignments.py
|
||||
rm -rf ./chalicelib/core/sessions_metas.py
|
||||
rm -rf ./chalicelib/core/sessions_mobs.py
|
||||
|
|
@ -65,19 +64,16 @@ rm -rf ./routers/subs/__init__.py
|
|||
rm -rf ./routers/__init__.py
|
||||
rm -rf ./chalicelib/core/assist.py
|
||||
rm -rf ./auth/auth_apikey.py
|
||||
rm -rf ./auth/auth_jwt.py
|
||||
rm -rf ./build.sh
|
||||
rm -rf ./routers/base.py
|
||||
rm -rf ./routers/core.py
|
||||
rm -rf ./routers/crons/core_crons.py
|
||||
rm -rf ./routers/subs/dashboard.py
|
||||
rm -rf ./db_changes.sql
|
||||
rm -rf ./Dockerfile.bundle
|
||||
rm -rf ./entrypoint.bundle.sh
|
||||
rm -rf ./chalicelib/core/heatmaps.py
|
||||
rm -rf ./routers/subs/insights.py
|
||||
rm -rf ./schemas.py
|
||||
rm -rf ./routers/subs/v1_api.py
|
||||
rm -rf ./routers/subs/metrics.py
|
||||
#exp rm -rf ./chalicelib/core/custom_metrics.py
|
||||
rm -rf ./chalicelib/core/performance_event.py
|
||||
rm -rf ./chalicelib/core/saved_search.py
|
||||
|
|
|
|||
|
|
@ -68,4 +68,10 @@ EXP_7D_MV=false
|
|||
EXP_ALERTS=false
|
||||
EXP_FUNNELS=false
|
||||
EXP_RESOURCES=true
|
||||
TRACE_PERIOD=300
|
||||
TRACE_PERIOD=300
|
||||
EFS_SESSION_MOB_PATTERN=%(sessionId)s/dom.mob
|
||||
EFS_DEVTOOLS_MOB_PATTERN=%(sessionId)s/devtools.mob
|
||||
SESSION_MOB_PATTERN_S=%(sessionId)s/dom.mobs
|
||||
SESSION_MOB_PATTERN_E=%(sessionId)s/dom.mobe
|
||||
DEVTOOLS_MOB_PATTERN=%(sessionId)s/devtools.mob
|
||||
PRESIGNED_URL_EXPIRATION=3600
|
||||
|
|
@ -1,17 +1,21 @@
|
|||
import json
|
||||
from typing import Callable
|
||||
|
||||
from fastapi import HTTPException, Depends
|
||||
from fastapi import Security
|
||||
from fastapi.routing import APIRoute
|
||||
from fastapi.security import SecurityScopes
|
||||
from starlette import status
|
||||
from starlette.exceptions import HTTPException
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import Response, JSONResponse
|
||||
|
||||
import schemas
|
||||
import schemas_ee
|
||||
from chalicelib.core import traces
|
||||
from chalicelib.core import permissions
|
||||
|
||||
|
||||
async def OR_context(request: Request) -> schemas.CurrentContext:
|
||||
async def OR_context(request: Request) -> schemas_ee.CurrentContext:
|
||||
if hasattr(request.state, "currentContext"):
|
||||
return request.state.currentContext
|
||||
else:
|
||||
|
|
@ -43,3 +47,14 @@ class ORRoute(APIRoute):
|
|||
return response
|
||||
|
||||
return custom_route_handler
|
||||
|
||||
|
||||
def __check(security_scopes: SecurityScopes, context: schemas_ee.CurrentContext = Depends(OR_context)):
|
||||
for scope in security_scopes.scopes:
|
||||
if scope not in context.permissions:
|
||||
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Not enough permissions")
|
||||
|
||||
|
||||
def OR_scope(*scopes):
|
||||
return Security(__check, scopes=list(scopes))
|
||||
|
|
|
|||
|
|
@ -1,19 +1,23 @@
|
|||
from typing import Optional
|
||||
from typing import Optional, Union
|
||||
|
||||
from decouple import config
|
||||
from fastapi import Body, Depends, BackgroundTasks
|
||||
from starlette.responses import RedirectResponse
|
||||
from starlette.responses import RedirectResponse, FileResponse
|
||||
|
||||
import schemas
|
||||
import schemas_ee
|
||||
from chalicelib.core import sessions
|
||||
from chalicelib.core import sessions, assist, heatmaps, sessions_favorite, sessions_assignments, errors, errors_viewed, \
|
||||
errors_favorite
|
||||
from chalicelib.core import sessions_viewed
|
||||
from chalicelib.core import tenants, users, projects, license
|
||||
from chalicelib.core import webhook
|
||||
from chalicelib.core.collaboration_slack import Slack
|
||||
from chalicelib.utils import SAML2_helper
|
||||
from chalicelib.utils import helper
|
||||
from or_dependencies import OR_context
|
||||
from chalicelib.utils.TimeUTC import TimeUTC
|
||||
from or_dependencies import OR_context, OR_scope
|
||||
from routers.base import get_routers
|
||||
from schemas_ee import Permissions
|
||||
|
||||
public_app, app, app_apikey = get_routers()
|
||||
|
||||
|
|
@ -171,3 +175,218 @@ def get_general_stats():
|
|||
def get_projects(context: schemas.CurrentContext = Depends(OR_context)):
|
||||
return {"data": projects.get_projects(tenant_id=context.tenant_id, recording_state=True, gdpr=True, recorded=True,
|
||||
stack_integrations=True, user_id=context.user_id)}
|
||||
|
||||
|
||||
@app.get('/{projectId}/sessions/{sessionId}', tags=["sessions"], dependencies=[OR_scope(Permissions.session_replay)])
|
||||
@app.get('/{projectId}/sessions2/{sessionId}', tags=["sessions"], dependencies=[OR_scope(Permissions.session_replay)])
|
||||
def get_session(projectId: int, sessionId: Union[int, str], background_tasks: BackgroundTasks,
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
if isinstance(sessionId, str):
|
||||
return {"errors": ["session not found"]}
|
||||
data = sessions.get_by_id2_pg(project_id=projectId, session_id=sessionId, full_data=True, user_id=context.user_id,
|
||||
include_fav_viewed=True, group_metadata=True, context=context)
|
||||
if data is None:
|
||||
return {"errors": ["session not found"]}
|
||||
if data.get("inDB"):
|
||||
background_tasks.add_task(sessions_viewed.view_session, project_id=projectId, user_id=context.user_id,
|
||||
session_id=sessionId)
|
||||
return {
|
||||
'data': data
|
||||
}
|
||||
|
||||
|
||||
@app.get('/{projectId}/sessions/{sessionId}/errors/{errorId}/sourcemaps', tags=["sessions", "sourcemaps"],
|
||||
dependencies=[OR_scope(Permissions.session_replay, Permissions.errors)])
|
||||
@app.get('/{projectId}/sessions2/{sessionId}/errors/{errorId}/sourcemaps', tags=["sessions", "sourcemaps"],
|
||||
dependencies=[OR_scope(Permissions.session_replay, Permissions.errors)])
|
||||
def get_error_trace(projectId: int, sessionId: int, errorId: str,
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = errors.get_trace(project_id=projectId, error_id=errorId)
|
||||
if "errors" in data:
|
||||
return data
|
||||
return {
|
||||
'data': data
|
||||
}
|
||||
|
||||
|
||||
@app.post('/{projectId}/errors/search', tags=['errors'], dependencies=[OR_scope(Permissions.errors)])
|
||||
def errors_search(projectId: int, data: schemas.SearchErrorsSchema = Body(...),
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
return {"data": errors.search(data, projectId, user_id=context.user_id)}
|
||||
|
||||
|
||||
@app.get('/{projectId}/errors/stats', tags=['errors'], dependencies=[OR_scope(Permissions.errors)])
|
||||
def errors_stats(projectId: int, startTimestamp: int, endTimestamp: int,
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
return errors.stats(projectId, user_id=context.user_id, startTimestamp=startTimestamp, endTimestamp=endTimestamp)
|
||||
|
||||
|
||||
@app.get('/{projectId}/errors/{errorId}', tags=['errors'], dependencies=[OR_scope(Permissions.errors)])
|
||||
def errors_get_details(projectId: int, errorId: str, background_tasks: BackgroundTasks, density24: int = 24,
|
||||
density30: int = 30, context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = errors.get_details(project_id=projectId, user_id=context.user_id, error_id=errorId,
|
||||
**{"density24": density24, "density30": density30})
|
||||
if data.get("data") is not None:
|
||||
background_tasks.add_task(errors_viewed.viewed_error, project_id=projectId, user_id=context.user_id,
|
||||
error_id=errorId)
|
||||
return data
|
||||
|
||||
|
||||
@app.get('/{projectId}/errors/{errorId}/stats', tags=['errors'], dependencies=[OR_scope(Permissions.errors)])
|
||||
def errors_get_details_right_column(projectId: int, errorId: str, startDate: int = TimeUTC.now(-7),
|
||||
endDate: int = TimeUTC.now(), density: int = 7,
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = errors.get_details_chart(project_id=projectId, user_id=context.user_id, error_id=errorId,
|
||||
**{"startDate": startDate, "endDate": endDate, "density": density})
|
||||
return data
|
||||
|
||||
|
||||
@app.get('/{projectId}/errors/{errorId}/sourcemaps', tags=['errors'], dependencies=[OR_scope(Permissions.errors)])
|
||||
def errors_get_details_sourcemaps(projectId: int, errorId: str,
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = errors.get_trace(project_id=projectId, error_id=errorId)
|
||||
if "errors" in data:
|
||||
return data
|
||||
return {
|
||||
'data': data
|
||||
}
|
||||
|
||||
|
||||
@app.get('/{projectId}/errors/{errorId}/{action}', tags=["errors"], dependencies=[OR_scope(Permissions.errors)])
|
||||
def add_remove_favorite_error(projectId: int, errorId: str, action: str, startDate: int = TimeUTC.now(-7),
|
||||
endDate: int = TimeUTC.now(), context: schemas.CurrentContext = Depends(OR_context)):
|
||||
if action == "favorite":
|
||||
return errors_favorite.favorite_error(project_id=projectId, user_id=context.user_id, error_id=errorId)
|
||||
elif action == "sessions":
|
||||
start_date = startDate
|
||||
end_date = endDate
|
||||
return {
|
||||
"data": errors.get_sessions(project_id=projectId, user_id=context.user_id, error_id=errorId,
|
||||
start_date=start_date, end_date=end_date)}
|
||||
elif action in list(errors.ACTION_STATE.keys()):
|
||||
return errors.change_state(project_id=projectId, user_id=context.user_id, error_id=errorId, action=action)
|
||||
else:
|
||||
return {"errors": ["undefined action"]}
|
||||
|
||||
|
||||
@app.get('/{projectId}/assist/sessions/{sessionId}', tags=["assist"], dependencies=[OR_scope(Permissions.assist_live)])
|
||||
def get_live_session(projectId: int, sessionId: str, background_tasks: BackgroundTasks,
|
||||
context: schemas_ee.CurrentContext = Depends(OR_context)):
|
||||
data = assist.get_live_session_by_id(project_id=projectId, session_id=sessionId)
|
||||
if data is None:
|
||||
data = sessions.get_by_id2_pg(project_id=projectId, session_id=sessionId, full_data=True,
|
||||
user_id=context.user_id, include_fav_viewed=True, group_metadata=True, live=False,
|
||||
context=context)
|
||||
if data is None:
|
||||
return {"errors": ["session not found"]}
|
||||
if data.get("inDB"):
|
||||
background_tasks.add_task(sessions_viewed.view_session, project_id=projectId,
|
||||
user_id=context.user_id, session_id=sessionId)
|
||||
return {'data': data}
|
||||
|
||||
|
||||
@app.get('/{projectId}/unprocessed/{sessionId}', tags=["assist"],
|
||||
dependencies=[OR_scope(Permissions.assist_live, Permissions.session_replay)])
|
||||
@app.get('/{projectId}/assist/sessions/{sessionId}/replay', tags=["assist"],
|
||||
dependencies=[OR_scope(Permissions.assist_live, Permissions.session_replay)])
|
||||
def get_live_session_replay_file(projectId: int, sessionId: Union[int, str],
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
if isinstance(sessionId, str) or not sessions.session_exists(project_id=projectId, session_id=sessionId):
|
||||
if isinstance(sessionId, str):
|
||||
print(f"{sessionId} not a valid number.")
|
||||
else:
|
||||
print(f"{projectId}/{sessionId} not found in DB.")
|
||||
|
||||
return {"errors": ["Replay file not found"]}
|
||||
path = assist.get_raw_mob_by_id(project_id=projectId, session_id=sessionId)
|
||||
if path is None:
|
||||
return {"errors": ["Replay file not found"]}
|
||||
|
||||
return FileResponse(path=path, media_type="application/octet-stream")
|
||||
|
||||
|
||||
@app.get('/{projectId}/unprocessed/{sessionId}/devtools', tags=["assist"],
|
||||
dependencies=[OR_scope(Permissions.assist_live, Permissions.session_replay, Permissions.dev_tools)])
|
||||
@app.get('/{projectId}/assist/sessions/{sessionId}/devtools', tags=["assist"],
|
||||
dependencies=[OR_scope(Permissions.assist_live, Permissions.session_replay, Permissions.dev_tools)])
|
||||
def get_live_session_devtools_file(projectId: int, sessionId: Union[int, str],
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
if isinstance(sessionId, str) or not sessions.session_exists(project_id=projectId, session_id=sessionId):
|
||||
if isinstance(sessionId, str):
|
||||
print(f"{sessionId} not a valid number.")
|
||||
else:
|
||||
print(f"{projectId}/{sessionId} not found in DB.")
|
||||
|
||||
return {"errors": ["Devtools file not found"]}
|
||||
path = assist.get_raw_devtools_by_id(project_id=projectId, session_id=sessionId)
|
||||
if path is None:
|
||||
return {"errors": ["Devtools file not found"]}
|
||||
|
||||
return FileResponse(path=path, media_type="application/octet-stream")
|
||||
|
||||
|
||||
@app.post('/{projectId}/heatmaps/url', tags=["heatmaps"], dependencies=[OR_scope(Permissions.session_replay)])
|
||||
def get_heatmaps_by_url(projectId: int, data: schemas.GetHeatmapPayloadSchema = Body(...),
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
return {"data": heatmaps.get_by_url(project_id=projectId, data=data.dict())}
|
||||
|
||||
|
||||
@app.get('/{projectId}/sessions/{sessionId}/favorite', tags=["sessions"],
|
||||
dependencies=[OR_scope(Permissions.session_replay)])
|
||||
@app.get('/{projectId}/sessions2/{sessionId}/favorite', tags=["sessions"],
|
||||
dependencies=[OR_scope(Permissions.session_replay)])
|
||||
def add_remove_favorite_session2(projectId: int, sessionId: int,
|
||||
context: schemas_ee.CurrentContext = Depends(OR_context)):
|
||||
return {
|
||||
"data": sessions_favorite.favorite_session(project_id=projectId, user_id=context.user_id,
|
||||
session_id=sessionId, context=context)}
|
||||
|
||||
|
||||
@app.get('/{projectId}/sessions/{sessionId}/assign', tags=["sessions"],
|
||||
dependencies=[OR_scope(Permissions.session_replay)])
|
||||
@app.get('/{projectId}/sessions2/{sessionId}/assign', tags=["sessions"],
|
||||
dependencies=[OR_scope(Permissions.session_replay)])
|
||||
def assign_session(projectId: int, sessionId, context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = sessions_assignments.get_by_session(project_id=projectId, session_id=sessionId,
|
||||
tenant_id=context.tenant_id,
|
||||
user_id=context.user_id)
|
||||
if "errors" in data:
|
||||
return data
|
||||
return {
|
||||
'data': data
|
||||
}
|
||||
|
||||
|
||||
@app.get('/{projectId}/sessions/{sessionId}/assign/{issueId}', tags=["sessions", "issueTracking"],
|
||||
dependencies=[OR_scope(Permissions.session_replay)])
|
||||
@app.get('/{projectId}/sessions2/{sessionId}/assign/{issueId}', tags=["sessions", "issueTracking"],
|
||||
dependencies=[OR_scope(Permissions.session_replay)])
|
||||
def assign_session(projectId: int, sessionId: int, issueId: str,
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = sessions_assignments.get(project_id=projectId, session_id=sessionId, assignment_id=issueId,
|
||||
tenant_id=context.tenant_id, user_id=context.user_id)
|
||||
if "errors" in data:
|
||||
return data
|
||||
return {
|
||||
'data': data
|
||||
}
|
||||
|
||||
|
||||
@app.post('/{projectId}/sessions/{sessionId}/assign/{issueId}/comment', tags=["sessions", "issueTracking"],
|
||||
dependencies=[OR_scope(Permissions.session_replay)])
|
||||
@app.put('/{projectId}/sessions/{sessionId}/assign/{issueId}/comment', tags=["sessions", "issueTracking"],
|
||||
dependencies=[OR_scope(Permissions.session_replay)])
|
||||
@app.post('/{projectId}/sessions2/{sessionId}/assign/{issueId}/comment', tags=["sessions", "issueTracking"],
|
||||
dependencies=[OR_scope(Permissions.session_replay)])
|
||||
@app.put('/{projectId}/sessions2/{sessionId}/assign/{issueId}/comment', tags=["sessions", "issueTracking"],
|
||||
dependencies=[OR_scope(Permissions.session_replay)])
|
||||
def comment_assignment(projectId: int, sessionId: int, issueId: str, data: schemas.CommentAssignmentSchema = Body(...),
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = sessions_assignments.comment(tenant_id=context.tenant_id, project_id=projectId,
|
||||
session_id=sessionId, assignment_id=issueId,
|
||||
user_id=context.user_id, message=data.message)
|
||||
if "errors" in data.keys():
|
||||
return data
|
||||
return {
|
||||
'data': data
|
||||
}
|
||||
|
|
|
|||
400
ee/api/routers/subs/dashboard.py
Normal file
400
ee/api/routers/subs/dashboard.py
Normal file
|
|
@ -0,0 +1,400 @@
|
|||
from fastapi import Body
|
||||
|
||||
import schemas
|
||||
from chalicelib.core import metadata
|
||||
from chalicelib.core import metrics
|
||||
from chalicelib.utils import helper
|
||||
from or_dependencies import OR_scope
|
||||
from routers.base import get_routers
|
||||
from schemas_ee import Permissions
|
||||
|
||||
public_app, app, app_apikey = get_routers([OR_scope(Permissions.metrics)])
|
||||
|
||||
|
||||
@app.get('/{projectId}/dashboard/metadata', tags=["dashboard", "metrics"])
|
||||
def get_metadata_map(projectId: int):
|
||||
metamap = []
|
||||
for m in metadata.get(project_id=projectId):
|
||||
metamap.append({"name": m["key"], "key": f"metadata{m['index']}"})
|
||||
return {"data": metamap}
|
||||
|
||||
|
||||
@app.post('/{projectId}/dashboard/sessions', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/sessions', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_processed_sessions(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.get_processed_sessions(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
@app.post('/{projectId}/dashboard/errors', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/errors', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_errors(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.get_errors(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
@app.post('/{projectId}/dashboard/errors_trend', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/errors_trend', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_errors_trend(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.get_errors_trend(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
@app.post('/{projectId}/dashboard/application_activity', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/application_activity', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_application_activity(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.get_application_activity(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
@app.post('/{projectId}/dashboard/page_metrics', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/page_metrics', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_page_metrics(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.get_page_metrics(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
@app.post('/{projectId}/dashboard/user_activity', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/user_activity', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_user_activity(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.get_user_activity(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
@app.post('/{projectId}/dashboard/performance', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/performance', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_performance(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.get_performance(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
@app.post('/{projectId}/dashboard/slowest_images', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/slowest_images', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_slowest_images(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.get_slowest_images(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
@app.post('/{projectId}/dashboard/missing_resources', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/missing_resources', tags=["dashboard", "metrics"])
|
||||
def get_performance_sessions(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.get_missing_resources_trend(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
@app.post('/{projectId}/dashboard/network', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/network', tags=["dashboard", "metrics"])
|
||||
def get_network_widget(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.get_network(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
@app.get('/{projectId}/dashboard/{widget}/search', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_autocomplete(projectId: int, widget: str, q: str, type: str = "", platform: str = None,
|
||||
key: str = ""):
|
||||
if q is None or len(q) == 0:
|
||||
return {"data": []}
|
||||
q = '^' + q
|
||||
|
||||
if widget in ['performance']:
|
||||
data = metrics.search(q, type, project_id=projectId,
|
||||
platform=platform, performance=True)
|
||||
elif widget in ['pages', 'pages_dom_buildtime', 'top_metrics', 'time_to_render',
|
||||
'impacted_sessions_by_slow_pages', 'pages_response_time']:
|
||||
data = metrics.search(q, type, project_id=projectId,
|
||||
platform=platform, pages_only=True)
|
||||
elif widget in ['resources_loading_time']:
|
||||
data = metrics.search(q, type, project_id=projectId,
|
||||
platform=platform, performance=False)
|
||||
elif widget in ['time_between_events', 'events']:
|
||||
data = metrics.search(q, type, project_id=projectId,
|
||||
platform=platform, performance=False, events_only=True)
|
||||
elif widget in ['metadata']:
|
||||
data = metrics.search(q, None, project_id=projectId,
|
||||
platform=platform, metadata=True, key=key)
|
||||
else:
|
||||
return {"errors": [f"unsupported widget: {widget}"]}
|
||||
return {'data': data}
|
||||
|
||||
|
||||
# 1
|
||||
@app.post('/{projectId}/dashboard/slowest_resources', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/slowest_resources', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_slowest_resources(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.get_slowest_resources(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
# 2
|
||||
@app.post('/{projectId}/dashboard/resources_loading_time', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/resources_loading_time', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_resources(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.get_resources_loading_time(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
# 3
|
||||
@app.post('/{projectId}/dashboard/pages_dom_buildtime', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/pages_dom_buildtime', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_pages_dom(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.get_pages_dom_build_time(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
# 4
|
||||
@app.post('/{projectId}/dashboard/busiest_time_of_day', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/busiest_time_of_day', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_busiest_time_of_day(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.get_busiest_time_of_day(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
# 5
|
||||
@app.post('/{projectId}/dashboard/sessions_location', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/sessions_location', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_sessions_location(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.get_sessions_location(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
# 6
|
||||
@app.post('/{projectId}/dashboard/speed_location', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/speed_location', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_speed_location(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.get_speed_index_location(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
# 7
|
||||
@app.post('/{projectId}/dashboard/pages_response_time', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/pages_response_time', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_pages_response_time(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.get_pages_response_time(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
# 8
|
||||
@app.post('/{projectId}/dashboard/pages_response_time_distribution', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/pages_response_time_distribution', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_pages_response_time_distribution(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.get_pages_response_time_distribution(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
# 9
|
||||
@app.post('/{projectId}/dashboard/top_metrics', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/top_metrics', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_top_metrics(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.get_top_metrics(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
# 10
|
||||
@app.post('/{projectId}/dashboard/time_to_render', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/time_to_render', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_time_to_render(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.get_time_to_render(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
# 11
|
||||
@app.post('/{projectId}/dashboard/impacted_sessions_by_slow_pages', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/impacted_sessions_by_slow_pages', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_impacted_sessions_by_slow_pages(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.get_impacted_sessions_by_slow_pages(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
# 12
|
||||
@app.post('/{projectId}/dashboard/memory_consumption', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/memory_consumption', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_memory_consumption(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.get_memory_consumption(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
# 12.1
|
||||
@app.post('/{projectId}/dashboard/fps', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/fps', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_avg_fps(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.get_avg_fps(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
# 12.2
|
||||
@app.post('/{projectId}/dashboard/cpu', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/cpu', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_avg_cpu(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.get_avg_cpu(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
# 13
|
||||
@app.post('/{projectId}/dashboard/crashes', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/crashes', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_impacted_sessions_by_slow_pages(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.get_crashes(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
# 14
|
||||
@app.post('/{projectId}/dashboard/domains_errors', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/domains_errors', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_domains_errors(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.get_domains_errors(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
# 14.1
|
||||
@app.post('/{projectId}/dashboard/domains_errors_4xx', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/domains_errors_4xx', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_domains_errors_4xx(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.get_domains_errors_4xx(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
# 14.2
|
||||
@app.post('/{projectId}/dashboard/domains_errors_5xx', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/domains_errors_5xx', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_domains_errors_5xx(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.get_domains_errors_5xx(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
# 15
|
||||
@app.post('/{projectId}/dashboard/slowest_domains', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/slowest_domains', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_slowest_domains(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.get_slowest_domains(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
# 16
|
||||
@app.post('/{projectId}/dashboard/errors_per_domains', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/errors_per_domains', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_errors_per_domains(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.get_errors_per_domains(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
# 17
|
||||
@app.post('/{projectId}/dashboard/sessions_per_browser', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/sessions_per_browser', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_sessions_per_browser(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.get_sessions_per_browser(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
# 18
|
||||
@app.post('/{projectId}/dashboard/calls_errors', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/calls_errors', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_calls_errors(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.get_calls_errors(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
# 18.1
|
||||
@app.post('/{projectId}/dashboard/calls_errors_4xx', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/calls_errors_4xx', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_calls_errors_4xx(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.get_calls_errors_4xx(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
# 18.2
|
||||
@app.post('/{projectId}/dashboard/calls_errors_5xx', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/calls_errors_5xx', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_calls_errors_5xx(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.get_calls_errors_5xx(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
# 19
|
||||
@app.post('/{projectId}/dashboard/errors_per_type', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/errors_per_type', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_errors_per_type(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.get_errors_per_type(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
# 20
|
||||
@app.post('/{projectId}/dashboard/resources_by_party', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/resources_by_party', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_resources_by_party(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.get_resources_by_party(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
# 21
|
||||
@app.post('/{projectId}/dashboard/resource_type_vs_response_end', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/resource_type_vs_response_end', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_errors_per_resource_type(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.resource_type_vs_response_end(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
# 22
|
||||
@app.post('/{projectId}/dashboard/resources_vs_visually_complete', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/resources_vs_visually_complete', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_resources_vs_visually_complete(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.get_resources_vs_visually_complete(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
# 23
|
||||
@app.post('/{projectId}/dashboard/impacted_sessions_by_js_errors', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/impacted_sessions_by_js_errors', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_impacted_sessions_by_js_errors(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.get_impacted_sessions_by_js_errors(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
# 24
|
||||
@app.post('/{projectId}/dashboard/resources_count_by_type', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/resources_count_by_type', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_resources_count_by_type(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": metrics.get_resources_count_by_type(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
# # 25
|
||||
# @app.post('/{projectId}/dashboard/time_between_events', tags=["dashboard", "metrics"])
|
||||
# @app.get('/{projectId}/dashboard/time_between_events', tags=["dashboard", "metrics"])
|
||||
# def get_dashboard_resources_count_by_type(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
# return {"errors": ["please choose 2 events"]}
|
||||
|
||||
|
||||
@app.post('/{projectId}/dashboard/overview', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/overview', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_group(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
results = [
|
||||
{"key": "count_sessions",
|
||||
"data": metrics.get_processed_sessions(project_id=projectId, **data.dict())},
|
||||
*helper.explode_widget(data={**metrics.get_application_activity(project_id=projectId, **data.dict()),
|
||||
"chart": metrics.get_performance(project_id=projectId, **data.dict())
|
||||
.get("chart", [])}),
|
||||
*helper.explode_widget(data=metrics.get_page_metrics(project_id=projectId, **data.dict())),
|
||||
*helper.explode_widget(data=metrics.get_user_activity(project_id=projectId, **data.dict())),
|
||||
{"key": "avg_pages_dom_buildtime",
|
||||
"data": metrics.get_pages_dom_build_time(project_id=projectId, **data.dict())},
|
||||
{"key": "avg_pages_response_time",
|
||||
"data": metrics.get_pages_response_time(project_id=projectId, **data.dict())
|
||||
},
|
||||
*helper.explode_widget(metrics.get_top_metrics(project_id=projectId, **data.dict())),
|
||||
{"key": "avg_time_to_render", "data": metrics.get_time_to_render(project_id=projectId, **data.dict())},
|
||||
{"key": "avg_used_js_heap_size", "data": metrics.get_memory_consumption(project_id=projectId, **data.dict())},
|
||||
{"key": "avg_cpu", "data": metrics.get_avg_cpu(project_id=projectId, **data.dict())},
|
||||
{"key": schemas.TemplatePredefinedKeys.avg_fps,
|
||||
"data": metrics.get_avg_fps(project_id=projectId, **data.dict())}
|
||||
]
|
||||
results = sorted(results, key=lambda r: r["key"])
|
||||
return {"data": results}
|
||||
|
||||
|
||||
@app.post('/{projectId}/dashboard/overview2', tags=["dashboard", "metrics"])
|
||||
@app.get('/{projectId}/dashboard/overview2', tags=["dashboard", "metrics"])
|
||||
def get_dashboard_group(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
results = [
|
||||
{"key": schemas.TemplatePredefinedKeys.count_sessions,
|
||||
"data": metrics.get_processed_sessions(project_id=projectId, **data.dict())},
|
||||
{"key": schemas.TemplatePredefinedKeys.avg_image_load_time,
|
||||
"data": metrics.get_application_activity_avg_image_load_time(project_id=projectId, **data.dict())},
|
||||
{"key": schemas.TemplatePredefinedKeys.avg_page_load_time,
|
||||
"data": metrics.get_application_activity_avg_page_load_time(project_id=projectId, **data.dict())},
|
||||
{"key": schemas.TemplatePredefinedKeys.avg_request_load_time,
|
||||
"data": metrics.get_application_activity_avg_request_load_time(project_id=projectId, **data.dict())},
|
||||
{"key": schemas.TemplatePredefinedKeys.avg_dom_content_load_start,
|
||||
"data": metrics.get_page_metrics_avg_dom_content_load_start(project_id=projectId, **data.dict())},
|
||||
{"key": schemas.TemplatePredefinedKeys.avg_first_contentful_pixel,
|
||||
"data": metrics.get_page_metrics_avg_first_contentful_pixel(project_id=projectId, **data.dict())},
|
||||
{"key": schemas.TemplatePredefinedKeys.avg_visited_pages,
|
||||
"data": metrics.get_user_activity_avg_visited_pages(project_id=projectId, **data.dict())},
|
||||
{"key": schemas.TemplatePredefinedKeys.avg_session_duration,
|
||||
"data": metrics.get_user_activity_avg_session_duration(project_id=projectId, **data.dict())},
|
||||
{"key": schemas.TemplatePredefinedKeys.avg_pages_dom_buildtime,
|
||||
"data": metrics.get_pages_dom_build_time(project_id=projectId, **data.dict())},
|
||||
{"key": schemas.TemplatePredefinedKeys.avg_pages_response_time,
|
||||
"data": metrics.get_pages_response_time(project_id=projectId, **data.dict())},
|
||||
{"key": schemas.TemplatePredefinedKeys.avg_response_time,
|
||||
"data": metrics.get_top_metrics_avg_response_time(project_id=projectId, **data.dict())},
|
||||
{"key": schemas.TemplatePredefinedKeys.avg_first_paint,
|
||||
"data": metrics.get_top_metrics_avg_first_paint(project_id=projectId, **data.dict())},
|
||||
{"key": schemas.TemplatePredefinedKeys.avg_dom_content_loaded,
|
||||
"data": metrics.get_top_metrics_avg_dom_content_loaded(project_id=projectId, **data.dict())},
|
||||
{"key": schemas.TemplatePredefinedKeys.avg_till_first_bit,
|
||||
"data": metrics.get_top_metrics_avg_till_first_bit(project_id=projectId, **data.dict())},
|
||||
{"key": schemas.TemplatePredefinedKeys.avg_time_to_interactive,
|
||||
"data": metrics.get_top_metrics_avg_time_to_interactive(project_id=projectId, **data.dict())},
|
||||
{"key": schemas.TemplatePredefinedKeys.count_requests,
|
||||
"data": metrics.get_top_metrics_count_requests(project_id=projectId, **data.dict())},
|
||||
{"key": schemas.TemplatePredefinedKeys.avg_time_to_render,
|
||||
"data": metrics.get_time_to_render(project_id=projectId, **data.dict())},
|
||||
{"key": schemas.TemplatePredefinedKeys.avg_used_js_heap_size,
|
||||
"data": metrics.get_memory_consumption(project_id=projectId, **data.dict())},
|
||||
{"key": schemas.TemplatePredefinedKeys.avg_cpu,
|
||||
"data": metrics.get_avg_cpu(project_id=projectId, **data.dict())},
|
||||
{"key": schemas.TemplatePredefinedKeys.avg_fps,
|
||||
"data": metrics.get_avg_fps(project_id=projectId, **data.dict())}
|
||||
]
|
||||
results = sorted(results, key=lambda r: r["key"])
|
||||
return {"data": results}
|
||||
110
ee/api/routers/subs/insights.py
Normal file
110
ee/api/routers/subs/insights.py
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
from fastapi import Body
|
||||
|
||||
import schemas
|
||||
from chalicelib.core import insights
|
||||
from or_dependencies import OR_scope
|
||||
from routers.base import get_routers
|
||||
from schemas_ee import Permissions
|
||||
|
||||
public_app, app, app_apikey = get_routers([OR_scope(Permissions.metrics)])
|
||||
|
||||
|
||||
@app.post('/{projectId}/insights/journey', tags=["insights"])
|
||||
@app.get('/{projectId}/insights/journey', tags=["insights"])
|
||||
def get_insights_journey(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": insights.journey(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
@app.post('/{projectId}/insights/users_acquisition', tags=["insights"])
|
||||
@app.get('/{projectId}/insights/users_acquisition', tags=["insights"])
|
||||
def get_users_acquisition(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": insights.users_acquisition(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
@app.post('/{projectId}/insights/users_retention', tags=["insights"])
|
||||
@app.get('/{projectId}/insights/users_retention', tags=["insights"])
|
||||
def get_users_retention(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": insights.users_retention(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
@app.post('/{projectId}/insights/feature_retention', tags=["insights"])
|
||||
@app.get('/{projectId}/insights/feature_retention', tags=["insights"])
|
||||
def get_feature_rentention(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": insights.feature_retention(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
@app.post('/{projectId}/insights/feature_acquisition', tags=["insights"])
|
||||
@app.get('/{projectId}/insights/feature_acquisition', tags=["insights"])
|
||||
def get_feature_acquisition(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": insights.feature_acquisition(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
@app.post('/{projectId}/insights/feature_popularity_frequency', tags=["insights"])
|
||||
@app.get('/{projectId}/insights/feature_popularity_frequency', tags=["insights"])
|
||||
def get_feature_popularity_frequency(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": insights.feature_popularity_frequency(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
@app.post('/{projectId}/insights/feature_intensity', tags=["insights"])
|
||||
@app.get('/{projectId}/insights/feature_intensity', tags=["insights"])
|
||||
def get_feature_intensity(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": insights.feature_intensity(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
@app.post('/{projectId}/insights/feature_adoption', tags=["insights"])
|
||||
@app.get('/{projectId}/insights/feature_adoption', tags=["insights"])
|
||||
def get_feature_adoption(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": insights.feature_adoption(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
@app.post('/{projectId}/insights/feature_adoption_top_users', tags=["insights"])
|
||||
@app.get('/{projectId}/insights/feature_adoption_top_users', tags=["insights"])
|
||||
def get_feature_adoption(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": insights.feature_adoption_top_users(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
@app.post('/{projectId}/insights/users_active', tags=["insights"])
|
||||
@app.get('/{projectId}/insights/users_active', tags=["insights"])
|
||||
def get_users_active(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": insights.users_active(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
@app.post('/{projectId}/insights/users_power', tags=["insights"])
|
||||
@app.get('/{projectId}/insights/users_power', tags=["insights"])
|
||||
def get_users_power(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": insights.users_power(project_id=projectId, **data.dict())}
|
||||
|
||||
|
||||
@app.post('/{projectId}/insights/users_slipping', tags=["insights"])
|
||||
@app.get('/{projectId}/insights/users_slipping', tags=["insights"])
|
||||
def get_users_slipping(projectId: int, data: schemas.MetricPayloadSchema = Body(...)):
|
||||
return {"data": insights.users_slipping(project_id=projectId, **data.dict())}
|
||||
|
||||
#
|
||||
#
|
||||
# @app.route('/{projectId}/dashboard/{widget}/search', methods=['GET'])
|
||||
# def get_dashboard_autocomplete(projectId:int, widget):
|
||||
# params = app.current_request.query_params
|
||||
# if params is None or params.get('q') is None or len(params.get('q')) == 0:
|
||||
# return {"data": []}
|
||||
# params['q'] = '^' + params['q']
|
||||
#
|
||||
# if widget in ['performance']:
|
||||
# data = dashboard.search(params.get('q', ''), params.get('type', ''), project_id=projectId,
|
||||
# platform=params.get('platform', None), performance=True)
|
||||
# elif widget in ['pages', 'pages_dom_buildtime', 'top_metrics', 'time_to_render',
|
||||
# 'impacted_sessions_by_slow_pages', 'pages_response_time']:
|
||||
# data = dashboard.search(params.get('q', ''), params.get('type', ''), project_id=projectId,
|
||||
# platform=params.get('platform', None), pages_only=True)
|
||||
# elif widget in ['resources_loading_time']:
|
||||
# data = dashboard.search(params.get('q', ''), params.get('type', ''), project_id=projectId,
|
||||
# platform=params.get('platform', None), performance=False)
|
||||
# elif widget in ['time_between_events', 'events']:
|
||||
# data = dashboard.search(params.get('q', ''), params.get('type', ''), project_id=projectId,
|
||||
# platform=params.get('platform', None), performance=False, events_only=True)
|
||||
# elif widget in ['metadata']:
|
||||
# data = dashboard.search(params.get('q', ''), None, project_id=projectId,
|
||||
# platform=params.get('platform', None), metadata=True, key=params.get("key"))
|
||||
# else:
|
||||
# return {"errors": [f"unsupported widget: {widget}"]}
|
||||
# return {'data': data}
|
||||
238
ee/api/routers/subs/metrics.py
Normal file
238
ee/api/routers/subs/metrics.py
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
from fastapi import Body, Depends
|
||||
|
||||
import schemas
|
||||
from chalicelib.core import dashboards, custom_metrics, funnels
|
||||
from or_dependencies import OR_context, OR_scope
|
||||
from routers.base import get_routers
|
||||
from schemas_ee import Permissions
|
||||
|
||||
public_app, app, app_apikey = get_routers([OR_scope(Permissions.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 dashboards.create_dashboard(project_id=projectId, user_id=context.user_id, data=data)
|
||||
|
||||
|
||||
@app.get('/{projectId}/dashboards', tags=["dashboard"])
|
||||
def get_dashboards(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
|
||||
return {"data": dashboards.get_dashboards(project_id=projectId, user_id=context.user_id)}
|
||||
|
||||
|
||||
@app.get('/{projectId}/dashboards/{dashboardId}', tags=["dashboard"])
|
||||
def get_dashboard(projectId: int, dashboardId: int, context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = dashboards.get_dashboard(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId)
|
||||
if data is None:
|
||||
return {"errors": ["dashboard not found"]}
|
||||
return {"data": data}
|
||||
|
||||
|
||||
@app.post('/{projectId}/dashboards/{dashboardId}', tags=["dashboard"])
|
||||
@app.put('/{projectId}/dashboards/{dashboardId}', tags=["dashboard"])
|
||||
def update_dashboard(projectId: int, dashboardId: int, data: schemas.EditDashboardSchema = Body(...),
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
return {"data": dashboards.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 dashboards.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": dashboards.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)):
|
||||
return {"data": dashboards.add_widget(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId,
|
||||
data=data)}
|
||||
|
||||
|
||||
@app.post('/{projectId}/dashboards/{dashboardId}/metrics', tags=["dashboard"])
|
||||
@app.put('/{projectId}/dashboards/{dashboardId}/metrics', tags=["dashboard"])
|
||||
def create_metric_and_add_to_dashboard(projectId: int, dashboardId: int,
|
||||
data: schemas.CreateCustomMetricsSchema = Body(...),
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
return {"data": dashboards.create_metric_add_widget(project_id=projectId, user_id=context.user_id,
|
||||
dashboard_id=dashboardId, data=data)}
|
||||
|
||||
|
||||
@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.UpdateWidgetPayloadSchema = Body(...),
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
return dashboards.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 dashboards.remove_widget(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId,
|
||||
widget_id=widgetId)
|
||||
|
||||
|
||||
@app.post('/{projectId}/dashboards/{dashboardId}/widgets/{widgetId}/chart', tags=["dashboard"])
|
||||
def get_widget_chart(projectId: int, dashboardId: int, widgetId: int,
|
||||
data: schemas.CustomMetricChartPayloadSchema = Body(...),
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = dashboards.make_chart_widget(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId,
|
||||
widget_id=widgetId, data=data)
|
||||
if data is None:
|
||||
return {"errors": ["widget not found"]}
|
||||
return {"data": data}
|
||||
|
||||
|
||||
@app.get('/{projectId}/metrics/templates', tags=["dashboard"])
|
||||
def get_templates(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
|
||||
return {"data": dashboards.get_templates(project_id=projectId, user_id=context.user_id)}
|
||||
|
||||
|
||||
@app.post('/{projectId}/metrics/try', tags=["dashboard"])
|
||||
@app.put('/{projectId}/metrics/try', tags=["dashboard"])
|
||||
@app.post('/{projectId}/custom_metrics/try', tags=["customMetrics"])
|
||||
@app.put('/{projectId}/custom_metrics/try', tags=["customMetrics"])
|
||||
def try_custom_metric(projectId: int, data: schemas.TryCustomMetricsPayloadSchema = Body(...),
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
return {"data": custom_metrics.merged_live(project_id=projectId, data=data, user_id=context.user_id)}
|
||||
|
||||
|
||||
@app.post('/{projectId}/metrics/try/sessions', tags=["dashboard"])
|
||||
@app.post('/{projectId}/custom_metrics/try/sessions', tags=["customMetrics"])
|
||||
def try_custom_metric_sessions(projectId: int, data: schemas.CustomMetricSessionsPayloadSchema = Body(...),
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = custom_metrics.try_sessions(project_id=projectId, user_id=context.user_id, data=data)
|
||||
return {"data": data}
|
||||
|
||||
|
||||
@app.post('/{projectId}/metrics/try/issues', tags=["dashboard"])
|
||||
@app.post('/{projectId}/custom_metrics/try/issues', tags=["customMetrics"])
|
||||
def try_custom_metric_funnel_issues(projectId: int, data: schemas.CustomMetricSessionsPayloadSchema = Body(...),
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
if len(data.series) == 0:
|
||||
return {"data": []}
|
||||
data.series[0].filter.startDate = data.startTimestamp
|
||||
data.series[0].filter.endDate = data.endTimestamp
|
||||
data = funnels.get_issues_on_the_fly_widget(project_id=projectId, data=data.series[0].filter)
|
||||
return {"data": data}
|
||||
|
||||
|
||||
@app.post('/{projectId}/metrics', tags=["dashboard"])
|
||||
@app.put('/{projectId}/metrics', tags=["dashboard"])
|
||||
@app.post('/{projectId}/custom_metrics', tags=["customMetrics"])
|
||||
@app.put('/{projectId}/custom_metrics', tags=["customMetrics"])
|
||||
def add_custom_metric(projectId: int, data: schemas.CreateCustomMetricsSchema = Body(...),
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
return custom_metrics.create(project_id=projectId, user_id=context.user_id, data=data)
|
||||
|
||||
|
||||
@app.get('/{projectId}/metrics', tags=["dashboard"])
|
||||
@app.get('/{projectId}/custom_metrics', tags=["customMetrics"])
|
||||
def get_custom_metrics(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
|
||||
return {"data": custom_metrics.get_all(project_id=projectId, user_id=context.user_id)}
|
||||
|
||||
|
||||
@app.get('/{projectId}/metrics/{metric_id}', tags=["dashboard"])
|
||||
@app.get('/{projectId}/custom_metrics/{metric_id}', tags=["customMetrics"])
|
||||
def get_custom_metric(projectId: int, metric_id: str, context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = custom_metrics.get(project_id=projectId, user_id=context.user_id, metric_id=metric_id)
|
||||
if data is None:
|
||||
return {"errors": ["custom metric not found"]}
|
||||
return {"data": data}
|
||||
|
||||
|
||||
@app.post('/{projectId}/metrics/{metric_id}/sessions', tags=["dashboard"])
|
||||
@app.post('/{projectId}/custom_metrics/{metric_id}/sessions', tags=["customMetrics"])
|
||||
def get_custom_metric_sessions(projectId: int, metric_id: int,
|
||||
data: schemas.CustomMetricSessionsPayloadSchema = Body(...),
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = custom_metrics.get_sessions(project_id=projectId, user_id=context.user_id, metric_id=metric_id, data=data)
|
||||
if data is None:
|
||||
return {"errors": ["custom metric not found"]}
|
||||
return {"data": data}
|
||||
|
||||
|
||||
@app.post('/{projectId}/metrics/{metric_id}/issues', tags=["dashboard"])
|
||||
@app.post('/{projectId}/custom_metrics/{metric_id}/issues', tags=["customMetrics"])
|
||||
def get_custom_metric_funnel_issues(projectId: int, metric_id: int,
|
||||
data: schemas.CustomMetricSessionsPayloadSchema = Body(...),
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = custom_metrics.get_funnel_issues(project_id=projectId, user_id=context.user_id, metric_id=metric_id,
|
||||
data=data)
|
||||
if data is None:
|
||||
return {"errors": ["custom metric not found"]}
|
||||
return {"data": data}
|
||||
|
||||
|
||||
@app.post('/{projectId}/metrics/{metric_id}/issues/{issueId}/sessions', tags=["dashboard"])
|
||||
@app.post('/{projectId}/custom_metrics/{metric_id}/issues/{issueId}/sessions', tags=["customMetrics"])
|
||||
def get_metric_funnel_issue_sessions(projectId: int, metric_id: int, issueId: str,
|
||||
data: schemas.CustomMetricSessionsPayloadSchema = Body(...),
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = custom_metrics.get_funnel_sessions_by_issue(project_id=projectId, user_id=context.user_id,
|
||||
metric_id=metric_id, issue_id=issueId, data=data)
|
||||
if data is None:
|
||||
return {"errors": ["custom metric not found"]}
|
||||
return {"data": data}
|
||||
|
||||
|
||||
@app.post('/{projectId}/metrics/{metric_id}/errors', tags=["dashboard"])
|
||||
@app.post('/{projectId}/custom_metrics/{metric_id}/errors', tags=["customMetrics"])
|
||||
def get_custom_metric_errors_list(projectId: int, metric_id: int,
|
||||
data: schemas.CustomMetricSessionsPayloadSchema = Body(...),
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = custom_metrics.get_errors_list(project_id=projectId, user_id=context.user_id, metric_id=metric_id,
|
||||
data=data)
|
||||
if data is None:
|
||||
return {"errors": ["custom metric not found"]}
|
||||
return {"data": data}
|
||||
|
||||
|
||||
@app.post('/{projectId}/metrics/{metric_id}/chart', tags=["dashboard"])
|
||||
@app.post('/{projectId}/custom_metrics/{metric_id}/chart', tags=["customMetrics"])
|
||||
def get_custom_metric_chart(projectId: int, metric_id: int, data: schemas.CustomMetricChartPayloadSchema = Body(...),
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = dashboards.make_chart_metrics(project_id=projectId, user_id=context.user_id, metric_id=metric_id,
|
||||
data=data)
|
||||
if data is None:
|
||||
return {"errors": ["custom metric not found"]}
|
||||
return {"data": data}
|
||||
|
||||
|
||||
@app.post('/{projectId}/metrics/{metric_id}', tags=["dashboard"])
|
||||
@app.put('/{projectId}/metrics/{metric_id}', tags=["dashboard"])
|
||||
@app.post('/{projectId}/custom_metrics/{metric_id}', tags=["customMetrics"])
|
||||
@app.put('/{projectId}/custom_metrics/{metric_id}', tags=["customMetrics"])
|
||||
def update_custom_metric(projectId: int, metric_id: int, data: schemas.UpdateCustomMetricsSchema = Body(...),
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = custom_metrics.update(project_id=projectId, user_id=context.user_id, metric_id=metric_id, data=data)
|
||||
if data is None:
|
||||
return {"errors": ["custom metric not found"]}
|
||||
return {"data": data}
|
||||
|
||||
|
||||
@app.post('/{projectId}/metrics/{metric_id}/status', tags=["dashboard"])
|
||||
@app.put('/{projectId}/metrics/{metric_id}/status', tags=["dashboard"])
|
||||
@app.post('/{projectId}/custom_metrics/{metric_id}/status', tags=["customMetrics"])
|
||||
@app.put('/{projectId}/custom_metrics/{metric_id}/status', tags=["customMetrics"])
|
||||
def update_custom_metric_state(projectId: int, metric_id: int,
|
||||
data: schemas.UpdateCustomMetricsStatusSchema = Body(...),
|
||||
context: schemas.CurrentContext = Depends(OR_context)):
|
||||
return {
|
||||
"data": custom_metrics.change_state(project_id=projectId, user_id=context.user_id, metric_id=metric_id,
|
||||
status=data.active)}
|
||||
|
||||
|
||||
@app.delete('/{projectId}/metrics/{metric_id}', tags=["dashboard"])
|
||||
@app.delete('/{projectId}/custom_metrics/{metric_id}', tags=["customMetrics"])
|
||||
def delete_custom_metric(projectId: int, metric_id: int, context: schemas.CurrentContext = Depends(OR_context)):
|
||||
return {"data": custom_metrics.delete(project_id=projectId, user_id=context.user_id, metric_id=metric_id)}
|
||||
|
|
@ -4,12 +4,26 @@ from pydantic import BaseModel, Field, EmailStr
|
|||
|
||||
import schemas
|
||||
from chalicelib.utils.TimeUTC import TimeUTC
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class Permissions(str, Enum):
|
||||
session_replay = "SESSION_REPLAY"
|
||||
dev_tools = "DEV_TOOLS"
|
||||
errors = "ERRORS"
|
||||
metrics = "METRICS"
|
||||
assist_live = "ASSIST_LIVE"
|
||||
assist_call = "ASSIST_CALL"
|
||||
|
||||
|
||||
class CurrentContext(schemas.CurrentContext):
|
||||
permissions: List[Optional[Permissions]] = Field(...)
|
||||
|
||||
|
||||
class RolePayloadSchema(BaseModel):
|
||||
name: str = Field(...)
|
||||
description: Optional[str] = Field(None)
|
||||
permissions: List[str] = Field(...)
|
||||
permissions: List[Permissions] = Field(...)
|
||||
all_projects: bool = Field(True)
|
||||
projects: List[int] = Field([])
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue