Compare commits
29 commits
test-redis
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
90510aa33b | ||
|
|
96a70f5d41 | ||
|
|
d4a13edcf0 | ||
|
|
51fad91a22 | ||
|
|
36abcda1e1 | ||
|
|
dd5f464f73 | ||
|
|
f9ada41272 | ||
|
|
9e24a3583e | ||
|
|
0a3129d3cd | ||
|
|
99d61db9d9 | ||
|
|
133958622e | ||
|
|
fb021f606f | ||
|
|
a2905fa8ed | ||
|
|
beec2283fd | ||
|
|
6c8b55019e | ||
|
|
e3e3e11227 | ||
|
|
c6f7de04cc | ||
|
|
2921c17cbf | ||
|
|
7eb3f5c4c8 | ||
|
|
5a9a8e588a | ||
|
|
4b14258266 | ||
|
|
744d2d4311 | ||
|
|
64242a5dc0 | ||
|
|
cae3002697 | ||
|
|
3d3c62196b | ||
|
|
e810958a5d | ||
|
|
39fa9787d1 | ||
|
|
c9c1ad4dde | ||
|
|
d9868928be |
21 changed files with 742 additions and 279 deletions
8
.github/workflows/patch-build-old.yaml
vendored
8
.github/workflows/patch-build-old.yaml
vendored
|
|
@ -8,7 +8,11 @@ on:
|
||||||
required: true
|
required: true
|
||||||
default: 'chalice,frontend'
|
default: 'chalice,frontend'
|
||||||
tag:
|
tag:
|
||||||
description: 'Tag to build patches from.'
|
description: 'Tag to update.'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
branch:
|
||||||
|
description: 'Branch to build patches from. Make sure the branch is uptodate with tag. Else itll cause missing commits.'
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
|
|
||||||
|
|
@ -73,7 +77,7 @@ jobs:
|
||||||
- name: Get HEAD Commit ID
|
- name: Get HEAD Commit ID
|
||||||
run: echo "HEAD_COMMIT_ID=$(git rev-parse HEAD)" >> $GITHUB_ENV
|
run: echo "HEAD_COMMIT_ID=$(git rev-parse HEAD)" >> $GITHUB_ENV
|
||||||
- name: Define Branch Name
|
- name: Define Branch Name
|
||||||
run: echo "BRANCH_NAME=patch/main/${HEAD_COMMIT_ID}" >> $GITHUB_ENV
|
run: echo "BRANCH_NAME=${{inputs.branch}}" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
id: build-image
|
id: build-image
|
||||||
|
|
|
||||||
243
.github/workflows/patch-build.yaml
vendored
243
.github/workflows/patch-build.yaml
vendored
|
|
@ -19,11 +19,20 @@ jobs:
|
||||||
DEPOT_PROJECT_ID: ${{ secrets.DEPOT_PROJECT_ID }}
|
DEPOT_PROJECT_ID: ${{ secrets.DEPOT_PROJECT_ID }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Rebase with main branch, to make sure the code has latest main changes
|
- name: Rebase with main branch, to make sure the code has latest main changes
|
||||||
|
if: github.ref != 'refs/heads/main'
|
||||||
run: |
|
run: |
|
||||||
git fetch origin main
|
git remote -v
|
||||||
git rebase origin/main
|
git config --global user.email "action@github.com"
|
||||||
|
git config --global user.name "GitHub Action"
|
||||||
|
git config --global rebase.autoStash true
|
||||||
|
git fetch origin main:main
|
||||||
|
git rebase main
|
||||||
|
git log -3
|
||||||
|
|
||||||
- name: Downloading yq
|
- name: Downloading yq
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -46,6 +55,8 @@ jobs:
|
||||||
aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin ${{ secrets.RELEASE_OSS_REGISTRY }}
|
aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin ${{ secrets.RELEASE_OSS_REGISTRY }}
|
||||||
|
|
||||||
- uses: depot/setup-action@v1
|
- uses: depot/setup-action@v1
|
||||||
|
env:
|
||||||
|
DEPOT_TOKEN: ${{ secrets.DEPOT_TOKEN }}
|
||||||
- name: Get HEAD Commit ID
|
- name: Get HEAD Commit ID
|
||||||
run: echo "HEAD_COMMIT_ID=$(git rev-parse HEAD)" >> $GITHUB_ENV
|
run: echo "HEAD_COMMIT_ID=$(git rev-parse HEAD)" >> $GITHUB_ENV
|
||||||
- name: Define Branch Name
|
- name: Define Branch Name
|
||||||
|
|
@ -63,78 +74,168 @@ jobs:
|
||||||
MSAAS_REPO_CLONE_TOKEN: ${{ secrets.MSAAS_REPO_CLONE_TOKEN }}
|
MSAAS_REPO_CLONE_TOKEN: ${{ secrets.MSAAS_REPO_CLONE_TOKEN }}
|
||||||
MSAAS_REPO_URL: ${{ secrets.MSAAS_REPO_URL }}
|
MSAAS_REPO_URL: ${{ secrets.MSAAS_REPO_URL }}
|
||||||
MSAAS_REPO_FOLDER: /tmp/msaas
|
MSAAS_REPO_FOLDER: /tmp/msaas
|
||||||
|
SERVICES_INPUT: ${{ github.event.inputs.services }}
|
||||||
run: |
|
run: |
|
||||||
set -exo pipefail
|
#!/bin/bash
|
||||||
git config --local user.email "action@github.com"
|
set -euo pipefail
|
||||||
git config --local user.name "GitHub Action"
|
|
||||||
git checkout -b $BRANCH_NAME
|
# Configuration
|
||||||
working_dir=$(pwd)
|
readonly WORKING_DIR=$(pwd)
|
||||||
function image_version(){
|
readonly BUILD_SCRIPT_NAME="build.sh"
|
||||||
local service=$1
|
readonly BACKEND_SERVICES_FILE="/tmp/backend.txt"
|
||||||
chart_path="$working_dir/scripts/helmcharts/openreplay/charts/$service/Chart.yaml"
|
|
||||||
current_version=$(yq eval '.AppVersion' $chart_path)
|
# Initialize git configuration
|
||||||
new_version=$(echo $current_version | awk -F. '{$NF += 1 ; print $1"."$2"."$3}')
|
setup_git() {
|
||||||
echo $new_version
|
git config --local user.email "action@github.com"
|
||||||
# yq eval ".AppVersion = \"$new_version\"" -i $chart_path
|
git config --local user.name "GitHub Action"
|
||||||
|
git checkout -b "$BRANCH_NAME"
|
||||||
}
|
}
|
||||||
function clone_msaas() {
|
|
||||||
[ -d $MSAAS_REPO_FOLDER ] || {
|
# Get and increment image version
|
||||||
git clone -b dev --recursive https://x-access-token:$MSAAS_REPO_CLONE_TOKEN@$MSAAS_REPO_URL $MSAAS_REPO_FOLDER
|
image_version() {
|
||||||
cd $MSAAS_REPO_FOLDER
|
local service=$1
|
||||||
cd openreplay && git fetch origin && git checkout main # This have to be changed to specific tag
|
local chart_path="$WORKING_DIR/scripts/helmcharts/openreplay/charts/$service/Chart.yaml"
|
||||||
git log -1
|
local current_version new_version
|
||||||
cd $MSAAS_REPO_FOLDER
|
|
||||||
bash git-init.sh
|
current_version=$(yq eval '.AppVersion' "$chart_path")
|
||||||
git checkout
|
new_version=$(echo "$current_version" | awk -F. '{$NF += 1; print $1"."$2"."$3}')
|
||||||
}
|
echo "$new_version"
|
||||||
}
|
}
|
||||||
function build_managed() {
|
|
||||||
local service=$1
|
# Clone MSAAS repository if not exists
|
||||||
local version=$2
|
clone_msaas() {
|
||||||
echo building managed
|
if [[ ! -d "$MSAAS_REPO_FOLDER" ]]; then
|
||||||
clone_msaas
|
git clone -b dev --recursive "https://x-access-token:${MSAAS_REPO_CLONE_TOKEN}@${MSAAS_REPO_URL}" "$MSAAS_REPO_FOLDER"
|
||||||
if [[ $service == 'chalice' ]]; then
|
cd "$MSAAS_REPO_FOLDER"
|
||||||
cd $MSAAS_REPO_FOLDER/openreplay/api
|
cd openreplay && git fetch origin && git checkout main
|
||||||
else
|
git log -1
|
||||||
cd $MSAAS_REPO_FOLDER/openreplay/$service
|
cd "$MSAAS_REPO_FOLDER"
|
||||||
fi
|
bash git-init.sh
|
||||||
IMAGE_TAG=$version DOCKER_RUNTIME="depot" DOCKER_BUILD_ARGS="--push" ARCH=arm64 DOCKER_REPO=$DOCKER_REPO_ARM PUSH_IMAGE=0 bash build.sh >> /tmp/arm.txt
|
git checkout
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
# Checking for backend images
|
|
||||||
ls backend/cmd >> /tmp/backend.txt
|
# Build managed services
|
||||||
echo Services: "${{ github.event.inputs.services }}"
|
build_managed() {
|
||||||
IFS=',' read -ra SERVICES <<< "${{ github.event.inputs.services }}"
|
local service=$1
|
||||||
BUILD_SCRIPT_NAME="build.sh"
|
local version=$2
|
||||||
# Build FOSS
|
|
||||||
for SERVICE in "${SERVICES[@]}"; do
|
echo "Building managed service: $service"
|
||||||
# Check if service is backend
|
clone_msaas
|
||||||
if grep -q $SERVICE /tmp/backend.txt; then
|
|
||||||
cd backend
|
if [[ $service == 'chalice' ]]; then
|
||||||
foss_build_args="nil $SERVICE"
|
cd "$MSAAS_REPO_FOLDER/openreplay/api"
|
||||||
ee_build_args="ee $SERVICE"
|
else
|
||||||
else
|
cd "$MSAAS_REPO_FOLDER/openreplay/$service"
|
||||||
[[ $SERVICE == 'chalice' || $SERVICE == 'alerts' || $SERVICE == 'crons' ]] && cd $working_dir/api || cd $SERVICE
|
fi
|
||||||
[[ $SERVICE == 'alerts' || $SERVICE == 'crons' ]] && BUILD_SCRIPT_NAME="build_${SERVICE}.sh"
|
|
||||||
ee_build_args="ee"
|
local build_cmd="IMAGE_TAG=$version DOCKER_RUNTIME=depot DOCKER_BUILD_ARGS=--push ARCH=arm64 DOCKER_REPO=$DOCKER_REPO_ARM PUSH_IMAGE=0 bash build.sh"
|
||||||
fi
|
|
||||||
version=$(image_version $SERVICE)
|
echo "Executing: $build_cmd"
|
||||||
echo IMAGE_TAG=$version DOCKER_RUNTIME="depot" DOCKER_BUILD_ARGS="--push" ARCH=amd64 DOCKER_REPO=$DOCKER_REPO_OSS PUSH_IMAGE=0 bash ${BUILD_SCRIPT_NAME} $foss_build_args
|
if ! eval "$build_cmd" 2>&1; then
|
||||||
IMAGE_TAG=$version DOCKER_RUNTIME="depot" DOCKER_BUILD_ARGS="--push" ARCH=amd64 DOCKER_REPO=$DOCKER_REPO_OSS PUSH_IMAGE=0 bash ${BUILD_SCRIPT_NAME} $foss_build_args
|
echo "Build failed for $service"
|
||||||
echo IMAGE_TAG=$version-ee DOCKER_RUNTIME="depot" DOCKER_BUILD_ARGS="--push" ARCH=amd64 DOCKER_REPO=$DOCKER_REPO_OSS PUSH_IMAGE=0 bash ${BUILD_SCRIPT_NAME} $ee_build_args
|
exit 1
|
||||||
IMAGE_TAG=$version-ee DOCKER_RUNTIME="depot" DOCKER_BUILD_ARGS="--push" ARCH=amd64 DOCKER_REPO=$DOCKER_REPO_OSS PUSH_IMAGE=0 bash ${BUILD_SCRIPT_NAME} $ee_build_args
|
fi
|
||||||
if [[ "$SERVICE" != "chalice" && "$SERVICE" != "frontend" ]]; then
|
}
|
||||||
IMAGE_TAG=$version DOCKER_RUNTIME="depot" DOCKER_BUILD_ARGS="--push" ARCH=arm64 DOCKER_REPO=$DOCKER_REPO_ARM PUSH_IMAGE=0 bash ${BUILD_SCRIPT_NAME} $foss_build_args
|
|
||||||
echo IMAGE_TAG=$version DOCKER_RUNTIME="depot" DOCKER_BUILD_ARGS="--push" ARCH=arm64 DOCKER_REPO=$DOCKER_REPO_ARM PUSH_IMAGE=0 bash ${BUILD_SCRIPT_NAME} $foss_build_args
|
# Build service with given arguments
|
||||||
else
|
build_service() {
|
||||||
build_managed $SERVICE $version
|
local service=$1
|
||||||
fi
|
local version=$2
|
||||||
cd $working_dir
|
local build_args=$3
|
||||||
chart_path="$working_dir/scripts/helmcharts/openreplay/charts/$SERVICE/Chart.yaml"
|
local build_script=${4:-$BUILD_SCRIPT_NAME}
|
||||||
yq eval ".AppVersion = \"$version\"" -i $chart_path
|
|
||||||
git add $chart_path
|
local command="IMAGE_TAG=$version DOCKER_RUNTIME=depot DOCKER_BUILD_ARGS=--push ARCH=amd64 DOCKER_REPO=$DOCKER_REPO_OSS PUSH_IMAGE=0 bash $build_script $build_args"
|
||||||
git commit -m "Increment $SERVICE chart version"
|
echo "Executing: $command"
|
||||||
git push --set-upstream origin $BRANCH_NAME
|
eval "$command"
|
||||||
done
|
}
|
||||||
|
|
||||||
|
# Update chart version and commit changes
|
||||||
|
update_chart_version() {
|
||||||
|
local service=$1
|
||||||
|
local version=$2
|
||||||
|
local chart_path="$WORKING_DIR/scripts/helmcharts/openreplay/charts/$service/Chart.yaml"
|
||||||
|
|
||||||
|
# Ensure we're in the original working directory/repository
|
||||||
|
cd "$WORKING_DIR"
|
||||||
|
yq eval ".AppVersion = \"$version\"" -i "$chart_path"
|
||||||
|
git add "$chart_path"
|
||||||
|
git commit -m "Increment $service chart version to $version"
|
||||||
|
git push --set-upstream origin "$BRANCH_NAME"
|
||||||
|
cd -
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main execution
|
||||||
|
main() {
|
||||||
|
setup_git
|
||||||
|
|
||||||
|
# Get backend services list
|
||||||
|
ls backend/cmd >"$BACKEND_SERVICES_FILE"
|
||||||
|
|
||||||
|
# Parse services input (fix for GitHub Actions syntax)
|
||||||
|
echo "Services: ${SERVICES_INPUT:-$1}"
|
||||||
|
IFS=',' read -ra services <<<"${SERVICES_INPUT:-$1}"
|
||||||
|
|
||||||
|
# Process each service
|
||||||
|
for service in "${services[@]}"; do
|
||||||
|
echo "Processing service: $service"
|
||||||
|
cd "$WORKING_DIR"
|
||||||
|
|
||||||
|
local foss_build_args="" ee_build_args="" build_script="$BUILD_SCRIPT_NAME"
|
||||||
|
|
||||||
|
# Determine build configuration based on service type
|
||||||
|
if grep -q "$service" "$BACKEND_SERVICES_FILE"; then
|
||||||
|
# Backend service
|
||||||
|
cd backend
|
||||||
|
foss_build_args="nil $service"
|
||||||
|
ee_build_args="ee $service"
|
||||||
|
else
|
||||||
|
# Non-backend service
|
||||||
|
case "$service" in
|
||||||
|
chalice | alerts | crons)
|
||||||
|
cd "$WORKING_DIR/api"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
cd "$service"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Special build scripts for alerts/crons
|
||||||
|
if [[ $service == 'alerts' || $service == 'crons' ]]; then
|
||||||
|
build_script="build_${service}.sh"
|
||||||
|
fi
|
||||||
|
|
||||||
|
ee_build_args="ee"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get version and build
|
||||||
|
local version
|
||||||
|
version=$(image_version "$service")
|
||||||
|
|
||||||
|
# Build FOSS and EE versions
|
||||||
|
build_service "$service" "$version" "$foss_build_args"
|
||||||
|
build_service "$service" "${version}-ee" "$ee_build_args"
|
||||||
|
|
||||||
|
# Build managed version for specific services
|
||||||
|
if [[ "$service" != "chalice" && "$service" != "frontend" ]]; then
|
||||||
|
echo "Nothing to build in managed for service $service"
|
||||||
|
else
|
||||||
|
build_managed "$service" "$version"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Update chart and commit
|
||||||
|
update_chart_version "$service" "$version"
|
||||||
|
done
|
||||||
|
cd "$WORKING_DIR"
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
rm -f "$BACKEND_SERVICES_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Working directory: $WORKING_DIR"
|
||||||
|
# Run main function with all arguments
|
||||||
|
main "$SERVICES_INPUT"
|
||||||
|
|
||||||
|
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: repo-sync/pull-request@v2
|
uses: repo-sync/pull-request@v2
|
||||||
|
|
|
||||||
|
|
@ -50,8 +50,8 @@ class JIRAIntegration(base.BaseIntegration):
|
||||||
cur.execute(
|
cur.execute(
|
||||||
cur.mogrify(
|
cur.mogrify(
|
||||||
"""SELECT username, token, url
|
"""SELECT username, token, url
|
||||||
FROM public.jira_cloud
|
FROM public.jira_cloud
|
||||||
WHERE user_id=%(user_id)s;""",
|
WHERE user_id = %(user_id)s;""",
|
||||||
{"user_id": self._user_id})
|
{"user_id": self._user_id})
|
||||||
)
|
)
|
||||||
data = helper.dict_to_camel_case(cur.fetchone())
|
data = helper.dict_to_camel_case(cur.fetchone())
|
||||||
|
|
@ -95,10 +95,9 @@ class JIRAIntegration(base.BaseIntegration):
|
||||||
def add(self, username, token, url, obfuscate=False):
|
def add(self, username, token, url, obfuscate=False):
|
||||||
with pg_client.PostgresClient() as cur:
|
with pg_client.PostgresClient() as cur:
|
||||||
cur.execute(
|
cur.execute(
|
||||||
cur.mogrify("""\
|
cur.mogrify(""" \
|
||||||
INSERT INTO public.jira_cloud(username, token, user_id,url)
|
INSERT INTO public.jira_cloud(username, token, user_id, url)
|
||||||
VALUES (%(username)s, %(token)s, %(user_id)s,%(url)s)
|
VALUES (%(username)s, %(token)s, %(user_id)s, %(url)s) RETURNING username, token, url;""",
|
||||||
RETURNING username, token, url;""",
|
|
||||||
{"user_id": self._user_id, "username": username,
|
{"user_id": self._user_id, "username": username,
|
||||||
"token": token, "url": url})
|
"token": token, "url": url})
|
||||||
)
|
)
|
||||||
|
|
@ -112,9 +111,10 @@ class JIRAIntegration(base.BaseIntegration):
|
||||||
def delete(self):
|
def delete(self):
|
||||||
with pg_client.PostgresClient() as cur:
|
with pg_client.PostgresClient() as cur:
|
||||||
cur.execute(
|
cur.execute(
|
||||||
cur.mogrify("""\
|
cur.mogrify(""" \
|
||||||
DELETE FROM public.jira_cloud
|
DELETE
|
||||||
WHERE user_id=%(user_id)s;""",
|
FROM public.jira_cloud
|
||||||
|
WHERE user_id = %(user_id)s;""",
|
||||||
{"user_id": self._user_id})
|
{"user_id": self._user_id})
|
||||||
)
|
)
|
||||||
return {"state": "success"}
|
return {"state": "success"}
|
||||||
|
|
@ -125,7 +125,7 @@ class JIRAIntegration(base.BaseIntegration):
|
||||||
changes={
|
changes={
|
||||||
"username": data.username,
|
"username": data.username,
|
||||||
"token": data.token if len(data.token) > 0 and data.token.find("***") == -1 \
|
"token": data.token if len(data.token) > 0 and data.token.find("***") == -1 \
|
||||||
else self.integration.token,
|
else self.integration["token"],
|
||||||
"url": str(data.url)
|
"url": str(data.url)
|
||||||
},
|
},
|
||||||
obfuscate=True
|
obfuscate=True
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,16 @@
|
||||||
|
SELECT 1
|
||||||
|
FROM (SELECT throwIf(platform = 'ios', 'IOS sessions found')
|
||||||
|
FROM experimental.sessions) AS raw
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
SELECT 1
|
||||||
|
FROM (SELECT throwIf(platform = 'android', 'Android sessions found')
|
||||||
|
FROM experimental.sessions) AS raw
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
ALTER TABLE experimental.sessions
|
||||||
|
MODIFY COLUMN platform Enum8('web'=1,'mobile'=2) DEFAULT 'web';
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION openreplay_version AS() -> 'v1.22.0-ee';
|
CREATE OR REPLACE FUNCTION openreplay_version AS() -> 'v1.22.0-ee';
|
||||||
|
|
||||||
SET allow_experimental_json_type = 1;
|
SET allow_experimental_json_type = 1;
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@ CREATE TABLE IF NOT EXISTS experimental.sessions
|
||||||
user_country Enum8('UN'=-128, 'RW'=-127, 'SO'=-126, 'YE'=-125, 'IQ'=-124, 'SA'=-123, 'IR'=-122, 'CY'=-121, 'TZ'=-120, 'SY'=-119, 'AM'=-118, 'KE'=-117, 'CD'=-116, 'DJ'=-115, 'UG'=-114, 'CF'=-113, 'SC'=-112, 'JO'=-111, 'LB'=-110, 'KW'=-109, 'OM'=-108, 'QA'=-107, 'BH'=-106, 'AE'=-105, 'IL'=-104, 'TR'=-103, 'ET'=-102, 'ER'=-101, 'EG'=-100, 'SD'=-99, 'GR'=-98, 'BI'=-97, 'EE'=-96, 'LV'=-95, 'AZ'=-94, 'LT'=-93, 'SJ'=-92, 'GE'=-91, 'MD'=-90, 'BY'=-89, 'FI'=-88, 'AX'=-87, 'UA'=-86, 'MK'=-85, 'HU'=-84, 'BG'=-83, 'AL'=-82, 'PL'=-81, 'RO'=-80, 'XK'=-79, 'ZW'=-78, 'ZM'=-77, 'KM'=-76, 'MW'=-75, 'LS'=-74, 'BW'=-73, 'MU'=-72, 'SZ'=-71, 'RE'=-70, 'ZA'=-69, 'YT'=-68, 'MZ'=-67, 'MG'=-66, 'AF'=-65, 'PK'=-64, 'BD'=-63, 'TM'=-62, 'TJ'=-61, 'LK'=-60, 'BT'=-59, 'IN'=-58, 'MV'=-57, 'IO'=-56, 'NP'=-55, 'MM'=-54, 'UZ'=-53, 'KZ'=-52, 'KG'=-51, 'TF'=-50, 'HM'=-49, 'CC'=-48, 'PW'=-47, 'VN'=-46, 'TH'=-45, 'ID'=-44, 'LA'=-43, 'TW'=-42, 'PH'=-41, 'MY'=-40, 'CN'=-39, 'HK'=-38, 'BN'=-37, 'MO'=-36, 'KH'=-35, 'KR'=-34, 'JP'=-33, 'KP'=-32, 'SG'=-31, 'CK'=-30, 'TL'=-29, 'RU'=-28, 'MN'=-27, 'AU'=-26, 'CX'=-25, 'MH'=-24, 'FM'=-23, 'PG'=-22, 'SB'=-21, 'TV'=-20, 'NR'=-19, 'VU'=-18, 'NC'=-17, 'NF'=-16, 'NZ'=-15, 'FJ'=-14, 'LY'=-13, 'CM'=-12, 'SN'=-11, 'CG'=-10, 'PT'=-9, 'LR'=-8, 'CI'=-7, 'GH'=-6, 'GQ'=-5, 'NG'=-4, 'BF'=-3, 'TG'=-2, 'GW'=-1, 'MR'=0, 'BJ'=1, 'GA'=2, 'SL'=3, 'ST'=4, 'GI'=5, 'GM'=6, 'GN'=7, 'TD'=8, 'NE'=9, 'ML'=10, 'EH'=11, 'TN'=12, 'ES'=13, 'MA'=14, 'MT'=15, 'DZ'=16, 'FO'=17, 'DK'=18, 'IS'=19, 'GB'=20, 'CH'=21, 'SE'=22, 'NL'=23, 'AT'=24, 'BE'=25, 'DE'=26, 'LU'=27, 'IE'=28, 'MC'=29, 'FR'=30, 'AD'=31, 'LI'=32, 'JE'=33, 'IM'=34, 'GG'=35, 'SK'=36, 'CZ'=37, 'NO'=38, 'VA'=39, 'SM'=40, 'IT'=41, 'SI'=42, 'ME'=43, 'HR'=44, 'BA'=45, 'AO'=46, 'NA'=47, 'SH'=48, 'BV'=49, 'BB'=50, 'CV'=51, 'GY'=52, 'GF'=53, 'SR'=54, 'PM'=55, 'GL'=56, 'PY'=57, 'UY'=58, 'BR'=59, 'FK'=60, 'GS'=61, 'JM'=62, 'DO'=63, 'CU'=64, 'MQ'=65, 'BS'=66, 'BM'=67, 'AI'=68, 'TT'=69, 'KN'=70, 'DM'=71, 'AG'=72, 'LC'=73, 'TC'=74, 'AW'=75, 'VG'=76, 'VC'=77, 'MS'=78, 'MF'=79, 'BL'=80, 'GP'=81, 'GD'=82, 'KY'=83, 'BZ'=84, 'SV'=85, 'GT'=86, 'HN'=87, 'NI'=88, 'CR'=89, 'VE'=90, 'EC'=91, 'CO'=92, 'PA'=93, 'HT'=94, 'AR'=95, 'CL'=96, 'BO'=97, 'PE'=98, 'MX'=99, 'PF'=100, 'PN'=101, 'KI'=102, 'TK'=103, 'TO'=104, 'WF'=105, 'WS'=106, 'NU'=107, 'MP'=108, 'GU'=109, 'PR'=110, 'VI'=111, 'UM'=112, 'AS'=113, 'CA'=114, 'US'=115, 'PS'=116, 'RS'=117, 'AQ'=118, 'SX'=119, 'CW'=120, 'BQ'=121, 'SS'=122,'BU'=123, 'VD'=124, 'YD'=125, 'DD'=126),
|
user_country Enum8('UN'=-128, 'RW'=-127, 'SO'=-126, 'YE'=-125, 'IQ'=-124, 'SA'=-123, 'IR'=-122, 'CY'=-121, 'TZ'=-120, 'SY'=-119, 'AM'=-118, 'KE'=-117, 'CD'=-116, 'DJ'=-115, 'UG'=-114, 'CF'=-113, 'SC'=-112, 'JO'=-111, 'LB'=-110, 'KW'=-109, 'OM'=-108, 'QA'=-107, 'BH'=-106, 'AE'=-105, 'IL'=-104, 'TR'=-103, 'ET'=-102, 'ER'=-101, 'EG'=-100, 'SD'=-99, 'GR'=-98, 'BI'=-97, 'EE'=-96, 'LV'=-95, 'AZ'=-94, 'LT'=-93, 'SJ'=-92, 'GE'=-91, 'MD'=-90, 'BY'=-89, 'FI'=-88, 'AX'=-87, 'UA'=-86, 'MK'=-85, 'HU'=-84, 'BG'=-83, 'AL'=-82, 'PL'=-81, 'RO'=-80, 'XK'=-79, 'ZW'=-78, 'ZM'=-77, 'KM'=-76, 'MW'=-75, 'LS'=-74, 'BW'=-73, 'MU'=-72, 'SZ'=-71, 'RE'=-70, 'ZA'=-69, 'YT'=-68, 'MZ'=-67, 'MG'=-66, 'AF'=-65, 'PK'=-64, 'BD'=-63, 'TM'=-62, 'TJ'=-61, 'LK'=-60, 'BT'=-59, 'IN'=-58, 'MV'=-57, 'IO'=-56, 'NP'=-55, 'MM'=-54, 'UZ'=-53, 'KZ'=-52, 'KG'=-51, 'TF'=-50, 'HM'=-49, 'CC'=-48, 'PW'=-47, 'VN'=-46, 'TH'=-45, 'ID'=-44, 'LA'=-43, 'TW'=-42, 'PH'=-41, 'MY'=-40, 'CN'=-39, 'HK'=-38, 'BN'=-37, 'MO'=-36, 'KH'=-35, 'KR'=-34, 'JP'=-33, 'KP'=-32, 'SG'=-31, 'CK'=-30, 'TL'=-29, 'RU'=-28, 'MN'=-27, 'AU'=-26, 'CX'=-25, 'MH'=-24, 'FM'=-23, 'PG'=-22, 'SB'=-21, 'TV'=-20, 'NR'=-19, 'VU'=-18, 'NC'=-17, 'NF'=-16, 'NZ'=-15, 'FJ'=-14, 'LY'=-13, 'CM'=-12, 'SN'=-11, 'CG'=-10, 'PT'=-9, 'LR'=-8, 'CI'=-7, 'GH'=-6, 'GQ'=-5, 'NG'=-4, 'BF'=-3, 'TG'=-2, 'GW'=-1, 'MR'=0, 'BJ'=1, 'GA'=2, 'SL'=3, 'ST'=4, 'GI'=5, 'GM'=6, 'GN'=7, 'TD'=8, 'NE'=9, 'ML'=10, 'EH'=11, 'TN'=12, 'ES'=13, 'MA'=14, 'MT'=15, 'DZ'=16, 'FO'=17, 'DK'=18, 'IS'=19, 'GB'=20, 'CH'=21, 'SE'=22, 'NL'=23, 'AT'=24, 'BE'=25, 'DE'=26, 'LU'=27, 'IE'=28, 'MC'=29, 'FR'=30, 'AD'=31, 'LI'=32, 'JE'=33, 'IM'=34, 'GG'=35, 'SK'=36, 'CZ'=37, 'NO'=38, 'VA'=39, 'SM'=40, 'IT'=41, 'SI'=42, 'ME'=43, 'HR'=44, 'BA'=45, 'AO'=46, 'NA'=47, 'SH'=48, 'BV'=49, 'BB'=50, 'CV'=51, 'GY'=52, 'GF'=53, 'SR'=54, 'PM'=55, 'GL'=56, 'PY'=57, 'UY'=58, 'BR'=59, 'FK'=60, 'GS'=61, 'JM'=62, 'DO'=63, 'CU'=64, 'MQ'=65, 'BS'=66, 'BM'=67, 'AI'=68, 'TT'=69, 'KN'=70, 'DM'=71, 'AG'=72, 'LC'=73, 'TC'=74, 'AW'=75, 'VG'=76, 'VC'=77, 'MS'=78, 'MF'=79, 'BL'=80, 'GP'=81, 'GD'=82, 'KY'=83, 'BZ'=84, 'SV'=85, 'GT'=86, 'HN'=87, 'NI'=88, 'CR'=89, 'VE'=90, 'EC'=91, 'CO'=92, 'PA'=93, 'HT'=94, 'AR'=95, 'CL'=96, 'BO'=97, 'PE'=98, 'MX'=99, 'PF'=100, 'PN'=101, 'KI'=102, 'TK'=103, 'TO'=104, 'WF'=105, 'WS'=106, 'NU'=107, 'MP'=108, 'GU'=109, 'PR'=110, 'VI'=111, 'UM'=112, 'AS'=113, 'CA'=114, 'US'=115, 'PS'=116, 'RS'=117, 'AQ'=118, 'SX'=119, 'CW'=120, 'BQ'=121, 'SS'=122,'BU'=123, 'VD'=124, 'YD'=125, 'DD'=126),
|
||||||
user_city LowCardinality(String),
|
user_city LowCardinality(String),
|
||||||
user_state LowCardinality(String),
|
user_state LowCardinality(String),
|
||||||
platform Enum8('web'=1,'ios'=2,'android'=3) DEFAULT 'web',
|
platform Enum8('web'=1,'mobile'=2) DEFAULT 'web',
|
||||||
datetime DateTime,
|
datetime DateTime,
|
||||||
timezone LowCardinality(Nullable(String)),
|
timezone LowCardinality(Nullable(String)),
|
||||||
duration UInt32,
|
duration UInt32,
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ function BottomButtons({
|
||||||
<Button
|
<Button
|
||||||
loading={loading}
|
loading={loading}
|
||||||
type="primary"
|
type="primary"
|
||||||
|
htmlType="submit"
|
||||||
disabled={loading || !instance.validate()}
|
disabled={loading || !instance.validate()}
|
||||||
id="submit-button"
|
id="submit-button"
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,7 @@ function DashboardView(props: Props) {
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
dashboardStore.resetPeriod();
|
||||||
if (queryParams.has('modal')) {
|
if (queryParams.has('modal')) {
|
||||||
onAddWidgets();
|
onAddWidgets();
|
||||||
trimQuery();
|
trimQuery();
|
||||||
|
|
|
||||||
|
|
@ -117,8 +117,6 @@ const ListView: React.FC<Props> = ({
|
||||||
if (disableSelection) {
|
if (disableSelection) {
|
||||||
const path = withSiteId(`/metrics/${metric.metricId}`, siteId);
|
const path = withSiteId(`/metrics/${metric.metricId}`, siteId);
|
||||||
history.push(path);
|
history.push(path);
|
||||||
} else {
|
|
||||||
toggleSelection?.(metric.metricId);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import {
|
||||||
LikeFilled,
|
LikeFilled,
|
||||||
LikeOutlined,
|
LikeOutlined,
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import { Tour, TourProps } from './.store/antd-virtual-7db13b4af6/package';
|
import { Tour, TourProps } from 'antd';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ function DropdownAudioPlayer({
|
||||||
return {
|
return {
|
||||||
url: data.url,
|
url: data.url,
|
||||||
timestamp: data.timestamp,
|
timestamp: data.timestamp,
|
||||||
start: startTs,
|
start: Math.max(0, startTs),
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
[audioEvents.length, sessionStart],
|
[audioEvents.length, sessionStart],
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,8 @@ import EventGroupWrapper from './EventGroupWrapper';
|
||||||
import EventSearch from './EventSearch/EventSearch';
|
import EventSearch from './EventSearch/EventSearch';
|
||||||
import styles from './eventsBlock.module.css';
|
import styles from './eventsBlock.module.css';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { CloseOutlined } from ".store/@ant-design-icons-virtual-42686020c5/package";
|
import { CloseOutlined } from "@ant-design/icons";
|
||||||
import { Tooltip } from ".store/antd-virtual-9dbfadb7f6/package";
|
import { Tooltip } from "antd";
|
||||||
import { getDefaultFramework, frameworkIcons } from "../UnitStepsModal";
|
import { getDefaultFramework, frameworkIcons } from "../UnitStepsModal";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,17 @@
|
||||||
/* eslint-disable i18next/no-literal-string */
|
/* eslint-disable i18next/no-literal-string */
|
||||||
import { ResourceType, Timed } from 'Player';
|
import { ResourceType, Timed } from 'Player';
|
||||||
|
import { WsChannel } from 'Player/web/messages';
|
||||||
import MobilePlayer from 'Player/mobile/IOSPlayer';
|
import MobilePlayer from 'Player/mobile/IOSPlayer';
|
||||||
import WebPlayer from 'Player/web/WebPlayer';
|
import WebPlayer from 'Player/web/WebPlayer';
|
||||||
import { observer } from 'mobx-react-lite';
|
import { observer } from 'mobx-react-lite';
|
||||||
import React, { useMemo, useState } from 'react';
|
import React, {
|
||||||
|
useMemo,
|
||||||
|
useState,
|
||||||
|
useEffect,
|
||||||
|
useCallback,
|
||||||
|
useRef,
|
||||||
|
} from 'react';
|
||||||
|
import i18n from 'App/i18n'
|
||||||
|
|
||||||
import { useModal } from 'App/components/Modal';
|
import { useModal } from 'App/components/Modal';
|
||||||
import {
|
import {
|
||||||
|
|
@ -12,25 +20,27 @@ import {
|
||||||
} from 'App/components/Session/playerContext';
|
} from 'App/components/Session/playerContext';
|
||||||
import { formatMs } from 'App/date';
|
import { formatMs } from 'App/date';
|
||||||
import { useStore } from 'App/mstore';
|
import { useStore } from 'App/mstore';
|
||||||
import { formatBytes } from 'App/utils';
|
import { formatBytes, debounceCall } from 'App/utils';
|
||||||
import { Icon, NoContent, Tabs } from 'UI';
|
import { Icon, NoContent, Tabs } from 'UI';
|
||||||
import { Tooltip, Input, Switch, Form } from 'antd';
|
import { Tooltip, Input, Switch, Form } from 'antd';
|
||||||
import { SearchOutlined, InfoCircleOutlined } from '@ant-design/icons';
|
import {
|
||||||
|
SearchOutlined,
|
||||||
|
InfoCircleOutlined,
|
||||||
|
} from '@ant-design/icons';
|
||||||
|
|
||||||
import FetchDetailsModal from 'Shared/FetchDetailsModal';
|
import FetchDetailsModal from 'Shared/FetchDetailsModal';
|
||||||
import { WsChannel } from 'App/player/web/messages';
|
|
||||||
|
|
||||||
import BottomBlock from '../BottomBlock';
|
import BottomBlock from '../BottomBlock';
|
||||||
import InfoLine from '../BottomBlock/InfoLine';
|
import InfoLine from '../BottomBlock/InfoLine';
|
||||||
import TabSelector from '../TabSelector';
|
import TabSelector from '../TabSelector';
|
||||||
import TimeTable from '../TimeTable';
|
import TimeTable from '../TimeTable';
|
||||||
import useAutoscroll, { getLastItemTime } from '../useAutoscroll';
|
import useAutoscroll, { getLastItemTime } from '../useAutoscroll';
|
||||||
import { useRegExListFilterMemo, useTabListFilterMemo } from '../useListFilter';
|
|
||||||
import WSPanel from './WSPanel';
|
import WSPanel from './WSPanel';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { mergeListsWithZoom, processInChunks } from './utils'
|
||||||
|
|
||||||
|
// Constants remain the same
|
||||||
const INDEX_KEY = 'network';
|
const INDEX_KEY = 'network';
|
||||||
|
|
||||||
const ALL = 'ALL';
|
const ALL = 'ALL';
|
||||||
const XHR = 'xhr';
|
const XHR = 'xhr';
|
||||||
const JS = 'js';
|
const JS = 'js';
|
||||||
|
|
@ -62,6 +72,9 @@ export const NETWORK_TABS = TAP_KEYS.map((tab) => ({
|
||||||
const DOM_LOADED_TIME_COLOR = 'teal';
|
const DOM_LOADED_TIME_COLOR = 'teal';
|
||||||
const LOAD_TIME_COLOR = 'red';
|
const LOAD_TIME_COLOR = 'red';
|
||||||
|
|
||||||
|
const BATCH_SIZE = 2500;
|
||||||
|
const INITIAL_LOAD_SIZE = 5000;
|
||||||
|
|
||||||
export function renderType(r: any) {
|
export function renderType(r: any) {
|
||||||
return (
|
return (
|
||||||
<Tooltip style={{ width: '100%' }} title={<div>{r.type}</div>}>
|
<Tooltip style={{ width: '100%' }} title={<div>{r.type}</div>}>
|
||||||
|
|
@ -79,13 +92,17 @@ export function renderName(r: any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderSize(r: any) {
|
function renderSize(r: any) {
|
||||||
const { t } = useTranslation();
|
const t = i18n.t;
|
||||||
if (r.responseBodySize) return formatBytes(r.responseBodySize);
|
const notCaptured = t('Not captured');
|
||||||
|
const resSizeStr = t('Resource size')
|
||||||
let triggerText;
|
let triggerText;
|
||||||
let content;
|
let content;
|
||||||
if (r.decodedBodySize == null || r.decodedBodySize === 0) {
|
if (r.responseBodySize) {
|
||||||
|
triggerText = formatBytes(r.responseBodySize);
|
||||||
|
content = undefined;
|
||||||
|
} else if (r.decodedBodySize == null || r.decodedBodySize === 0) {
|
||||||
triggerText = 'x';
|
triggerText = 'x';
|
||||||
content = t('Not captured');
|
content = notCaptured;
|
||||||
} else {
|
} else {
|
||||||
const headerSize = r.headerSize || 0;
|
const headerSize = r.headerSize || 0;
|
||||||
const showTransferred = r.headerSize != null;
|
const showTransferred = r.headerSize != null;
|
||||||
|
|
@ -100,7 +117,7 @@ function renderSize(r: any) {
|
||||||
)} transferred over network`}
|
)} transferred over network`}
|
||||||
</li>
|
</li>
|
||||||
)}
|
)}
|
||||||
<li>{`${t('Resource size')}: ${formatBytes(r.decodedBodySize)} `}</li>
|
<li>{`${resSizeStr}: ${formatBytes(r.decodedBodySize)} `}</li>
|
||||||
</ul>
|
</ul>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -168,6 +185,8 @@ function renderStatus({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Main component for Network Panel
|
||||||
function NetworkPanelCont({ panelHeight }: { panelHeight: number }) {
|
function NetworkPanelCont({ panelHeight }: { panelHeight: number }) {
|
||||||
const { player, store } = React.useContext(PlayerContext);
|
const { player, store } = React.useContext(PlayerContext);
|
||||||
const { sessionStore, uiPlayerStore } = useStore();
|
const { sessionStore, uiPlayerStore } = useStore();
|
||||||
|
|
@ -216,6 +235,7 @@ function NetworkPanelCont({ panelHeight }: { panelHeight: number }) {
|
||||||
|
|
||||||
const getTabNum = (tab: string) => tabsArr.findIndex((t) => t === tab) + 1;
|
const getTabNum = (tab: string) => tabsArr.findIndex((t) => t === tab) + 1;
|
||||||
const getTabName = (tabId: string) => tabNames[tabId];
|
const getTabName = (tabId: string) => tabNames[tabId];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NetworkPanelComp
|
<NetworkPanelComp
|
||||||
loadTime={loadTime}
|
loadTime={loadTime}
|
||||||
|
|
@ -228,8 +248,8 @@ function NetworkPanelCont({ panelHeight }: { panelHeight: number }) {
|
||||||
resourceListNow={resourceListNow}
|
resourceListNow={resourceListNow}
|
||||||
player={player}
|
player={player}
|
||||||
startedAt={startedAt}
|
startedAt={startedAt}
|
||||||
websocketList={websocketList as WSMessage[]}
|
websocketList={websocketList}
|
||||||
websocketListNow={websocketListNow as WSMessage[]}
|
websocketListNow={websocketListNow}
|
||||||
getTabNum={getTabNum}
|
getTabNum={getTabNum}
|
||||||
getTabName={getTabName}
|
getTabName={getTabName}
|
||||||
showSingleTab={showSingleTab}
|
showSingleTab={showSingleTab}
|
||||||
|
|
@ -269,9 +289,7 @@ function MobileNetworkPanelCont({ panelHeight }: { panelHeight: number }) {
|
||||||
resourceListNow={resourceListNow}
|
resourceListNow={resourceListNow}
|
||||||
player={player}
|
player={player}
|
||||||
startedAt={startedAt}
|
startedAt={startedAt}
|
||||||
// @ts-ignore
|
|
||||||
websocketList={websocketList}
|
websocketList={websocketList}
|
||||||
// @ts-ignore
|
|
||||||
websocketListNow={websocketListNow}
|
websocketListNow={websocketListNow}
|
||||||
zoomEnabled={zoomEnabled}
|
zoomEnabled={zoomEnabled}
|
||||||
zoomStartTs={zoomStartTs}
|
zoomStartTs={zoomStartTs}
|
||||||
|
|
@ -280,12 +298,35 @@ function MobileNetworkPanelCont({ panelHeight }: { panelHeight: number }) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
type WSMessage = Timed & {
|
const useInfiniteScroll = (loadMoreCallback: () => void, hasMore: boolean) => {
|
||||||
channelName: string;
|
const observerRef = useRef<IntersectionObserver>(null);
|
||||||
data: string;
|
const loadingRef = useRef<HTMLDivElement>(null);
|
||||||
timestamp: number;
|
|
||||||
dir: 'up' | 'down';
|
useEffect(() => {
|
||||||
messageType: string;
|
const observer = new IntersectionObserver(
|
||||||
|
(entries) => {
|
||||||
|
if (entries[0]?.isIntersecting && hasMore) {
|
||||||
|
loadMoreCallback();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ threshold: 0.1 },
|
||||||
|
);
|
||||||
|
|
||||||
|
if (loadingRef.current) {
|
||||||
|
observer.observe(loadingRef.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
observerRef.current = observer;
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (observerRef.current) {
|
||||||
|
observerRef.current.disconnect();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [loadMoreCallback, hasMore, loadingRef]);
|
||||||
|
|
||||||
|
return loadingRef;
|
||||||
};
|
};
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|
@ -302,8 +343,8 @@ interface Props {
|
||||||
resourceList: Timed[];
|
resourceList: Timed[];
|
||||||
fetchListNow: Timed[];
|
fetchListNow: Timed[];
|
||||||
resourceListNow: Timed[];
|
resourceListNow: Timed[];
|
||||||
websocketList: Array<WSMessage>;
|
websocketList: Array<WsChannel>;
|
||||||
websocketListNow: Array<WSMessage>;
|
websocketListNow: Array<WsChannel>;
|
||||||
player: WebPlayer | MobilePlayer;
|
player: WebPlayer | MobilePlayer;
|
||||||
startedAt: number;
|
startedAt: number;
|
||||||
isMobile?: boolean;
|
isMobile?: boolean;
|
||||||
|
|
@ -349,107 +390,189 @@ export const NetworkPanelComp = observer(
|
||||||
>(null);
|
>(null);
|
||||||
const { showModal } = useModal();
|
const { showModal } = useModal();
|
||||||
const [showOnlyErrors, setShowOnlyErrors] = useState(false);
|
const [showOnlyErrors, setShowOnlyErrors] = useState(false);
|
||||||
|
|
||||||
const [isDetailsModalActive, setIsDetailsModalActive] = useState(false);
|
const [isDetailsModalActive, setIsDetailsModalActive] = useState(false);
|
||||||
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
const [isProcessing, setIsProcessing] = useState(false);
|
||||||
|
const [displayedItems, setDisplayedItems] = useState([]);
|
||||||
|
const [totalItems, setTotalItems] = useState(0);
|
||||||
|
const [summaryStats, setSummaryStats] = useState({
|
||||||
|
resourcesSize: 0,
|
||||||
|
transferredSize: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
const originalListRef = useRef([]);
|
||||||
|
const socketListRef = useRef([]);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
sessionStore: { devTools },
|
sessionStore: { devTools },
|
||||||
} = useStore();
|
} = useStore();
|
||||||
const { filter } = devTools[INDEX_KEY];
|
const { filter } = devTools[INDEX_KEY];
|
||||||
const { activeTab } = devTools[INDEX_KEY];
|
const { activeTab } = devTools[INDEX_KEY];
|
||||||
const activeIndex = activeOutsideIndex ?? devTools[INDEX_KEY].index;
|
const activeIndex = activeOutsideIndex ?? devTools[INDEX_KEY].index;
|
||||||
|
const [inputFilterValue, setInputFilterValue] = useState(filter);
|
||||||
|
|
||||||
const socketList = useMemo(
|
const debouncedFilter = useCallback(
|
||||||
() =>
|
debounceCall((filterValue) => {
|
||||||
websocketList.filter(
|
devTools.update(INDEX_KEY, { filter: filterValue });
|
||||||
(ws, i, arr) =>
|
}, 300),
|
||||||
arr.findIndex((it) => it.channelName === ws.channelName) === i,
|
[],
|
||||||
),
|
|
||||||
[websocketList],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const list = useMemo(
|
// Process socket lists once
|
||||||
() =>
|
useEffect(() => {
|
||||||
// TODO: better merge (with body size info) - do it in player
|
const uniqueSocketList = websocketList.filter(
|
||||||
resourceList
|
(ws, i, arr) =>
|
||||||
.filter(
|
arr.findIndex((it) => it.channelName === ws.channelName) === i,
|
||||||
(res) =>
|
|
||||||
!fetchList.some((ft) => {
|
|
||||||
// res.url !== ft.url doesn't work on relative URLs appearing within fetchList (to-fix in player)
|
|
||||||
if (res.name === ft.name) {
|
|
||||||
if (res.time === ft.time) return true;
|
|
||||||
if (res.url.includes(ft.url)) {
|
|
||||||
return (
|
|
||||||
Math.abs(res.time - ft.time) < 350 ||
|
|
||||||
Math.abs(res.timestamp - ft.timestamp) < 350
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res.name !== ft.name) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (Math.abs(res.time - ft.time) > 250) {
|
|
||||||
return false;
|
|
||||||
} // TODO: find good epsilons
|
|
||||||
if (Math.abs(res.duration - ft.duration) > 200) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.concat(fetchList)
|
|
||||||
.concat(
|
|
||||||
socketList.map((ws) => ({
|
|
||||||
...ws,
|
|
||||||
type: 'websocket',
|
|
||||||
method: 'ws',
|
|
||||||
url: ws.channelName,
|
|
||||||
name: ws.channelName,
|
|
||||||
status: '101',
|
|
||||||
duration: 0,
|
|
||||||
transferredBodySize: 0,
|
|
||||||
})),
|
|
||||||
)
|
|
||||||
.filter((req) =>
|
|
||||||
zoomEnabled
|
|
||||||
? req.time >= zoomStartTs! && req.time <= zoomEndTs!
|
|
||||||
: true,
|
|
||||||
)
|
|
||||||
.sort((a, b) => a.time - b.time),
|
|
||||||
[resourceList.length, fetchList.length, socketList.length],
|
|
||||||
);
|
|
||||||
|
|
||||||
let filteredList = useMemo(() => {
|
|
||||||
if (!showOnlyErrors) {
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
return list.filter(
|
|
||||||
(it) => parseInt(it.status) >= 400 || !it.success || it.error,
|
|
||||||
);
|
);
|
||||||
}, [showOnlyErrors, list]);
|
socketListRef.current = uniqueSocketList;
|
||||||
filteredList = useRegExListFilterMemo(
|
}, [websocketList.length]);
|
||||||
filteredList,
|
|
||||||
(it) => [it.status, it.name, it.type, it.method],
|
|
||||||
filter,
|
|
||||||
);
|
|
||||||
filteredList = useTabListFilterMemo(
|
|
||||||
filteredList,
|
|
||||||
(it) => TYPE_TO_TAB[it.type],
|
|
||||||
ALL,
|
|
||||||
activeTab,
|
|
||||||
);
|
|
||||||
|
|
||||||
const onTabClick = (activeTab: (typeof TAP_KEYS)[number]) =>
|
// Initial data processing - do this only once when data changes
|
||||||
|
useEffect(() => {
|
||||||
|
setIsLoading(true);
|
||||||
|
|
||||||
|
// Heaviest operation here, will create a final merged network list
|
||||||
|
const processData = async () => {
|
||||||
|
const fetchUrls = new Set(
|
||||||
|
fetchList.map((ft) => {
|
||||||
|
return `${ft.name}-${Math.floor(ft.time / 100)}-${Math.floor(ft.duration / 100)}`;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
// We want to get resources that aren't in fetch list
|
||||||
|
const filteredResources = await processInChunks(resourceList, (chunk) =>
|
||||||
|
chunk.filter((res: any) => {
|
||||||
|
const key = `${res.name}-${Math.floor(res.time / 100)}-${Math.floor(res.duration / 100)}`;
|
||||||
|
return !fetchUrls.has(key);
|
||||||
|
}),
|
||||||
|
BATCH_SIZE,
|
||||||
|
25,
|
||||||
|
);
|
||||||
|
|
||||||
|
const processedSockets = socketListRef.current.map((ws: any) => ({
|
||||||
|
...ws,
|
||||||
|
type: 'websocket',
|
||||||
|
method: 'ws',
|
||||||
|
url: ws.channelName,
|
||||||
|
name: ws.channelName,
|
||||||
|
status: '101',
|
||||||
|
duration: 0,
|
||||||
|
transferredBodySize: 0,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const mergedList: Timed[] = mergeListsWithZoom(
|
||||||
|
filteredResources as Timed[],
|
||||||
|
fetchList,
|
||||||
|
processedSockets as Timed[],
|
||||||
|
{ enabled: Boolean(zoomEnabled), start: zoomStartTs ?? 0, end: zoomEndTs ?? 0 }
|
||||||
|
)
|
||||||
|
|
||||||
|
originalListRef.current = mergedList;
|
||||||
|
setTotalItems(mergedList.length);
|
||||||
|
|
||||||
|
calculateResourceStats(resourceList);
|
||||||
|
|
||||||
|
// Only display initial chunk
|
||||||
|
setDisplayedItems(mergedList.slice(0, INITIAL_LOAD_SIZE));
|
||||||
|
setIsLoading(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
void processData();
|
||||||
|
}, [
|
||||||
|
resourceList.length,
|
||||||
|
fetchList.length,
|
||||||
|
socketListRef.current.length,
|
||||||
|
zoomEnabled,
|
||||||
|
zoomStartTs,
|
||||||
|
zoomEndTs,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const calculateResourceStats = (resourceList: Record<string, any>) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
let resourcesSize = 0
|
||||||
|
let transferredSize = 0
|
||||||
|
resourceList.forEach(({ decodedBodySize, headerSize, encodedBodySize }: any) => {
|
||||||
|
resourcesSize += decodedBodySize || 0
|
||||||
|
transferredSize += (headerSize || 0) + (encodedBodySize || 0)
|
||||||
|
})
|
||||||
|
|
||||||
|
setSummaryStats({
|
||||||
|
resourcesSize,
|
||||||
|
transferredSize,
|
||||||
|
});
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (originalListRef.current.length === 0) return;
|
||||||
|
setIsProcessing(true);
|
||||||
|
const applyFilters = async () => {
|
||||||
|
let filteredItems: any[] = originalListRef.current;
|
||||||
|
|
||||||
|
filteredItems = await processInChunks(filteredItems, (chunk) =>
|
||||||
|
chunk.filter(
|
||||||
|
(it) => {
|
||||||
|
let valid = true;
|
||||||
|
if (showOnlyErrors) {
|
||||||
|
valid = parseInt(it.status) >= 400 || !it.success || it.error
|
||||||
|
}
|
||||||
|
if (filter) {
|
||||||
|
try {
|
||||||
|
const regex = new RegExp(filter, 'i');
|
||||||
|
valid = valid && regex.test(it.status) || regex.test(it.name) || regex.test(it.type) || regex.test(it.method);
|
||||||
|
} catch (e) {
|
||||||
|
valid = valid && String(it.status).includes(filter) || it.name.includes(filter) || it.type.includes(filter) || (it.method && it.method.includes(filter));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (activeTab !== ALL) {
|
||||||
|
valid = valid && TYPE_TO_TAB[it.type] === activeTab;
|
||||||
|
}
|
||||||
|
|
||||||
|
return valid;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update displayed items
|
||||||
|
setDisplayedItems(filteredItems.slice(0, INITIAL_LOAD_SIZE));
|
||||||
|
setTotalItems(filteredItems.length);
|
||||||
|
setIsProcessing(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
void applyFilters();
|
||||||
|
}, [filter, activeTab, showOnlyErrors]);
|
||||||
|
|
||||||
|
const loadMoreItems = useCallback(() => {
|
||||||
|
if (isProcessing) return;
|
||||||
|
|
||||||
|
setIsProcessing(true);
|
||||||
|
setTimeout(() => {
|
||||||
|
setDisplayedItems((prevItems) => {
|
||||||
|
const currentLength = prevItems.length;
|
||||||
|
const newItems = originalListRef.current.slice(
|
||||||
|
currentLength,
|
||||||
|
currentLength + BATCH_SIZE,
|
||||||
|
);
|
||||||
|
return [...prevItems, ...newItems];
|
||||||
|
});
|
||||||
|
setIsProcessing(false);
|
||||||
|
}, 10);
|
||||||
|
}, [isProcessing]);
|
||||||
|
|
||||||
|
const hasMoreItems = displayedItems.length < totalItems;
|
||||||
|
const loadingRef = useInfiniteScroll(loadMoreItems, hasMoreItems);
|
||||||
|
|
||||||
|
const onTabClick = (activeTab) => {
|
||||||
devTools.update(INDEX_KEY, { activeTab });
|
devTools.update(INDEX_KEY, { activeTab });
|
||||||
const onFilterChange = ({
|
};
|
||||||
target: { value },
|
|
||||||
}: React.ChangeEvent<HTMLInputElement>) =>
|
const onFilterChange = ({ target: { value } }) => {
|
||||||
devTools.update(INDEX_KEY, { filter: value });
|
setInputFilterValue(value)
|
||||||
|
debouncedFilter(value);
|
||||||
|
};
|
||||||
|
|
||||||
// AutoScroll
|
|
||||||
const [timeoutStartAutoscroll, stopAutoscroll] = useAutoscroll(
|
const [timeoutStartAutoscroll, stopAutoscroll] = useAutoscroll(
|
||||||
filteredList,
|
displayedItems,
|
||||||
getLastItemTime(fetchListNow, resourceListNow),
|
getLastItemTime(fetchListNow, resourceListNow),
|
||||||
activeIndex,
|
activeIndex,
|
||||||
(index) => devTools.update(INDEX_KEY, { index }),
|
(index) => devTools.update(INDEX_KEY, { index }),
|
||||||
|
|
@ -462,24 +585,6 @@ export const NetworkPanelComp = observer(
|
||||||
timeoutStartAutoscroll();
|
timeoutStartAutoscroll();
|
||||||
};
|
};
|
||||||
|
|
||||||
const resourcesSize = useMemo(
|
|
||||||
() =>
|
|
||||||
resourceList.reduce(
|
|
||||||
(sum, { decodedBodySize }) => sum + (decodedBodySize || 0),
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
[resourceList.length],
|
|
||||||
);
|
|
||||||
const transferredSize = useMemo(
|
|
||||||
() =>
|
|
||||||
resourceList.reduce(
|
|
||||||
(sum, { headerSize, encodedBodySize }) =>
|
|
||||||
sum + (headerSize || 0) + (encodedBodySize || 0),
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
[resourceList.length],
|
|
||||||
);
|
|
||||||
|
|
||||||
const referenceLines = useMemo(() => {
|
const referenceLines = useMemo(() => {
|
||||||
const arr = [];
|
const arr = [];
|
||||||
|
|
||||||
|
|
@ -513,7 +618,7 @@ export const NetworkPanelComp = observer(
|
||||||
isSpot={isSpot}
|
isSpot={isSpot}
|
||||||
time={item.time + startedAt}
|
time={item.time + startedAt}
|
||||||
resource={item}
|
resource={item}
|
||||||
rows={filteredList}
|
rows={displayedItems}
|
||||||
fetchPresented={fetchList.length > 0}
|
fetchPresented={fetchList.length > 0}
|
||||||
/>,
|
/>,
|
||||||
{
|
{
|
||||||
|
|
@ -525,12 +630,10 @@ export const NetworkPanelComp = observer(
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
devTools.update(INDEX_KEY, { index: filteredList.indexOf(item) });
|
|
||||||
stopAutoscroll();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const tableCols = React.useMemo(() => {
|
const tableCols = useMemo(() => {
|
||||||
const cols: any[] = [
|
const cols = [
|
||||||
{
|
{
|
||||||
label: t('Status'),
|
label: t('Status'),
|
||||||
dataKey: 'status',
|
dataKey: 'status',
|
||||||
|
|
@ -585,7 +688,7 @@ export const NetworkPanelComp = observer(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return cols;
|
return cols;
|
||||||
}, [showSingleTab]);
|
}, [showSingleTab, activeTab, t, getTabName, getTabNum, isSpot]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BottomBlock
|
<BottomBlock
|
||||||
|
|
@ -617,7 +720,7 @@ export const NetworkPanelComp = observer(
|
||||||
name="filter"
|
name="filter"
|
||||||
onChange={onFilterChange}
|
onChange={onFilterChange}
|
||||||
width={280}
|
width={280}
|
||||||
value={filter}
|
value={inputFilterValue}
|
||||||
size="small"
|
size="small"
|
||||||
prefix={<SearchOutlined className="text-neutral-400" />}
|
prefix={<SearchOutlined className="text-neutral-400" />}
|
||||||
/>
|
/>
|
||||||
|
|
@ -625,7 +728,7 @@ export const NetworkPanelComp = observer(
|
||||||
</BottomBlock.Header>
|
</BottomBlock.Header>
|
||||||
<BottomBlock.Content>
|
<BottomBlock.Content>
|
||||||
<div className="flex items-center justify-between px-4 border-b bg-teal/5 h-8">
|
<div className="flex items-center justify-between px-4 border-b bg-teal/5 h-8">
|
||||||
<div>
|
<div className="flex items-center">
|
||||||
<Form.Item name="show-errors-only" className="mb-0">
|
<Form.Item name="show-errors-only" className="mb-0">
|
||||||
<label
|
<label
|
||||||
style={{
|
style={{
|
||||||
|
|
@ -642,21 +745,29 @@ export const NetworkPanelComp = observer(
|
||||||
<span className="text-sm ms-2">4xx-5xx Only</span>
|
<span className="text-sm ms-2">4xx-5xx Only</span>
|
||||||
</label>
|
</label>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
|
{isProcessing && (
|
||||||
|
<span className="text-xs text-gray-500 ml-4">
|
||||||
|
Processing data...
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<InfoLine>
|
<InfoLine>
|
||||||
|
<InfoLine.Point label={`${totalItems}`} value="requests" />
|
||||||
<InfoLine.Point
|
<InfoLine.Point
|
||||||
label={`${filteredList.length}`}
|
label={`${displayedItems.length}/${totalItems}`}
|
||||||
value=" requests"
|
value="displayed"
|
||||||
|
display={displayedItems.length < totalItems}
|
||||||
/>
|
/>
|
||||||
<InfoLine.Point
|
<InfoLine.Point
|
||||||
label={formatBytes(transferredSize)}
|
label={formatBytes(summaryStats.transferredSize)}
|
||||||
value="transferred"
|
value="transferred"
|
||||||
display={transferredSize > 0}
|
display={summaryStats.transferredSize > 0}
|
||||||
/>
|
/>
|
||||||
<InfoLine.Point
|
<InfoLine.Point
|
||||||
label={formatBytes(resourcesSize)}
|
label={formatBytes(summaryStats.resourcesSize)}
|
||||||
value="resources"
|
value="resources"
|
||||||
display={resourcesSize > 0}
|
display={summaryStats.resourcesSize > 0}
|
||||||
/>
|
/>
|
||||||
<InfoLine.Point
|
<InfoLine.Point
|
||||||
label={formatMs(domBuildingTime)}
|
label={formatMs(domBuildingTime)}
|
||||||
|
|
@ -679,42 +790,67 @@ export const NetworkPanelComp = observer(
|
||||||
/>
|
/>
|
||||||
</InfoLine>
|
</InfoLine>
|
||||||
</div>
|
</div>
|
||||||
<NoContent
|
|
||||||
title={
|
{isLoading ? (
|
||||||
<div className="capitalize flex items-center gap-2">
|
<div className="flex items-center justify-center h-full">
|
||||||
<InfoCircleOutlined size={18} />
|
<div className="text-center">
|
||||||
{t('No Data')}
|
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900 mx-auto mb-2"></div>
|
||||||
|
<p>Processing initial network data...</p>
|
||||||
</div>
|
</div>
|
||||||
}
|
</div>
|
||||||
size="small"
|
) : (
|
||||||
show={filteredList.length === 0}
|
<NoContent
|
||||||
>
|
title={
|
||||||
{/* @ts-ignore */}
|
<div className="capitalize flex items-center gap-2">
|
||||||
<TimeTable
|
<InfoCircleOutlined size={18} />
|
||||||
rows={filteredList}
|
{t('No Data')}
|
||||||
tableHeight={panelHeight - 102}
|
</div>
|
||||||
referenceLines={referenceLines}
|
}
|
||||||
renderPopup
|
size="small"
|
||||||
onRowClick={showDetailsModal}
|
show={displayedItems.length === 0}
|
||||||
sortBy="time"
|
|
||||||
sortAscending
|
|
||||||
onJump={(row: any) => {
|
|
||||||
devTools.update(INDEX_KEY, {
|
|
||||||
index: filteredList.indexOf(row),
|
|
||||||
});
|
|
||||||
player.jump(row.time);
|
|
||||||
}}
|
|
||||||
activeIndex={activeIndex}
|
|
||||||
>
|
>
|
||||||
{tableCols}
|
<div>
|
||||||
</TimeTable>
|
<TimeTable
|
||||||
{selectedWsChannel ? (
|
rows={displayedItems}
|
||||||
<WSPanel
|
tableHeight={panelHeight - 102 - (hasMoreItems ? 30 : 0)}
|
||||||
socketMsgList={selectedWsChannel}
|
referenceLines={referenceLines}
|
||||||
onClose={() => setSelectedWsChannel(null)}
|
renderPopup
|
||||||
/>
|
onRowClick={showDetailsModal}
|
||||||
) : null}
|
sortBy="time"
|
||||||
</NoContent>
|
sortAscending
|
||||||
|
onJump={(row) => {
|
||||||
|
devTools.update(INDEX_KEY, {
|
||||||
|
index: displayedItems.indexOf(row),
|
||||||
|
});
|
||||||
|
player.jump(row.time);
|
||||||
|
}}
|
||||||
|
activeIndex={activeIndex}
|
||||||
|
>
|
||||||
|
{tableCols}
|
||||||
|
</TimeTable>
|
||||||
|
|
||||||
|
{hasMoreItems && (
|
||||||
|
<div
|
||||||
|
ref={loadingRef}
|
||||||
|
className="flex justify-center items-center text-xs text-gray-500"
|
||||||
|
>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-gray-600 mr-2"></div>
|
||||||
|
Loading more data ({totalItems - displayedItems.length}{' '}
|
||||||
|
remaining)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{selectedWsChannel ? (
|
||||||
|
<WSPanel
|
||||||
|
socketMsgList={selectedWsChannel}
|
||||||
|
onClose={() => setSelectedWsChannel(null)}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</NoContent>
|
||||||
|
)}
|
||||||
</BottomBlock.Content>
|
</BottomBlock.Content>
|
||||||
</BottomBlock>
|
</BottomBlock>
|
||||||
);
|
);
|
||||||
|
|
@ -722,7 +858,6 @@ export const NetworkPanelComp = observer(
|
||||||
);
|
);
|
||||||
|
|
||||||
const WebNetworkPanel = observer(NetworkPanelCont);
|
const WebNetworkPanel = observer(NetworkPanelCont);
|
||||||
|
|
||||||
const MobileNetworkPanel = observer(MobileNetworkPanelCont);
|
const MobileNetworkPanel = observer(MobileNetworkPanelCont);
|
||||||
|
|
||||||
export { WebNetworkPanel, MobileNetworkPanel };
|
export { WebNetworkPanel, MobileNetworkPanel };
|
||||||
|
|
|
||||||
178
frontend/app/components/shared/DevTools/NetworkPanel/utils.ts
Normal file
178
frontend/app/components/shared/DevTools/NetworkPanel/utils.ts
Normal file
|
|
@ -0,0 +1,178 @@
|
||||||
|
export function mergeListsWithZoom<
|
||||||
|
T extends Record<string, any>,
|
||||||
|
Y extends Record<string, any>,
|
||||||
|
Z extends Record<string, any>,
|
||||||
|
>(
|
||||||
|
arr1: T[],
|
||||||
|
arr2: Y[],
|
||||||
|
arr3: Z[],
|
||||||
|
zoom?: { enabled: boolean; start: number; end: number },
|
||||||
|
): Array<T | Y | Z> {
|
||||||
|
// Early return for empty arrays
|
||||||
|
if (arr1.length === 0 && arr2.length === 0 && arr3.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optimized for common case - no zoom
|
||||||
|
if (!zoom?.enabled) {
|
||||||
|
return mergeThreeSortedArrays(arr1, arr2, arr3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Binary search for start indexes (faster than linear search for large arrays)
|
||||||
|
const index1 = binarySearchStartIndex(arr1, zoom.start);
|
||||||
|
const index2 = binarySearchStartIndex(arr2, zoom.start);
|
||||||
|
const index3 = binarySearchStartIndex(arr3, zoom.start);
|
||||||
|
|
||||||
|
// Merge arrays within zoom range
|
||||||
|
return mergeThreeSortedArraysWithinRange(
|
||||||
|
arr1,
|
||||||
|
arr2,
|
||||||
|
arr3,
|
||||||
|
index1,
|
||||||
|
index2,
|
||||||
|
index3,
|
||||||
|
zoom.start,
|
||||||
|
zoom.end,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function binarySearchStartIndex<T extends Record<string, any>>(
|
||||||
|
arr: T[],
|
||||||
|
threshold: number,
|
||||||
|
): number {
|
||||||
|
if (arr.length === 0) return 0;
|
||||||
|
|
||||||
|
let low = 0;
|
||||||
|
let high = arr.length - 1;
|
||||||
|
|
||||||
|
// Handle edge cases first for better performance
|
||||||
|
if (arr[high].time < threshold) return arr.length;
|
||||||
|
if (arr[low].time >= threshold) return 0;
|
||||||
|
|
||||||
|
while (low <= high) {
|
||||||
|
const mid = Math.floor((low + high) / 2);
|
||||||
|
|
||||||
|
if (arr[mid].time < threshold) {
|
||||||
|
low = mid + 1;
|
||||||
|
} else {
|
||||||
|
high = mid - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return low;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergeThreeSortedArrays<
|
||||||
|
T extends Record<string, any>,
|
||||||
|
Y extends Record<string, any>,
|
||||||
|
Z extends Record<string, any>,
|
||||||
|
>(arr1: T[], arr2: Y[], arr3: Z[]): Array<T | Y | Z> {
|
||||||
|
const totalLength = arr1.length + arr2.length + arr3.length;
|
||||||
|
// prealloc array size
|
||||||
|
const result = new Array(totalLength);
|
||||||
|
|
||||||
|
let i = 0,
|
||||||
|
j = 0,
|
||||||
|
k = 0,
|
||||||
|
index = 0;
|
||||||
|
|
||||||
|
while (i < arr1.length || j < arr2.length || k < arr3.length) {
|
||||||
|
const val1 = i < arr1.length ? arr1[i].time : Infinity;
|
||||||
|
const val2 = j < arr2.length ? arr2[j].time : Infinity;
|
||||||
|
const val3 = k < arr3.length ? arr3[k].time : Infinity;
|
||||||
|
|
||||||
|
if (val1 <= val2 && val1 <= val3) {
|
||||||
|
result[index++] = arr1[i++];
|
||||||
|
} else if (val2 <= val1 && val2 <= val3) {
|
||||||
|
result[index++] = arr2[j++];
|
||||||
|
} else {
|
||||||
|
result[index++] = arr3[k++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// same as above, just with zoom stuff
|
||||||
|
function mergeThreeSortedArraysWithinRange<
|
||||||
|
T extends Record<string, any>,
|
||||||
|
Y extends Record<string, any>,
|
||||||
|
Z extends Record<string, any>,
|
||||||
|
>(
|
||||||
|
arr1: T[],
|
||||||
|
arr2: Y[],
|
||||||
|
arr3: Z[],
|
||||||
|
startIdx1: number,
|
||||||
|
startIdx2: number,
|
||||||
|
startIdx3: number,
|
||||||
|
start: number,
|
||||||
|
end: number,
|
||||||
|
): Array<T | Y | Z> {
|
||||||
|
// we don't know beforehand how many items will be there
|
||||||
|
const result = [];
|
||||||
|
|
||||||
|
let i = startIdx1;
|
||||||
|
let j = startIdx2;
|
||||||
|
let k = startIdx3;
|
||||||
|
|
||||||
|
while (i < arr1.length || j < arr2.length || k < arr3.length) {
|
||||||
|
const val1 = i < arr1.length ? arr1[i].time : Infinity;
|
||||||
|
const val2 = j < arr2.length ? arr2[j].time : Infinity;
|
||||||
|
const val3 = k < arr3.length ? arr3[k].time : Infinity;
|
||||||
|
|
||||||
|
// Early termination: if all remaining values exceed end time
|
||||||
|
if (Math.min(val1, val2, val3) > end) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (val1 <= val2 && val1 <= val3) {
|
||||||
|
if (val1 <= end) {
|
||||||
|
result.push(arr1[i]);
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
} else if (val2 <= val1 && val2 <= val3) {
|
||||||
|
if (val2 <= end) {
|
||||||
|
result.push(arr2[j]);
|
||||||
|
}
|
||||||
|
j++;
|
||||||
|
} else {
|
||||||
|
if (val3 <= end) {
|
||||||
|
result.push(arr3[k]);
|
||||||
|
}
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function processInChunks(
|
||||||
|
items: any[],
|
||||||
|
processFn: (item: any) => any,
|
||||||
|
chunkSize = 1000,
|
||||||
|
overscan = 0,
|
||||||
|
) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
if (items.length === 0) {
|
||||||
|
resolve([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let result: any[] = [];
|
||||||
|
let index = 0;
|
||||||
|
|
||||||
|
const processNextChunk = () => {
|
||||||
|
const chunk = items.slice(index, index + chunkSize + overscan);
|
||||||
|
result = result.concat(processFn(chunk));
|
||||||
|
index += chunkSize;
|
||||||
|
|
||||||
|
if (index < items.length) {
|
||||||
|
setTimeout(processNextChunk, 0);
|
||||||
|
} else {
|
||||||
|
resolve(result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
processNextChunk();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
import { makeAutoObservable, runInAction, reaction } from 'mobx';
|
import { makeAutoObservable, runInAction, reaction } from 'mobx';
|
||||||
import { dashboardService, metricService } from 'App/services';
|
import { dashboardService, metricService } from 'App/services';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import Period, { LAST_24_HOURS, LAST_7_DAYS } from 'Types/app/period';
|
import Period, { LAST_24_HOURS } from 'Types/app/period';
|
||||||
import { getRE } from 'App/utils';
|
import { getRE } from 'App/utils';
|
||||||
import Filter from './types/filter';
|
import Filter from './types/filter';
|
||||||
import Widget from './types/widget';
|
import Widget from './types/widget';
|
||||||
import Dashboard from './types/dashboard';
|
import Dashboard from './types/dashboard';
|
||||||
import { calculateGranularities } from '@/components/Dashboard/components/WidgetDateRange/RangeGranularity';
|
import { calculateGranularities } from '@/components/Dashboard/components/WidgetDateRange/RangeGranularity';
|
||||||
|
import { CUSTOM_RANGE } from '@/dateRange';
|
||||||
|
|
||||||
interface DashboardFilter {
|
interface DashboardFilter {
|
||||||
query?: string;
|
query?: string;
|
||||||
|
|
@ -90,16 +91,20 @@ export default class DashboardStore {
|
||||||
() => this.period,
|
() => this.period,
|
||||||
(period) => {
|
(period) => {
|
||||||
this.createDensity(period.getDuration());
|
this.createDensity(period.getDuration());
|
||||||
}
|
},
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resetDensity = () => {
|
||||||
|
this.createDensity(this.period.getDuration());
|
||||||
|
};
|
||||||
|
|
||||||
createDensity = (duration: number) => {
|
createDensity = (duration: number) => {
|
||||||
const densityOpts = calculateGranularities(duration);
|
const densityOpts = calculateGranularities(duration);
|
||||||
const defaultOption = densityOpts[densityOpts.length - 2];
|
const defaultOption = densityOpts[densityOpts.length - 2];
|
||||||
|
|
||||||
this.setDensity(defaultOption.key)
|
this.setDensity(defaultOption.key);
|
||||||
}
|
};
|
||||||
|
|
||||||
setDensity = (density: number) => {
|
setDensity = (density: number) => {
|
||||||
this.selectedDensity = density;
|
this.selectedDensity = density;
|
||||||
|
|
@ -462,7 +467,7 @@ export default class DashboardStore {
|
||||||
this.isSaving = true;
|
this.isSaving = true;
|
||||||
try {
|
try {
|
||||||
try {
|
try {
|
||||||
const response = await dashboardService.addWidget(dashboard, metricIds);
|
await dashboardService.addWidget(dashboard, metricIds);
|
||||||
toast.success('Card added to dashboard.');
|
toast.success('Card added to dashboard.');
|
||||||
} catch {
|
} catch {
|
||||||
toast.error('Card could not be added.');
|
toast.error('Card could not be added.');
|
||||||
|
|
@ -472,6 +477,17 @@ export default class DashboardStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resetPeriod = () => {
|
||||||
|
if (this.period) {
|
||||||
|
const range = this.period.rangeName;
|
||||||
|
if (range !== CUSTOM_RANGE) {
|
||||||
|
this.period = Period({ rangeName: this.period.rangeName });
|
||||||
|
} else {
|
||||||
|
this.period = Period({ rangeName: LAST_24_HOURS });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
setPeriod(period: any) {
|
setPeriod(period: any) {
|
||||||
this.period = Period({
|
this.period = Period({
|
||||||
start: period.start,
|
start: period.start,
|
||||||
|
|
@ -545,7 +561,7 @@ export default class DashboardStore {
|
||||||
const data = await metricService.getMetricChartData(
|
const data = await metricService.getMetricChartData(
|
||||||
metric,
|
metric,
|
||||||
params,
|
params,
|
||||||
isSaved
|
isSaved,
|
||||||
);
|
);
|
||||||
resolve(metric.setData(data, period, isComparison, density));
|
resolve(metric.setData(data, period, isComparison, density));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { makeAutoObservable } from 'mobx';
|
import { makeAutoObservable } from 'mobx';
|
||||||
import { issueReportsService } from 'App/services';
|
import { issueReportsService } from 'App/services';
|
||||||
import { makePersistable } from '.store/mobx-persist-store-virtual-858ce4d906/package';
|
|
||||||
import ReportedIssue from '../types/session/assignment';
|
import ReportedIssue from '../types/session/assignment';
|
||||||
|
|
||||||
export default class IssueReportingStore {
|
export default class IssueReportingStore {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import {
|
||||||
SITE_ID_STORAGE_KEY,
|
SITE_ID_STORAGE_KEY,
|
||||||
} from 'App/constants/storageKeys';
|
} from 'App/constants/storageKeys';
|
||||||
import { projectsService } from 'App/services';
|
import { projectsService } from 'App/services';
|
||||||
import { toast } from '.store/react-toastify-virtual-9dd0f3eae1/package';
|
|
||||||
import GDPR from './types/gdpr';
|
import GDPR from './types/gdpr';
|
||||||
import Project from './types/project';
|
import Project from './types/project';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,6 @@ export function debounceCall(func, wait) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function randomInt(a, b) {
|
export function randomInt(a, b) {
|
||||||
const min = (b ? a : 0) - 0.5;
|
const min = (b ? a : 0) - 0.5;
|
||||||
const max = b || a || Number.MAX_SAFE_INTEGER;
|
const max = b || a || Number.MAX_SAFE_INTEGER;
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,25 @@ server {
|
||||||
add_header 'Access-Control-Allow-Headers' 'Content-Type,Authorization,Content-Encoding';
|
add_header 'Access-Control-Allow-Headers' 'Content-Type,Authorization,Content-Encoding';
|
||||||
add_header 'Access-Control-Expose-Headers' 'Content-Length';
|
add_header 'Access-Control-Expose-Headers' 'Content-Length';
|
||||||
}
|
}
|
||||||
|
location /integrations/ {
|
||||||
|
rewrite ^/integrations/(.*) /$1 break;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "Upgrade";
|
||||||
|
proxy_set_header X-Forwarded-For $real_ip;
|
||||||
|
proxy_set_header X-Forwarded-Host $real_ip;
|
||||||
|
proxy_set_header X-Real-IP $real_ip;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_pass http://integrations-openreplay:8080;
|
||||||
|
proxy_read_timeout 300;
|
||||||
|
proxy_connect_timeout 120;
|
||||||
|
proxy_send_timeout 300;
|
||||||
|
# CORS Headers
|
||||||
|
add_header 'Access-Control-Allow-Origin' '*';
|
||||||
|
add_header 'Access-Control-Allow-Methods' 'POST,PATCH,OPTIONS,DELETE';
|
||||||
|
add_header 'Access-Control-Allow-Headers' 'Content-Type,Authorization,Content-Encoding,X-Openreplay-Batch';
|
||||||
|
add_header 'Access-Control-Expose-Headers' 'Content-Length';
|
||||||
|
}
|
||||||
|
|
||||||
location /api/ {
|
location /api/ {
|
||||||
rewrite ^/api/(.*) /$1 break;
|
rewrite ^/api/(.*) /$1 break;
|
||||||
|
|
|
||||||
|
|
@ -18,4 +18,4 @@ version: 0.1.10
|
||||||
# incremented each time you make changes to the application. Versions are not expected to
|
# incremented each time you make changes to the application. Versions are not expected to
|
||||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||||
# It is recommended to use it with quotes.
|
# It is recommended to use it with quotes.
|
||||||
AppVersion: "v1.22.34"
|
AppVersion: "v1.22.42"
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@ CREATE TABLE IF NOT EXISTS experimental.sessions
|
||||||
user_country Enum8('UN'=-128, 'RW'=-127, 'SO'=-126, 'YE'=-125, 'IQ'=-124, 'SA'=-123, 'IR'=-122, 'CY'=-121, 'TZ'=-120, 'SY'=-119, 'AM'=-118, 'KE'=-117, 'CD'=-116, 'DJ'=-115, 'UG'=-114, 'CF'=-113, 'SC'=-112, 'JO'=-111, 'LB'=-110, 'KW'=-109, 'OM'=-108, 'QA'=-107, 'BH'=-106, 'AE'=-105, 'IL'=-104, 'TR'=-103, 'ET'=-102, 'ER'=-101, 'EG'=-100, 'SD'=-99, 'GR'=-98, 'BI'=-97, 'EE'=-96, 'LV'=-95, 'AZ'=-94, 'LT'=-93, 'SJ'=-92, 'GE'=-91, 'MD'=-90, 'BY'=-89, 'FI'=-88, 'AX'=-87, 'UA'=-86, 'MK'=-85, 'HU'=-84, 'BG'=-83, 'AL'=-82, 'PL'=-81, 'RO'=-80, 'XK'=-79, 'ZW'=-78, 'ZM'=-77, 'KM'=-76, 'MW'=-75, 'LS'=-74, 'BW'=-73, 'MU'=-72, 'SZ'=-71, 'RE'=-70, 'ZA'=-69, 'YT'=-68, 'MZ'=-67, 'MG'=-66, 'AF'=-65, 'PK'=-64, 'BD'=-63, 'TM'=-62, 'TJ'=-61, 'LK'=-60, 'BT'=-59, 'IN'=-58, 'MV'=-57, 'IO'=-56, 'NP'=-55, 'MM'=-54, 'UZ'=-53, 'KZ'=-52, 'KG'=-51, 'TF'=-50, 'HM'=-49, 'CC'=-48, 'PW'=-47, 'VN'=-46, 'TH'=-45, 'ID'=-44, 'LA'=-43, 'TW'=-42, 'PH'=-41, 'MY'=-40, 'CN'=-39, 'HK'=-38, 'BN'=-37, 'MO'=-36, 'KH'=-35, 'KR'=-34, 'JP'=-33, 'KP'=-32, 'SG'=-31, 'CK'=-30, 'TL'=-29, 'RU'=-28, 'MN'=-27, 'AU'=-26, 'CX'=-25, 'MH'=-24, 'FM'=-23, 'PG'=-22, 'SB'=-21, 'TV'=-20, 'NR'=-19, 'VU'=-18, 'NC'=-17, 'NF'=-16, 'NZ'=-15, 'FJ'=-14, 'LY'=-13, 'CM'=-12, 'SN'=-11, 'CG'=-10, 'PT'=-9, 'LR'=-8, 'CI'=-7, 'GH'=-6, 'GQ'=-5, 'NG'=-4, 'BF'=-3, 'TG'=-2, 'GW'=-1, 'MR'=0, 'BJ'=1, 'GA'=2, 'SL'=3, 'ST'=4, 'GI'=5, 'GM'=6, 'GN'=7, 'TD'=8, 'NE'=9, 'ML'=10, 'EH'=11, 'TN'=12, 'ES'=13, 'MA'=14, 'MT'=15, 'DZ'=16, 'FO'=17, 'DK'=18, 'IS'=19, 'GB'=20, 'CH'=21, 'SE'=22, 'NL'=23, 'AT'=24, 'BE'=25, 'DE'=26, 'LU'=27, 'IE'=28, 'MC'=29, 'FR'=30, 'AD'=31, 'LI'=32, 'JE'=33, 'IM'=34, 'GG'=35, 'SK'=36, 'CZ'=37, 'NO'=38, 'VA'=39, 'SM'=40, 'IT'=41, 'SI'=42, 'ME'=43, 'HR'=44, 'BA'=45, 'AO'=46, 'NA'=47, 'SH'=48, 'BV'=49, 'BB'=50, 'CV'=51, 'GY'=52, 'GF'=53, 'SR'=54, 'PM'=55, 'GL'=56, 'PY'=57, 'UY'=58, 'BR'=59, 'FK'=60, 'GS'=61, 'JM'=62, 'DO'=63, 'CU'=64, 'MQ'=65, 'BS'=66, 'BM'=67, 'AI'=68, 'TT'=69, 'KN'=70, 'DM'=71, 'AG'=72, 'LC'=73, 'TC'=74, 'AW'=75, 'VG'=76, 'VC'=77, 'MS'=78, 'MF'=79, 'BL'=80, 'GP'=81, 'GD'=82, 'KY'=83, 'BZ'=84, 'SV'=85, 'GT'=86, 'HN'=87, 'NI'=88, 'CR'=89, 'VE'=90, 'EC'=91, 'CO'=92, 'PA'=93, 'HT'=94, 'AR'=95, 'CL'=96, 'BO'=97, 'PE'=98, 'MX'=99, 'PF'=100, 'PN'=101, 'KI'=102, 'TK'=103, 'TO'=104, 'WF'=105, 'WS'=106, 'NU'=107, 'MP'=108, 'GU'=109, 'PR'=110, 'VI'=111, 'UM'=112, 'AS'=113, 'CA'=114, 'US'=115, 'PS'=116, 'RS'=117, 'AQ'=118, 'SX'=119, 'CW'=120, 'BQ'=121, 'SS'=122,'BU'=123, 'VD'=124, 'YD'=125, 'DD'=126),
|
user_country Enum8('UN'=-128, 'RW'=-127, 'SO'=-126, 'YE'=-125, 'IQ'=-124, 'SA'=-123, 'IR'=-122, 'CY'=-121, 'TZ'=-120, 'SY'=-119, 'AM'=-118, 'KE'=-117, 'CD'=-116, 'DJ'=-115, 'UG'=-114, 'CF'=-113, 'SC'=-112, 'JO'=-111, 'LB'=-110, 'KW'=-109, 'OM'=-108, 'QA'=-107, 'BH'=-106, 'AE'=-105, 'IL'=-104, 'TR'=-103, 'ET'=-102, 'ER'=-101, 'EG'=-100, 'SD'=-99, 'GR'=-98, 'BI'=-97, 'EE'=-96, 'LV'=-95, 'AZ'=-94, 'LT'=-93, 'SJ'=-92, 'GE'=-91, 'MD'=-90, 'BY'=-89, 'FI'=-88, 'AX'=-87, 'UA'=-86, 'MK'=-85, 'HU'=-84, 'BG'=-83, 'AL'=-82, 'PL'=-81, 'RO'=-80, 'XK'=-79, 'ZW'=-78, 'ZM'=-77, 'KM'=-76, 'MW'=-75, 'LS'=-74, 'BW'=-73, 'MU'=-72, 'SZ'=-71, 'RE'=-70, 'ZA'=-69, 'YT'=-68, 'MZ'=-67, 'MG'=-66, 'AF'=-65, 'PK'=-64, 'BD'=-63, 'TM'=-62, 'TJ'=-61, 'LK'=-60, 'BT'=-59, 'IN'=-58, 'MV'=-57, 'IO'=-56, 'NP'=-55, 'MM'=-54, 'UZ'=-53, 'KZ'=-52, 'KG'=-51, 'TF'=-50, 'HM'=-49, 'CC'=-48, 'PW'=-47, 'VN'=-46, 'TH'=-45, 'ID'=-44, 'LA'=-43, 'TW'=-42, 'PH'=-41, 'MY'=-40, 'CN'=-39, 'HK'=-38, 'BN'=-37, 'MO'=-36, 'KH'=-35, 'KR'=-34, 'JP'=-33, 'KP'=-32, 'SG'=-31, 'CK'=-30, 'TL'=-29, 'RU'=-28, 'MN'=-27, 'AU'=-26, 'CX'=-25, 'MH'=-24, 'FM'=-23, 'PG'=-22, 'SB'=-21, 'TV'=-20, 'NR'=-19, 'VU'=-18, 'NC'=-17, 'NF'=-16, 'NZ'=-15, 'FJ'=-14, 'LY'=-13, 'CM'=-12, 'SN'=-11, 'CG'=-10, 'PT'=-9, 'LR'=-8, 'CI'=-7, 'GH'=-6, 'GQ'=-5, 'NG'=-4, 'BF'=-3, 'TG'=-2, 'GW'=-1, 'MR'=0, 'BJ'=1, 'GA'=2, 'SL'=3, 'ST'=4, 'GI'=5, 'GM'=6, 'GN'=7, 'TD'=8, 'NE'=9, 'ML'=10, 'EH'=11, 'TN'=12, 'ES'=13, 'MA'=14, 'MT'=15, 'DZ'=16, 'FO'=17, 'DK'=18, 'IS'=19, 'GB'=20, 'CH'=21, 'SE'=22, 'NL'=23, 'AT'=24, 'BE'=25, 'DE'=26, 'LU'=27, 'IE'=28, 'MC'=29, 'FR'=30, 'AD'=31, 'LI'=32, 'JE'=33, 'IM'=34, 'GG'=35, 'SK'=36, 'CZ'=37, 'NO'=38, 'VA'=39, 'SM'=40, 'IT'=41, 'SI'=42, 'ME'=43, 'HR'=44, 'BA'=45, 'AO'=46, 'NA'=47, 'SH'=48, 'BV'=49, 'BB'=50, 'CV'=51, 'GY'=52, 'GF'=53, 'SR'=54, 'PM'=55, 'GL'=56, 'PY'=57, 'UY'=58, 'BR'=59, 'FK'=60, 'GS'=61, 'JM'=62, 'DO'=63, 'CU'=64, 'MQ'=65, 'BS'=66, 'BM'=67, 'AI'=68, 'TT'=69, 'KN'=70, 'DM'=71, 'AG'=72, 'LC'=73, 'TC'=74, 'AW'=75, 'VG'=76, 'VC'=77, 'MS'=78, 'MF'=79, 'BL'=80, 'GP'=81, 'GD'=82, 'KY'=83, 'BZ'=84, 'SV'=85, 'GT'=86, 'HN'=87, 'NI'=88, 'CR'=89, 'VE'=90, 'EC'=91, 'CO'=92, 'PA'=93, 'HT'=94, 'AR'=95, 'CL'=96, 'BO'=97, 'PE'=98, 'MX'=99, 'PF'=100, 'PN'=101, 'KI'=102, 'TK'=103, 'TO'=104, 'WF'=105, 'WS'=106, 'NU'=107, 'MP'=108, 'GU'=109, 'PR'=110, 'VI'=111, 'UM'=112, 'AS'=113, 'CA'=114, 'US'=115, 'PS'=116, 'RS'=117, 'AQ'=118, 'SX'=119, 'CW'=120, 'BQ'=121, 'SS'=122,'BU'=123, 'VD'=124, 'YD'=125, 'DD'=126),
|
||||||
user_city LowCardinality(String),
|
user_city LowCardinality(String),
|
||||||
user_state LowCardinality(String),
|
user_state LowCardinality(String),
|
||||||
platform Enum8('web'=1,'ios'=2,'android'=3) DEFAULT 'web',
|
platform Enum8('web'=1,'mobile'=2) DEFAULT 'web',
|
||||||
datetime DateTime,
|
datetime DateTime,
|
||||||
timezone LowCardinality(Nullable(String)),
|
timezone LowCardinality(Nullable(String)),
|
||||||
duration UInt32,
|
duration UInt32,
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@ CREATE TABLE IF NOT EXISTS experimental.sessions
|
||||||
user_country Enum8('UN'=-128, 'RW'=-127, 'SO'=-126, 'YE'=-125, 'IQ'=-124, 'SA'=-123, 'IR'=-122, 'CY'=-121, 'TZ'=-120, 'SY'=-119, 'AM'=-118, 'KE'=-117, 'CD'=-116, 'DJ'=-115, 'UG'=-114, 'CF'=-113, 'SC'=-112, 'JO'=-111, 'LB'=-110, 'KW'=-109, 'OM'=-108, 'QA'=-107, 'BH'=-106, 'AE'=-105, 'IL'=-104, 'TR'=-103, 'ET'=-102, 'ER'=-101, 'EG'=-100, 'SD'=-99, 'GR'=-98, 'BI'=-97, 'EE'=-96, 'LV'=-95, 'AZ'=-94, 'LT'=-93, 'SJ'=-92, 'GE'=-91, 'MD'=-90, 'BY'=-89, 'FI'=-88, 'AX'=-87, 'UA'=-86, 'MK'=-85, 'HU'=-84, 'BG'=-83, 'AL'=-82, 'PL'=-81, 'RO'=-80, 'XK'=-79, 'ZW'=-78, 'ZM'=-77, 'KM'=-76, 'MW'=-75, 'LS'=-74, 'BW'=-73, 'MU'=-72, 'SZ'=-71, 'RE'=-70, 'ZA'=-69, 'YT'=-68, 'MZ'=-67, 'MG'=-66, 'AF'=-65, 'PK'=-64, 'BD'=-63, 'TM'=-62, 'TJ'=-61, 'LK'=-60, 'BT'=-59, 'IN'=-58, 'MV'=-57, 'IO'=-56, 'NP'=-55, 'MM'=-54, 'UZ'=-53, 'KZ'=-52, 'KG'=-51, 'TF'=-50, 'HM'=-49, 'CC'=-48, 'PW'=-47, 'VN'=-46, 'TH'=-45, 'ID'=-44, 'LA'=-43, 'TW'=-42, 'PH'=-41, 'MY'=-40, 'CN'=-39, 'HK'=-38, 'BN'=-37, 'MO'=-36, 'KH'=-35, 'KR'=-34, 'JP'=-33, 'KP'=-32, 'SG'=-31, 'CK'=-30, 'TL'=-29, 'RU'=-28, 'MN'=-27, 'AU'=-26, 'CX'=-25, 'MH'=-24, 'FM'=-23, 'PG'=-22, 'SB'=-21, 'TV'=-20, 'NR'=-19, 'VU'=-18, 'NC'=-17, 'NF'=-16, 'NZ'=-15, 'FJ'=-14, 'LY'=-13, 'CM'=-12, 'SN'=-11, 'CG'=-10, 'PT'=-9, 'LR'=-8, 'CI'=-7, 'GH'=-6, 'GQ'=-5, 'NG'=-4, 'BF'=-3, 'TG'=-2, 'GW'=-1, 'MR'=0, 'BJ'=1, 'GA'=2, 'SL'=3, 'ST'=4, 'GI'=5, 'GM'=6, 'GN'=7, 'TD'=8, 'NE'=9, 'ML'=10, 'EH'=11, 'TN'=12, 'ES'=13, 'MA'=14, 'MT'=15, 'DZ'=16, 'FO'=17, 'DK'=18, 'IS'=19, 'GB'=20, 'CH'=21, 'SE'=22, 'NL'=23, 'AT'=24, 'BE'=25, 'DE'=26, 'LU'=27, 'IE'=28, 'MC'=29, 'FR'=30, 'AD'=31, 'LI'=32, 'JE'=33, 'IM'=34, 'GG'=35, 'SK'=36, 'CZ'=37, 'NO'=38, 'VA'=39, 'SM'=40, 'IT'=41, 'SI'=42, 'ME'=43, 'HR'=44, 'BA'=45, 'AO'=46, 'NA'=47, 'SH'=48, 'BV'=49, 'BB'=50, 'CV'=51, 'GY'=52, 'GF'=53, 'SR'=54, 'PM'=55, 'GL'=56, 'PY'=57, 'UY'=58, 'BR'=59, 'FK'=60, 'GS'=61, 'JM'=62, 'DO'=63, 'CU'=64, 'MQ'=65, 'BS'=66, 'BM'=67, 'AI'=68, 'TT'=69, 'KN'=70, 'DM'=71, 'AG'=72, 'LC'=73, 'TC'=74, 'AW'=75, 'VG'=76, 'VC'=77, 'MS'=78, 'MF'=79, 'BL'=80, 'GP'=81, 'GD'=82, 'KY'=83, 'BZ'=84, 'SV'=85, 'GT'=86, 'HN'=87, 'NI'=88, 'CR'=89, 'VE'=90, 'EC'=91, 'CO'=92, 'PA'=93, 'HT'=94, 'AR'=95, 'CL'=96, 'BO'=97, 'PE'=98, 'MX'=99, 'PF'=100, 'PN'=101, 'KI'=102, 'TK'=103, 'TO'=104, 'WF'=105, 'WS'=106, 'NU'=107, 'MP'=108, 'GU'=109, 'PR'=110, 'VI'=111, 'UM'=112, 'AS'=113, 'CA'=114, 'US'=115, 'PS'=116, 'RS'=117, 'AQ'=118, 'SX'=119, 'CW'=120, 'BQ'=121, 'SS'=122,'BU'=123, 'VD'=124, 'YD'=125, 'DD'=126),
|
user_country Enum8('UN'=-128, 'RW'=-127, 'SO'=-126, 'YE'=-125, 'IQ'=-124, 'SA'=-123, 'IR'=-122, 'CY'=-121, 'TZ'=-120, 'SY'=-119, 'AM'=-118, 'KE'=-117, 'CD'=-116, 'DJ'=-115, 'UG'=-114, 'CF'=-113, 'SC'=-112, 'JO'=-111, 'LB'=-110, 'KW'=-109, 'OM'=-108, 'QA'=-107, 'BH'=-106, 'AE'=-105, 'IL'=-104, 'TR'=-103, 'ET'=-102, 'ER'=-101, 'EG'=-100, 'SD'=-99, 'GR'=-98, 'BI'=-97, 'EE'=-96, 'LV'=-95, 'AZ'=-94, 'LT'=-93, 'SJ'=-92, 'GE'=-91, 'MD'=-90, 'BY'=-89, 'FI'=-88, 'AX'=-87, 'UA'=-86, 'MK'=-85, 'HU'=-84, 'BG'=-83, 'AL'=-82, 'PL'=-81, 'RO'=-80, 'XK'=-79, 'ZW'=-78, 'ZM'=-77, 'KM'=-76, 'MW'=-75, 'LS'=-74, 'BW'=-73, 'MU'=-72, 'SZ'=-71, 'RE'=-70, 'ZA'=-69, 'YT'=-68, 'MZ'=-67, 'MG'=-66, 'AF'=-65, 'PK'=-64, 'BD'=-63, 'TM'=-62, 'TJ'=-61, 'LK'=-60, 'BT'=-59, 'IN'=-58, 'MV'=-57, 'IO'=-56, 'NP'=-55, 'MM'=-54, 'UZ'=-53, 'KZ'=-52, 'KG'=-51, 'TF'=-50, 'HM'=-49, 'CC'=-48, 'PW'=-47, 'VN'=-46, 'TH'=-45, 'ID'=-44, 'LA'=-43, 'TW'=-42, 'PH'=-41, 'MY'=-40, 'CN'=-39, 'HK'=-38, 'BN'=-37, 'MO'=-36, 'KH'=-35, 'KR'=-34, 'JP'=-33, 'KP'=-32, 'SG'=-31, 'CK'=-30, 'TL'=-29, 'RU'=-28, 'MN'=-27, 'AU'=-26, 'CX'=-25, 'MH'=-24, 'FM'=-23, 'PG'=-22, 'SB'=-21, 'TV'=-20, 'NR'=-19, 'VU'=-18, 'NC'=-17, 'NF'=-16, 'NZ'=-15, 'FJ'=-14, 'LY'=-13, 'CM'=-12, 'SN'=-11, 'CG'=-10, 'PT'=-9, 'LR'=-8, 'CI'=-7, 'GH'=-6, 'GQ'=-5, 'NG'=-4, 'BF'=-3, 'TG'=-2, 'GW'=-1, 'MR'=0, 'BJ'=1, 'GA'=2, 'SL'=3, 'ST'=4, 'GI'=5, 'GM'=6, 'GN'=7, 'TD'=8, 'NE'=9, 'ML'=10, 'EH'=11, 'TN'=12, 'ES'=13, 'MA'=14, 'MT'=15, 'DZ'=16, 'FO'=17, 'DK'=18, 'IS'=19, 'GB'=20, 'CH'=21, 'SE'=22, 'NL'=23, 'AT'=24, 'BE'=25, 'DE'=26, 'LU'=27, 'IE'=28, 'MC'=29, 'FR'=30, 'AD'=31, 'LI'=32, 'JE'=33, 'IM'=34, 'GG'=35, 'SK'=36, 'CZ'=37, 'NO'=38, 'VA'=39, 'SM'=40, 'IT'=41, 'SI'=42, 'ME'=43, 'HR'=44, 'BA'=45, 'AO'=46, 'NA'=47, 'SH'=48, 'BV'=49, 'BB'=50, 'CV'=51, 'GY'=52, 'GF'=53, 'SR'=54, 'PM'=55, 'GL'=56, 'PY'=57, 'UY'=58, 'BR'=59, 'FK'=60, 'GS'=61, 'JM'=62, 'DO'=63, 'CU'=64, 'MQ'=65, 'BS'=66, 'BM'=67, 'AI'=68, 'TT'=69, 'KN'=70, 'DM'=71, 'AG'=72, 'LC'=73, 'TC'=74, 'AW'=75, 'VG'=76, 'VC'=77, 'MS'=78, 'MF'=79, 'BL'=80, 'GP'=81, 'GD'=82, 'KY'=83, 'BZ'=84, 'SV'=85, 'GT'=86, 'HN'=87, 'NI'=88, 'CR'=89, 'VE'=90, 'EC'=91, 'CO'=92, 'PA'=93, 'HT'=94, 'AR'=95, 'CL'=96, 'BO'=97, 'PE'=98, 'MX'=99, 'PF'=100, 'PN'=101, 'KI'=102, 'TK'=103, 'TO'=104, 'WF'=105, 'WS'=106, 'NU'=107, 'MP'=108, 'GU'=109, 'PR'=110, 'VI'=111, 'UM'=112, 'AS'=113, 'CA'=114, 'US'=115, 'PS'=116, 'RS'=117, 'AQ'=118, 'SX'=119, 'CW'=120, 'BQ'=121, 'SS'=122,'BU'=123, 'VD'=124, 'YD'=125, 'DD'=126),
|
||||||
user_city LowCardinality(String),
|
user_city LowCardinality(String),
|
||||||
user_state LowCardinality(String),
|
user_state LowCardinality(String),
|
||||||
platform Enum8('web'=1,'ios'=2,'android'=3) DEFAULT 'web',
|
platform Enum8('web'=1,'mobile'=2) DEFAULT 'web',
|
||||||
datetime DateTime,
|
datetime DateTime,
|
||||||
timezone LowCardinality(Nullable(String)),
|
timezone LowCardinality(Nullable(String)),
|
||||||
duration UInt32,
|
duration UInt32,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue