Api v1.14.0 (#1391)

* refactored(chalice): refactored restore user
refactored(chalice): refactored add user

* fix(chalice): allow FastAPI to handel async automatically
fix(chalice): EE support of multiworkers
refactor(chalice): cleaned SSO

* fix(chalice): allow FastAPI to handel async automatically

* feat(DB): associate sessions to feature-flags

* feat(DB): CH associate sessions to feature-flags
This commit is contained in:
Kraiem Taha Yassine 2023-07-06 17:13:06 +01:00 committed by GitHub
parent 4a9f451c0e
commit 8226e66299
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 603 additions and 614 deletions

View file

@ -12,7 +12,7 @@ def reset(data: schemas.ForgetPasswordPayloadSchema):
return {"errors": ["no SMTP configuration found, you can ask your admin to reset your password"]}
a_users = users.get_by_email_only(data.email)
if a_users:
invitation_link = users.generate_new_invitation(user_id=a_users["id"])
invitation_link = users.generate_new_invitation(user_id=a_users["userId"])
email_helper.send_forgot_password(recipient=data.email, invitation_link=invitation_link)
else:
print(f"!!!invalid email address [{data.email}]")

View file

@ -29,7 +29,6 @@ def create_new_member(email, invitation_token, admin, name, owner=False):
RETURNING invitation_token
)
SELECT u.user_id,
u.user_id AS id,
u.email,
u.role,
u.name,
@ -52,6 +51,13 @@ def create_new_member(email, invitation_token, admin, name, owner=False):
def restore_member(user_id, email, invitation_token, admin, name, owner=False):
with pg_client.PostgresClient() as cur:
query = cur.mogrify(f"""\
WITH ua AS (UPDATE public.basic_authentication
SET invitation_token = %(invitation_token)s,
invited_at = timezone('utc'::text, now()),
change_pwd_expire_at = NULL,
change_pwd_token = NULL
WHERE user_id=%(user_id)s
RETURNING invitation_token)
UPDATE public.users
SET name= %(name)s,
role = %(role)s,
@ -59,32 +65,24 @@ def restore_member(user_id, email, invitation_token, admin, name, owner=False):
created_at = timezone('utc'::text, now()),
api_key= generate_api_key(20)
WHERE user_id=%(user_id)s
RETURNING user_id AS id,
RETURNING
user_id,
email,
role,
name,
(CASE WHEN role = 'owner' THEN TRUE ELSE FALSE END) AS super_admin,
(CASE WHEN role = 'admin' THEN TRUE ELSE FALSE END) AS admin,
(CASE WHEN role = 'member' THEN TRUE ELSE FALSE END) AS member,
created_at;""",
created_at,
(SELECT invitation_token FROM ua) AS invitation_token;""",
{"user_id": user_id, "email": email,
"role": "owner" if owner else "admin" if admin else "member", "name": name})
"role": "owner" if owner else "admin" if admin else "member",
"name": name, "invitation_token": invitation_token})
cur.execute(query)
result = cur.fetchone()
query = cur.mogrify("""\
UPDATE public.basic_authentication
SET invitation_token = %(invitation_token)s,
invited_at = timezone('utc'::text, now()),
change_pwd_expire_at = NULL,
change_pwd_token = NULL
WHERE user_id=%(user_id)s
RETURNING invitation_token;""",
{"user_id": user_id, "invitation_token": invitation_token})
cur.execute(query)
result["invitation_token"] = cur.fetchone()["invitation_token"]
result["created_at"] = TimeUTC.datetime_to_timestamp(result["created_at"])
return helper.dict_to_camel_case(result)
return helper.dict_to_camel_case(result)
def generate_new_invitation(user_id):
@ -141,7 +139,7 @@ def update(tenant_id, user_id, changes, output=True):
FROM public.basic_authentication
WHERE users.user_id = %(user_id)s
AND users.user_id = basic_authentication.user_id
RETURNING users.user_id AS id,
RETURNING users.user_id,
users.email,
users.role,
users.name,
@ -158,7 +156,7 @@ def update(tenant_id, user_id, changes, output=True):
FROM public.users AS users
WHERE basic_authentication.user_id = %(user_id)s
AND users.user_id = basic_authentication.user_id
RETURNING users.user_id AS id,
RETURNING users.user_id,
users.email,
users.role,
users.name,
@ -328,7 +326,7 @@ def get_by_email_only(email):
cur.execute(
cur.mogrify(
f"""SELECT
users.user_id AS id,
users.user_id,
1 AS tenant_id,
users.email,
users.role,
@ -347,30 +345,6 @@ def get_by_email_only(email):
return helper.dict_to_camel_case(r)
def get_by_email_reset(email, reset_token):
with pg_client.PostgresClient() as cur:
cur.execute(
cur.mogrify(
f"""SELECT
users.user_id AS id,
1 AS tenant_id,
users.email,
users.role,
users.name,
(CASE WHEN users.role = 'owner' THEN TRUE ELSE FALSE END) AS super_admin,
(CASE WHEN users.role = 'admin' THEN TRUE ELSE FALSE END) AS admin,
(CASE WHEN users.role = 'member' THEN TRUE ELSE FALSE END) AS member
FROM public.users LEFT JOIN public.basic_authentication ON users.user_id=basic_authentication.user_id
WHERE
users.email = %(email)s
AND basic_authentication.token =%(token)s
AND users.deleted_at IS NULL""",
{"email": email, "token": reset_token})
)
r = cur.fetchone()
return helper.dict_to_camel_case(r)
def get_member(tenant_id, user_id):
with pg_client.PostgresClient() as cur:
cur.execute(
@ -568,8 +542,12 @@ def get_by_invitation_token(token, pass_token=None):
def auth_exists(user_id, tenant_id, jwt_iat, jwt_aud):
with pg_client.PostgresClient() as cur:
cur.execute(
cur.mogrify(
f"SELECT user_id AS id,jwt_iat, changed_at FROM public.users INNER JOIN public.basic_authentication USING(user_id) WHERE user_id = %(userId)s AND deleted_at IS NULL LIMIT 1;",
cur.mogrify(f"""SELECT user_id,jwt_iat, changed_at
FROM public.users
INNER JOIN public.basic_authentication USING(user_id)
WHERE user_id = %(userId)s
AND deleted_at IS NULL
LIMIT 1;""",
{"userId": user_id})
)
r = cur.fetchone()
@ -584,17 +562,16 @@ def auth_exists(user_id, tenant_id, jwt_iat, jwt_aud):
def change_jwt_iat(user_id):
with pg_client.PostgresClient() as cur:
query = cur.mogrify(
f"""UPDATE public.users
SET jwt_iat = timezone('utc'::text, now())
WHERE user_id = %(user_id)s
RETURNING jwt_iat;""",
query = cur.mogrify(f"""UPDATE public.users
SET jwt_iat = timezone('utc'::text, now())
WHERE user_id = %(user_id)s
RETURNING jwt_iat;""",
{"user_id": user_id})
cur.execute(query)
return cur.fetchone().get("jwt_iat")
def authenticate(email, password, for_change_password=False):
def authenticate(email, password, for_change_password=False) -> dict | None:
with pg_client.PostgresClient() as cur:
query = cur.mogrify(
f"""SELECT

View file

@ -19,26 +19,26 @@ public_app, app, app_apikey = get_routers()
@app.post('/{projectId}/sessions/search', tags=["sessions"])
async def sessions_search(projectId: int, data: schemas.FlatSessionsSearchPayloadSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def sessions_search(projectId: int, data: schemas.FlatSessionsSearchPayloadSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
data = sessions.search_sessions(data=data, project_id=projectId, user_id=context.user_id)
return {'data': data}
@app.post('/{projectId}/sessions/search/ids', tags=["sessions"])
async def session_ids_search(projectId: int, data: schemas.FlatSessionsSearchPayloadSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def session_ids_search(projectId: int, data: schemas.FlatSessionsSearchPayloadSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
data = sessions.search_sessions(data=data, project_id=projectId, user_id=context.user_id, ids_only=True)
return {'data': data}
@app.get('/{projectId}/events/search', tags=["events"])
async def events_search(projectId: int, q: str,
type: Union[schemas.FilterType, schemas.EventType,
schemas.PerformanceEventType, schemas.FetchFilterType,
schemas.GraphqlFilterType, str] = None,
key: str = None, source: str = None, live: bool = False,
context: schemas.CurrentContext = Depends(OR_context)):
def events_search(projectId: int, q: str,
type: Union[schemas.FilterType, schemas.EventType,
schemas.PerformanceEventType, schemas.FetchFilterType,
schemas.GraphqlFilterType, str] = None,
key: str = None, source: str = None, live: bool = False,
context: schemas.CurrentContext = Depends(OR_context)):
if len(q) == 0:
return {"data": []}
if live:
@ -65,7 +65,7 @@ async def events_search(projectId: int, q: str,
@app.get('/{projectId}/integrations', tags=["integrations"])
async def get_integrations_status(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
def get_integrations_status(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
data = integrations_global.get_global_integrations_status(tenant_id=context.tenant_id,
user_id=context.user_id,
project_id=projectId)
@ -73,9 +73,9 @@ async def get_integrations_status(projectId: int, context: schemas.CurrentContex
@app.post('/{projectId}/integrations/{integration}/notify/{webhookId}/{source}/{sourceId}', tags=["integrations"])
async def integration_notify(projectId: int, integration: str, webhookId: int, source: str, sourceId: str,
data: schemas.IntegrationNotificationSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def integration_notify(projectId: int, integration: str, webhookId: int, source: str, sourceId: str,
data: schemas.IntegrationNotificationSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
comment = None
if data.comment:
comment = data.comment
@ -97,222 +97,222 @@ async def integration_notify(projectId: int, integration: str, webhookId: int, s
@app.get('/integrations/sentry', tags=["integrations"])
async def get_all_sentry(context: schemas.CurrentContext = Depends(OR_context)):
def get_all_sentry(context: schemas.CurrentContext = Depends(OR_context)):
return {"data": log_tool_sentry.get_all(tenant_id=context.tenant_id)}
@app.get('/{projectId}/integrations/sentry', tags=["integrations"])
async def get_sentry(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
def get_sentry(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
return {"data": log_tool_sentry.get(project_id=projectId)}
@app.post('/{projectId}/integrations/sentry', tags=["integrations"])
async def add_edit_sentry(projectId: int, data: schemas.SentrySchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def add_edit_sentry(projectId: int, data: schemas.SentrySchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return {"data": log_tool_sentry.add_edit(tenant_id=context.tenant_id, project_id=projectId, data=data.dict())}
@app.delete('/{projectId}/integrations/sentry', tags=["integrations"])
async def delete_sentry(projectId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
def delete_sentry(projectId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
return {"data": log_tool_sentry.delete(tenant_id=context.tenant_id, project_id=projectId)}
@app.get('/{projectId}/integrations/sentry/events/{eventId}', tags=["integrations"])
async def proxy_sentry(projectId: int, eventId: str, context: schemas.CurrentContext = Depends(OR_context)):
def proxy_sentry(projectId: int, eventId: str, context: schemas.CurrentContext = Depends(OR_context)):
return {"data": log_tool_sentry.proxy_get(tenant_id=context.tenant_id, project_id=projectId, event_id=eventId)}
@app.get('/integrations/datadog', tags=["integrations"])
async def get_all_datadog(context: schemas.CurrentContext = Depends(OR_context)):
def get_all_datadog(context: schemas.CurrentContext = Depends(OR_context)):
return {"data": log_tool_datadog.get_all(tenant_id=context.tenant_id)}
@app.get('/{projectId}/integrations/datadog', tags=["integrations"])
async def get_datadog(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
def get_datadog(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
return {"data": log_tool_datadog.get(project_id=projectId)}
@app.post('/{projectId}/integrations/datadog', tags=["integrations"])
async def add_edit_datadog(projectId: int, data: schemas.DatadogSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def add_edit_datadog(projectId: int, data: schemas.DatadogSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return {"data": log_tool_datadog.add_edit(tenant_id=context.tenant_id, project_id=projectId, data=data.dict())}
@app.delete('/{projectId}/integrations/datadog', tags=["integrations"])
async def delete_datadog(projectId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
def delete_datadog(projectId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
return {"data": log_tool_datadog.delete(tenant_id=context.tenant_id, project_id=projectId)}
@app.get('/integrations/stackdriver', tags=["integrations"])
async def get_all_stackdriver(context: schemas.CurrentContext = Depends(OR_context)):
def get_all_stackdriver(context: schemas.CurrentContext = Depends(OR_context)):
return {"data": log_tool_stackdriver.get_all(tenant_id=context.tenant_id)}
@app.get('/{projectId}/integrations/stackdriver', tags=["integrations"])
async def get_stackdriver(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
def get_stackdriver(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
return {"data": log_tool_stackdriver.get(project_id=projectId)}
@app.post('/{projectId}/integrations/stackdriver', tags=["integrations"])
async def add_edit_stackdriver(projectId: int, data: schemas.StackdriverSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def add_edit_stackdriver(projectId: int, data: schemas.StackdriverSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return {"data": log_tool_stackdriver.add_edit(tenant_id=context.tenant_id, project_id=projectId, data=data.dict())}
@app.delete('/{projectId}/integrations/stackdriver', tags=["integrations"])
async def delete_stackdriver(projectId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
def delete_stackdriver(projectId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
return {"data": log_tool_stackdriver.delete(tenant_id=context.tenant_id, project_id=projectId)}
@app.get('/integrations/newrelic', tags=["integrations"])
async def get_all_newrelic(context: schemas.CurrentContext = Depends(OR_context)):
def get_all_newrelic(context: schemas.CurrentContext = Depends(OR_context)):
return {"data": log_tool_newrelic.get_all(tenant_id=context.tenant_id)}
@app.get('/{projectId}/integrations/newrelic', tags=["integrations"])
async def get_newrelic(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
def get_newrelic(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
return {"data": log_tool_newrelic.get(project_id=projectId)}
@app.post('/{projectId}/integrations/newrelic', tags=["integrations"])
async def add_edit_newrelic(projectId: int, data: schemas.NewrelicSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def add_edit_newrelic(projectId: int, data: schemas.NewrelicSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return {"data": log_tool_newrelic.add_edit(tenant_id=context.tenant_id, project_id=projectId, data=data.dict())}
@app.delete('/{projectId}/integrations/newrelic', tags=["integrations"])
async def delete_newrelic(projectId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
def delete_newrelic(projectId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
return {"data": log_tool_newrelic.delete(tenant_id=context.tenant_id, project_id=projectId)}
@app.get('/integrations/rollbar', tags=["integrations"])
async def get_all_rollbar(context: schemas.CurrentContext = Depends(OR_context)):
def get_all_rollbar(context: schemas.CurrentContext = Depends(OR_context)):
return {"data": log_tool_rollbar.get_all(tenant_id=context.tenant_id)}
@app.get('/{projectId}/integrations/rollbar', tags=["integrations"])
async def get_rollbar(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
def get_rollbar(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
return {"data": log_tool_rollbar.get(project_id=projectId)}
@app.post('/{projectId}/integrations/rollbar', tags=["integrations"])
async def add_edit_rollbar(projectId: int, data: schemas.RollbarSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def add_edit_rollbar(projectId: int, data: schemas.RollbarSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return {"data": log_tool_rollbar.add_edit(tenant_id=context.tenant_id, project_id=projectId, data=data.dict())}
@app.delete('/{projectId}/integrations/rollbar', tags=["integrations"])
async def delete_datadog(projectId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
def delete_datadog(projectId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
return {"data": log_tool_rollbar.delete(tenant_id=context.tenant_id, project_id=projectId)}
@app.post('/integrations/bugsnag/list_projects', tags=["integrations"])
async def list_projects_bugsnag(data: schemas.BugsnagBasicSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def list_projects_bugsnag(data: schemas.BugsnagBasicSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return {"data": log_tool_bugsnag.list_projects(auth_token=data.authorizationToken)}
@app.get('/integrations/bugsnag', tags=["integrations"])
async def get_all_bugsnag(context: schemas.CurrentContext = Depends(OR_context)):
def get_all_bugsnag(context: schemas.CurrentContext = Depends(OR_context)):
return {"data": log_tool_bugsnag.get_all(tenant_id=context.tenant_id)}
@app.get('/{projectId}/integrations/bugsnag', tags=["integrations"])
async def get_bugsnag(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
def get_bugsnag(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
return {"data": log_tool_bugsnag.get(project_id=projectId)}
@app.post('/{projectId}/integrations/bugsnag', tags=["integrations"])
async def add_edit_bugsnag(projectId: int, data: schemas.BugsnagSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def add_edit_bugsnag(projectId: int, data: schemas.BugsnagSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return {"data": log_tool_bugsnag.add_edit(tenant_id=context.tenant_id, project_id=projectId, data=data.dict())}
@app.delete('/{projectId}/integrations/bugsnag', tags=["integrations"])
async def delete_bugsnag(projectId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
def delete_bugsnag(projectId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
return {"data": log_tool_bugsnag.delete(tenant_id=context.tenant_id, project_id=projectId)}
@app.post('/integrations/cloudwatch/list_groups', tags=["integrations"])
async def list_groups_cloudwatch(data: schemas.CloudwatchBasicSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def list_groups_cloudwatch(data: schemas.CloudwatchBasicSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return {"data": log_tool_cloudwatch.list_log_groups(aws_access_key_id=data.awsAccessKeyId,
aws_secret_access_key=data.awsSecretAccessKey,
region=data.region)}
@app.get('/integrations/cloudwatch', tags=["integrations"])
async def get_all_cloudwatch(context: schemas.CurrentContext = Depends(OR_context)):
def get_all_cloudwatch(context: schemas.CurrentContext = Depends(OR_context)):
return {"data": log_tool_cloudwatch.get_all(tenant_id=context.tenant_id)}
@app.get('/{projectId}/integrations/cloudwatch', tags=["integrations"])
async def get_cloudwatch(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
def get_cloudwatch(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
return {"data": log_tool_cloudwatch.get(project_id=projectId)}
@app.post('/{projectId}/integrations/cloudwatch', tags=["integrations"])
async def add_edit_cloudwatch(projectId: int, data: schemas.CloudwatchSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def add_edit_cloudwatch(projectId: int, data: schemas.CloudwatchSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return {"data": log_tool_cloudwatch.add_edit(tenant_id=context.tenant_id, project_id=projectId, data=data.dict())}
@app.delete('/{projectId}/integrations/cloudwatch', tags=["integrations"])
async def delete_cloudwatch(projectId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
def delete_cloudwatch(projectId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
return {"data": log_tool_cloudwatch.delete(tenant_id=context.tenant_id, project_id=projectId)}
@app.get('/integrations/elasticsearch', tags=["integrations"])
async def get_all_elasticsearch(context: schemas.CurrentContext = Depends(OR_context)):
def get_all_elasticsearch(context: schemas.CurrentContext = Depends(OR_context)):
return {"data": log_tool_elasticsearch.get_all(tenant_id=context.tenant_id)}
@app.get('/{projectId}/integrations/elasticsearch', tags=["integrations"])
async def get_elasticsearch(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
def get_elasticsearch(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
return {"data": log_tool_elasticsearch.get(project_id=projectId)}
@app.post('/integrations/elasticsearch/test', tags=["integrations"])
async def test_elasticsearch_connection(data: schemas.ElasticsearchBasicSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def test_elasticsearch_connection(data: schemas.ElasticsearchBasicSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return {"data": log_tool_elasticsearch.ping(tenant_id=context.tenant_id, **data.dict())}
@app.post('/{projectId}/integrations/elasticsearch', tags=["integrations"])
async def add_edit_elasticsearch(projectId: int, data: schemas.ElasticsearchSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def add_edit_elasticsearch(projectId: int, data: schemas.ElasticsearchSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return {
"data": log_tool_elasticsearch.add_edit(tenant_id=context.tenant_id, project_id=projectId, data=data.dict())}
@app.delete('/{projectId}/integrations/elasticsearch', tags=["integrations"])
async def delete_elasticsearch(projectId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
def delete_elasticsearch(projectId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
return {"data": log_tool_elasticsearch.delete(tenant_id=context.tenant_id, project_id=projectId)}
@app.get('/integrations/sumologic', tags=["integrations"])
async def get_all_sumologic(context: schemas.CurrentContext = Depends(OR_context)):
def get_all_sumologic(context: schemas.CurrentContext = Depends(OR_context)):
return {"data": log_tool_sumologic.get_all(tenant_id=context.tenant_id)}
@app.get('/{projectId}/integrations/sumologic', tags=["integrations"])
async def get_sumologic(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
def get_sumologic(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
return {"data": log_tool_sumologic.get(project_id=projectId)}
@app.post('/{projectId}/integrations/sumologic', tags=["integrations"])
async def add_edit_sumologic(projectId: int, data: schemas.SumologicSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def add_edit_sumologic(projectId: int, data: schemas.SumologicSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return {"data": log_tool_sumologic.add_edit(tenant_id=context.tenant_id, project_id=projectId, data=data.dict())}
@app.delete('/{projectId}/integrations/sumologic', tags=["integrations"])
async def delete_sumologic(projectId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
def delete_sumologic(projectId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
return {"data": log_tool_sumologic.delete(tenant_id=context.tenant_id, project_id=projectId)}
@app.get('/integrations/issues', tags=["integrations"])
async def get_integration_status(context: schemas.CurrentContext = Depends(OR_context)):
def get_integration_status(context: schemas.CurrentContext = Depends(OR_context)):
error, integration = integrations_manager.get_integration(tenant_id=context.tenant_id,
user_id=context.user_id)
if error is not None and integration is None:
@ -321,7 +321,7 @@ async def get_integration_status(context: schemas.CurrentContext = Depends(OR_co
@app.get('/integrations/jira', tags=["integrations"])
async def get_integration_status_jira(context: schemas.CurrentContext = Depends(OR_context)):
def get_integration_status_jira(context: schemas.CurrentContext = Depends(OR_context)):
error, integration = integrations_manager.get_integration(tenant_id=context.tenant_id,
user_id=context.user_id,
tool=integration_jira_cloud.PROVIDER)
@ -331,7 +331,7 @@ async def get_integration_status_jira(context: schemas.CurrentContext = Depends(
@app.get('/integrations/github', tags=["integrations"])
async def get_integration_status_github(context: schemas.CurrentContext = Depends(OR_context)):
def get_integration_status_github(context: schemas.CurrentContext = Depends(OR_context)):
error, integration = integrations_manager.get_integration(tenant_id=context.tenant_id,
user_id=context.user_id,
tool=integration_github.PROVIDER)
@ -341,8 +341,8 @@ async def get_integration_status_github(context: schemas.CurrentContext = Depend
@app.post('/integrations/jira', tags=["integrations"])
async def add_edit_jira_cloud(data: schemas.JiraSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def add_edit_jira_cloud(data: schemas.JiraSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
if not data.url.endswith('atlassian.net'):
return {"errors": ["url must be a valid JIRA URL (example.atlassian.net)"]}
error, integration = integrations_manager.get_integration(tool=integration_jira_cloud.PROVIDER,
@ -354,8 +354,8 @@ async def add_edit_jira_cloud(data: schemas.JiraSchema = Body(...),
@app.post('/integrations/github', tags=["integrations"])
async def add_edit_github(data: schemas.GithubSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def add_edit_github(data: schemas.GithubSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
error, integration = integrations_manager.get_integration(tool=integration_github.PROVIDER,
tenant_id=context.tenant_id,
user_id=context.user_id)
@ -365,7 +365,7 @@ async def add_edit_github(data: schemas.GithubSchema = Body(...),
@app.delete('/integrations/issues', tags=["integrations"])
async def delete_default_issue_tracking_tool(_=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
def delete_default_issue_tracking_tool(_=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
error, integration = integrations_manager.get_integration(tenant_id=context.tenant_id,
user_id=context.user_id)
if error is not None and integration is None:
@ -374,7 +374,7 @@ async def delete_default_issue_tracking_tool(_=Body(None), context: schemas.Curr
@app.delete('/integrations/jira', tags=["integrations"])
async def delete_jira_cloud(_=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
def delete_jira_cloud(_=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
error, integration = integrations_manager.get_integration(tool=integration_jira_cloud.PROVIDER,
tenant_id=context.tenant_id,
user_id=context.user_id,
@ -385,7 +385,7 @@ async def delete_jira_cloud(_=Body(None), context: schemas.CurrentContext = Depe
@app.delete('/integrations/github', tags=["integrations"])
async def delete_github(_=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
def delete_github(_=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
error, integration = integrations_manager.get_integration(tool=integration_github.PROVIDER,
tenant_id=context.tenant_id,
user_id=context.user_id,
@ -396,7 +396,7 @@ async def delete_github(_=Body(None), context: schemas.CurrentContext = Depends(
@app.get('/integrations/issues/list_projects', tags=["integrations"])
async def get_all_issue_tracking_projects(context: schemas.CurrentContext = Depends(OR_context)):
def get_all_issue_tracking_projects(context: schemas.CurrentContext = Depends(OR_context)):
error, integration = integrations_manager.get_integration(tenant_id=context.tenant_id,
user_id=context.user_id)
if error is not None:
@ -408,7 +408,7 @@ async def get_all_issue_tracking_projects(context: schemas.CurrentContext = Depe
@app.get('/integrations/issues/{integrationProjectId}', tags=["integrations"])
async def get_integration_metadata(integrationProjectId: int, context: schemas.CurrentContext = Depends(OR_context)):
def get_integration_metadata(integrationProjectId: int, context: schemas.CurrentContext = Depends(OR_context)):
error, integration = integrations_manager.get_integration(tenant_id=context.tenant_id,
user_id=context.user_id)
if error is not None:
@ -420,7 +420,7 @@ async def get_integration_metadata(integrationProjectId: int, context: schemas.C
@app.get('/{projectId}/assignments', tags=["assignment"])
async def get_all_assignments(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
def get_all_assignments(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
data = sessions_assignments.get_all(project_id=projectId, user_id=context.user_id)
return {
'data': data
@ -428,9 +428,9 @@ async def get_all_assignments(projectId: int, context: schemas.CurrentContext =
@app.post('/{projectId}/sessions/{sessionId}/assign/projects/{integrationProjectId}', tags=["assignment"])
async def create_issue_assignment(projectId: int, sessionId: int, integrationProjectId,
data: schemas.AssignmentSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def create_issue_assignment(projectId: int, sessionId: int, integrationProjectId,
data: schemas.AssignmentSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
data = sessions_assignments.create_new_assignment(tenant_id=context.tenant_id, project_id=projectId,
session_id=sessionId,
creator_id=context.user_id, assignee=data.assignee,
@ -445,13 +445,13 @@ async def create_issue_assignment(projectId: int, sessionId: int, integrationPro
@app.get('/{projectId}/gdpr', tags=["projects", "gdpr"])
async def get_gdpr(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
def get_gdpr(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
return {"data": projects.get_gdpr(project_id=projectId)}
@app.post('/{projectId}/gdpr', tags=["projects", "gdpr"])
async def edit_gdpr(projectId: int, data: schemas.GdprSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def edit_gdpr(projectId: int, data: schemas.GdprSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
result = projects.edit_gdpr(project_id=projectId, gdpr=data.dict())
if "errors" in result:
return result
@ -459,44 +459,44 @@ async def edit_gdpr(projectId: int, data: schemas.GdprSchema = Body(...),
@public_app.post('/password/reset-link', tags=["reset password"])
async def reset_password_handler(data: schemas.ForgetPasswordPayloadSchema = Body(...)):
def reset_password_handler(data: schemas.ForgetPasswordPayloadSchema = Body(...)):
if len(data.email) < 5:
return {"errors": ["please provide a valid email address"]}
return reset_password.reset(data=data)
@app.get('/{projectId}/metadata', tags=["metadata"])
async def get_metadata(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
def get_metadata(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
return {"data": metadata.get(project_id=projectId)}
@app.post('/{projectId}/metadata/list', tags=["metadata"])
async def add_edit_delete_metadata(projectId: int, data: schemas.MetadataListSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def add_edit_delete_metadata(projectId: int, data: schemas.MetadataListSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return metadata.add_edit_delete(tenant_id=context.tenant_id, project_id=projectId, new_metas=data.list)
@app.post('/{projectId}/metadata', tags=["metadata"])
async def add_metadata(projectId: int, data: schemas.MetadataBasicSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def add_metadata(projectId: int, data: schemas.MetadataBasicSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return metadata.add(tenant_id=context.tenant_id, project_id=projectId, new_name=data.key)
@app.post('/{projectId}/metadata/{index}', tags=["metadata"])
async def edit_metadata(projectId: int, index: int, data: schemas.MetadataBasicSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def edit_metadata(projectId: int, index: int, data: schemas.MetadataBasicSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return metadata.edit(tenant_id=context.tenant_id, project_id=projectId, index=index,
new_name=data.key)
@app.delete('/{projectId}/metadata/{index}', tags=["metadata"])
async def delete_metadata(projectId: int, index: int, _=Body(None),
context: schemas.CurrentContext = Depends(OR_context)):
def delete_metadata(projectId: int, index: int, _=Body(None),
context: schemas.CurrentContext = Depends(OR_context)):
return metadata.delete(tenant_id=context.tenant_id, project_id=projectId, index=index)
@app.get('/{projectId}/metadata/search', tags=["metadata"])
async def search_metadata(projectId: int, q: str, key: str, context: schemas.CurrentContext = Depends(OR_context)):
def search_metadata(projectId: int, q: str, key: str, context: schemas.CurrentContext = Depends(OR_context)):
if len(q) == 0 and len(key) == 0:
return {"data": []}
if len(q) == 0:
@ -507,74 +507,74 @@ async def search_metadata(projectId: int, q: str, key: str, context: schemas.Cur
@app.get('/{projectId}/integration/sources', tags=["integrations"])
async def search_integrations(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
def search_integrations(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
return log_tools.search(project_id=projectId)
@app.get('/{projectId}/sample_rate', tags=["projects"])
async def get_capture_status(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
def get_capture_status(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
return {"data": projects.get_capture_status(project_id=projectId)}
@app.post('/{projectId}/sample_rate', tags=["projects"])
async def update_capture_status(projectId: int, data: schemas.SampleRateSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def update_capture_status(projectId: int, data: schemas.SampleRateSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return {"data": projects.update_capture_status(project_id=projectId, changes=data.dict())}
@app.get('/announcements', tags=["announcements"])
async def get_all_announcements(context: schemas.CurrentContext = Depends(OR_context)):
def get_all_announcements(context: schemas.CurrentContext = Depends(OR_context)):
return {"data": announcements.get_all(user_id=context.user_id)}
@app.get('/announcements/view', tags=["announcements"])
async def get_all_announcements(context: schemas.CurrentContext = Depends(OR_context)):
def get_all_announcements(context: schemas.CurrentContext = Depends(OR_context)):
return {"data": announcements.view(user_id=context.user_id)}
@app.get('/show_banner', tags=["banner"])
async def errors_merge(context: schemas.CurrentContext = Depends(OR_context)):
def errors_merge(context: schemas.CurrentContext = Depends(OR_context)):
return {"data": False}
@app.post('/{projectId}/alerts', tags=["alerts"])
async def create_alert(projectId: int, data: schemas.AlertSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def create_alert(projectId: int, data: schemas.AlertSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return alerts.create(project_id=projectId, data=data)
@app.get('/{projectId}/alerts', tags=["alerts"])
async def get_all_alerts(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
def get_all_alerts(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
return {"data": alerts.get_all(project_id=projectId)}
@app.get('/{projectId}/alerts/triggers', tags=["alerts", "customMetrics"])
async def get_alerts_triggers(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
def get_alerts_triggers(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
return {"data": alerts.get_predefined_values() \
+ custom_metrics.get_series_for_alert(project_id=projectId, user_id=context.user_id)}
@app.get('/{projectId}/alerts/{alertId}', tags=["alerts"])
async def get_alert(projectId: int, alertId: int, context: schemas.CurrentContext = Depends(OR_context)):
def get_alert(projectId: int, alertId: int, context: schemas.CurrentContext = Depends(OR_context)):
return {"data": alerts.get(id=alertId)}
@app.post('/{projectId}/alerts/{alertId}', tags=["alerts"])
async def update_alert(projectId: int, alertId: int, data: schemas.AlertSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def update_alert(projectId: int, alertId: int, data: schemas.AlertSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return alerts.update(id=alertId, data=data)
@app.delete('/{projectId}/alerts/{alertId}', tags=["alerts"])
async def delete_alert(projectId: int, alertId: int, _=Body(None),
context: schemas.CurrentContext = Depends(OR_context)):
def delete_alert(projectId: int, alertId: int, _=Body(None),
context: schemas.CurrentContext = Depends(OR_context)):
return alerts.delete(project_id=projectId, alert_id=alertId)
@app_apikey.put('/{projectKey}/sourcemaps/', tags=["sourcemaps"])
@app_apikey.put('/{projectKey}/sourcemaps', tags=["sourcemaps"])
async def sign_sourcemap_for_upload(projectKey: str, data: schemas.SourcemapUploadPayloadSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def sign_sourcemap_for_upload(projectKey: str, data: schemas.SourcemapUploadPayloadSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
project_id = projects.get_internal_project_id(projectKey)
if project_id is None:
return {"errors": ["Project not found."]}
@ -583,53 +583,53 @@ async def sign_sourcemap_for_upload(projectKey: str, data: schemas.SourcemapUplo
@app.get('/config/weekly_report', tags=["weekly report config"])
async def get_weekly_report_config(context: schemas.CurrentContext = Depends(OR_context)):
def get_weekly_report_config(context: schemas.CurrentContext = Depends(OR_context)):
return {"data": weekly_report.get_config(user_id=context.user_id)}
@app.post('/config/weekly_report', tags=["weekly report config"])
async def edit_weekly_report_config(data: schemas.WeeklyReportConfigSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def edit_weekly_report_config(data: schemas.WeeklyReportConfigSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return {"data": weekly_report.edit_config(user_id=context.user_id, weekly_report=data.weekly_report)}
@app.get('/{projectId}/issue_types', tags=["issues"])
async def issue_types(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
def issue_types(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
return {"data": issues.get_all_types()}
@app.get('/issue_types', tags=["issues"])
async def all_issue_types(context: schemas.CurrentContext = Depends(OR_context)):
def all_issue_types(context: schemas.CurrentContext = Depends(OR_context)):
return {"data": issues.get_all_types()}
@app.get('/{projectId}/assist/sessions', tags=["assist"])
async def get_sessions_live(projectId: int, userId: str = None, context: schemas.CurrentContext = Depends(OR_context)):
def get_sessions_live(projectId: int, userId: str = None, context: schemas.CurrentContext = Depends(OR_context)):
data = assist.get_live_sessions_ws_user_id(projectId, user_id=userId)
return {'data': data}
@app.post('/{projectId}/assist/sessions', tags=["assist"])
async def sessions_live(projectId: int, data: schemas.LiveSessionsSearchPayloadSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def sessions_live(projectId: int, data: schemas.LiveSessionsSearchPayloadSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
data = assist.get_live_sessions_ws(projectId, body=data)
return {'data': data}
@app.post('/{projectId}/mobile/{sessionId}/urls', tags=['mobile'])
async def mobile_signe(projectId: int, sessionId: int, data: schemas.MobileSignPayloadSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def mobile_signe(projectId: int, sessionId: int, data: schemas.MobileSignPayloadSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return {"data": mobile.sign_keys(project_id=projectId, session_id=sessionId, keys=data.keys)}
@app.post('/projects', tags=['projects'])
async def create_project(data: schemas.CreateProjectSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def create_project(data: schemas.CreateProjectSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return projects.create(tenant_id=context.tenant_id, user_id=context.user_id, data=data)
@app.get('/projects/{projectId}', tags=['projects'])
async def get_project(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
def get_project(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
data = projects.get_project(tenant_id=context.tenant_id, project_id=projectId, include_last_session=True,
include_gdpr=True)
if data is None:
@ -638,41 +638,41 @@ async def get_project(projectId: int, context: schemas.CurrentContext = Depends(
@app.put('/projects/{projectId}', tags=['projects'])
async def edit_project(projectId: int, data: schemas.CreateProjectSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def edit_project(projectId: int, data: schemas.CreateProjectSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return projects.edit(tenant_id=context.tenant_id, user_id=context.user_id, data=data, project_id=projectId)
@app.delete('/projects/{projectId}', tags=['projects'])
async def delete_project(projectId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
def delete_project(projectId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
return projects.delete(tenant_id=context.tenant_id, user_id=context.user_id, project_id=projectId)
@app.get('/client/new_api_key', tags=['client'])
async def generate_new_tenant_token(context: schemas.CurrentContext = Depends(OR_context)):
def generate_new_tenant_token(context: schemas.CurrentContext = Depends(OR_context)):
return {
'data': tenants.generate_new_api_key(context.tenant_id)
}
@app.get('/notifications', tags=['notifications'])
async def get_notifications(context: schemas.CurrentContext = Depends(OR_context)):
def get_notifications(context: schemas.CurrentContext = Depends(OR_context)):
return {"data": notifications.get_all(tenant_id=context.tenant_id, user_id=context.user_id)}
@app.get('/notifications/count', tags=['notifications'])
async def get_notifications_count(context: schemas.CurrentContext = Depends(OR_context)):
def get_notifications_count(context: schemas.CurrentContext = Depends(OR_context)):
return {"data": notifications.get_all_count(tenant_id=context.tenant_id, user_id=context.user_id)}
@app.get('/notifications/{notificationId}/view', tags=['notifications'])
async def view_notifications(notificationId: int, context: schemas.CurrentContext = Depends(OR_context)):
def view_notifications(notificationId: int, context: schemas.CurrentContext = Depends(OR_context)):
return {"data": notifications.view_notification(notification_ids=[notificationId], user_id=context.user_id)}
@app.post('/notifications/view', tags=['notifications'])
async def batch_view_notifications(data: schemas.NotificationsViewSchema,
context: schemas.CurrentContext = Depends(OR_context)):
def batch_view_notifications(data: schemas.NotificationsViewSchema,
context: schemas.CurrentContext = Depends(OR_context)):
return {"data": notifications.view_notification(notification_ids=data.ids,
startTimestamp=data.startTimestamp,
endTimestamp=data.endTimestamp,
@ -681,119 +681,119 @@ async def batch_view_notifications(data: schemas.NotificationsViewSchema,
@app.get('/boarding', tags=['boarding'])
async def get_boarding_state(context: schemas.CurrentContext = Depends(OR_context)):
def get_boarding_state(context: schemas.CurrentContext = Depends(OR_context)):
return {"data": boarding.get_state(tenant_id=context.tenant_id)}
@app.get('/boarding/installing', tags=['boarding'])
async def get_boarding_state_installing(context: schemas.CurrentContext = Depends(OR_context)):
def get_boarding_state_installing(context: schemas.CurrentContext = Depends(OR_context)):
return {"data": boarding.get_state_installing(tenant_id=context.tenant_id)}
@app.get('/boarding/identify-users', tags=["boarding"])
async def get_boarding_state_identify_users(context: schemas.CurrentContext = Depends(OR_context)):
def get_boarding_state_identify_users(context: schemas.CurrentContext = Depends(OR_context)):
return {"data": boarding.get_state_identify_users(tenant_id=context.tenant_id)}
@app.get('/boarding/manage-users', tags=["boarding"])
async def get_boarding_state_manage_users(context: schemas.CurrentContext = Depends(OR_context)):
def get_boarding_state_manage_users(context: schemas.CurrentContext = Depends(OR_context)):
return {"data": boarding.get_state_manage_users(tenant_id=context.tenant_id)}
@app.get('/boarding/integrations', tags=["boarding"])
async def get_boarding_state_integrations(context: schemas.CurrentContext = Depends(OR_context)):
def get_boarding_state_integrations(context: schemas.CurrentContext = Depends(OR_context)):
return {"data": boarding.get_state_integrations(tenant_id=context.tenant_id)}
@app.get('/integrations/slack/channels', tags=["integrations"])
async def get_slack_channels(context: schemas.CurrentContext = Depends(OR_context)):
def get_slack_channels(context: schemas.CurrentContext = Depends(OR_context)):
return {"data": webhook.get_by_type(tenant_id=context.tenant_id, webhook_type=schemas.WebhookType.slack)}
@app.get('/integrations/slack/{webhookId}', tags=["integrations"])
async def get_slack_webhook(webhookId: int, context: schemas.CurrentContext = Depends(OR_context)):
def get_slack_webhook(webhookId: int, context: schemas.CurrentContext = Depends(OR_context)):
return {"data": Slack.get_integration(tenant_id=context.tenant_id, integration_id=webhookId)}
@app.delete('/integrations/slack/{webhookId}', tags=["integrations"])
async def delete_slack_integration(webhookId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
def delete_slack_integration(webhookId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
return webhook.delete(tenant_id=context.tenant_id, webhook_id=webhookId)
@app.put('/webhooks', tags=["webhooks"])
async def add_edit_webhook(data: schemas.CreateEditWebhookSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def add_edit_webhook(data: schemas.CreateEditWebhookSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return {"data": webhook.add_edit(tenant_id=context.tenant_id, data=data.dict(), replace_none=True)}
@app.get('/webhooks', tags=["webhooks"])
async def get_webhooks(context: schemas.CurrentContext = Depends(OR_context)):
def get_webhooks(context: schemas.CurrentContext = Depends(OR_context)):
return {"data": webhook.get_by_tenant(tenant_id=context.tenant_id, replace_none=True)}
@app.delete('/webhooks/{webhookId}', tags=["webhooks"])
async def delete_webhook(webhookId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
def delete_webhook(webhookId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
return {"data": webhook.delete(tenant_id=context.tenant_id, webhook_id=webhookId)}
@app.get('/client/members', tags=["client"])
async def get_members(context: schemas.CurrentContext = Depends(OR_context)):
def get_members(context: schemas.CurrentContext = Depends(OR_context)):
return {"data": users.get_members(tenant_id=context.tenant_id)}
@app.get('/client/members/{memberId}/reset', tags=["client"])
async def reset_reinvite_member(memberId: int, context: schemas.CurrentContext = Depends(OR_context)):
def reset_reinvite_member(memberId: int, context: schemas.CurrentContext = Depends(OR_context)):
return users.reset_member(tenant_id=context.tenant_id, editor_id=context.user_id, user_id_to_update=memberId)
@app.delete('/client/members/{memberId}', tags=["client"])
async def delete_member(memberId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
def delete_member(memberId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
return users.delete_member(tenant_id=context.tenant_id, user_id=context.user_id, id_to_delete=memberId)
@app.get('/account/new_api_key', tags=["account"])
async def generate_new_user_token(context: schemas.CurrentContext = Depends(OR_context)):
def generate_new_user_token(context: schemas.CurrentContext = Depends(OR_context)):
return {"data": users.generate_new_api_key(user_id=context.user_id)}
@app.post('/account/password', tags=["account"])
async def change_client_password(data: schemas.EditUserPasswordSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def change_client_password(data: schemas.EditUserPasswordSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return users.change_password(email=context.email, old_password=data.old_password,
new_password=data.new_password, tenant_id=context.tenant_id,
user_id=context.user_id)
@app.post('/{projectId}/saved_search', tags=["savedSearch"])
async def add_saved_search(projectId: int, data: schemas.SavedSearchSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def add_saved_search(projectId: int, data: schemas.SavedSearchSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return saved_search.create(project_id=projectId, user_id=context.user_id, data=data)
@app.get('/{projectId}/saved_search', tags=["savedSearch"])
async def get_saved_searches(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
def get_saved_searches(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
return {"data": saved_search.get_all(project_id=projectId, user_id=context.user_id, details=True)}
@app.get('/{projectId}/saved_search/{search_id}', tags=["savedSearch"])
async def get_saved_search(projectId: int, search_id: int, context: schemas.CurrentContext = Depends(OR_context)):
def get_saved_search(projectId: int, search_id: int, context: schemas.CurrentContext = Depends(OR_context)):
return {"data": saved_search.get(project_id=projectId, search_id=search_id, user_id=context.user_id)}
@app.post('/{projectId}/saved_search/{search_id}', tags=["savedSearch"])
async def update_saved_search(projectId: int, search_id: int, data: schemas.SavedSearchSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def update_saved_search(projectId: int, search_id: int, data: schemas.SavedSearchSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return {"data": saved_search.update(user_id=context.user_id, search_id=search_id, data=data, project_id=projectId)}
@app.delete('/{projectId}/saved_search/{search_id}', tags=["savedSearch"])
async def delete_saved_search(projectId: int, search_id: int, _=Body(None),
context: schemas.CurrentContext = Depends(OR_context)):
def delete_saved_search(projectId: int, search_id: int, _=Body(None),
context: schemas.CurrentContext = Depends(OR_context)):
return {"data": saved_search.delete(project_id=projectId, user_id=context.user_id, search_id=search_id)}
@app.get('/limits', tags=['accounts'])
async def get_limits(context: schemas.CurrentContext = Depends(OR_context)):
def get_limits(context: schemas.CurrentContext = Depends(OR_context)):
return {
'data': {
"teamMember": -1,
@ -803,13 +803,13 @@ async def get_limits(context: schemas.CurrentContext = Depends(OR_context)):
@app.get('/integrations/msteams/channels', tags=["integrations"])
async def get_msteams_channels(context: schemas.CurrentContext = Depends(OR_context)):
def get_msteams_channels(context: schemas.CurrentContext = Depends(OR_context)):
return {"data": webhook.get_by_type(tenant_id=context.tenant_id, webhook_type=schemas.WebhookType.msteams)}
@app.post('/integrations/msteams', tags=['integrations'])
async def add_msteams_integration(data: schemas.AddCollaborationSchema,
context: schemas.CurrentContext = Depends(OR_context)):
def add_msteams_integration(data: schemas.AddCollaborationSchema,
context: schemas.CurrentContext = Depends(OR_context)):
n = MSTeams.add(tenant_id=context.tenant_id, data=data)
if n is None:
return {
@ -820,8 +820,8 @@ async def add_msteams_integration(data: schemas.AddCollaborationSchema,
@app.post('/integrations/msteams/{webhookId}', tags=['integrations'])
async def edit_msteams_integration(webhookId: int, data: schemas.EditCollaborationSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def edit_msteams_integration(webhookId: int, data: schemas.EditCollaborationSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
if len(data.url) > 0:
old = MSTeams.get_integration(tenant_id=context.tenant_id, integration_id=webhookId)
if not old:
@ -837,8 +837,8 @@ async def edit_msteams_integration(webhookId: int, data: schemas.EditCollaborati
@app.delete('/integrations/msteams/{webhookId}', tags=["integrations"])
async def delete_msteams_integration(webhookId: int, _=Body(None),
context: schemas.CurrentContext = Depends(OR_context)):
def delete_msteams_integration(webhookId: int, _=Body(None),
context: schemas.CurrentContext = Depends(OR_context)):
return webhook.delete(tenant_id=context.tenant_id, webhook_id=webhookId)
@ -865,5 +865,5 @@ async def check_recording_status(project_id: int):
@public_app.get('/', tags=["health"])
async def health_check():
def health_check():
return {}

View file

@ -22,7 +22,7 @@ public_app, app, app_apikey = get_routers()
@public_app.get('/signup', tags=['signup'])
async def get_all_signup():
def get_all_signup():
return {"data": {"tenants": tenants.tenants_exists(),
"sso": None,
"ssoProvider": None,
@ -33,12 +33,12 @@ async def get_all_signup():
if not tenants.tenants_exists(use_pool=False):
@public_app.post('/signup', tags=['signup'])
@public_app.put('/signup', tags=['signup'])
async def signup_handler(data: schemas.UserSignupSchema = Body(...)):
def signup_handler(data: schemas.UserSignupSchema = Body(...)):
return signup.create_tenant(data)
@public_app.post('/login', tags=["authentication"])
async def login_user(data: schemas.UserLoginSchema = Body(...)):
def login_user(data: schemas.UserLoginSchema = Body(...)):
if helper.allow_captcha() and not captcha.is_valid(data.g_recaptcha_response):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
@ -69,12 +69,12 @@ async def login_user(data: schemas.UserLoginSchema = Body(...)):
@app.get('/logout', tags=["login", "logout"])
async def logout_user(context: schemas.CurrentContext = Depends(OR_context)):
def logout_user(context: schemas.CurrentContext = Depends(OR_context)):
return {"data": "success"}
@app.get('/account', tags=['accounts'])
async def get_account(context: schemas.CurrentContext = Depends(OR_context)):
def get_account(context: schemas.CurrentContext = Depends(OR_context)):
r = users.get(tenant_id=context.tenant_id, user_id=context.user_id)
t = tenants.get_by_tenant_id(context.tenant_id)
if t is not None:
@ -92,15 +92,15 @@ async def get_account(context: schemas.CurrentContext = Depends(OR_context)):
@app.post('/account', tags=["account"])
async def edit_account(data: schemas.EditAccountSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def edit_account(data: schemas.EditAccountSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return users.edit_account(tenant_id=context.tenant_id, user_id=context.user_id, changes=data)
@app.post('/integrations/slack', tags=['integrations'])
@app.put('/integrations/slack', tags=['integrations'])
async def add_slack_integration(data: schemas.AddCollaborationSchema,
context: schemas.CurrentContext = Depends(OR_context)):
def add_slack_integration(data: schemas.AddCollaborationSchema,
context: schemas.CurrentContext = Depends(OR_context)):
n = Slack.add(tenant_id=context.tenant_id, data=data)
if n is None:
return {
@ -110,8 +110,8 @@ async def add_slack_integration(data: schemas.AddCollaborationSchema,
@app.post('/integrations/slack/{integrationId}', tags=['integrations'])
async def edit_slack_integration(integrationId: int, data: schemas.EditCollaborationSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def edit_slack_integration(integrationId: int, data: schemas.EditCollaborationSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
if len(data.url) > 0:
old = Slack.get_integration(tenant_id=context.tenant_id, integration_id=integrationId)
if not old:
@ -127,14 +127,14 @@ async def edit_slack_integration(integrationId: int, data: schemas.EditCollabora
@app.post('/client/members', tags=["client"])
async def add_member(background_tasks: BackgroundTasks, data: schemas.CreateMemberSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def add_member(background_tasks: BackgroundTasks, data: schemas.CreateMemberSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return users.create_member(tenant_id=context.tenant_id, user_id=context.user_id, data=data.dict(),
background_tasks=background_tasks)
@public_app.get('/users/invitation', tags=['users'])
async def process_invitation_link(token: str):
def process_invitation_link(token: str):
if token is None or len(token) < 64:
return {"errors": ["please provide a valid invitation"]}
user = users.get_by_invitation_token(token)
@ -151,7 +151,7 @@ async def process_invitation_link(token: str):
@public_app.post('/password/reset', tags=["users"])
async def change_password_by_invitation(data: schemas.EditPasswordByInvitationSchema = Body(...)):
def change_password_by_invitation(data: schemas.EditPasswordByInvitationSchema = Body(...)):
if data is None or len(data.invitation) < 64 or len(data.passphrase) < 8:
return {"errors": ["please provide a valid invitation & pass"]}
user = users.get_by_invitation_token(token=data.invitation, pass_token=data.passphrase)
@ -164,15 +164,15 @@ async def change_password_by_invitation(data: schemas.EditPasswordByInvitationSc
@app.put('/client/members/{memberId}', tags=["client"])
async def edit_member(memberId: int, data: schemas.EditMemberSchema,
context: schemas.CurrentContext = Depends(OR_context)):
def edit_member(memberId: int, data: schemas.EditMemberSchema,
context: schemas.CurrentContext = Depends(OR_context)):
return users.edit_member(tenant_id=context.tenant_id, editor_id=context.user_id, changes=data,
user_id_to_update=memberId)
@app.get('/metadata/session_search', tags=["metadata"])
async def search_sessions_by_metadata(key: str, value: str, projectId: Optional[int] = None,
context: schemas.CurrentContext = Depends(OR_context)):
def search_sessions_by_metadata(key: str, value: str, projectId: Optional[int] = None,
context: schemas.CurrentContext = Depends(OR_context)):
if key is None or value is None or len(value) == 0 and len(key) == 0:
return {"errors": ["please provide a key&value for search"]}
if len(value) == 0:
@ -185,14 +185,14 @@ async def search_sessions_by_metadata(key: str, value: str, projectId: Optional[
@app.get('/projects', tags=['projects'])
async def get_projects(context: schemas.CurrentContext = Depends(OR_context)):
def get_projects(context: schemas.CurrentContext = Depends(OR_context)):
return {"data": projects.get_projects(tenant_id=context.tenant_id, gdpr=True, recorded=True)}
# for backward compatibility
@app.get('/{projectId}/sessions/{sessionId}', tags=["sessions", "replay"])
async def get_session(projectId: int, sessionId: Union[int, str], background_tasks: BackgroundTasks,
context: schemas.CurrentContext = Depends(OR_context)):
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_replay.get_by_id2_pg(project_id=projectId, session_id=sessionId, full_data=True,
@ -208,8 +208,8 @@ async def get_session(projectId: int, sessionId: Union[int, str], background_tas
@app.get('/{projectId}/sessions/{sessionId}/replay', tags=["sessions", "replay"])
async def get_session_events(projectId: int, sessionId: Union[int, str], background_tasks: BackgroundTasks,
context: schemas.CurrentContext = Depends(OR_context)):
def get_session_events(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_replay.get_replay(project_id=projectId, session_id=sessionId, full_data=True,
@ -225,8 +225,8 @@ async def get_session_events(projectId: int, sessionId: Union[int, str], backgro
@app.get('/{projectId}/sessions/{sessionId}/events', tags=["sessions", "replay"])
async def get_session_events(projectId: int, sessionId: Union[int, str],
context: schemas.CurrentContext = Depends(OR_context)):
def get_session_events(projectId: int, sessionId: Union[int, str],
context: schemas.CurrentContext = Depends(OR_context)):
if isinstance(sessionId, str):
return {"errors": ["session not found"]}
data = sessions_replay.get_events(project_id=projectId, session_id=sessionId)
@ -239,8 +239,8 @@ async def get_session_events(projectId: int, sessionId: Union[int, str],
@app.get('/{projectId}/sessions/{sessionId}/errors/{errorId}/sourcemaps', tags=["sessions", "sourcemaps"])
async def get_error_trace(projectId: int, sessionId: int, errorId: str,
context: schemas.CurrentContext = Depends(OR_context)):
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
@ -250,20 +250,20 @@ async def get_error_trace(projectId: int, sessionId: int, errorId: str,
@app.post('/{projectId}/errors/search', tags=['errors'])
async def errors_search(projectId: int, data: schemas.SearchErrorsSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
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'])
async def errors_stats(projectId: int, startTimestamp: int, endTimestamp: int,
context: schemas.CurrentContext = Depends(OR_context)):
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'])
async def errors_get_details(projectId: int, errorId: str, background_tasks: BackgroundTasks, density24: int = 24,
density30: int = 30, context: schemas.CurrentContext = Depends(OR_context)):
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:
@ -273,17 +273,17 @@ async def errors_get_details(projectId: int, errorId: str, background_tasks: Bac
@app.get('/{projectId}/errors/{errorId}/stats', tags=['errors'])
async 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)):
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'])
async def errors_get_details_sourcemaps(projectId: int, errorId: str,
context: schemas.CurrentContext = Depends(OR_context)):
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
@ -293,9 +293,9 @@ async def errors_get_details_sourcemaps(projectId: int, errorId: str,
@app.get('/{projectId}/errors/{errorId}/{action}', tags=["errors"])
async 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)):
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":
@ -311,8 +311,8 @@ async def add_remove_favorite_error(projectId: int, errorId: str, action: str, s
@app.get('/{projectId}/assist/sessions/{sessionId}', tags=["assist"])
async def get_live_session(projectId: int, sessionId: str, background_tasks: BackgroundTasks,
context: schemas.CurrentContext = Depends(OR_context)):
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_replay.get_replay(context=context, project_id=projectId, session_id=sessionId,
@ -326,8 +326,8 @@ async def get_live_session(projectId: int, sessionId: str, background_tasks: Bac
@app.get('/{projectId}/unprocessed/{sessionId}/dom.mob', tags=["assist"])
async def get_live_session_replay_file(projectId: int, sessionId: Union[int, str],
context: schemas.CurrentContext = Depends(OR_context)):
def get_live_session_replay_file(projectId: int, sessionId: Union[int, str],
context: schemas.CurrentContext = Depends(OR_context)):
not_found = {"errors": ["Replay file not found"]}
if isinstance(sessionId, str):
print(f"{sessionId} not a valid number.")
@ -346,8 +346,8 @@ async def get_live_session_replay_file(projectId: int, sessionId: Union[int, str
@app.get('/{projectId}/unprocessed/{sessionId}/devtools.mob', tags=["assist"])
async def get_live_session_devtools_file(projectId: int, sessionId: Union[int, str],
context: schemas.CurrentContext = Depends(OR_context)):
def get_live_session_devtools_file(projectId: int, sessionId: Union[int, str],
context: schemas.CurrentContext = Depends(OR_context)):
not_found = {"errors": ["Devtools file not found"]}
if isinstance(sessionId, str):
print(f"{sessionId} not a valid number.")
@ -366,20 +366,20 @@ async def get_live_session_devtools_file(projectId: int, sessionId: Union[int, s
@app.post('/{projectId}/heatmaps/url', tags=["heatmaps"])
async def get_heatmaps_by_url(projectId: int, data: schemas.GetHeatmapPayloadSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
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)}
@app.get('/{projectId}/sessions/{sessionId}/favorite', tags=["sessions"])
async def add_remove_favorite_session2(projectId: int, sessionId: int,
context: schemas.CurrentContext = Depends(OR_context)):
def add_remove_favorite_session2(projectId: int, sessionId: int,
context: schemas.CurrentContext = Depends(OR_context)):
return {
"data": sessions_favorite.favorite_session(context=context, project_id=projectId, session_id=sessionId)}
@app.get('/{projectId}/sessions/{sessionId}/assign', tags=["sessions"])
async def assign_session(projectId: int, sessionId, context: schemas.CurrentContext = Depends(OR_context)):
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)
@ -391,8 +391,8 @@ async def assign_session(projectId: int, sessionId, context: schemas.CurrentCont
@app.get('/{projectId}/sessions/{sessionId}/assign/{issueId}', tags=["sessions", "issueTracking"])
async def assign_session(projectId: int, sessionId: int, issueId: str,
context: schemas.CurrentContext = Depends(OR_context)):
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:
@ -403,9 +403,9 @@ async def assign_session(projectId: int, sessionId: int, issueId: str,
@app.post('/{projectId}/sessions/{sessionId}/assign/{issueId}/comment', tags=["sessions", "issueTracking"])
async def comment_assignment(projectId: int, sessionId: int, issueId: str,
data: schemas.CommentAssignmentSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
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)
@ -417,8 +417,8 @@ async def comment_assignment(projectId: int, sessionId: int, issueId: str,
@app.post('/{projectId}/sessions/{sessionId}/notes', tags=["sessions", "notes"])
async def create_note(projectId: int, sessionId: int, data: schemas.SessionNoteSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def create_note(projectId: int, sessionId: int, data: schemas.SessionNoteSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
if not sessions.session_exists(project_id=projectId, session_id=sessionId):
return {"errors": ["Session not found"]}
data = sessions_notes.create(tenant_id=context.tenant_id, project_id=projectId,
@ -431,7 +431,7 @@ async def create_note(projectId: int, sessionId: int, data: schemas.SessionNoteS
@app.get('/{projectId}/sessions/{sessionId}/notes', tags=["sessions", "notes"])
async def get_session_notes(projectId: int, sessionId: int, context: schemas.CurrentContext = Depends(OR_context)):
def get_session_notes(projectId: int, sessionId: int, context: schemas.CurrentContext = Depends(OR_context)):
data = sessions_notes.get_session_notes(tenant_id=context.tenant_id, project_id=projectId,
session_id=sessionId, user_id=context.user_id)
if "errors" in data:
@ -442,8 +442,8 @@ async def get_session_notes(projectId: int, sessionId: int, context: schemas.Cur
@app.post('/{projectId}/notes/{noteId}', tags=["sessions", "notes"])
async def edit_note(projectId: int, noteId: int, data: schemas.SessionUpdateNoteSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def edit_note(projectId: int, noteId: int, data: schemas.SessionUpdateNoteSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
data = sessions_notes.edit(tenant_id=context.tenant_id, project_id=projectId, user_id=context.user_id,
note_id=noteId, data=data)
if "errors" in data.keys():
@ -454,29 +454,29 @@ async def edit_note(projectId: int, noteId: int, data: schemas.SessionUpdateNote
@app.delete('/{projectId}/notes/{noteId}', tags=["sessions", "notes"])
async def delete_note(projectId: int, noteId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
def delete_note(projectId: int, noteId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
data = sessions_notes.delete(tenant_id=context.tenant_id, project_id=projectId, user_id=context.user_id,
note_id=noteId)
return data
@app.get('/{projectId}/notes/{noteId}/slack/{webhookId}', tags=["sessions", "notes"])
async def share_note_to_slack(projectId: int, noteId: int, webhookId: int,
context: schemas.CurrentContext = Depends(OR_context)):
def share_note_to_slack(projectId: int, noteId: int, webhookId: int,
context: schemas.CurrentContext = Depends(OR_context)):
return sessions_notes.share_to_slack(tenant_id=context.tenant_id, project_id=projectId, user_id=context.user_id,
note_id=noteId, webhook_id=webhookId)
@app.get('/{projectId}/notes/{noteId}/msteams/{webhookId}', tags=["sessions", "notes"])
async def share_note_to_msteams(projectId: int, noteId: int, webhookId: int,
context: schemas.CurrentContext = Depends(OR_context)):
def share_note_to_msteams(projectId: int, noteId: int, webhookId: int,
context: schemas.CurrentContext = Depends(OR_context)):
return sessions_notes.share_to_msteams(tenant_id=context.tenant_id, project_id=projectId, user_id=context.user_id,
note_id=noteId, webhook_id=webhookId)
@app.post('/{projectId}/notes', tags=["sessions", "notes"])
async def get_all_notes(projectId: int, data: schemas.SearchNoteSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def get_all_notes(projectId: int, data: schemas.SearchNoteSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
data = sessions_notes.get_all_notes_by_project_id(tenant_id=context.tenant_id, project_id=projectId,
user_id=context.user_id, data=data)
if "errors" in data:
@ -485,8 +485,8 @@ async def get_all_notes(projectId: int, data: schemas.SearchNoteSchema = Body(..
@app.post('/{projectId}/click_maps/search', tags=["click maps"])
async def click_map_search(projectId: int, data: schemas.FlatClickMapSessionsSearch = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def click_map_search(projectId: int, data: schemas.FlatClickMapSessionsSearch = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return {"data": click_maps.search_short_session(user_id=context.user_id, data=data, project_id=projectId)}

View file

@ -7,13 +7,13 @@ public_app, app, app_apikey = get_routers()
@app.get('/healthz', tags=["health-check"])
async def get_global_health_status():
def get_global_health_status():
return {"data": health.get_health()}
if not tenants.tenants_exists(use_pool=False):
@public_app.get('/health', tags=["health-check"])
async def get_public_health_status():
def get_public_health_status():
if tenants.tenants_exists():
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Not Found")

View file

@ -12,18 +12,18 @@ public_app, app, app_apikey = get_routers()
@app.post('/{projectId}/dashboards', tags=["dashboard"])
@app.put('/{projectId}/dashboards', tags=["dashboard"])
async def create_dashboards(projectId: int, data: schemas.CreateDashboardSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
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"])
async def get_dashboards(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
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"])
async def get_dashboard(projectId: int, dashboardId: int, context: schemas.CurrentContext = Depends(OR_context)):
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"]}
@ -32,60 +32,60 @@ async def get_dashboard(projectId: int, dashboardId: int, context: schemas.Curre
@app.post('/{projectId}/dashboards/{dashboardId}', tags=["dashboard"])
@app.put('/{projectId}/dashboards/{dashboardId}', tags=["dashboard"])
async def update_dashboard(projectId: int, dashboardId: int, data: schemas.EditDashboardSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
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"])
async def delete_dashboard(projectId: int, dashboardId: int, _=Body(None),
context: schemas.CurrentContext = Depends(OR_context)):
def delete_dashboard(projectId: int, dashboardId: int, _=Body(None),
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"])
async def pin_dashboard(projectId: int, dashboardId: int, context: schemas.CurrentContext = Depends(OR_context)):
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}/cards', tags=["cards"])
@app.post('/{projectId}/dashboards/{dashboardId}/widgets', tags=["dashboard"])
@app.put('/{projectId}/dashboards/{dashboardId}/widgets', tags=["dashboard"])
async def add_card_to_dashboard(projectId: int, dashboardId: int,
data: schemas.AddWidgetToDashboardPayloadSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def add_card_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"])
async def create_metric_and_add_to_dashboard(projectId: int, dashboardId: int,
data: schemas.CardSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def create_metric_and_add_to_dashboard(projectId: int, dashboardId: int,
data: schemas.CardSchema = 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"])
async def update_widget_in_dashboard(projectId: int, dashboardId: int, widgetId: int,
data: schemas.UpdateWidgetPayloadSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
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"])
async def remove_widget_from_dashboard(projectId: int, dashboardId: int, widgetId: int, _=Body(None),
context: schemas.CurrentContext = Depends(OR_context)):
def remove_widget_from_dashboard(projectId: int, dashboardId: int, widgetId: int, _=Body(None),
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"])
# async def get_widget_chart(projectId: int, dashboardId: int, widgetId: int,
# def get_widget_chart(projectId: int, dashboardId: int, widgetId: int,
# data: schemas.CardChartSchema = Body(...),
# context: schemas.CurrentContext = Depends(OR_context)):
# data = dashboards.make_chart_widget(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId,
@ -100,16 +100,16 @@ async def remove_widget_from_dashboard(projectId: int, dashboardId: int, widgetI
@app.put('/{projectId}/metrics/try', tags=["dashboard"])
@app.post('/{projectId}/custom_metrics/try', tags=["customMetrics"])
@app.put('/{projectId}/custom_metrics/try', tags=["customMetrics"])
async def try_card(projectId: int, data: schemas.CardSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def try_card(projectId: int, data: schemas.CardSchema = 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}/cards/try/sessions', tags=["cards"])
@app.post('/{projectId}/metrics/try/sessions', tags=["dashboard"])
@app.post('/{projectId}/custom_metrics/try/sessions', tags=["customMetrics"])
async def try_card_sessions(projectId: int, data: schemas.CardSessionsSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def try_card_sessions(projectId: int, data: schemas.CardSessionsSchema = 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}
@ -117,8 +117,8 @@ async def try_card_sessions(projectId: int, data: schemas.CardSessionsSchema = B
@app.post('/{projectId}/cards/try/issues', tags=["cards"])
@app.post('/{projectId}/metrics/try/issues', tags=["dashboard"])
@app.post('/{projectId}/custom_metrics/try/issues', tags=["customMetrics"])
async def try_card_funnel_issues(projectId: int, data: schemas.CardSessionsSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def try_card_funnel_issues(projectId: int, data: schemas.CardSessionsSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
if len(data.series) == 0:
return {"data": []}
data.series[0].filter.startDate = data.startTimestamp
@ -130,7 +130,7 @@ async def try_card_funnel_issues(projectId: int, data: schemas.CardSessionsSchem
@app.get('/{projectId}/cards', tags=["cards"])
@app.get('/{projectId}/metrics', tags=["dashboard"])
@app.get('/{projectId}/custom_metrics', tags=["customMetrics"])
async def get_cards(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
def get_cards(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
return {"data": custom_metrics.get_all(project_id=projectId, user_id=context.user_id)}
@ -139,23 +139,23 @@ async def get_cards(projectId: int, context: schemas.CurrentContext = Depends(OR
@app.put('/{projectId}/metrics', tags=["dashboard"])
@app.post('/{projectId}/custom_metrics', tags=["customMetrics"])
@app.put('/{projectId}/custom_metrics', tags=["customMetrics"])
async def create_card(projectId: int, data: schemas.CardSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def create_card(projectId: int, data: schemas.CardSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return custom_metrics.create(project_id=projectId, user_id=context.user_id, data=data)
@app.post('/{projectId}/cards/search', tags=["cards"])
@app.post('/{projectId}/metrics/search', tags=["dashboard"])
@app.post('/{projectId}/custom_metrics/search', tags=["customMetrics"])
async def search_cards(projectId: int, data: schemas.SearchCardsSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def search_cards(projectId: int, data: schemas.SearchCardsSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return {"data": custom_metrics.search_all(project_id=projectId, user_id=context.user_id, data=data)}
@app.get('/{projectId}/cards/{metric_id}', tags=["cards"])
@app.get('/{projectId}/metrics/{metric_id}', tags=["dashboard"])
@app.get('/{projectId}/custom_metrics/{metric_id}', tags=["customMetrics"])
async def get_card(projectId: int, metric_id: Union[int, str], context: schemas.CurrentContext = Depends(OR_context)):
def get_card(projectId: int, metric_id: Union[int, str], context: schemas.CurrentContext = Depends(OR_context)):
if not isinstance(metric_id, int):
return {"errors": ["invalid card_id"]}
data = custom_metrics.get_card(project_id=projectId, user_id=context.user_id, metric_id=metric_id)
@ -165,7 +165,7 @@ async def get_card(projectId: int, metric_id: Union[int, str], context: schemas.
# @app.get('/{projectId}/cards/{metric_id}/thumbnail', tags=["cards"])
# async def sign_thumbnail_for_upload(projectId: int, metric_id: Union[int, str],
# def sign_thumbnail_for_upload(projectId: int, metric_id: Union[int, str],
# context: schemas.CurrentContext = Depends(OR_context)):
# if not isinstance(metric_id, int):
# return {"errors": ["invalid card_id"]}
@ -175,9 +175,9 @@ async def get_card(projectId: int, metric_id: Union[int, str], context: schemas.
@app.post('/{projectId}/cards/{metric_id}/sessions', tags=["cards"])
@app.post('/{projectId}/metrics/{metric_id}/sessions', tags=["dashboard"])
@app.post('/{projectId}/custom_metrics/{metric_id}/sessions', tags=["customMetrics"])
async def get_card_sessions(projectId: int, metric_id: int,
data: schemas.CardSessionsSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def get_card_sessions(projectId: int, metric_id: int,
data: schemas.CardSessionsSchema = 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"]}
@ -187,9 +187,9 @@ async def get_card_sessions(projectId: int, metric_id: int,
@app.post('/{projectId}/cards/{metric_id}/issues', tags=["cards"])
@app.post('/{projectId}/metrics/{metric_id}/issues', tags=["dashboard"])
@app.post('/{projectId}/custom_metrics/{metric_id}/issues', tags=["customMetrics"])
async def get_card_funnel_issues(projectId: int, metric_id: Union[int, str],
data: schemas.CardSessionsSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def get_card_funnel_issues(projectId: int, metric_id: Union[int, str],
data: schemas.CardSessionsSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
if not isinstance(metric_id, int):
return {"errors": [f"invalid card_id: {metric_id}"]}
@ -203,9 +203,9 @@ async def get_card_funnel_issues(projectId: int, metric_id: Union[int, str],
@app.post('/{projectId}/cards/{metric_id}/issues/{issueId}/sessions', tags=["dashboard"])
@app.post('/{projectId}/metrics/{metric_id}/issues/{issueId}/sessions', tags=["dashboard"])
@app.post('/{projectId}/custom_metrics/{metric_id}/issues/{issueId}/sessions', tags=["customMetrics"])
async def get_metric_funnel_issue_sessions(projectId: int, metric_id: int, issueId: str,
data: schemas.CardSessionsSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def get_metric_funnel_issue_sessions(projectId: int, metric_id: int, issueId: str,
data: schemas.CardSessionsSchema = 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:
@ -216,9 +216,9 @@ async def get_metric_funnel_issue_sessions(projectId: int, metric_id: int, issue
@app.post('/{projectId}/cards/{metric_id}/errors', tags=["dashboard"])
@app.post('/{projectId}/metrics/{metric_id}/errors', tags=["dashboard"])
@app.post('/{projectId}/custom_metrics/{metric_id}/errors', tags=["customMetrics"])
async def get_custom_metric_errors_list(projectId: int, metric_id: int,
data: schemas.CardSessionsSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def get_custom_metric_errors_list(projectId: int, metric_id: int,
data: schemas.CardSessionsSchema = 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:
@ -229,8 +229,8 @@ async def get_custom_metric_errors_list(projectId: int, metric_id: int,
@app.post('/{projectId}/cards/{metric_id}/chart', tags=["card"])
@app.post('/{projectId}/metrics/{metric_id}/chart', tags=["dashboard"])
@app.post('/{projectId}/custom_metrics/{metric_id}/chart', tags=["customMetrics"])
async def get_card_chart(projectId: int, metric_id: int, request: Request, data: schemas.CardChartSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def get_card_chart(projectId: int, metric_id: int, request: Request, data: schemas.CardChartSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
data = custom_metrics.make_chart_from_card(project_id=projectId, user_id=context.user_id, metric_id=metric_id,
data=data)
return {"data": data}
@ -241,8 +241,8 @@ async def get_card_chart(projectId: int, metric_id: int, request: Request, data:
@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"])
async def update_custom_metric(projectId: int, metric_id: int, data: schemas.UpdateCardSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def update_custom_metric(projectId: int, metric_id: int, data: schemas.UpdateCardSchema = 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"]}
@ -254,9 +254,9 @@ async def update_custom_metric(projectId: int, metric_id: int, data: schemas.Upd
@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"])
async def update_custom_metric_state(projectId: int, metric_id: int,
data: schemas.UpdateCustomMetricsStatusSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
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)}
@ -265,6 +265,6 @@ async def update_custom_metric_state(projectId: int, metric_id: int,
@app.delete('/{projectId}/cards/{metric_id}', tags=["dashboard"])
@app.delete('/{projectId}/metrics/{metric_id}', tags=["dashboard"])
@app.delete('/{projectId}/custom_metrics/{metric_id}', tags=["customMetrics"])
async def delete_custom_metric(projectId: int, metric_id: int, _=Body(None),
context: schemas.CurrentContext = Depends(OR_context)):
def delete_custom_metric(projectId: int, metric_id: int, _=Body(None),
context: schemas.CurrentContext = Depends(OR_context)):
return {"data": custom_metrics.delete(project_id=projectId, user_id=context.user_id, metric_id=metric_id)}

View file

@ -9,7 +9,7 @@ public_app, app, app_apikey = get_routers()
@app_apikey.get('/v1/{projectKey}/users/{userId}/sessions', tags=["api"])
async def get_user_sessions(projectKey: str, userId: str, start_date: int = None, end_date: int = None):
def get_user_sessions(projectKey: str, userId: str, start_date: int = None, end_date: int = None):
projectId = projects.get_internal_project_id(projectKey)
if projectId is None:
return {"errors": ["invalid projectKey"]}
@ -24,7 +24,7 @@ async def get_user_sessions(projectKey: str, userId: str, start_date: int = None
@app_apikey.get('/v1/{projectKey}/sessions/{sessionId}/events', tags=["api"])
async def get_session_events(projectKey: str, sessionId: int):
def get_session_events(projectKey: str, sessionId: int):
projectId = projects.get_internal_project_id(projectKey)
if projectId is None:
return {"errors": ["invalid projectKey"]}
@ -37,7 +37,7 @@ async def get_session_events(projectKey: str, sessionId: int):
@app_apikey.get('/v1/{projectKey}/users/{userId}', tags=["api"])
async def get_user_details(projectKey: str, userId: str):
def get_user_details(projectKey: str, userId: str):
projectId = projects.get_internal_project_id(projectKey)
if projectId is None:
return {"errors": ["invalid projectKey"]}
@ -50,7 +50,7 @@ async def get_user_details(projectKey: str, userId: str):
@app_apikey.delete('/v1/{projectKey}/users/{userId}', tags=["api"])
async def schedule_to_delete_user_data(projectKey: str, userId: str, _=Body(None)):
def schedule_to_delete_user_data(projectKey: str, userId: str, _=Body(None)):
projectId = projects.get_internal_project_id(projectKey)
if projectId is None:
return {"errors": ["invalid projectKey"]}
@ -59,7 +59,7 @@ async def schedule_to_delete_user_data(projectKey: str, userId: str, _=Body(None
@app_apikey.get('/v1/{projectKey}/jobs', tags=["api"])
async def get_jobs(projectKey: str):
def get_jobs(projectKey: str):
projectId = projects.get_internal_project_id(projectKey)
if projectId is None:
return {"errors": ["invalid projectKey"]}
@ -67,12 +67,12 @@ async def get_jobs(projectKey: str):
@app_apikey.get('/v1/{projectKey}/jobs/{jobId}', tags=["api"])
async def get_job(projectKey: str, jobId: int):
def get_job(projectKey: str, jobId: int):
return {"data": jobs.get(job_id=jobId)}
@app_apikey.delete('/v1/{projectKey}/jobs/{jobId}', tags=["api"])
async def cancel_job(projectKey: str, jobId: int, _=Body(None)):
def cancel_job(projectKey: str, jobId: int, _=Body(None)):
job = jobs.get(job_id=jobId)
job_not_found = len(job.keys()) == 0
@ -86,7 +86,7 @@ async def cancel_job(projectKey: str, jobId: int, _=Body(None)):
@app_apikey.get('/v1/projects', tags=["api"])
async def get_projects(context: schemas.CurrentContext = Depends(OR_context)):
def get_projects(context: schemas.CurrentContext = Depends(OR_context)):
records = projects.get_projects(tenant_id=context.tenant_id)
for record in records:
del record['projectId']
@ -95,15 +95,15 @@ async def get_projects(context: schemas.CurrentContext = Depends(OR_context)):
@app_apikey.get('/v1/projects/{projectKey}', tags=["api"])
async def get_project(projectKey: str, context: schemas.CurrentContext = Depends(OR_context)):
def get_project(projectKey: str, context: schemas.CurrentContext = Depends(OR_context)):
return {
"data": projects.get_project_by_key(tenant_id=context.tenant_id, project_key=projectKey)
}
@app_apikey.post('/v1/projects', tags=["api"])
async def create_project(data: schemas.CreateProjectSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def create_project(data: schemas.CreateProjectSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
record = projects.create(
tenant_id=context.tenant_id,
user_id=None,

View file

@ -20,7 +20,7 @@ def reset(data: schemas.ForgetPasswordPayloadSchema):
if config("enforce_SSO", cast=bool, default=False) and not a_user["superAdmin"] and helper.is_saml2_available():
return {"errors": ["Please use your SSO to login, enforced by admin"]}
# ----------
invitation_link = users.generate_new_invitation(user_id=a_user["id"])
invitation_link = users.generate_new_invitation(user_id=a_user["userId"])
email_helper.send_forgot_password(recipient=data.email, invitation_link=invitation_link)
else:
print(f"!!!invalid email address [{data.email}]")

View file

@ -32,8 +32,7 @@ def create_new_member(tenant_id, email, invitation_token, admin, name, owner=Fal
VALUES ((SELECT user_id FROM u), %(invitation_token)s, timezone('utc'::text, now()))
RETURNING invitation_token
)
SELECT u.user_id AS id,
u.user_id,
SELECT u.user_id,
u.email,
u.role,
u.name,
@ -44,7 +43,6 @@ def create_new_member(tenant_id, email, invitation_token, admin, name, owner=Fal
au.invitation_token,
u.role_id,
roles.name AS role_name,
roles.permissions,
TRUE AS has_password
FROM au,u LEFT JOIN roles USING(tenant_id) WHERE roles.role_id IS NULL OR roles.role_id = (SELECT u.role_id FROM u);""",
{"tenant_id": tenant_id, "email": email,
@ -62,24 +60,23 @@ def restore_member(tenant_id, user_id, email, invitation_token, admin, name, own
with pg_client.PostgresClient() as cur:
query = cur.mogrify(f"""\
WITH u AS (UPDATE public.users
SET name= %(name)s,
role = %(role)s,
deleted_at= NULL,
created_at = timezone('utc'::text, now()),
tenant_id= %(tenant_id)s,
api_key= generate_api_key(20),
role_id= (SELECT COALESCE((SELECT role_id FROM roles WHERE tenant_id = %(tenant_id)s AND role_id = %(role_id)s),
(SELECT role_id FROM roles WHERE tenant_id = %(tenant_id)s AND name = 'Member' LIMIT 1),
(SELECT role_id FROM roles WHERE tenant_id = %(tenant_id)s AND name != 'Owner' LIMIT 1)))
WHERE user_id=%(user_id)s
RETURNING
tenant_id,
user_id AS id,
user_id,
email,
role,
name,
created_at,role_id),
SET name= %(name)s,
role = %(role)s,
deleted_at= NULL,
created_at = timezone('utc'::text, now()),
tenant_id= %(tenant_id)s,
api_key= generate_api_key(20),
role_id= (SELECT COALESCE((SELECT role_id FROM roles WHERE tenant_id = %(tenant_id)s AND role_id = %(role_id)s),
(SELECT role_id FROM roles WHERE tenant_id = %(tenant_id)s AND name = 'Member' LIMIT 1),
(SELECT role_id FROM roles WHERE tenant_id = %(tenant_id)s AND name != 'Owner' LIMIT 1)))
WHERE user_id=%(user_id)s
RETURNING
tenant_id,
user_id,
email,
role,
name,
created_at,role_id),
au AS (UPDATE public.basic_authentication
SET invitation_token = %(invitation_token)s,
invited_at = timezone('utc'::text, now()),
@ -87,8 +84,7 @@ def restore_member(tenant_id, user_id, email, invitation_token, admin, name, own
change_pwd_token = NULL
WHERE user_id=%(user_id)s
RETURNING invitation_token)
SELECT u.id,
u.user_id,
SELECT u.user_id,
u.email,
u.role,
u.name,
@ -99,7 +95,6 @@ def restore_member(tenant_id, user_id, email, invitation_token, admin, name, own
au.invitation_token,
u.role_id,
roles.name AS role_name,
roles.permissions,
TRUE AS has_password
FROM au,u LEFT JOIN roles USING(tenant_id)
WHERE roles.role_id IS NULL OR roles.role_id = (SELECT u.role_id FROM u);""",
@ -173,7 +168,7 @@ def update(tenant_id, user_id, changes, output=True):
WHERE users.user_id = %(user_id)s
AND users.tenant_id = %(tenant_id)s
AND users.user_id = basic_authentication.user_id
RETURNING users.user_id AS id,
RETURNING users.user_id,
users.email,
users.role,
users.name,
@ -418,30 +413,6 @@ def get_by_email_only(email):
return helper.dict_to_camel_case(r)
def get_by_email_reset(email, reset_token):
with pg_client.PostgresClient() as cur:
cur.execute(
cur.mogrify(
f"""SELECT
users.user_id AS id,
users.tenant_id,
users.email,
users.role,
users.name,
(CASE WHEN users.role = 'owner' THEN TRUE ELSE FALSE END) AS super_admin,
(CASE WHEN users.role = 'admin' THEN TRUE ELSE FALSE END) AS admin,
(CASE WHEN users.role = 'member' THEN TRUE ELSE FALSE END) AS member
FROM public.users LEFT JOIN public.basic_authentication ON users.user_id=basic_authentication.user_id
WHERE
users.email = %(email)s
AND basic_authentication.token =%(token)s
AND users.deleted_at IS NULL""",
{"email": email, "token": reset_token})
)
r = cur.fetchone()
return helper.dict_to_camel_case(r)
def get_member(tenant_id, user_id):
with pg_client.PostgresClient() as cur:
cur.execute(
@ -680,7 +651,7 @@ def change_jwt_iat(user_id):
return cur.fetchone().get("jwt_iat")
def authenticate(email, password, for_change_password=False):
def authenticate(email, password, for_change_password=False) -> dict | None:
with pg_client.PostgresClient() as cur:
query = cur.mogrify(
f"""SELECT

View file

@ -70,36 +70,16 @@ else:
def init_saml_auth(req):
# auth = OneLogin_Saml2_Auth(req, custom_base_path=environ['SAML_PATH'])
print("--------------------")
print(dir(req))
print("--------------------")
if idp is None:
raise Exception("No SAML2 config provided")
return OneLogin_Saml2_Auth(req, old_settings=SAML2)
async def prepare_request(request: Request):
print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
print(SAML2)
print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
request.args = dict(request.query_params).copy() if request.query_params else {}
form: FormData = await request.form()
request.form = dict(form)
cookie_str = request.headers.get("cookie", "")
print(">>>>>>>>>>>>>>>>>>>>>>>>")
print(dir(request))
print(">>>>>>>>>>>>>>>>>>>>>>>>")
print(request.url)
print(request.url.port)
print(request.url.path)
# print(request.base_url)
# print(request.base_url.path)
# print(request.base_url.port)
if request.url.port is None:
# request.url.port = 443
request.url.__setattr__("port",443)
print(">>>>>>>>>>>>>>>>>>>>>>>>")
if "session" in cookie_str:
cookie = cookies.SimpleCookie()
cookie.load(cookie_str)

View file

@ -2,4 +2,6 @@
sh env_vars.sh
source /tmp/.env.override
uvicorn app:app --host 0.0.0.0 --port $LISTEN_PORT --proxy-headers
#uvicorn app:app --host 0.0.0.0 --port $LISTEN_PORT --proxy-headers
NB_WORKERS="${NB_WORKERS:=4}"
gunicorn app:app --workers $NB_WORKERS --worker-class uvicorn.workers.UvicornWorker --bind 0.0.0.0:$LISTEN_PORT

View file

@ -10,6 +10,7 @@ jira==3.5.1
fastapi==0.97.0
uvicorn[standard]==0.22.0
gunicorn==20.1.0
python-decouple==3.8
pydantic[email]==1.10.8
apscheduler==3.10.1

View file

@ -26,7 +26,7 @@ public_app, app, app_apikey = get_routers()
@public_app.get('/signup', tags=['signup'])
async def get_all_signup():
def get_all_signup():
return {"data": {"tenants": tenants.tenants_exists(),
"sso": SAML2_helper.is_saml2_available(),
"ssoProvider": SAML2_helper.get_saml2_provider(),
@ -37,12 +37,12 @@ async def get_all_signup():
if config("MULTI_TENANTS", cast=bool, default=False) or not tenants.tenants_exists(use_pool=False):
@public_app.post('/signup', tags=['signup'])
@public_app.put('/signup', tags=['signup'])
async def signup_handler(data: schemas.UserSignupSchema = Body(...)):
def signup_handler(data: schemas.UserSignupSchema = Body(...)):
return signup.create_tenant(data)
@public_app.post('/login', tags=["authentication"])
async def login_user(data: schemas.UserLoginSchema = Body(...)):
def login_user(data: schemas.UserLoginSchema = Body(...)):
if helper.allow_captcha() and not captcha.is_valid(data.g_recaptcha_response):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
@ -73,12 +73,12 @@ async def login_user(data: schemas.UserLoginSchema = Body(...)):
@app.get('/logout', tags=["login", "logout"])
async def logout_user(context: schemas.CurrentContext = Depends(OR_context)):
def logout_user(context: schemas.CurrentContext = Depends(OR_context)):
return {"data": "success"}
@app.get('/account', tags=['accounts'])
async def get_account(context: schemas.CurrentContext = Depends(OR_context)):
def get_account(context: schemas.CurrentContext = Depends(OR_context)):
r = users.get(tenant_id=context.tenant_id, user_id=context.user_id)
t = tenants.get_by_tenant_id(context.tenant_id)
if t is not None:
@ -97,15 +97,15 @@ async def get_account(context: schemas.CurrentContext = Depends(OR_context)):
@app.post('/account', tags=["account"])
async def edit_account(data: schemas.EditAccountSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def edit_account(data: schemas.EditAccountSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return users.edit_account(tenant_id=context.tenant_id, user_id=context.user_id, changes=data)
@app.post('/integrations/slack', tags=['integrations'])
@app.put('/integrations/slack', tags=['integrations'])
async def add_slack_integration(data: schemas.AddCollaborationSchema,
context: schemas.CurrentContext = Depends(OR_context)):
def add_slack_integration(data: schemas.AddCollaborationSchema,
context: schemas.CurrentContext = Depends(OR_context)):
n = Slack.add(tenant_id=context.tenant_id, data=data)
if n is None:
return {
@ -115,8 +115,8 @@ async def add_slack_integration(data: schemas.AddCollaborationSchema,
@app.post('/integrations/slack/{integrationId}', tags=['integrations'])
async def edit_slack_integration(integrationId: int, data: schemas.EditCollaborationSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def edit_slack_integration(integrationId: int, data: schemas.EditCollaborationSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
if len(data.url) > 0:
old = Slack.get_integration(tenant_id=context.tenant_id, integration_id=integrationId)
if not old:
@ -132,8 +132,8 @@ async def edit_slack_integration(integrationId: int, data: schemas.EditCollabora
@app.post('/client/members', tags=["client"])
async def add_member(background_tasks: BackgroundTasks, data: schemas_ee.CreateMemberSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def add_member(background_tasks: BackgroundTasks, data: schemas_ee.CreateMemberSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return users.create_member(tenant_id=context.tenant_id, user_id=context.user_id, data=data.dict(),
background_tasks=background_tasks)
@ -158,7 +158,7 @@ async def process_invitation_link(token: str, request: Request):
@public_app.post('/password/reset', tags=["users"])
async def change_password_by_invitation(data: schemas.EditPasswordByInvitationSchema = Body(...)):
def change_password_by_invitation(data: schemas.EditPasswordByInvitationSchema = Body(...)):
if data is None or len(data.invitation) < 64 or len(data.passphrase) < 8:
return {"errors": ["please provide a valid invitation & pass"]}
user = users.get_by_invitation_token(token=data.invitation, pass_token=data.passphrase)
@ -171,15 +171,15 @@ async def change_password_by_invitation(data: schemas.EditPasswordByInvitationSc
@app.put('/client/members/{memberId}', tags=["client"])
async def edit_member(memberId: int, data: schemas_ee.EditMemberSchema,
context: schemas.CurrentContext = Depends(OR_context)):
def edit_member(memberId: int, data: schemas_ee.EditMemberSchema,
context: schemas.CurrentContext = Depends(OR_context)):
return users.edit_member(tenant_id=context.tenant_id, editor_id=context.user_id, changes=data,
user_id_to_update=memberId)
@app.get('/metadata/session_search', tags=["metadata"])
async def search_sessions_by_metadata(key: str, value: str, projectId: Optional[int] = None,
context: schemas.CurrentContext = Depends(OR_context)):
def search_sessions_by_metadata(key: str, value: str, projectId: Optional[int] = None,
context: schemas.CurrentContext = Depends(OR_context)):
if key is None or value is None or len(value) == 0 and len(key) == 0:
return {"errors": ["please provide a key&value for search"]}
@ -196,7 +196,7 @@ async def search_sessions_by_metadata(key: str, value: str, projectId: Optional[
@app.get('/projects', tags=['projects'])
async def get_projects(context: schemas.CurrentContext = Depends(OR_context)):
def get_projects(context: schemas.CurrentContext = Depends(OR_context)):
return {"data": projects.get_projects(tenant_id=context.tenant_id, gdpr=True,
recorded=True, user_id=context.user_id)}
@ -204,8 +204,8 @@ async def get_projects(context: schemas.CurrentContext = Depends(OR_context)):
# for backward compatibility
@app.get('/{projectId}/sessions/{sessionId}', tags=["sessions", "replay"],
dependencies=[OR_scope(Permissions.session_replay)])
async def get_session(projectId: int, sessionId: Union[int, str], background_tasks: BackgroundTasks,
context: schemas.CurrentContext = Depends(OR_context)):
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_replay.get_by_id2_pg(project_id=projectId, session_id=sessionId, full_data=True,
@ -222,8 +222,8 @@ async def get_session(projectId: int, sessionId: Union[int, str], background_tas
@app.get('/{projectId}/sessions/{sessionId}/replay', tags=["sessions", "replay"],
dependencies=[OR_scope(Permissions.session_replay)])
async def get_session_events(projectId: int, sessionId: Union[int, str], background_tasks: BackgroundTasks,
context: schemas.CurrentContext = Depends(OR_context)):
def get_session_events(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_replay.get_replay(project_id=projectId, session_id=sessionId, full_data=True,
@ -240,8 +240,8 @@ async def get_session_events(projectId: int, sessionId: Union[int, str], backgro
@app.get('/{projectId}/sessions/{sessionId}/events', tags=["sessions", "replay"],
dependencies=[OR_scope(Permissions.session_replay)])
async def get_session_events(projectId: int, sessionId: Union[int, str],
context: schemas.CurrentContext = Depends(OR_context)):
def get_session_events(projectId: int, sessionId: Union[int, str],
context: schemas.CurrentContext = Depends(OR_context)):
if isinstance(sessionId, str):
return {"errors": ["session not found"]}
data = sessions_replay.get_events(project_id=projectId, session_id=sessionId)
@ -255,8 +255,8 @@ async def get_session_events(projectId: int, sessionId: Union[int, str],
@app.get('/{projectId}/sessions/{sessionId}/errors/{errorId}/sourcemaps', tags=["sessions", "sourcemaps"],
dependencies=[OR_scope(Permissions.dev_tools)])
async def get_error_trace(projectId: int, sessionId: int, errorId: str,
context: schemas.CurrentContext = Depends(OR_context)):
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
@ -266,20 +266,20 @@ async def get_error_trace(projectId: int, sessionId: int, errorId: str,
@app.post('/{projectId}/errors/search', tags=['errors'], dependencies=[OR_scope(Permissions.dev_tools)])
async def errors_search(projectId: int, data: schemas.SearchErrorsSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
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.dev_tools)])
async def errors_stats(projectId: int, startTimestamp: int, endTimestamp: int,
context: schemas.CurrentContext = Depends(OR_context)):
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.dev_tools)])
async def errors_get_details(projectId: int, errorId: str, background_tasks: BackgroundTasks, density24: int = 24,
density30: int = 30, context: schemas.CurrentContext = Depends(OR_context)):
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:
@ -289,17 +289,17 @@ async def errors_get_details(projectId: int, errorId: str, background_tasks: Bac
@app.get('/{projectId}/errors/{errorId}/stats', tags=['errors'], dependencies=[OR_scope(Permissions.dev_tools)])
async 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)):
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.dev_tools)])
async def errors_get_details_sourcemaps(projectId: int, errorId: str,
context: schemas.CurrentContext = Depends(OR_context)):
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
@ -309,9 +309,9 @@ async def errors_get_details_sourcemaps(projectId: int, errorId: str,
@app.get('/{projectId}/errors/{errorId}/{action}', tags=["errors"], dependencies=[OR_scope(Permissions.dev_tools)])
async 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)):
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":
@ -327,8 +327,8 @@ async def add_remove_favorite_error(projectId: int, errorId: str, action: str, s
@app.get('/{projectId}/assist/sessions/{sessionId}', tags=["assist"], dependencies=[OR_scope(Permissions.assist_live)])
async def get_live_session(projectId: int, sessionId: str, background_tasks: BackgroundTasks,
context: schemas_ee.CurrentContext = Depends(OR_context)):
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_replay.get_replay(context=context, project_id=projectId, session_id=sessionId,
@ -343,8 +343,8 @@ async def get_live_session(projectId: int, sessionId: str, background_tasks: Bac
@app.get('/{projectId}/unprocessed/{sessionId}/dom.mob', tags=["assist"],
dependencies=[OR_scope(Permissions.assist_live, Permissions.session_replay)])
async def get_live_session_replay_file(projectId: int, sessionId: Union[int, str],
context: schemas.CurrentContext = Depends(OR_context)):
def get_live_session_replay_file(projectId: int, sessionId: Union[int, str],
context: schemas.CurrentContext = Depends(OR_context)):
not_found = {"errors": ["Replay file not found"]}
if isinstance(sessionId, str):
print(f"{sessionId} not a valid number.")
@ -364,8 +364,8 @@ async def get_live_session_replay_file(projectId: int, sessionId: Union[int, str
@app.get('/{projectId}/unprocessed/{sessionId}/devtools.mob', tags=["assist"],
dependencies=[OR_scope(Permissions.assist_live, Permissions.session_replay, Permissions.dev_tools)])
async def get_live_session_devtools_file(projectId: int, sessionId: Union[int, str],
context: schemas.CurrentContext = Depends(OR_context)):
def get_live_session_devtools_file(projectId: int, sessionId: Union[int, str],
context: schemas.CurrentContext = Depends(OR_context)):
not_found = {"errors": ["Devtools file not found"]}
if isinstance(sessionId, str):
print(f"{sessionId} not a valid number.")
@ -384,22 +384,22 @@ async def get_live_session_devtools_file(projectId: int, sessionId: Union[int, s
@app.post('/{projectId}/heatmaps/url', tags=["heatmaps"], dependencies=[OR_scope(Permissions.session_replay)])
async def get_heatmaps_by_url(projectId: int, data: schemas.GetHeatmapPayloadSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
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)}
@app.get('/{projectId}/sessions/{sessionId}/favorite', tags=["sessions"],
dependencies=[OR_scope(Permissions.session_replay)])
async def add_remove_favorite_session2(projectId: int, sessionId: int,
context: schemas_ee.CurrentContext = Depends(OR_context)):
def add_remove_favorite_session2(projectId: int, sessionId: int,
context: schemas_ee.CurrentContext = Depends(OR_context)):
return {
"data": sessions_favorite.favorite_session(context=context, project_id=projectId, session_id=sessionId)}
@app.get('/{projectId}/sessions/{sessionId}/assign', tags=["sessions"],
dependencies=[OR_scope(Permissions.session_replay)])
async def assign_session(projectId: int, sessionId, context: schemas.CurrentContext = Depends(OR_context)):
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)
@ -412,8 +412,8 @@ async def assign_session(projectId: int, sessionId, context: schemas.CurrentCont
@app.get('/{projectId}/sessions/{sessionId}/assign/{issueId}', tags=["sessions", "issueTracking"],
dependencies=[OR_scope(Permissions.session_replay)])
async def assign_session(projectId: int, sessionId: int, issueId: str,
context: schemas.CurrentContext = Depends(OR_context)):
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:
@ -425,9 +425,9 @@ async def assign_session(projectId: int, sessionId: int, issueId: str,
@app.post('/{projectId}/sessions/{sessionId}/assign/{issueId}/comment', tags=["sessions", "issueTracking"],
dependencies=[OR_scope(Permissions.session_replay)])
async def comment_assignment(projectId: int, sessionId: int, issueId: str,
data: schemas.CommentAssignmentSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
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)
@ -440,8 +440,8 @@ async def comment_assignment(projectId: int, sessionId: int, issueId: str,
@app.post('/{projectId}/sessions/{sessionId}/notes', tags=["sessions", "notes"],
dependencies=[OR_scope(Permissions.session_replay)])
async def create_note(projectId: int, sessionId: int, data: schemas.SessionNoteSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def create_note(projectId: int, sessionId: int, data: schemas.SessionNoteSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
if not sessions.session_exists(project_id=projectId, session_id=sessionId):
return {"errors": ["Session not found"]}
data = sessions_notes.create(tenant_id=context.tenant_id, project_id=projectId,
@ -455,7 +455,7 @@ async def create_note(projectId: int, sessionId: int, data: schemas.SessionNoteS
@app.get('/{projectId}/sessions/{sessionId}/notes', tags=["sessions", "notes"],
dependencies=[OR_scope(Permissions.session_replay)])
async def get_session_notes(projectId: int, sessionId: int, context: schemas.CurrentContext = Depends(OR_context)):
def get_session_notes(projectId: int, sessionId: int, context: schemas.CurrentContext = Depends(OR_context)):
data = sessions_notes.get_session_notes(tenant_id=context.tenant_id, project_id=projectId,
session_id=sessionId, user_id=context.user_id)
if "errors" in data:
@ -467,8 +467,8 @@ async def get_session_notes(projectId: int, sessionId: int, context: schemas.Cur
@app.post('/{projectId}/notes/{noteId}', tags=["sessions", "notes"],
dependencies=[OR_scope(Permissions.session_replay)])
async def edit_note(projectId: int, noteId: int, data: schemas.SessionUpdateNoteSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def edit_note(projectId: int, noteId: int, data: schemas.SessionUpdateNoteSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
data = sessions_notes.edit(tenant_id=context.tenant_id, project_id=projectId, user_id=context.user_id,
note_id=noteId, data=data)
if "errors" in data.keys():
@ -480,7 +480,7 @@ async def edit_note(projectId: int, noteId: int, data: schemas.SessionUpdateNote
@app.delete('/{projectId}/notes/{noteId}', tags=["sessions", "notes"],
dependencies=[OR_scope(Permissions.session_replay)])
async def delete_note(projectId: int, noteId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
def delete_note(projectId: int, noteId: int, _=Body(None), context: schemas.CurrentContext = Depends(OR_context)):
data = sessions_notes.delete(tenant_id=context.tenant_id, project_id=projectId, user_id=context.user_id,
note_id=noteId)
return data
@ -488,22 +488,22 @@ async def delete_note(projectId: int, noteId: int, _=Body(None), context: schema
@app.get('/{projectId}/notes/{noteId}/slack/{webhookId}', tags=["sessions", "notes"],
dependencies=[OR_scope(Permissions.session_replay)])
async def share_note_to_slack(projectId: int, noteId: int, webhookId: int,
context: schemas.CurrentContext = Depends(OR_context)):
def share_note_to_slack(projectId: int, noteId: int, webhookId: int,
context: schemas.CurrentContext = Depends(OR_context)):
return sessions_notes.share_to_slack(tenant_id=context.tenant_id, project_id=projectId, user_id=context.user_id,
note_id=noteId, webhook_id=webhookId)
@app.get('/{projectId}/notes/{noteId}/msteams/{webhookId}', tags=["sessions", "notes"])
async def share_note_to_msteams(projectId: int, noteId: int, webhookId: int,
context: schemas.CurrentContext = Depends(OR_context)):
def share_note_to_msteams(projectId: int, noteId: int, webhookId: int,
context: schemas.CurrentContext = Depends(OR_context)):
return sessions_notes.share_to_msteams(tenant_id=context.tenant_id, project_id=projectId, user_id=context.user_id,
note_id=noteId, webhook_id=webhookId)
@app.post('/{projectId}/notes', tags=["sessions", "notes"], dependencies=[OR_scope(Permissions.session_replay)])
async def get_all_notes(projectId: int, data: schemas.SearchNoteSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def get_all_notes(projectId: int, data: schemas.SearchNoteSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
data = sessions_notes.get_all_notes_by_project_id(tenant_id=context.tenant_id, project_id=projectId,
user_id=context.user_id, data=data)
if "errors" in data:
@ -512,35 +512,35 @@ async def get_all_notes(projectId: int, data: schemas.SearchNoteSchema = Body(..
@app.post('/{projectId}/click_maps/search', tags=["click maps"], dependencies=[OR_scope(Permissions.session_replay)])
async def click_map_search(projectId: int, data: schemas.FlatClickMapSessionsSearch = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def click_map_search(projectId: int, data: schemas.FlatClickMapSessionsSearch = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return {"data": click_maps.search_short_session(user_id=context.user_id, data=data, project_id=projectId)}
@app.post('/{project_id}/feature-flags/search', tags=["feature flags"],
dependencies=[OR_scope(Permissions.feature_flags)])
async def search_feature_flags(project_id: int,
data: schemas.SearchFlagsSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def search_feature_flags(project_id: int,
data: schemas.SearchFlagsSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return feature_flags.search_feature_flags(project_id=project_id, user_id=context.user_id, data=data)
@app.get('/{project_id}/feature-flags/{feature_flag_id}', tags=["feature flags"],
dependencies=[OR_scope(Permissions.feature_flags)])
async def get_feature_flag(project_id: int, feature_flag_id: int):
def get_feature_flag(project_id: int, feature_flag_id: int):
return feature_flags.get_feature_flag(project_id=project_id, feature_flag_id=feature_flag_id)
@app.post('/{project_id}/feature-flags', tags=["feature flags"], dependencies=[OR_scope(Permissions.feature_flags)])
async def add_feature_flag(project_id: int, data: schemas.FeatureFlagSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def add_feature_flag(project_id: int, data: schemas.FeatureFlagSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return feature_flags.create_feature_flag(project_id=project_id, user_id=context.user_id, feature_flag_data=data)
@app.put('/{project_id}/feature-flags/{feature_flag_id}', tags=["feature flags"],
dependencies=[OR_scope(Permissions.feature_flags)])
async def update_feature_flag(project_id: int, feature_flag_id: int, data: schemas.FeatureFlagSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def update_feature_flag(project_id: int, feature_flag_id: int, data: schemas.FeatureFlagSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return feature_flags.update_feature_flag(project_id=project_id, feature_flag_id=feature_flag_id,
user_id=context.user_id,
feature_flag=data)
@ -548,5 +548,5 @@ async def update_feature_flag(project_id: int, feature_flag_id: int, data: schem
@app.delete('/{project_id}/feature-flags/{feature_flag_id}', tags=["feature flags"],
dependencies=[OR_scope(Permissions.feature_flags)])
async def delete_feature_flag(project_id: int, feature_flag_id: int, _=Body(None)):
def delete_feature_flag(project_id: int, feature_flag_id: int, _=Body(None)):
return feature_flags.delete_feature_flag(project_id=project_id, feature_flag_id=feature_flag_id)

View file

@ -14,7 +14,7 @@ public_app, app, app_apikey = get_routers()
@app.get('/client/roles', tags=["client", "roles"])
async def get_roles(context: schemas_ee.CurrentContext = Depends(OR_context)):
def get_roles(context: schemas_ee.CurrentContext = Depends(OR_context)):
return {
'data': roles.get_roles(tenant_id=context.tenant_id)
}
@ -22,8 +22,8 @@ async def get_roles(context: schemas_ee.CurrentContext = Depends(OR_context)):
@app.post('/client/roles', tags=["client", "roles"])
@app.put('/client/roles', tags=["client", "roles"])
async def add_role(data: schemas_ee.RolePayloadSchema = Body(...),
context: schemas_ee.CurrentContext = Depends(OR_context)):
def add_role(data: schemas_ee.RolePayloadSchema = Body(...),
context: schemas_ee.CurrentContext = Depends(OR_context)):
data = roles.create(tenant_id=context.tenant_id, user_id=context.user_id, data=data)
if "errors" in data:
return data
@ -35,8 +35,8 @@ async def add_role(data: schemas_ee.RolePayloadSchema = Body(...),
@app.post('/client/roles/{roleId}', tags=["client", "roles"])
@app.put('/client/roles/{roleId}', tags=["client", "roles"])
async def edit_role(roleId: int, data: schemas_ee.RolePayloadSchema = Body(...),
context: schemas_ee.CurrentContext = Depends(OR_context)):
def edit_role(roleId: int, data: schemas_ee.RolePayloadSchema = Body(...),
context: schemas_ee.CurrentContext = Depends(OR_context)):
data = roles.update(tenant_id=context.tenant_id, user_id=context.user_id, role_id=roleId, data=data)
if "errors" in data:
return data
@ -47,7 +47,7 @@ async def edit_role(roleId: int, data: schemas_ee.RolePayloadSchema = Body(...),
@app.delete('/client/roles/{roleId}', tags=["client", "roles"])
async def delete_role(roleId: int, _=Body(None), context: schemas_ee.CurrentContext = Depends(OR_context)):
def delete_role(roleId: int, _=Body(None), context: schemas_ee.CurrentContext = Depends(OR_context)):
data = roles.delete(tenant_id=context.tenant_id, user_id=context.user_id, role_id=roleId)
if "errors" in data:
return data
@ -58,53 +58,53 @@ async def delete_role(roleId: int, _=Body(None), context: schemas_ee.CurrentCont
@app.get('/config/assist/credentials', tags=["assist"])
@app.get('/assist/credentials', tags=["assist"])
async def get_assist_credentials():
def get_assist_credentials():
return {"data": assist_helper.get_full_config()}
@app.post('/trails', tags=["traces", "trails"])
async def get_trails(data: schemas_ee.TrailSearchPayloadSchema = Body(...),
context: schemas_ee.CurrentContext = Depends(OR_context)):
def get_trails(data: schemas_ee.TrailSearchPayloadSchema = Body(...),
context: schemas_ee.CurrentContext = Depends(OR_context)):
return {
'data': traces.get_all(tenant_id=context.tenant_id, data=data)
}
@app.post('/trails/actions', tags=["traces", "trails"])
async def get_available_trail_actions(context: schemas_ee.CurrentContext = Depends(OR_context)):
def get_available_trail_actions(context: schemas_ee.CurrentContext = Depends(OR_context)):
return {'data': traces.get_available_actions(tenant_id=context.tenant_id)}
@app.put('/{projectId}/assist/save', tags=["assist"])
async def sign_record_for_upload(projectId: int, data: schemas_ee.AssistRecordPayloadSchema = Body(...),
context: schemas_ee.CurrentContext = Depends(OR_context)):
def sign_record_for_upload(projectId: int, data: schemas_ee.AssistRecordPayloadSchema = Body(...),
context: schemas_ee.CurrentContext = Depends(OR_context)):
if not sessions.session_exists(project_id=projectId, session_id=data.session_id):
return {"errors": ["Session not found"]}
return {"data": assist_records.presign_record(project_id=projectId, data=data, context=context)}
@app.put('/{projectId}/assist/save/done', tags=["assist"])
async def save_record_after_upload(projectId: int, data: schemas_ee.AssistRecordSavePayloadSchema = Body(...),
context: schemas_ee.CurrentContext = Depends(OR_context)):
def save_record_after_upload(projectId: int, data: schemas_ee.AssistRecordSavePayloadSchema = Body(...),
context: schemas_ee.CurrentContext = Depends(OR_context)):
if not sessions.session_exists(project_id=projectId, session_id=data.session_id):
return {"errors": ["Session not found"]}
return {"data": {"URL": assist_records.save_record(project_id=projectId, data=data, context=context)}}
@app.post('/{projectId}/assist/records', tags=["assist"])
async def search_records(projectId: int, data: schemas_ee.AssistRecordSearchPayloadSchema = Body(...),
context: schemas_ee.CurrentContext = Depends(OR_context)):
def search_records(projectId: int, data: schemas_ee.AssistRecordSearchPayloadSchema = Body(...),
context: schemas_ee.CurrentContext = Depends(OR_context)):
return {"data": assist_records.search_records(project_id=projectId, data=data, context=context)}
@app.get('/{projectId}/assist/records/{recordId}', tags=["assist"])
async def get_record(projectId: int, recordId: int, context: schemas_ee.CurrentContext = Depends(OR_context)):
def get_record(projectId: int, recordId: int, context: schemas_ee.CurrentContext = Depends(OR_context)):
return {"data": assist_records.get_record(project_id=projectId, record_id=recordId, context=context)}
@app.post('/{projectId}/assist/records/{recordId}', tags=["assist"])
async def update_record(projectId: int, recordId: int, data: schemas_ee.AssistRecordUpdatePayloadSchema = Body(...),
context: schemas_ee.CurrentContext = Depends(OR_context)):
def update_record(projectId: int, recordId: int, data: schemas_ee.AssistRecordUpdatePayloadSchema = Body(...),
context: schemas_ee.CurrentContext = Depends(OR_context)):
result = assist_records.update_record(project_id=projectId, record_id=recordId, data=data, context=context)
if "errors" in result:
return result
@ -112,8 +112,8 @@ async def update_record(projectId: int, recordId: int, data: schemas_ee.AssistRe
@app.delete('/{projectId}/assist/records/{recordId}', tags=["assist"])
async def delete_record(projectId: int, recordId: int, _=Body(None),
context: schemas_ee.CurrentContext = Depends(OR_context)):
def delete_record(projectId: int, recordId: int, _=Body(None),
context: schemas_ee.CurrentContext = Depends(OR_context)):
result = assist_records.delete_record(project_id=projectId, record_id=recordId, context=context)
if "errors" in result:
return result
@ -121,8 +121,8 @@ async def delete_record(projectId: int, recordId: int, _=Body(None),
@app.post('/{projectId}/signals', tags=['signals'])
async def send_interactions(projectId: int, data: schemas_ee.SignalsSchema = Body(...),
context: schemas_ee.CurrentContext = Depends(OR_context)):
def send_interactions(projectId: int, data: schemas_ee.SignalsSchema = Body(...),
context: schemas_ee.CurrentContext = Depends(OR_context)):
data = signals.handle_frontend_signals_queued(project_id=projectId, user_id=context.user_id, data=data)
if "errors" in data:
@ -132,6 +132,6 @@ async def send_interactions(projectId: int, data: schemas_ee.SignalsSchema = Bod
@app.post('/{projectId}/dashboard/insights', tags=["insights"])
@app.post('/{projectId}/dashboard/insights', tags=["insights"])
async def sessions_search(projectId: int, data: schemas_ee.GetInsightsSchema = Body(...),
context: schemas_ee.CurrentContext = Depends(OR_context)):
def sessions_search(projectId: int, data: schemas_ee.GetInsightsSchema = Body(...),
context: schemas_ee.CurrentContext = Depends(OR_context)):
return {'data': sessions_insights.fetch_selected(data=data, project_id=projectId)}

View file

@ -17,7 +17,7 @@ from starlette.responses import RedirectResponse
@public_app.get("/sso/saml2/", tags=["saml2"])
async def start_sso(request: Request):
request.path = ''
req = await prepare_request(request=request, initial=True)
req = await prepare_request(request=request)
auth = init_saml_auth(req)
sso_built_url = auth.login()
return RedirectResponse(url=sso_built_url)

View file

@ -10,13 +10,13 @@ public_app, app, app_apikey = get_routers()
@app.get('/healthz', tags=["health-check"])
async def get_global_health_status(context: schemas.CurrentContext = Depends(OR_context)):
def get_global_health_status(context: schemas.CurrentContext = Depends(OR_context)):
return {"data": health.get_health(tenant_id=context.tenant_id)}
if not tenants.tenants_exists(use_pool=False):
@public_app.get('/health', tags=["health-check"])
async def get_public_health_status():
def get_public_health_status():
if tenants.tenants_exists():
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Not Found")

View file

@ -14,18 +14,18 @@ public_app, app, app_apikey = get_routers([OR_scope(Permissions.metrics)])
@app.post('/{projectId}/dashboards', tags=["dashboard"])
@app.put('/{projectId}/dashboards', tags=["dashboard"])
async def create_dashboards(projectId: int, data: schemas.CreateDashboardSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
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"])
async def get_dashboards(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
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"])
async def get_dashboard(projectId: int, dashboardId: int, context: schemas.CurrentContext = Depends(OR_context)):
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"]}
@ -34,60 +34,60 @@ async def get_dashboard(projectId: int, dashboardId: int, context: schemas.Curre
@app.post('/{projectId}/dashboards/{dashboardId}', tags=["dashboard"])
@app.put('/{projectId}/dashboards/{dashboardId}', tags=["dashboard"])
async def update_dashboard(projectId: int, dashboardId: int, data: schemas.EditDashboardSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
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"])
async def delete_dashboard(projectId: int, dashboardId: int, _=Body(None),
context: schemas.CurrentContext = Depends(OR_context)):
def delete_dashboard(projectId: int, dashboardId: int, _=Body(None),
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"])
async def pin_dashboard(projectId: int, dashboardId: int, context: schemas.CurrentContext = Depends(OR_context)):
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}/cards', tags=["cards"])
@app.post('/{projectId}/dashboards/{dashboardId}/widgets', tags=["dashboard"])
@app.put('/{projectId}/dashboards/{dashboardId}/widgets', tags=["dashboard"])
async def add_card_to_dashboard(projectId: int, dashboardId: int,
data: schemas.AddWidgetToDashboardPayloadSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def add_card_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"])
async def create_metric_and_add_to_dashboard(projectId: int, dashboardId: int,
data: schemas_ee.CardSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def create_metric_and_add_to_dashboard(projectId: int, dashboardId: int,
data: schemas_ee.CardSchema = 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"])
async def update_widget_in_dashboard(projectId: int, dashboardId: int, widgetId: int,
data: schemas.UpdateWidgetPayloadSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
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"])
async def remove_widget_from_dashboard(projectId: int, dashboardId: int, widgetId: int, _=Body(None),
context: schemas.CurrentContext = Depends(OR_context)):
def remove_widget_from_dashboard(projectId: int, dashboardId: int, widgetId: int, _=Body(None),
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"])
# async def get_widget_chart(projectId: int, dashboardId: int, widgetId: int,
# def get_widget_chart(projectId: int, dashboardId: int, widgetId: int,
# data: schemas.CardChartSchema = Body(...),
# context: schemas.CurrentContext = Depends(OR_context)):
# data = dashboards.make_chart_widget(project_id=projectId, user_id=context.user_id, dashboard_id=dashboardId,
@ -102,16 +102,16 @@ async def remove_widget_from_dashboard(projectId: int, dashboardId: int, widgetI
@app.put('/{projectId}/metrics/try', tags=["dashboard"])
@app.post('/{projectId}/custom_metrics/try', tags=["customMetrics"])
@app.put('/{projectId}/custom_metrics/try', tags=["customMetrics"])
async def try_card(projectId: int, data: schemas_ee.CardSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def try_card(projectId: int, data: schemas_ee.CardSchema = 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}/cards/try/sessions', tags=["cards"])
@app.post('/{projectId}/metrics/try/sessions', tags=["dashboard"])
@app.post('/{projectId}/custom_metrics/try/sessions', tags=["customMetrics"])
async def try_card_sessions(projectId: int, data: schemas.CardSessionsSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def try_card_sessions(projectId: int, data: schemas.CardSessionsSchema = 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}
@ -119,8 +119,8 @@ async def try_card_sessions(projectId: int, data: schemas.CardSessionsSchema = B
@app.post('/{projectId}/cards/try/issues', tags=["cards"])
@app.post('/{projectId}/metrics/try/issues', tags=["dashboard"])
@app.post('/{projectId}/custom_metrics/try/issues', tags=["customMetrics"])
async def try_card_funnel_issues(projectId: int, data: schemas.CardSessionsSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def try_card_funnel_issues(projectId: int, data: schemas.CardSessionsSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
if len(data.series) == 0:
return {"data": []}
data.series[0].filter.startDate = data.startTimestamp
@ -132,7 +132,7 @@ async def try_card_funnel_issues(projectId: int, data: schemas.CardSessionsSchem
@app.get('/{projectId}/cards', tags=["cards"])
@app.get('/{projectId}/metrics', tags=["dashboard"])
@app.get('/{projectId}/custom_metrics', tags=["customMetrics"])
async def get_cards(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
def get_cards(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
return {"data": custom_metrics.get_all(project_id=projectId, user_id=context.user_id)}
@ -141,23 +141,23 @@ async def get_cards(projectId: int, context: schemas.CurrentContext = Depends(OR
@app.put('/{projectId}/metrics', tags=["dashboard"])
@app.post('/{projectId}/custom_metrics', tags=["customMetrics"])
@app.put('/{projectId}/custom_metrics', tags=["customMetrics"])
async def create_card(projectId: int, data: schemas_ee.CardSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def create_card(projectId: int, data: schemas_ee.CardSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return custom_metrics.create(project_id=projectId, user_id=context.user_id, data=data)
@app.post('/{projectId}/cards/search', tags=["cards"])
@app.post('/{projectId}/metrics/search', tags=["dashboard"])
@app.post('/{projectId}/custom_metrics/search', tags=["customMetrics"])
async def search_cards(projectId: int, data: schemas.SearchCardsSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def search_cards(projectId: int, data: schemas.SearchCardsSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
return {"data": custom_metrics.search_all(project_id=projectId, user_id=context.user_id, data=data)}
@app.get('/{projectId}/cards/{metric_id}', tags=["cards"])
@app.get('/{projectId}/metrics/{metric_id}', tags=["dashboard"])
@app.get('/{projectId}/custom_metrics/{metric_id}', tags=["customMetrics"])
async def get_card(projectId: int, metric_id: Union[int, str], context: schemas.CurrentContext = Depends(OR_context)):
def get_card(projectId: int, metric_id: Union[int, str], context: schemas.CurrentContext = Depends(OR_context)):
if not isinstance(metric_id, int):
return {"errors": ["invalid card_id"]}
data = custom_metrics.get_card(project_id=projectId, user_id=context.user_id, metric_id=metric_id)
@ -167,7 +167,7 @@ async def get_card(projectId: int, metric_id: Union[int, str], context: schemas.
# @app.get('/{projectId}/cards/{metric_id}/thumbnail', tags=["cards"])
# async def sign_thumbnail_for_upload(projectId: int, metric_id: Union[int, str],
# def sign_thumbnail_for_upload(projectId: int, metric_id: Union[int, str],
# context: schemas.CurrentContext = Depends(OR_context)):
# if not isinstance(metric_id, int):
# return {"errors": ["invalid card_id"]}
@ -177,9 +177,9 @@ async def get_card(projectId: int, metric_id: Union[int, str], context: schemas.
@app.post('/{projectId}/cards/{metric_id}/sessions', tags=["cards"])
@app.post('/{projectId}/metrics/{metric_id}/sessions', tags=["dashboard"])
@app.post('/{projectId}/custom_metrics/{metric_id}/sessions', tags=["customMetrics"])
async def get_card_sessions(projectId: int, metric_id: int,
data: schemas.CardSessionsSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def get_card_sessions(projectId: int, metric_id: int,
data: schemas.CardSessionsSchema = 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"]}
@ -189,9 +189,9 @@ async def get_card_sessions(projectId: int, metric_id: int,
@app.post('/{projectId}/cards/{metric_id}/issues', tags=["cards"])
@app.post('/{projectId}/metrics/{metric_id}/issues', tags=["dashboard"])
@app.post('/{projectId}/custom_metrics/{metric_id}/issues', tags=["customMetrics"])
async def get_card_funnel_issues(projectId: int, metric_id: Union[int, str],
data: schemas.CardSessionsSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def get_card_funnel_issues(projectId: int, metric_id: Union[int, str],
data: schemas.CardSessionsSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
if not isinstance(metric_id, int):
return {"errors": [f"invalid card_id: {metric_id}"]}
@ -205,9 +205,9 @@ async def get_card_funnel_issues(projectId: int, metric_id: Union[int, str],
@app.post('/{projectId}/cards/{metric_id}/issues/{issueId}/sessions', tags=["dashboard"])
@app.post('/{projectId}/metrics/{metric_id}/issues/{issueId}/sessions', tags=["dashboard"])
@app.post('/{projectId}/custom_metrics/{metric_id}/issues/{issueId}/sessions', tags=["customMetrics"])
async def get_metric_funnel_issue_sessions(projectId: int, metric_id: int, issueId: str,
data: schemas.CardSessionsSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def get_metric_funnel_issue_sessions(projectId: int, metric_id: int, issueId: str,
data: schemas.CardSessionsSchema = 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:
@ -218,9 +218,9 @@ async def get_metric_funnel_issue_sessions(projectId: int, metric_id: int, issue
@app.post('/{projectId}/cards/{metric_id}/errors', tags=["dashboard"])
@app.post('/{projectId}/metrics/{metric_id}/errors', tags=["dashboard"])
@app.post('/{projectId}/custom_metrics/{metric_id}/errors', tags=["customMetrics"])
async def get_custom_metric_errors_list(projectId: int, metric_id: int,
data: schemas.CardSessionsSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def get_custom_metric_errors_list(projectId: int, metric_id: int,
data: schemas.CardSessionsSchema = 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:
@ -231,8 +231,8 @@ async def get_custom_metric_errors_list(projectId: int, metric_id: int,
@app.post('/{projectId}/cards/{metric_id}/chart', tags=["card"])
@app.post('/{projectId}/metrics/{metric_id}/chart', tags=["dashboard"])
@app.post('/{projectId}/custom_metrics/{metric_id}/chart', tags=["customMetrics"])
async def get_card_chart(projectId: int, metric_id: int, request: Request, data: schemas.CardChartSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def get_card_chart(projectId: int, metric_id: int, request: Request, data: schemas.CardChartSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
data = custom_metrics.make_chart_from_card(project_id=projectId, user_id=context.user_id, metric_id=metric_id,
data=data)
return {"data": data}
@ -243,8 +243,8 @@ async def get_card_chart(projectId: int, metric_id: int, request: Request, data:
@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"])
async def update_custom_metric(projectId: int, metric_id: int, data: schemas_ee.UpdateCardSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def update_custom_metric(projectId: int, metric_id: int, data: schemas_ee.UpdateCardSchema = 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"]}
@ -256,9 +256,9 @@ async def update_custom_metric(projectId: int, metric_id: int, data: schemas_ee.
@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"])
async def update_custom_metric_state(projectId: int, metric_id: int,
data: schemas.UpdateCustomMetricsStatusSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
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)}
@ -267,6 +267,6 @@ async def update_custom_metric_state(projectId: int, metric_id: int,
@app.delete('/{projectId}/cards/{metric_id}', tags=["dashboard"])
@app.delete('/{projectId}/metrics/{metric_id}', tags=["dashboard"])
@app.delete('/{projectId}/custom_metrics/{metric_id}', tags=["customMetrics"])
async def delete_custom_metric(projectId: int, metric_id: int, _=Body(None),
context: schemas.CurrentContext = Depends(OR_context)):
def delete_custom_metric(projectId: int, metric_id: int, _=Body(None),
context: schemas.CurrentContext = Depends(OR_context)):
return {"data": custom_metrics.delete(project_id=projectId, user_id=context.user_id, metric_id=metric_id)}

View file

@ -11,7 +11,7 @@ public_app, app, app_apikey = get_routers()
@app_apikey.get('/v1/assist/credentials', tags=["api"])
async def get_assist_credentials():
def get_assist_credentials():
credentials = assist_helper.get_temporary_credentials()
if "errors" in credentials:
return credentials
@ -19,17 +19,17 @@ async def get_assist_credentials():
@app_apikey.get('/v1/{projectKey}/assist/sessions', tags=["api"])
async def get_sessions_live(projectKey: str, userId: str = None, context: schemas.CurrentContext = Depends(OR_context)):
def get_sessions_live(projectKey: str, userId: str = None, context: schemas.CurrentContext = Depends(OR_context)):
projectId = projects.get_internal_project_id(projectKey)
if projectId is None:
return {"errors": ["invalid projectKey"]}
return await core.get_sessions_live(projectId=projectId, userId=userId, context=context)
return core.get_sessions_live(projectId=projectId, userId=userId, context=context)
@app_apikey.post('/v1/{projectKey}/assist/sessions', tags=["api"])
async def sessions_live(projectKey: str, data: schemas.LiveSessionsSearchPayloadSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
def sessions_live(projectKey: str, data: schemas.LiveSessionsSearchPayloadSchema = Body(...),
context: schemas.CurrentContext = Depends(OR_context)):
projectId = projects.get_internal_project_id(projectKey)
if projectId is None:
return {"errors": ["invalid projectKey"]}
return await core.sessions_live(projectId=projectId, data=data, context=context)
return core.sessions_live(projectId=projectId, data=data, context=context)

View file

@ -0,0 +1,15 @@
CREATE OR REPLACE FUNCTION openreplay_version AS() -> 'v1.14.0-ee';
CREATE TABLE IF NOT EXISTS experimental.sessions_feature_flags
(
session_id UInt64,
project_id UInt16,
feature_flag_id UInt16,
condition_id UInt16,
datetime DateTime,
_timestamp DateTime DEFAULT now()
) ENGINE = ReplacingMergeTree(_timestamp)
PARTITION BY toYYYYMM(datetime)
ORDER BY (project_id, datetime, session_id, feature_flag_id, condition_id)
TTL datetime + INTERVAL 3 MONTH;

View file

@ -390,3 +390,17 @@ WHERE datetime >= now() - INTERVAL 7 DAY
-- INNER JOIN experimental.sessions USING (session_id)
-- WHERE event_type = 'ERROR'
-- AND source = 'js_exception';
CREATE TABLE IF NOT EXISTS experimental.sessions_feature_flags
(
session_id UInt64,
project_id UInt16,
feature_flag_id UInt16,
condition_id UInt16,
datetime DateTime,
_timestamp DateTime DEFAULT now()
) ENGINE = ReplacingMergeTree(_timestamp)
PARTITION BY toYYYYMM(datetime)
ORDER BY (project_id, datetime, session_id, feature_flag_id, condition_id)
TTL datetime + INTERVAL 3 MONTH;

View file

@ -47,6 +47,13 @@ CREATE TABLE IF NOT EXISTS public.feature_flags_conditions
filters jsonb NOT NULL DEFAULT '[]'::jsonb
);
CREATE TABLE IF NOT EXISTS public.sessions_feature_flags
(
session_id bigint NOT NULL REFERENCES sessions (session_id) ON DELETE CASCADE,
feature_flag_id integer NOT NULL REFERENCES feature_flags (feature_flag_id) ON DELETE CASCADE,
condition_id integer NULL REFERENCES feature_flags_conditions (condition_id) ON DELETE SET NULL
);
UPDATE public.roles
SET permissions = (SELECT array_agg(distinct e) FROM unnest(permissions || '{FEATURE_FLAGS}') AS e)
where not permissions @> '{FEATURE_FLAGS}';

View file

@ -135,7 +135,8 @@ $$
('projects_stats'),
('frontend_signals'),
('feature_flags'),
('feature_flags_conditions'))
('feature_flags_conditions'),
('sessions_feature_flags'))
select bool_and(exists(select *
from information_schema.tables t
where table_schema = 'public'
@ -897,7 +898,7 @@ $$
CREATE INDEX IF NOT EXISTS projects_stats_project_id_idx ON public.projects_stats (project_id);
CREATE TABLE IF NOT EXISTS public.feature_flags
CREATE TABLE IF NOT EXISTS public.feature_flags
(
feature_flag_id integer generated BY DEFAULT AS IDENTITY PRIMARY KEY,
project_id integer NOT NULL REFERENCES projects (project_id) ON DELETE CASCADE,
@ -925,6 +926,13 @@ $$
filters jsonb NOT NULL DEFAULT '[]'::jsonb
);
CREATE TABLE IF NOT EXISTS public.sessions_feature_flags
(
session_id bigint NOT NULL REFERENCES sessions (session_id) ON DELETE CASCADE,
feature_flag_id integer NOT NULL REFERENCES feature_flags (feature_flag_id) ON DELETE CASCADE,
condition_id integer NULL REFERENCES feature_flags_conditions (condition_id) ON DELETE SET NULL
);
RAISE NOTICE 'Created missing public schema tables';
END IF;
END;

View file

@ -24,8 +24,8 @@ CREATE TABLE IF NOT EXISTS public.feature_flags
feature_flag_id integer generated BY DEFAULT AS IDENTITY PRIMARY KEY,
project_id integer NOT NULL REFERENCES projects (project_id) ON DELETE CASCADE,
flag_key text NOT NULL,
description text DEFAULT NULL::text,
payload text DEFAULT NULL::text,
description text DEFAULT NULL::text,
payload text DEFAULT NULL::text,
flag_type text NOT NULL,
is_persist boolean NOT NULL DEFAULT FALSE,
is_active boolean NOT NULL DEFAULT FALSE,
@ -52,12 +52,19 @@ CREATE TABLE IF NOT EXISTS public.feature_flags_conditions
CREATE TABLE IF NOT EXISTS public.feature_flags_variants
(
variant_id integer generated BY DEFAULT AS IDENTITY PRIMARY KEY,
feature_flag_id integer NOT NULL REFERENCES feature_flags (feature_flag_id) ON DELETE CASCADE,
value text NOT NULL,
description text DEFAULT NULL::text,
payload jsonb DEFAULT NULL,
rollout_percentage integer DEFAULT 0
variant_id integer generated BY DEFAULT AS IDENTITY PRIMARY KEY,
feature_flag_id integer NOT NULL REFERENCES feature_flags (feature_flag_id) ON DELETE CASCADE,
value text NOT NULL,
description text DEFAULT NULL::text,
payload jsonb DEFAULT NULL,
rollout_percentage integer DEFAULT 0
);
CREATE TABLE IF NOT EXISTS public.sessions_feature_flags
(
session_id bigint NOT NULL REFERENCES sessions (session_id) ON DELETE CASCADE,
feature_flag_id integer NOT NULL REFERENCES feature_flags (feature_flag_id) ON DELETE CASCADE,
condition_id integer NULL REFERENCES feature_flags_conditions (condition_id) ON DELETE SET NULL
);
COMMIT;

View file

@ -1006,6 +1006,13 @@ $$
filters jsonb NOT NULL DEFAULT '[]'::jsonb
);
CREATE TABLE IF NOT EXISTS public.sessions_feature_flags
(
session_id bigint NOT NULL REFERENCES sessions (session_id) ON DELETE CASCADE,
feature_flag_id integer NOT NULL REFERENCES feature_flags (feature_flag_id) ON DELETE CASCADE,
condition_id integer NULL REFERENCES feature_flags_conditions (condition_id) ON DELETE SET NULL
);
raise notice 'DB created';
END IF;
END;