feat(api): tag and watch (#1834)
This commit is contained in:
parent
622b189d39
commit
97ee6c725c
12 changed files with 161 additions and 11 deletions
|
|
@ -98,6 +98,22 @@ def get_by_session_id(session_id, project_id, group_clickrage=False, event_type:
|
|||
return rows
|
||||
|
||||
|
||||
def _search_tags(project_id, value, key=None, source=None):
|
||||
with pg_client.PostgresClient() as cur:
|
||||
query = f"""
|
||||
SELECT public.tags.name
|
||||
'{events.EventType.TAG.ui_type}' AS type
|
||||
FROM public.tags
|
||||
WHERE public.tags.project_id = %(project_id)s
|
||||
ORDER BY SIMILARITY(public.tags.name, %(value)s) DESC
|
||||
LIMIT 10
|
||||
"""
|
||||
query = cur.mogrify(query, {'project_id': project_id, 'value': value})
|
||||
cur.execute(query)
|
||||
results = helper.list_to_camel_case(cur.fetchall())
|
||||
return results
|
||||
|
||||
|
||||
class EventType:
|
||||
CLICK = Event(ui_type=schemas.EventType.click, table="events.clicks", column="label")
|
||||
INPUT = Event(ui_type=schemas.EventType.input, table="events.inputs", column="label")
|
||||
|
|
@ -106,6 +122,7 @@ class EventType:
|
|||
REQUEST = Event(ui_type=schemas.EventType.request, table="events_common.requests", column="path")
|
||||
GRAPHQL = Event(ui_type=schemas.EventType.graphql, table="events.graphql", column="name")
|
||||
STATEACTION = Event(ui_type=schemas.EventType.state_action, table="events.state_actions", column="name")
|
||||
TAG = Event(ui_type=schemas.EventType.tag, table="events.tags", column="tag_id")
|
||||
ERROR = Event(ui_type=schemas.EventType.error, table="events.errors",
|
||||
column=None) # column=None because errors are searched by name or message
|
||||
METADATA = Event(ui_type=schemas.FilterType.metadata, table="public.sessions", column=None)
|
||||
|
|
@ -139,6 +156,7 @@ SUPPORTED_TYPES = {
|
|||
EventType.STATEACTION.ui_type: SupportedFilter(get=autocomplete.__generic_autocomplete(EventType.STATEACTION),
|
||||
query=autocomplete.__generic_query(
|
||||
typename=EventType.STATEACTION.ui_type)),
|
||||
EventType.TAG.ui_type: SupportedFilter(get=_search_tags, query=None),
|
||||
EventType.ERROR.ui_type: SupportedFilter(get=autocomplete.__search_errors,
|
||||
query=None),
|
||||
EventType.METADATA.ui_type: SupportedFilter(get=autocomplete.__search_metadata,
|
||||
|
|
|
|||
|
|
@ -712,6 +712,11 @@ def search_query_parts(data: schemas.SessionsSearchPayloadSchema, error_status,
|
|||
sh.multi_conditions(f"main.{events.EventType.CLICK_IOS.column} {op} %({e_k})s", event.value,
|
||||
value_key=e_k))
|
||||
|
||||
elif event_type == events.EventType.TAG.ui_type:
|
||||
event_from = event_from % f"{events.EventType.TAG.table} AS main "
|
||||
if not is_any:
|
||||
event_where.append(
|
||||
sh.multi_conditions(f"main.tag_id = %({e_k})s", event.value, value_key=e_k))
|
||||
elif event_type == events.EventType.INPUT.ui_type:
|
||||
if platform == "web":
|
||||
event_from = event_from % f"{events.EventType.INPUT.table} AS main "
|
||||
|
|
|
|||
69
api/chalicelib/core/tags.py
Normal file
69
api/chalicelib/core/tags.py
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
import schemas
|
||||
from chalicelib.utils import helper
|
||||
from chalicelib.utils import pg_client
|
||||
|
||||
|
||||
def create_tag(project_id: int, data: schemas.TagCreate) -> int:
|
||||
query = """
|
||||
INSERT INTO public.tags (project_id, name, selector, ignore_click_rage, ignore_dead_click)
|
||||
VALUES (%(project_id)s, %(name)s, %(selector)s, %(ignore_click_rage)s, %(ignore_dead_click)s)
|
||||
RETURNING tag_id;
|
||||
"""
|
||||
|
||||
data = {
|
||||
'project_id': project_id,
|
||||
'name': data.name.strip(),
|
||||
'selector': data.selector,
|
||||
'ignore_click_rage': data.ignoreClickRage,
|
||||
'ignore_dead_click': data.ignoreDeadClick
|
||||
}
|
||||
|
||||
with pg_client.PostgresClient() as cur:
|
||||
query = cur.mogrify(query, data)
|
||||
cur.execute(query)
|
||||
row = cur.fetchone()
|
||||
|
||||
return row['tag_id']
|
||||
|
||||
|
||||
def list_tags(project_id: int):
|
||||
query = """
|
||||
SELECT tag_id, name, selector, ignore_click_rage, ignore_dead_click
|
||||
FROM public.tags
|
||||
WHERE project_id = %(project_id)s
|
||||
AND deleted_at IS NULL
|
||||
"""
|
||||
|
||||
with pg_client.PostgresClient() as cur:
|
||||
query = cur.mogrify(query, {'project_id': project_id})
|
||||
cur.execute(query)
|
||||
rows = cur.fetchall()
|
||||
|
||||
return helper.list_to_camel_case(rows)
|
||||
|
||||
|
||||
def update_tag(project_id: int, tag_id: int, data: schemas.TagUpdate):
|
||||
query = """
|
||||
UPDATE public.tags
|
||||
SET name = %(name)s
|
||||
WHERE tag_id = %(tag_id)s AND project_id = %(project_id)s
|
||||
"""
|
||||
|
||||
with pg_client.PostgresClient() as cur:
|
||||
query = cur.mogrify(query, {'tag_id': tag_id, 'name': data.name, 'project_id': project_id})
|
||||
cur.execute(query)
|
||||
|
||||
return True
|
||||
|
||||
def delete_tag(project_id: int, tag_id: int):
|
||||
query = """
|
||||
UPDATE public.tags
|
||||
SET deleted_at = now() at time zone 'utc'
|
||||
WHERE tag_id = %(tag_id)s AND project_id = %(project_id)s
|
||||
"""
|
||||
|
||||
with pg_client.PostgresClient() as cur:
|
||||
query = cur.mogrify(query, {'tag_id': tag_id, 'project_id': project_id})
|
||||
cur.execute(query)
|
||||
|
||||
return True
|
||||
|
|
@ -10,7 +10,7 @@ from chalicelib.core import log_tool_rollbar, sourcemaps, events, sessions_assig
|
|||
log_tool_stackdriver, reset_password, log_tool_cloudwatch, log_tool_sentry, log_tool_sumologic, log_tools, sessions, \
|
||||
log_tool_newrelic, announcements, log_tool_bugsnag, weekly_report, integration_jira_cloud, integration_github, \
|
||||
assist, mobile, tenants, boarding, notifications, webhook, users, \
|
||||
custom_metrics, saved_search, integrations_global
|
||||
custom_metrics, saved_search, integrations_global, tags
|
||||
from chalicelib.core.collaboration_msteams import MSTeams
|
||||
from chalicelib.core.collaboration_slack import Slack
|
||||
from or_dependencies import OR_context, OR_role
|
||||
|
|
@ -871,3 +871,28 @@ async def check_recording_status(project_id: int):
|
|||
@public_app.get('/', tags=["health"])
|
||||
def health_check():
|
||||
return {}
|
||||
|
||||
# tags
|
||||
|
||||
@app.post('/{projectId}/tags', tags=["tags"])
|
||||
def tags_create(projectId: int, data: schemas.TagCreate = Body(), context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = tags.create_tag(project_id=projectId, data=data)
|
||||
return {'data': data}
|
||||
|
||||
|
||||
@app.put('/{projectId}/tags/{tagId}', tags=["tags"])
|
||||
def tags_update(projectId: int, tagId: int, data: schemas.TagUpdate = Body(), context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = tags.update_tag(project_id=projectId, tag_id=tagId, data=data)
|
||||
return {'data': data}
|
||||
|
||||
|
||||
@app.get('/{projectId}/tags', tags=["tags"])
|
||||
def tags_list(projectId: int, context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = tags.list_tags(project_id=projectId)
|
||||
return {'data': data}
|
||||
|
||||
|
||||
@app.delete('/{projectId}/tags/{tagId}', tags=["tags"])
|
||||
def tags_delete(projectId: int, tagId: int, context: schemas.CurrentContext = Depends(OR_context)):
|
||||
data = tags.delete_tag(projectId, tag_id=tagId)
|
||||
return {'data': data}
|
||||
|
|
|
|||
|
|
@ -470,6 +470,7 @@ class EventType(str, Enum):
|
|||
graphql = "graphql"
|
||||
state_action = "stateAction"
|
||||
error = "error"
|
||||
tag = "tag"
|
||||
click_ios = "tapIos"
|
||||
input_ios = "inputIos"
|
||||
view_ios = "viewIos"
|
||||
|
|
@ -606,7 +607,7 @@ class RequestGraphqlFilterSchema(BaseModel):
|
|||
|
||||
class SessionSearchEventSchema2(BaseModel):
|
||||
is_event: Literal[True] = True
|
||||
value: List[str] = Field(...)
|
||||
value: List[Union[str, int]] = Field(...)
|
||||
type: Union[EventType, PerformanceEventType] = Field(...)
|
||||
operator: Union[SearchEventOperator, ClickEventExtraOperator] = Field(...)
|
||||
source: Optional[List[Union[ErrorSource, int, str]]] = Field(default=None)
|
||||
|
|
@ -1576,3 +1577,15 @@ class ModuleStatus(BaseModel):
|
|||
"offline-recordings", "alerts", "assist-statts", "recommendations", "feature-flags"] = Field(...,
|
||||
description="Possible values: assist, notes, bug-reports, offline-recordings, alerts, assist-statts, recommendations, feature-flags")
|
||||
status: bool = Field(...)
|
||||
|
||||
|
||||
class TagUpdate(BaseModel):
|
||||
name: str = Field(..., min_length=1, max_length=100, pattern='^[a-zA-Z0-9][a-zA-Z0-9_ -]+$')
|
||||
|
||||
|
||||
class TagCreate(TagUpdate):
|
||||
selector: str = Field(..., min_length=1, max_length=255)
|
||||
ignoreClickRage: bool = Field(default=False)
|
||||
ignoreDeadClick: bool = Field(default=False)
|
||||
|
||||
|
||||
|
|
|
|||
1
ee/api/.gitignore
vendored
1
ee/api/.gitignore
vendored
|
|
@ -230,6 +230,7 @@ Pipfile.lock
|
|||
/chalicelib/core/socket_ios.py
|
||||
/chalicelib/core/sourcemaps.py
|
||||
/chalicelib/core/sourcemaps_parser.py
|
||||
/chalicelib/core/tags.py
|
||||
/chalicelib/saml
|
||||
/chalicelib/utils/__init__.py
|
||||
/chalicelib/utils/args_transformer.py
|
||||
|
|
|
|||
|
|
@ -104,6 +104,22 @@ def get_by_session_id(session_id, project_id, group_clickrage=False, event_type:
|
|||
return rows
|
||||
|
||||
|
||||
def _search_tags(project_id, value, key=None, source=None):
|
||||
with pg_client.PostgresClient() as cur:
|
||||
query = f"""
|
||||
SELECT public.tags.name
|
||||
'{events.EventType.TAG.ui_type}' AS type
|
||||
FROM public.tags
|
||||
WHERE public.tags.project_id = %(project_id)s
|
||||
ORDER BY SIMILARITY(public.tags.name, %(value)s) DESC
|
||||
LIMIT 10
|
||||
"""
|
||||
query = cur.mogrify(query, {'project_id': project_id, 'value': value})
|
||||
cur.execute(query)
|
||||
results = helper.list_to_camel_case(cur.fetchall())
|
||||
return results
|
||||
|
||||
|
||||
class EventType:
|
||||
CLICK = Event(ui_type=schemas.EventType.click, table="events.clicks", column="label")
|
||||
INPUT = Event(ui_type=schemas.EventType.input, table="events.inputs", column="label")
|
||||
|
|
@ -112,6 +128,7 @@ class EventType:
|
|||
REQUEST = Event(ui_type=schemas.EventType.request, table="events_common.requests", column="path")
|
||||
GRAPHQL = Event(ui_type=schemas.EventType.graphql, table="events.graphql", column="name")
|
||||
STATEACTION = Event(ui_type=schemas.EventType.state_action, table="events.state_actions", column="name")
|
||||
TAG = Event(ui_type=schemas.EventType.tag, table="events.tags", column="tag_id")
|
||||
ERROR = Event(ui_type=schemas.EventType.error, table="events.errors",
|
||||
column=None) # column=None because errors are searched by name or message
|
||||
METADATA = Event(ui_type=schemas.FilterType.metadata, table="public.sessions", column=None)
|
||||
|
|
@ -145,6 +162,7 @@ SUPPORTED_TYPES = {
|
|||
EventType.STATEACTION.ui_type: SupportedFilter(get=autocomplete.__generic_autocomplete(EventType.STATEACTION),
|
||||
query=autocomplete.__generic_query(
|
||||
typename=EventType.STATEACTION.ui_type)),
|
||||
EventType.TAG.ui_type: SupportedFilter(get=_search_tags, query=None),
|
||||
EventType.ERROR.ui_type: SupportedFilter(get=autocomplete.__search_errors,
|
||||
query=None),
|
||||
EventType.METADATA.ui_type: SupportedFilter(get=autocomplete.__search_metadata,
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ rm -rf ./chalicelib/core/socket_ios.py
|
|||
rm -rf ./chalicelib/core/sourcemaps.py
|
||||
rm -rf ./chalicelib/core/sourcemaps_parser.py
|
||||
rm -rf ./chalicelib/core/user_testing.py
|
||||
rm -rf ./chalicelib/core/tags.py
|
||||
rm -rf ./chalicelib/saml
|
||||
rm -rf ./chalicelib/utils/__init__.py
|
||||
rm -rf ./chalicelib/utils/args_transformer.py
|
||||
|
|
@ -93,4 +94,4 @@ rm -rf ./schemas/overrides.py
|
|||
rm -rf ./schemas/schemas.py
|
||||
rm -rf ./schemas/transformers_validators.py
|
||||
rm -rf ./orpy.py
|
||||
rm -rf ./chalicelib/core/usability_testing/
|
||||
rm -rf ./chalicelib/core/usability_testing/
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ CREATE TABLE IF NOT EXISTS public.projects_conditions
|
|||
|
||||
CREATE TABLE IF NOT EXISTS public.tags
|
||||
(
|
||||
tag_id bigint NOT NULL PRIMARY KEY,
|
||||
tag_id serial NOT NULL PRIMARY KEY,
|
||||
name text NOT NULL,
|
||||
project_id integer NOT NULL REFERENCES public.projects (project_id) ON DELETE CASCADE,
|
||||
selector text NOT NULL,
|
||||
|
|
@ -58,7 +58,7 @@ CREATE TABLE IF NOT EXISTS events.tags
|
|||
session_id bigint NOT NULL REFERENCES public.sessions (session_id) ON DELETE CASCADE,
|
||||
timestamp bigint NOT NULL,
|
||||
seq_index integer NOT NULL,
|
||||
tag_id bigint NOT NULL REFERENCES public.tags (tag_id) ON DELETE CASCADE,
|
||||
tag_id integer NOT NULL REFERENCES public.tags (tag_id) ON DELETE CASCADE,
|
||||
PRIMARY KEY (session_id, timestamp, seq_index)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS tags_session_id_idx ON events.tags (session_id);
|
||||
|
|
|
|||
|
|
@ -1156,7 +1156,7 @@ $$
|
|||
|
||||
CREATE TABLE public.tags
|
||||
(
|
||||
tag_id bigint NOT NULL PRIMARY KEY,
|
||||
tag_id serial NOT NULL PRIMARY KEY,
|
||||
name text NOT NULL,
|
||||
project_id integer NOT NULL REFERENCES public.projects (project_id) ON DELETE CASCADE,
|
||||
selector text NOT NULL,
|
||||
|
|
@ -1171,7 +1171,7 @@ $$
|
|||
session_id bigint NOT NULL REFERENCES public.sessions (session_id) ON DELETE CASCADE,
|
||||
timestamp bigint NOT NULL,
|
||||
seq_index integer NOT NULL,
|
||||
tag_id bigint NOT NULL REFERENCES public.tags (tag_id) ON DELETE CASCADE,
|
||||
tag_id integer NOT NULL REFERENCES public.tags (tag_id) ON DELETE CASCADE,
|
||||
PRIMARY KEY (session_id, timestamp, seq_index)
|
||||
);
|
||||
CREATE INDEX tags_session_id_idx ON events.tags (session_id);
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ CREATE TABLE IF NOT EXISTS public.projects_conditions
|
|||
|
||||
CREATE TABLE IF NOT EXISTS public.tags
|
||||
(
|
||||
tag_id bigint NOT NULL PRIMARY KEY,
|
||||
tag_id serial NOT NULL PRIMARY KEY,
|
||||
name text NOT NULL,
|
||||
project_id integer NOT NULL REFERENCES public.projects (project_id) ON DELETE CASCADE,
|
||||
selector text NOT NULL,
|
||||
|
|
@ -58,7 +58,7 @@ CREATE TABLE IF NOT EXISTS events.tags
|
|||
session_id bigint NOT NULL REFERENCES public.sessions (session_id) ON DELETE CASCADE,
|
||||
timestamp bigint NOT NULL,
|
||||
seq_index integer NOT NULL,
|
||||
tag_id bigint NOT NULL REFERENCES public.tags (tag_id) ON DELETE CASCADE,
|
||||
tag_id integer NOT NULL REFERENCES public.tags (tag_id) ON DELETE CASCADE,
|
||||
PRIMARY KEY (session_id, timestamp, seq_index)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS tags_session_id_idx ON events.tags (session_id);
|
||||
|
|
|
|||
|
|
@ -1117,7 +1117,7 @@ $$
|
|||
|
||||
CREATE TABLE public.tags
|
||||
(
|
||||
tag_id bigint NOT NULL PRIMARY KEY,
|
||||
tag_id serial NOT NULL PRIMARY KEY,
|
||||
name text NOT NULL,
|
||||
project_id integer NOT NULL REFERENCES public.projects (project_id) ON DELETE CASCADE,
|
||||
selector text NOT NULL,
|
||||
|
|
@ -1132,7 +1132,7 @@ $$
|
|||
session_id bigint NOT NULL REFERENCES public.sessions (session_id) ON DELETE CASCADE,
|
||||
timestamp bigint NOT NULL,
|
||||
seq_index integer NOT NULL,
|
||||
tag_id bigint NOT NULL REFERENCES public.tags (tag_id) ON DELETE CASCADE,
|
||||
tag_id integer NOT NULL REFERENCES public.tags (tag_id) ON DELETE CASCADE,
|
||||
PRIMARY KEY (session_id, timestamp, seq_index)
|
||||
);
|
||||
CREATE INDEX tags_session_id_idx ON events.tags (session_id);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue