change(api): keep the original formatting
This commit is contained in:
parent
cef251db6a
commit
5cc9945f16
1 changed files with 133 additions and 297 deletions
|
|
@ -7,43 +7,29 @@ from pydantic.functional_validators import BeforeValidator
|
||||||
|
|
||||||
from chalicelib.utils.TimeUTC import TimeUTC
|
from chalicelib.utils.TimeUTC import TimeUTC
|
||||||
from .overrides import BaseModel, Enum, ORUnion
|
from .overrides import BaseModel, Enum, ORUnion
|
||||||
from .transformers_validators import (
|
from .transformers_validators import transform_email, remove_whitespace, remove_duplicate_values, single_to_list, \
|
||||||
transform_email,
|
force_is_event, NAME_PATTERN, int_to_string, check_alphanumeric
|
||||||
remove_whitespace,
|
|
||||||
remove_duplicate_values,
|
|
||||||
single_to_list,
|
|
||||||
force_is_event,
|
|
||||||
NAME_PATTERN,
|
|
||||||
int_to_string,
|
|
||||||
check_alphanumeric,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class _GRecaptcha(BaseModel):
|
class _GRecaptcha(BaseModel):
|
||||||
g_recaptcha_response: Optional[str] = Field(
|
g_recaptcha_response: Optional[str] = Field(default=None, alias='g-recaptcha-response')
|
||||||
default=None, alias="g-recaptcha-response"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class UserLoginSchema(_GRecaptcha):
|
class UserLoginSchema(_GRecaptcha):
|
||||||
email: EmailStr = Field(...)
|
email: EmailStr = Field(...)
|
||||||
password: SecretStr = Field(...)
|
password: SecretStr = Field(...)
|
||||||
|
|
||||||
_transform_email = field_validator("email", mode="before")(transform_email)
|
_transform_email = field_validator('email', mode='before')(transform_email)
|
||||||
|
|
||||||
|
|
||||||
class UserSignupSchema(UserLoginSchema):
|
class UserSignupSchema(UserLoginSchema):
|
||||||
fullname: str = Field(..., min_length=1)
|
fullname: str = Field(..., min_length=1)
|
||||||
organizationName: str = Field(..., min_length=1)
|
organizationName: str = Field(..., min_length=1)
|
||||||
|
|
||||||
_transform_fullname = field_validator("fullname", mode="before")(remove_whitespace)
|
_transform_fullname = field_validator('fullname', mode='before')(remove_whitespace)
|
||||||
_transform_organizationName = field_validator("organizationName", mode="before")(
|
_transform_organizationName = field_validator('organizationName', mode='before')(remove_whitespace)
|
||||||
remove_whitespace
|
|
||||||
)
|
|
||||||
|
|
||||||
_check_alphanumeric = field_validator("fullname", "organizationName")(
|
_check_alphanumeric = field_validator('fullname', 'organizationName')(check_alphanumeric)
|
||||||
check_alphanumeric
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class EditAccountSchema(BaseModel):
|
class EditAccountSchema(BaseModel):
|
||||||
|
|
@ -51,17 +37,15 @@ class EditAccountSchema(BaseModel):
|
||||||
tenantName: Optional[str] = Field(default=None)
|
tenantName: Optional[str] = Field(default=None)
|
||||||
opt_out: Optional[bool] = Field(default=None)
|
opt_out: Optional[bool] = Field(default=None)
|
||||||
|
|
||||||
_transform_name = field_validator("name", mode="before")(remove_whitespace)
|
_transform_name = field_validator('name', mode='before')(remove_whitespace)
|
||||||
_transform_tenantName = field_validator("tenantName", mode="before")(
|
_transform_tenantName = field_validator('tenantName', mode='before')(remove_whitespace)
|
||||||
remove_whitespace
|
_check_alphanumeric = field_validator('name', 'tenantName')(check_alphanumeric)
|
||||||
)
|
|
||||||
_check_alphanumeric = field_validator("name", "tenantName")(check_alphanumeric)
|
|
||||||
|
|
||||||
|
|
||||||
class ForgetPasswordPayloadSchema(_GRecaptcha):
|
class ForgetPasswordPayloadSchema(_GRecaptcha):
|
||||||
email: EmailStr = Field(...)
|
email: EmailStr = Field(...)
|
||||||
|
|
||||||
_transform_email = field_validator("email", mode="before")(transform_email)
|
_transform_email = field_validator('email', mode='before')(transform_email)
|
||||||
|
|
||||||
|
|
||||||
class EditUserPasswordSchema(BaseModel):
|
class EditUserPasswordSchema(BaseModel):
|
||||||
|
|
@ -73,7 +57,7 @@ class CreateProjectSchema(BaseModel):
|
||||||
name: str = Field(default="my first project", pattern=NAME_PATTERN)
|
name: str = Field(default="my first project", pattern=NAME_PATTERN)
|
||||||
platform: Literal["web", "ios"] = Field(default="web")
|
platform: Literal["web", "ios"] = Field(default="web")
|
||||||
|
|
||||||
_transform_name = field_validator("name", mode="before")(remove_whitespace)
|
_transform_name = field_validator('name', mode='before')(remove_whitespace)
|
||||||
|
|
||||||
|
|
||||||
class ProjectContext(BaseModel):
|
class ProjectContext(BaseModel):
|
||||||
|
|
@ -93,7 +77,7 @@ class CurrentContext(CurrentAPIContext):
|
||||||
email: EmailStr = Field(...)
|
email: EmailStr = Field(...)
|
||||||
role: str = Field(...)
|
role: str = Field(...)
|
||||||
|
|
||||||
_transform_email = field_validator("email", mode="before")(transform_email)
|
_transform_email = field_validator('email', mode='before')(transform_email)
|
||||||
|
|
||||||
@computed_field
|
@computed_field
|
||||||
@property
|
@property
|
||||||
|
|
@ -115,8 +99,8 @@ class AddCollaborationSchema(BaseModel):
|
||||||
name: str = Field(..., pattern=NAME_PATTERN)
|
name: str = Field(..., pattern=NAME_PATTERN)
|
||||||
url: HttpUrl = Field(...)
|
url: HttpUrl = Field(...)
|
||||||
|
|
||||||
_transform_name = field_validator("name", mode="before")(remove_whitespace)
|
_transform_name = field_validator('name', mode='before')(remove_whitespace)
|
||||||
_transform_url = field_validator("url", mode="before")(remove_whitespace)
|
_transform_url = field_validator('url', mode='before')(remove_whitespace)
|
||||||
|
|
||||||
|
|
||||||
class EditCollaborationSchema(AddCollaborationSchema):
|
class EditCollaborationSchema(AddCollaborationSchema):
|
||||||
|
|
@ -139,15 +123,12 @@ class _TimedSchema(BaseModel):
|
||||||
@model_validator(mode="after")
|
@model_validator(mode="after")
|
||||||
def __time_validator(self):
|
def __time_validator(self):
|
||||||
if self.startTimestamp is not None:
|
if self.startTimestamp is not None:
|
||||||
assert (
|
assert 0 <= self.startTimestamp, "startTimestamp must be greater or equal to 0"
|
||||||
0 <= self.startTimestamp
|
|
||||||
), "startTimestamp must be greater or equal to 0"
|
|
||||||
if self.endTimestamp is not None:
|
if self.endTimestamp is not None:
|
||||||
assert 0 <= self.endTimestamp, "endTimestamp must be greater or equal to 0"
|
assert 0 <= self.endTimestamp, "endTimestamp must be greater or equal to 0"
|
||||||
if self.startTimestamp is not None and self.endTimestamp is not None:
|
if self.startTimestamp is not None and self.endTimestamp is not None:
|
||||||
assert (
|
assert self.startTimestamp <= self.endTimestamp, \
|
||||||
self.startTimestamp <= self.endTimestamp
|
"endTimestamp must be greater or equal to startTimestamp"
|
||||||
), "endTimestamp must be greater or equal to startTimestamp"
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -169,7 +150,7 @@ class IssueTrackingJiraSchema(IssueTrackingIntegration):
|
||||||
username: str = Field(...)
|
username: str = Field(...)
|
||||||
url: HttpUrl = Field(...)
|
url: HttpUrl = Field(...)
|
||||||
|
|
||||||
@field_validator("url")
|
@field_validator('url')
|
||||||
@classmethod
|
@classmethod
|
||||||
def transform_url(cls, v: HttpUrl):
|
def transform_url(cls, v: HttpUrl):
|
||||||
return HttpUrl.build(scheme=v.scheme.lower(), host=v.host.lower())
|
return HttpUrl.build(scheme=v.scheme.lower(), host=v.host.lower())
|
||||||
|
|
@ -182,7 +163,7 @@ class WebhookSchema(BaseModel):
|
||||||
auth_header: Optional[str] = Field(default=None)
|
auth_header: Optional[str] = Field(default=None)
|
||||||
name: str = Field(default="", max_length=100, pattern=NAME_PATTERN)
|
name: str = Field(default="", max_length=100, pattern=NAME_PATTERN)
|
||||||
|
|
||||||
_transform_name = field_validator("name", mode="before")(remove_whitespace)
|
_transform_name = field_validator('name', mode='before')(remove_whitespace)
|
||||||
|
|
||||||
|
|
||||||
class CreateMemberSchema(BaseModel):
|
class CreateMemberSchema(BaseModel):
|
||||||
|
|
@ -191,8 +172,8 @@ class CreateMemberSchema(BaseModel):
|
||||||
email: EmailStr = Field(...)
|
email: EmailStr = Field(...)
|
||||||
admin: Optional[bool] = Field(default=False)
|
admin: Optional[bool] = Field(default=False)
|
||||||
|
|
||||||
_transform_email = field_validator("email", mode="before")(transform_email)
|
_transform_email = field_validator('email', mode='before')(transform_email)
|
||||||
_transform_name = field_validator("name", mode="before")(remove_whitespace)
|
_transform_name = field_validator('name', mode='before')(remove_whitespace)
|
||||||
|
|
||||||
|
|
||||||
class EditMemberSchema(BaseModel):
|
class EditMemberSchema(BaseModel):
|
||||||
|
|
@ -200,9 +181,9 @@ class EditMemberSchema(BaseModel):
|
||||||
email: EmailStr = Field(...)
|
email: EmailStr = Field(...)
|
||||||
admin: bool = Field(default=False)
|
admin: bool = Field(default=False)
|
||||||
|
|
||||||
_transform_email = field_validator("email", mode="before")(transform_email)
|
_transform_email = field_validator('email', mode='before')(transform_email)
|
||||||
_transform_name = field_validator("name", mode="before")(remove_whitespace)
|
_transform_name = field_validator('name', mode='before')(remove_whitespace)
|
||||||
_check_alphanumeric = field_validator("name")(check_alphanumeric)
|
_check_alphanumeric = field_validator('name')(check_alphanumeric)
|
||||||
|
|
||||||
|
|
||||||
class EditPasswordByInvitationSchema(BaseModel):
|
class EditPasswordByInvitationSchema(BaseModel):
|
||||||
|
|
@ -217,7 +198,7 @@ class AssignmentSchema(BaseModel):
|
||||||
title: str = Field(...)
|
title: str = Field(...)
|
||||||
issue_type: str = Field(...)
|
issue_type: str = Field(...)
|
||||||
|
|
||||||
_transform_title = field_validator("title", mode="before")(remove_whitespace)
|
_transform_title = field_validator('title', mode='before')(remove_whitespace)
|
||||||
|
|
||||||
|
|
||||||
class CommentAssignmentSchema(BaseModel):
|
class CommentAssignmentSchema(BaseModel):
|
||||||
|
|
@ -313,14 +294,14 @@ class MetadataSchema(BaseModel):
|
||||||
index: Optional[int] = Field(default=None)
|
index: Optional[int] = Field(default=None)
|
||||||
key: str = Field(...)
|
key: str = Field(...)
|
||||||
|
|
||||||
_transform_key = field_validator("key", mode="before")(remove_whitespace)
|
_transform_key = field_validator('key', mode='before')(remove_whitespace)
|
||||||
|
|
||||||
|
|
||||||
class _AlertMessageSchema(BaseModel):
|
class _AlertMessageSchema(BaseModel):
|
||||||
type: str = Field(...)
|
type: str = Field(...)
|
||||||
value: str = Field(...)
|
value: str = Field(...)
|
||||||
|
|
||||||
_transform_value = field_validator("value", mode="before")(int_to_string)
|
_transform_value = field_validator('value', mode='before')(int_to_string)
|
||||||
|
|
||||||
|
|
||||||
class AlertDetectionType(str, Enum):
|
class AlertDetectionType(str, Enum):
|
||||||
|
|
@ -338,9 +319,7 @@ class _AlertOptionSchema(BaseModel):
|
||||||
|
|
||||||
class AlertColumn(str, Enum):
|
class AlertColumn(str, Enum):
|
||||||
PERFORMANCE__DOM_CONTENT_LOADED__AVERAGE = "performance.dom_content_loaded.average"
|
PERFORMANCE__DOM_CONTENT_LOADED__AVERAGE = "performance.dom_content_loaded.average"
|
||||||
PERFORMANCE__FIRST_MEANINGFUL_PAINT__AVERAGE = (
|
PERFORMANCE__FIRST_MEANINGFUL_PAINT__AVERAGE = "performance.first_meaningful_paint.average"
|
||||||
"performance.first_meaningful_paint.average"
|
|
||||||
)
|
|
||||||
PERFORMANCE__PAGE_LOAD_TIME__AVERAGE = "performance.page_load_time.average"
|
PERFORMANCE__PAGE_LOAD_TIME__AVERAGE = "performance.page_load_time.average"
|
||||||
PERFORMANCE__DOM_BUILD_TIME__AVERAGE = "performance.dom_build_time.average"
|
PERFORMANCE__DOM_BUILD_TIME__AVERAGE = "performance.dom_build_time.average"
|
||||||
PERFORMANCE__SPEED_INDEX__AVERAGE = "performance.speed_index.average"
|
PERFORMANCE__SPEED_INDEX__AVERAGE = "performance.speed_index.average"
|
||||||
|
|
@ -511,30 +490,30 @@ class SearchEventOrder(str, Enum):
|
||||||
|
|
||||||
|
|
||||||
class IssueType(str, Enum):
|
class IssueType(str, Enum):
|
||||||
CLICK_RAGE = "click_rage"
|
CLICK_RAGE = 'click_rage'
|
||||||
DEAD_CLICK = "dead_click"
|
DEAD_CLICK = 'dead_click'
|
||||||
EXCESSIVE_SCROLLING = "excessive_scrolling"
|
EXCESSIVE_SCROLLING = 'excessive_scrolling'
|
||||||
BAD_REQUEST = "bad_request"
|
BAD_REQUEST = 'bad_request'
|
||||||
MISSING_RESOURCE = "missing_resource"
|
MISSING_RESOURCE = 'missing_resource'
|
||||||
MEMORY = "memory"
|
MEMORY = 'memory'
|
||||||
CPU = "cpu"
|
CPU = 'cpu'
|
||||||
SLOW_RESOURCE = "slow_resource"
|
SLOW_RESOURCE = 'slow_resource'
|
||||||
SLOW_PAGE_LOAD = "slow_page_load"
|
SLOW_PAGE_LOAD = 'slow_page_load'
|
||||||
CRASH = "crash"
|
CRASH = 'crash'
|
||||||
CUSTOM = "custom"
|
CUSTOM = 'custom'
|
||||||
JS_EXCEPTION = "js_exception"
|
JS_EXCEPTION = 'js_exception'
|
||||||
MOUSE_THRASHING = "mouse_thrashing"
|
MOUSE_THRASHING = 'mouse_thrashing'
|
||||||
# IOS
|
# IOS
|
||||||
TAP_RAGE = "tap_rage"
|
TAP_RAGE = 'tap_rage'
|
||||||
|
|
||||||
|
|
||||||
class MetricFormatType(str, Enum):
|
class MetricFormatType(str, Enum):
|
||||||
SESSION_COUNT = "sessionCount"
|
SESSION_COUNT = 'sessionCount'
|
||||||
|
|
||||||
|
|
||||||
class MetricExtendedFormatType(str, Enum):
|
class MetricExtendedFormatType(str, Enum):
|
||||||
SESSION_COUNT = "sessionCount"
|
SESSION_COUNT = 'sessionCount'
|
||||||
USER_COUNT = "userCount"
|
USER_COUNT = 'userCount'
|
||||||
|
|
||||||
|
|
||||||
class FetchFilterType(str, Enum):
|
class FetchFilterType(str, Enum):
|
||||||
|
|
@ -561,13 +540,8 @@ class RequestGraphqlFilterSchema(BaseModel):
|
||||||
@model_validator(mode="before")
|
@model_validator(mode="before")
|
||||||
@classmethod
|
@classmethod
|
||||||
def _transform_data(cls, values):
|
def _transform_data(cls, values):
|
||||||
if values.get("type") in [
|
if values.get("type") in [FetchFilterType.FETCH_DURATION, FetchFilterType.FETCH_STATUS_CODE]:
|
||||||
FetchFilterType.FETCH_DURATION,
|
values["value"] = [int(v) for v in values["value"] if v is not None and str(v).isnumeric()]
|
||||||
FetchFilterType.FETCH_STATUS_CODE,
|
|
||||||
]:
|
|
||||||
values["value"] = [
|
|
||||||
int(v) for v in values["value"] if v is not None and str(v).isnumeric()
|
|
||||||
]
|
|
||||||
return values
|
return values
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -580,10 +554,8 @@ class SessionSearchEventSchema2(BaseModel):
|
||||||
sourceOperator: Optional[MathOperator] = Field(default=None)
|
sourceOperator: Optional[MathOperator] = Field(default=None)
|
||||||
filters: Optional[List[RequestGraphqlFilterSchema]] = Field(default_factory=list)
|
filters: Optional[List[RequestGraphqlFilterSchema]] = Field(default_factory=list)
|
||||||
|
|
||||||
_remove_duplicate_values = field_validator("value", mode="before")(
|
_remove_duplicate_values = field_validator('value', mode='before')(remove_duplicate_values)
|
||||||
remove_duplicate_values
|
_single_to_list_values = field_validator('value', mode='before')(single_to_list)
|
||||||
)
|
|
||||||
_single_to_list_values = field_validator("value", mode="before")(single_to_list)
|
|
||||||
|
|
||||||
@model_validator(mode="after")
|
@model_validator(mode="after")
|
||||||
def event_validator(self):
|
def event_validator(self):
|
||||||
|
|
@ -591,32 +563,24 @@ class SessionSearchEventSchema2(BaseModel):
|
||||||
if self.type == PerformanceEventType.FETCH_FAILED:
|
if self.type == PerformanceEventType.FETCH_FAILED:
|
||||||
return self
|
return self
|
||||||
|
|
||||||
assert (
|
assert self.sourceOperator is not None, \
|
||||||
self.sourceOperator is not None
|
"sourceOperator should not be null for PerformanceEventType"
|
||||||
), "sourceOperator should not be null for PerformanceEventType"
|
|
||||||
assert self.source is not None, f"source is required for {self.type}"
|
assert self.source is not None, f"source is required for {self.type}"
|
||||||
assert isinstance(
|
assert isinstance(self.source, list), f"source of type list is required for {self.type}"
|
||||||
self.source, list
|
|
||||||
), f"source of type list is required for {self.type}"
|
|
||||||
for c in self.source:
|
for c in self.source:
|
||||||
assert isinstance(
|
assert isinstance(c, int), f"source value should be of type int for {self.type}"
|
||||||
c, int
|
|
||||||
), f"source value should be of type int for {self.type}"
|
|
||||||
elif self.type == EventType.ERROR and self.source is None:
|
elif self.type == EventType.ERROR and self.source is None:
|
||||||
self.source = [ErrorSource.JS_EXCEPTION]
|
self.source = [ErrorSource.JS_EXCEPTION]
|
||||||
elif self.type == EventType.REQUEST_DETAILS:
|
elif self.type == EventType.REQUEST_DETAILS:
|
||||||
assert (
|
assert isinstance(self.filters, List) and len(self.filters) > 0, \
|
||||||
isinstance(self.filters, List) and len(self.filters) > 0
|
f"filters should be defined for {EventType.REQUEST_DETAILS}"
|
||||||
), f"filters should be defined for {EventType.REQUEST_DETAILS}"
|
|
||||||
elif self.type == EventType.GRAPHQL:
|
elif self.type == EventType.GRAPHQL:
|
||||||
assert (
|
assert isinstance(self.filters, List) and len(self.filters) > 0, \
|
||||||
isinstance(self.filters, List) and len(self.filters) > 0
|
f"filters should be defined for {EventType.GRAPHQL}"
|
||||||
), f"filters should be defined for {EventType.GRAPHQL}"
|
|
||||||
|
|
||||||
if isinstance(self.operator, ClickEventExtraOperator):
|
if isinstance(self.operator, ClickEventExtraOperator):
|
||||||
assert (
|
assert self.type == EventType.CLICK, \
|
||||||
self.type == EventType.CLICK
|
f"operator:{self.operator} is only available for event-type: {EventType.CLICK}"
|
||||||
), f"operator:{self.operator} is only available for event-type: {EventType.CLICK}"
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -627,10 +591,8 @@ class SessionSearchFilterSchema(BaseModel):
|
||||||
operator: Union[SearchEventOperator, MathOperator] = Field(...)
|
operator: Union[SearchEventOperator, MathOperator] = Field(...)
|
||||||
source: Optional[Union[ErrorSource, str]] = Field(default=None)
|
source: Optional[Union[ErrorSource, str]] = Field(default=None)
|
||||||
|
|
||||||
_remove_duplicate_values = field_validator("value", mode="before")(
|
_remove_duplicate_values = field_validator('value', mode='before')(remove_duplicate_values)
|
||||||
remove_duplicate_values
|
_single_to_list_values = field_validator('value', mode='before')(single_to_list)
|
||||||
)
|
|
||||||
_single_to_list_values = field_validator("value", mode="before")(single_to_list)
|
|
||||||
|
|
||||||
@model_validator(mode="before")
|
@model_validator(mode="before")
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
@ -648,44 +610,33 @@ class SessionSearchFilterSchema(BaseModel):
|
||||||
@model_validator(mode="after")
|
@model_validator(mode="after")
|
||||||
def filter_validator(self):
|
def filter_validator(self):
|
||||||
if self.type == FilterType.METADATA:
|
if self.type == FilterType.METADATA:
|
||||||
assert (
|
assert self.source is not None and len(self.source) > 0, \
|
||||||
self.source is not None and len(self.source) > 0
|
"must specify a valid 'source' for metadata filter"
|
||||||
), "must specify a valid 'source' for metadata filter"
|
|
||||||
elif self.type == FilterType.ISSUE:
|
elif self.type == FilterType.ISSUE:
|
||||||
for i, v in enumerate(self.value):
|
for i, v in enumerate(self.value):
|
||||||
if IssueType.has_value(v):
|
if IssueType.has_value(v):
|
||||||
self.value[i] = IssueType(v)
|
self.value[i] = IssueType(v)
|
||||||
else:
|
else:
|
||||||
raise ValueError(
|
raise ValueError(f"value should be of type IssueType for {self.type} filter")
|
||||||
f"value should be of type IssueType for {self.type} filter"
|
|
||||||
)
|
|
||||||
elif self.type == FilterType.PLATFORM:
|
elif self.type == FilterType.PLATFORM:
|
||||||
for i, v in enumerate(self.value):
|
for i, v in enumerate(self.value):
|
||||||
if PlatformType.has_value(v):
|
if PlatformType.has_value(v):
|
||||||
self.value[i] = PlatformType(v)
|
self.value[i] = PlatformType(v)
|
||||||
else:
|
else:
|
||||||
raise ValueError(
|
raise ValueError(f"value should be of type PlatformType for {self.type} filter")
|
||||||
f"value should be of type PlatformType for {self.type} filter"
|
|
||||||
)
|
|
||||||
elif self.type == FilterType.EVENTS_COUNT:
|
elif self.type == FilterType.EVENTS_COUNT:
|
||||||
if MathOperator.has_value(self.operator):
|
if MathOperator.has_value(self.operator):
|
||||||
self.operator = MathOperator(self.operator)
|
self.operator = MathOperator(self.operator)
|
||||||
else:
|
else:
|
||||||
raise ValueError(
|
raise ValueError(f"operator should be of type MathOperator for {self.type} filter")
|
||||||
f"operator should be of type MathOperator for {self.type} filter"
|
|
||||||
)
|
|
||||||
|
|
||||||
for v in self.value:
|
for v in self.value:
|
||||||
assert isinstance(
|
assert isinstance(v, int), f"value should be of type int for {self.type} filter"
|
||||||
v, int
|
|
||||||
), f"value should be of type int for {self.type} filter"
|
|
||||||
else:
|
else:
|
||||||
if SearchEventOperator.has_value(self.operator):
|
if SearchEventOperator.has_value(self.operator):
|
||||||
self.operator = SearchEventOperator(self.operator)
|
self.operator = SearchEventOperator(self.operator)
|
||||||
else:
|
else:
|
||||||
raise ValueError(
|
raise ValueError(f"operator should be of type SearchEventOperator for {self.type} filter")
|
||||||
f"operator should be of type SearchEventOperator for {self.type} filter"
|
|
||||||
)
|
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
@ -702,26 +653,19 @@ class SortOrderType(str, Enum):
|
||||||
|
|
||||||
def add_missing_is_event(values: dict):
|
def add_missing_is_event(values: dict):
|
||||||
if values.get("isEvent") is None:
|
if values.get("isEvent") is None:
|
||||||
values["isEvent"] = (
|
values["isEvent"] = (EventType.has_value(values["type"])
|
||||||
EventType.has_value(values["type"])
|
or PerformanceEventType.has_value(values["type"])
|
||||||
or PerformanceEventType.has_value(values["type"])
|
or ProductAnalyticsSelectedEventType.has_value(values["type"]))
|
||||||
or ProductAnalyticsSelectedEventType.has_value(values["type"])
|
|
||||||
)
|
|
||||||
return values
|
return values
|
||||||
|
|
||||||
|
|
||||||
# this type is created to allow mixing events&filters and specifying a discriminator
|
# this type is created to allow mixing events&filters and specifying a discriminator
|
||||||
GroupedFilterType = Annotated[
|
GroupedFilterType = Annotated[Union[SessionSearchFilterSchema, SessionSearchEventSchema2],
|
||||||
Union[SessionSearchFilterSchema, SessionSearchEventSchema2],
|
Field(discriminator='is_event'), BeforeValidator(add_missing_is_event)]
|
||||||
Field(discriminator="is_event"),
|
|
||||||
BeforeValidator(add_missing_is_event),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class SessionsSearchPayloadSchema(_TimedSchema, _PaginatedSchema):
|
class SessionsSearchPayloadSchema(_TimedSchema, _PaginatedSchema):
|
||||||
events: List[SessionSearchEventSchema2] = Field(
|
events: List[SessionSearchEventSchema2] = Field(default_factory=list, doc_hidden=True)
|
||||||
default_factory=list, doc_hidden=True
|
|
||||||
)
|
|
||||||
filters: List[GroupedFilterType] = Field(default_factory=list)
|
filters: List[GroupedFilterType] = Field(default_factory=list)
|
||||||
sort: str = Field(default="startTs")
|
sort: str = Field(default="startTs")
|
||||||
order: SortOrderType = Field(default=SortOrderType.DESC)
|
order: SortOrderType = Field(default=SortOrderType.DESC)
|
||||||
|
|
@ -765,9 +709,8 @@ class SessionsSearchPayloadSchema(_TimedSchema, _PaginatedSchema):
|
||||||
for v in f.get("value", []):
|
for v in f.get("value", []):
|
||||||
if f.get("type", "") == FilterType.DURATION.value and v is None:
|
if f.get("type", "") == FilterType.DURATION.value and v is None:
|
||||||
v = 0
|
v = 0
|
||||||
if v is not None and (
|
if v is not None and (f.get("type", "") != FilterType.DURATION.value
|
||||||
f.get("type", "") != FilterType.DURATION.value or str(v).isnumeric()
|
or str(v).isnumeric()):
|
||||||
):
|
|
||||||
vals.append(v)
|
vals.append(v)
|
||||||
f["value"] = vals
|
f["value"] = vals
|
||||||
return values
|
return values
|
||||||
|
|
@ -798,14 +741,9 @@ class SessionsSearchPayloadSchema(_TimedSchema, _PaginatedSchema):
|
||||||
continue
|
continue
|
||||||
j = i + 1
|
j = i + 1
|
||||||
while j < len(values):
|
while j < len(values):
|
||||||
if (
|
if values[i].type == values[j].type \
|
||||||
values[i].type == values[j].type
|
and values[i].operator == values[j].operator \
|
||||||
and values[i].operator == values[j].operator
|
and (values[i].type != FilterType.METADATA or values[i].source == values[j].source):
|
||||||
and (
|
|
||||||
values[i].type != FilterType.METADATA
|
|
||||||
or values[i].source == values[j].source
|
|
||||||
)
|
|
||||||
):
|
|
||||||
values[i].value += values[j].value
|
values[i].value += values[j].value
|
||||||
del values[j]
|
del values[j]
|
||||||
else:
|
else:
|
||||||
|
|
@ -817,16 +755,16 @@ class SessionsSearchPayloadSchema(_TimedSchema, _PaginatedSchema):
|
||||||
|
|
||||||
|
|
||||||
class ErrorStatus(str, Enum):
|
class ErrorStatus(str, Enum):
|
||||||
ALL = "all"
|
ALL = 'all'
|
||||||
UNRESOLVED = "unresolved"
|
UNRESOLVED = 'unresolved'
|
||||||
RESOLVED = "resolved"
|
RESOLVED = 'resolved'
|
||||||
IGNORED = "ignored"
|
IGNORED = 'ignored'
|
||||||
|
|
||||||
|
|
||||||
class ErrorSort(str, Enum):
|
class ErrorSort(str, Enum):
|
||||||
OCCURRENCE = "occurrence"
|
OCCURRENCE = 'occurrence'
|
||||||
USERS_COUNT = "users"
|
USERS_COUNT = 'users'
|
||||||
SESSIONS_COUNT = "sessions"
|
SESSIONS_COUNT = 'sessions'
|
||||||
|
|
||||||
|
|
||||||
class SearchErrorsSchema(SessionsSearchPayloadSchema):
|
class SearchErrorsSchema(SessionsSearchPayloadSchema):
|
||||||
|
|
@ -849,9 +787,7 @@ class PathAnalysisSubFilterSchema(BaseModel):
|
||||||
type: ProductAnalyticsSelectedEventType = Field(...)
|
type: ProductAnalyticsSelectedEventType = Field(...)
|
||||||
operator: Union[SearchEventOperator, ClickEventExtraOperator] = Field(...)
|
operator: Union[SearchEventOperator, ClickEventExtraOperator] = Field(...)
|
||||||
|
|
||||||
_remove_duplicate_values = field_validator("value", mode="before")(
|
_remove_duplicate_values = field_validator('value', mode='before')(remove_duplicate_values)
|
||||||
remove_duplicate_values
|
|
||||||
)
|
|
||||||
|
|
||||||
@model_validator(mode="before")
|
@model_validator(mode="before")
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
@ -863,36 +799,26 @@ class PathAnalysisSubFilterSchema(BaseModel):
|
||||||
class _ProductAnalyticsFilter(BaseModel):
|
class _ProductAnalyticsFilter(BaseModel):
|
||||||
is_event: Literal[False] = False
|
is_event: Literal[False] = False
|
||||||
type: FilterType
|
type: FilterType
|
||||||
operator: Union[SearchEventOperator, ClickEventExtraOperator, MathOperator] = Field(
|
operator: Union[SearchEventOperator, ClickEventExtraOperator, MathOperator] = Field(...)
|
||||||
...
|
|
||||||
)
|
|
||||||
value: List[Union[IssueType, PlatformType, int, str]] = Field(...)
|
value: List[Union[IssueType, PlatformType, int, str]] = Field(...)
|
||||||
source: Optional[str] = Field(default=None)
|
source: Optional[str] = Field(default=None)
|
||||||
|
|
||||||
_remove_duplicate_values = field_validator("value", mode="before")(
|
_remove_duplicate_values = field_validator('value', mode='before')(remove_duplicate_values)
|
||||||
remove_duplicate_values
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class _ProductAnalyticsEventFilter(BaseModel):
|
class _ProductAnalyticsEventFilter(BaseModel):
|
||||||
is_event: Literal[True] = True
|
is_event: Literal[True] = True
|
||||||
type: ProductAnalyticsSelectedEventType
|
type: ProductAnalyticsSelectedEventType
|
||||||
operator: Union[SearchEventOperator, ClickEventExtraOperator, MathOperator] = Field(
|
operator: Union[SearchEventOperator, ClickEventExtraOperator, MathOperator] = Field(...)
|
||||||
...
|
|
||||||
)
|
|
||||||
# TODO: support session metadata filters
|
# TODO: support session metadata filters
|
||||||
value: List[Union[IssueType, PlatformType, int, str]] = Field(...)
|
value: List[Union[IssueType, PlatformType, int, str]] = Field(...)
|
||||||
|
|
||||||
_remove_duplicate_values = field_validator("value", mode="before")(
|
_remove_duplicate_values = field_validator('value', mode='before')(remove_duplicate_values)
|
||||||
remove_duplicate_values
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# this type is created to allow mixing events&filters and specifying a discriminator for PathAnalysis series filter
|
# this type is created to allow mixing events&filters and specifying a discriminator for PathAnalysis series filter
|
||||||
ProductAnalyticsFilter = Annotated[
|
ProductAnalyticsFilter = Annotated[Union[_ProductAnalyticsFilter, _ProductAnalyticsEventFilter],
|
||||||
Union[_ProductAnalyticsFilter, _ProductAnalyticsEventFilter],
|
Field(discriminator='is_event')]
|
||||||
Field(discriminator="is_event"),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class PathAnalysisSchema(_TimedSchema, _PaginatedSchema):
|
class PathAnalysisSchema(_TimedSchema, _PaginatedSchema):
|
||||||
|
|
@ -900,9 +826,8 @@ class PathAnalysisSchema(_TimedSchema, _PaginatedSchema):
|
||||||
filters: List[ProductAnalyticsFilter] = Field(default_factory=list)
|
filters: List[ProductAnalyticsFilter] = Field(default_factory=list)
|
||||||
type: Optional[str] = Field(default=None)
|
type: Optional[str] = Field(default=None)
|
||||||
|
|
||||||
_transform_filters = field_validator("filters", mode="before")(
|
_transform_filters = field_validator('filters', mode='before') \
|
||||||
force_is_event(events_enum=[ProductAnalyticsSelectedEventType])
|
(force_is_event(events_enum=[ProductAnalyticsSelectedEventType]))
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class MobileSignPayloadSchema(BaseModel):
|
class MobileSignPayloadSchema(BaseModel):
|
||||||
|
|
@ -999,9 +924,8 @@ class CardSessionsSchema(_TimedSchema, _PaginatedSchema):
|
||||||
# Used mainly for PathAnalysis, and could be used by other cards
|
# Used mainly for PathAnalysis, and could be used by other cards
|
||||||
hide_excess: Optional[bool] = Field(default=False, description="Hide extra values")
|
hide_excess: Optional[bool] = Field(default=False, description="Hide extra values")
|
||||||
|
|
||||||
_transform_filters = field_validator("filters", mode="before")(
|
_transform_filters = field_validator('filters', mode='before') \
|
||||||
force_is_event(events_enum=[EventType, PerformanceEventType])
|
(force_is_event(events_enum=[EventType, PerformanceEventType]))
|
||||||
)
|
|
||||||
|
|
||||||
@model_validator(mode="before")
|
@model_validator(mode="before")
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
@ -1127,17 +1051,12 @@ class CardTable(__CardSchema):
|
||||||
metric_type: Literal[MetricType.TABLE]
|
metric_type: Literal[MetricType.TABLE]
|
||||||
metric_of: MetricOfTable = Field(default=MetricOfTable.USER_ID)
|
metric_of: MetricOfTable = Field(default=MetricOfTable.USER_ID)
|
||||||
view_type: MetricTableViewType = Field(...)
|
view_type: MetricTableViewType = Field(...)
|
||||||
metric_format: MetricExtendedFormatType = Field(
|
metric_format: MetricExtendedFormatType = Field(default=MetricExtendedFormatType.SESSION_COUNT)
|
||||||
default=MetricExtendedFormatType.SESSION_COUNT
|
|
||||||
)
|
|
||||||
|
|
||||||
@model_validator(mode="before")
|
@model_validator(mode="before")
|
||||||
@classmethod
|
@classmethod
|
||||||
def __enforce_default(cls, values):
|
def __enforce_default(cls, values):
|
||||||
if (
|
if values.get("metricOf") is not None and values.get("metricOf") != MetricOfTable.ISSUES:
|
||||||
values.get("metricOf") is not None
|
|
||||||
and values.get("metricOf") != MetricOfTable.ISSUES
|
|
||||||
):
|
|
||||||
values["metricValue"] = []
|
values["metricValue"] = []
|
||||||
return values
|
return values
|
||||||
|
|
||||||
|
|
@ -1148,18 +1067,12 @@ class CardTable(__CardSchema):
|
||||||
|
|
||||||
@model_validator(mode="after")
|
@model_validator(mode="after")
|
||||||
def __validator(self):
|
def __validator(self):
|
||||||
if self.metric_of not in (
|
if self.metric_of not in (MetricOfTable.ISSUES, MetricOfTable.USER_BROWSER,
|
||||||
MetricOfTable.ISSUES,
|
MetricOfTable.USER_DEVICE, MetricOfTable.USER_COUNTRY,
|
||||||
MetricOfTable.USER_BROWSER,
|
MetricOfTable.VISITED_URL, MetricOfTable.REFERRER,
|
||||||
MetricOfTable.USER_DEVICE,
|
MetricOfTable.FETCH):
|
||||||
MetricOfTable.USER_COUNTRY,
|
assert self.metric_format == MetricExtendedFormatType.SESSION_COUNT, \
|
||||||
MetricOfTable.VISITED_URL,
|
f'metricFormat:{MetricExtendedFormatType.USER_COUNT.value} is not supported for this metricOf'
|
||||||
MetricOfTable.REFERRER,
|
|
||||||
MetricOfTable.FETCH,
|
|
||||||
):
|
|
||||||
assert (
|
|
||||||
self.metric_format == MetricExtendedFormatType.SESSION_COUNT
|
|
||||||
), f"metricFormat:{MetricExtendedFormatType.USER_COUNT.value} is not supported for this metricOf"
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1167,9 +1080,7 @@ class CardFunnel(__CardSchema):
|
||||||
metric_type: Literal[MetricType.FUNNEL]
|
metric_type: Literal[MetricType.FUNNEL]
|
||||||
metric_of: MetricOfFunnels = Field(default=MetricOfFunnels.SESSION_COUNT)
|
metric_of: MetricOfFunnels = Field(default=MetricOfFunnels.SESSION_COUNT)
|
||||||
view_type: MetricOtherViewType = Field(...)
|
view_type: MetricOtherViewType = Field(...)
|
||||||
metric_format: MetricExtendedFormatType = Field(
|
metric_format: MetricExtendedFormatType = Field(default=MetricExtendedFormatType.SESSION_COUNT)
|
||||||
default=MetricExtendedFormatType.SESSION_COUNT
|
|
||||||
)
|
|
||||||
|
|
||||||
@model_validator(mode="before")
|
@model_validator(mode="before")
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
@ -1211,16 +1122,10 @@ class CardPathAnalysisSeriesSchema(CardSeriesSchema):
|
||||||
@model_validator(mode="before")
|
@model_validator(mode="before")
|
||||||
@classmethod
|
@classmethod
|
||||||
def __enforce_default(cls, values):
|
def __enforce_default(cls, values):
|
||||||
if (
|
if values.get("filter") is None and values.get("startTimestamp") and values.get("endTimestamp"):
|
||||||
values.get("filter") is None
|
values["filter"] = PathAnalysisSchema(startTimestamp=values["startTimestamp"],
|
||||||
and values.get("startTimestamp")
|
endTimestamp=values["endTimestamp"],
|
||||||
and values.get("endTimestamp")
|
density=values.get("density", 4))
|
||||||
):
|
|
||||||
values["filter"] = PathAnalysisSchema(
|
|
||||||
startTimestamp=values["startTimestamp"],
|
|
||||||
endTimestamp=values["endTimestamp"],
|
|
||||||
density=values.get("density", 4),
|
|
||||||
)
|
|
||||||
return values
|
return values
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1270,23 +1175,20 @@ class CardPathAnalysis(__CardSchema):
|
||||||
for f in self.excludes:
|
for f in self.excludes:
|
||||||
exclude_values[f.type] = exclude_values.get(f.type, []) + f.value
|
exclude_values[f.type] = exclude_values.get(f.type, []) + f.value
|
||||||
|
|
||||||
assert (
|
assert len(
|
||||||
len(self.start_point) <= 1
|
self.start_point) <= 1, \
|
||||||
), f"Only 1 startPoint with multiple values OR 1 endPoint with multiple values is allowed"
|
f"Only 1 startPoint with multiple values OR 1 endPoint with multiple values is allowed"
|
||||||
for t in exclude_values:
|
for t in exclude_values:
|
||||||
for v in t:
|
for v in t:
|
||||||
assert v not in s_e_values.get(
|
assert v not in s_e_values.get(t, []), f"startPoint and endPoint cannot be excluded, value: {v}"
|
||||||
t, []
|
|
||||||
), f"startPoint and endPoint cannot be excluded, value: {v}"
|
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
# Union of cards-schemas that doesn't change between FOSS and EE
|
# Union of cards-schemas that doesn't change between FOSS and EE
|
||||||
__cards_union_base = Union[
|
__cards_union_base = Union[
|
||||||
CardTimeSeries, CardTable, CardFunnel, CardHeatMap, CardPathAnalysis
|
CardTimeSeries, CardTable, CardFunnel, CardHeatMap, CardPathAnalysis]
|
||||||
]
|
CardSchema = ORUnion(__cards_union_base, discriminator='metric_type')
|
||||||
CardSchema = ORUnion(__cards_union_base, discriminator="metric_type")
|
|
||||||
|
|
||||||
|
|
||||||
class UpdateCardStatusSchema(BaseModel):
|
class UpdateCardStatusSchema(BaseModel):
|
||||||
|
|
@ -1314,7 +1216,7 @@ class ProjectSettings(BaseModel):
|
||||||
|
|
||||||
class CreateDashboardSchema(BaseModel):
|
class CreateDashboardSchema(BaseModel):
|
||||||
name: str = Field(..., min_length=1)
|
name: str = Field(..., min_length=1)
|
||||||
description: Optional[str] = Field(default="")
|
description: Optional[str] = Field(default='')
|
||||||
is_public: bool = Field(default=False)
|
is_public: bool = Field(default=False)
|
||||||
is_pinned: bool = Field(default=False)
|
is_pinned: bool = Field(default=False)
|
||||||
metrics: Optional[List[int]] = Field(default_factory=list)
|
metrics: Optional[List[int]] = Field(default_factory=list)
|
||||||
|
|
@ -1367,16 +1269,13 @@ class LiveSessionSearchFilterSchema(BaseModel):
|
||||||
value: Union[List[str], str] = Field(...)
|
value: Union[List[str], str] = Field(...)
|
||||||
type: LiveFilterType = Field(...)
|
type: LiveFilterType = Field(...)
|
||||||
source: Optional[str] = Field(default=None)
|
source: Optional[str] = Field(default=None)
|
||||||
operator: Literal[SearchEventOperator.IS, SearchEventOperator.CONTAINS] = Field(
|
operator: Literal[SearchEventOperator.IS, SearchEventOperator.CONTAINS] \
|
||||||
default=SearchEventOperator.CONTAINS
|
= Field(default=SearchEventOperator.CONTAINS)
|
||||||
)
|
|
||||||
|
|
||||||
@model_validator(mode="after")
|
@model_validator(mode="after")
|
||||||
def __validator(self):
|
def __validator(self):
|
||||||
if self.type is not None and self.type == LiveFilterType.METADATA:
|
if self.type is not None and self.type == LiveFilterType.METADATA:
|
||||||
assert (
|
assert self.source is not None, "source should not be null for METADATA type"
|
||||||
self.source is not None
|
|
||||||
), "source should not be null for METADATA type"
|
|
||||||
assert len(self.source) > 0, "source should not be empty for METADATA type"
|
assert len(self.source) > 0, "source should not be empty for METADATA type"
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
@ -1394,10 +1293,7 @@ class LiveSessionsSearchPayloadSchema(_PaginatedSchema):
|
||||||
if values.get("filters") is not None:
|
if values.get("filters") is not None:
|
||||||
i = 0
|
i = 0
|
||||||
while i < len(values["filters"]):
|
while i < len(values["filters"]):
|
||||||
if (
|
if values["filters"][i]["value"] is None or len(values["filters"][i]["value"]) == 0:
|
||||||
values["filters"][i]["value"] is None
|
|
||||||
or len(values["filters"][i]["value"]) == 0
|
|
||||||
):
|
|
||||||
del values["filters"][i]
|
del values["filters"][i]
|
||||||
else:
|
else:
|
||||||
i += 1
|
i += 1
|
||||||
|
|
@ -1453,11 +1349,8 @@ class SessionUpdateNoteSchema(SessionNoteSchema):
|
||||||
|
|
||||||
@model_validator(mode="after")
|
@model_validator(mode="after")
|
||||||
def __validator(self):
|
def __validator(self):
|
||||||
assert (
|
assert self.message is not None or self.timestamp is not None or self.is_public is not None, \
|
||||||
self.message is not None
|
"at least 1 attribute should be provided for update"
|
||||||
or self.timestamp is not None
|
|
||||||
or self.is_public is not None
|
|
||||||
), "at least 1 attribute should be provided for update"
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1475,56 +1368,13 @@ class SearchCardsSchema(_PaginatedSchema):
|
||||||
query: Optional[str] = Field(default=None)
|
query: Optional[str] = Field(default=None)
|
||||||
|
|
||||||
|
|
||||||
class MetricSortColumnType(str, Enum):
|
|
||||||
NAME = "name"
|
|
||||||
METRIC_TYPE = "metric_type"
|
|
||||||
METRIC_OF = "metric_of"
|
|
||||||
IS_PUBLIC = "is_public"
|
|
||||||
CREATED_AT = "created_at"
|
|
||||||
EDITED_AT = "edited_at"
|
|
||||||
|
|
||||||
|
|
||||||
class MetricFilterColumnType(str, Enum):
|
|
||||||
NAME = "name"
|
|
||||||
METRIC_TYPE = "metric_type"
|
|
||||||
METRIC_OF = "metric_of"
|
|
||||||
IS_PUBLIC = "is_public"
|
|
||||||
USER_ID = "user_id"
|
|
||||||
CREATED_AT = "created_at"
|
|
||||||
EDITED_AT = "edited_at"
|
|
||||||
|
|
||||||
|
|
||||||
class MetricListSort(BaseModel):
|
|
||||||
# column_key: Optional[MetricSortColumnType] = Field(
|
|
||||||
# default=MetricSortColumnType.CREATED_AT
|
|
||||||
# )
|
|
||||||
field: Optional[str] = Field(default=None)
|
|
||||||
order: Optional[str] = Field(default=SortOrderType.DESC)
|
|
||||||
|
|
||||||
|
|
||||||
class MetricFilter(BaseModel):
|
|
||||||
type: Optional[str] = Field(default=None)
|
|
||||||
query: Optional[str] = Field(default=None)
|
|
||||||
|
|
||||||
|
|
||||||
class MetricSearchSchema(_PaginatedSchema):
|
|
||||||
# order: SortOrderType = Field(default=SortOrderType.DESC)
|
|
||||||
filter: Optional[MetricFilter] = Field(default=None)
|
|
||||||
sort: Optional[MetricListSort] = Field(default=MetricListSort())
|
|
||||||
shared_only: bool = Field(default=False)
|
|
||||||
mine_only: bool = Field(default=False)
|
|
||||||
# query: Optional[str] = Field(default=None)
|
|
||||||
|
|
||||||
|
|
||||||
class _HeatMapSearchEventRaw(SessionSearchEventSchema2):
|
class _HeatMapSearchEventRaw(SessionSearchEventSchema2):
|
||||||
type: Literal[EventType.LOCATION] = Field(...)
|
type: Literal[EventType.LOCATION] = Field(...)
|
||||||
|
|
||||||
|
|
||||||
class HeatMapSessionsSearch(SessionsSearchPayloadSchema):
|
class HeatMapSessionsSearch(SessionsSearchPayloadSchema):
|
||||||
events: Optional[List[_HeatMapSearchEventRaw]] = Field(default_factory=list)
|
events: Optional[List[_HeatMapSearchEventRaw]] = Field(default_factory=list)
|
||||||
filters: List[Union[SessionSearchFilterSchema, _HeatMapSearchEventRaw]] = Field(
|
filters: List[Union[SessionSearchFilterSchema, _HeatMapSearchEventRaw]] = Field(default_factory=list)
|
||||||
default_factory=list
|
|
||||||
)
|
|
||||||
|
|
||||||
@model_validator(mode="before")
|
@model_validator(mode="before")
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
@ -1533,21 +1383,13 @@ class HeatMapSessionsSearch(SessionsSearchPayloadSchema):
|
||||||
if f.get("type") == FilterType.DURATION:
|
if f.get("type") == FilterType.DURATION:
|
||||||
return values
|
return values
|
||||||
values["filters"] = values.get("filters", [])
|
values["filters"] = values.get("filters", [])
|
||||||
values["filters"].append(
|
values["filters"].append({"value": [5000], "type": FilterType.DURATION,
|
||||||
{
|
"operator": SearchEventOperator.IS, "filters": []})
|
||||||
"value": [5000],
|
|
||||||
"type": FilterType.DURATION,
|
|
||||||
"operator": SearchEventOperator.IS,
|
|
||||||
"filters": [],
|
|
||||||
}
|
|
||||||
)
|
|
||||||
return values
|
return values
|
||||||
|
|
||||||
|
|
||||||
class HeatMapFilterSchema(BaseModel):
|
class HeatMapFilterSchema(BaseModel):
|
||||||
value: List[Literal[IssueType.CLICK_RAGE, IssueType.DEAD_CLICK]] = Field(
|
value: List[Literal[IssueType.CLICK_RAGE, IssueType.DEAD_CLICK]] = Field(default_factory=list)
|
||||||
default_factory=list
|
|
||||||
)
|
|
||||||
type: Literal[FilterType.ISSUE] = Field(...)
|
type: Literal[FilterType.ISSUE] = Field(...)
|
||||||
operator: Literal[SearchEventOperator.IS, MathOperator.EQUAL] = Field(...)
|
operator: Literal[SearchEventOperator.IS, MathOperator.EQUAL] = Field(...)
|
||||||
|
|
||||||
|
|
@ -1556,12 +1398,8 @@ class GetHeatMapPayloadSchema(_TimedSchema):
|
||||||
url: Optional[str] = Field(default=None)
|
url: Optional[str] = Field(default=None)
|
||||||
filters: List[HeatMapFilterSchema] = Field(default_factory=list)
|
filters: List[HeatMapFilterSchema] = Field(default_factory=list)
|
||||||
click_rage: bool = Field(default=False)
|
click_rage: bool = Field(default=False)
|
||||||
operator: Literal[
|
operator: Literal[SearchEventOperator.IS, SearchEventOperator.STARTS_WITH,
|
||||||
SearchEventOperator.IS,
|
SearchEventOperator.CONTAINS, SearchEventOperator.ENDS_WITH] = Field(default=SearchEventOperator.STARTS_WITH)
|
||||||
SearchEventOperator.STARTS_WITH,
|
|
||||||
SearchEventOperator.CONTAINS,
|
|
||||||
SearchEventOperator.ENDS_WITH,
|
|
||||||
] = Field(default=SearchEventOperator.STARTS_WITH)
|
|
||||||
|
|
||||||
|
|
||||||
class GetClickMapPayloadSchema(GetHeatMapPayloadSchema):
|
class GetClickMapPayloadSchema(GetHeatMapPayloadSchema):
|
||||||
|
|
@ -1582,9 +1420,7 @@ class FeatureFlagConditionFilterSchema(BaseModel):
|
||||||
value: List[str] = Field(default_factory=list, min_length=1)
|
value: List[str] = Field(default_factory=list, min_length=1)
|
||||||
operator: Union[SearchEventOperator, MathOperator] = Field(...)
|
operator: Union[SearchEventOperator, MathOperator] = Field(...)
|
||||||
source: Optional[str] = Field(default=None)
|
source: Optional[str] = Field(default=None)
|
||||||
sourceOperator: Optional[Union[SearchEventOperator, MathOperator]] = Field(
|
sourceOperator: Optional[Union[SearchEventOperator, MathOperator]] = Field(default=None)
|
||||||
default=None
|
|
||||||
)
|
|
||||||
|
|
||||||
@model_validator(mode="before")
|
@model_validator(mode="before")
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
@ -1619,7 +1455,7 @@ class FeatureFlagStatus(BaseModel):
|
||||||
|
|
||||||
class FeatureFlagSchema(BaseModel):
|
class FeatureFlagSchema(BaseModel):
|
||||||
payload: Optional[str] = Field(default=None)
|
payload: Optional[str] = Field(default=None)
|
||||||
flag_key: str = Field(..., pattern=r"^[a-zA-Z0-9\-]+$")
|
flag_key: str = Field(..., pattern=r'^[a-zA-Z0-9\-]+$')
|
||||||
description: Optional[str] = Field(default=None)
|
description: Optional[str] = Field(default=None)
|
||||||
flag_type: FeatureFlagType = Field(default=FeatureFlagType.SINGLE_VARIANT)
|
flag_type: FeatureFlagType = Field(default=FeatureFlagType.SINGLE_VARIANT)
|
||||||
is_persist: Optional[bool] = Field(default=False)
|
is_persist: Optional[bool] = Field(default=False)
|
||||||
|
|
@ -1646,7 +1482,7 @@ class ModuleStatus(BaseModel):
|
||||||
|
|
||||||
|
|
||||||
class TagUpdate(BaseModel):
|
class TagUpdate(BaseModel):
|
||||||
name: str = Field(..., min_length=1, max_length=100, pattern='^[a-zA-Z0-9" -]*$')
|
name: str = Field(..., min_length=1, max_length=100, pattern='^[a-zA-Z0-9\" -]*$')
|
||||||
|
|
||||||
|
|
||||||
class TagCreate(TagUpdate):
|
class TagCreate(TagUpdate):
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue