Skip to content
This repository was archived by the owner on Feb 24, 2023. It is now read-only.

Commit 2aaa34e

Browse files
authored
Merge pull request #106 from scale8/bot-filter
Added bot filter in API
2 parents f0a1500 + 7fa7f13 commit 2aaa34e

File tree

9 files changed

+128
-0
lines changed

9 files changed

+128
-0
lines changed

api/src/backends/databases/GoogleCloudBigQuery.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import BaseLogger from '../logging/abstractions/BaseLogger';
1616
import { getStorageProviderConfig } from '../../utils/IngestEndpointEnvironmentUtils';
1717
import { GCBigQueryStreamConfig } from '../../Types';
1818
import { getBigQueryConfig, getServiceAccountJson } from '../../utils/GoogleCloudUtils';
19+
import { WebTrafficType } from '../../enums/WebTrafficType';
1920

2021
@injectable()
2122
export default class GoogleCloudBigQuery extends BaseDatabase {
@@ -57,6 +58,9 @@ export default class GoogleCloudBigQuery extends BaseDatabase {
5758
return bigQuery;
5859
}
5960

61+
protected readonly BOT_TEST =
62+
'(browser_name IS NULL OR INSTR(LOWER(browser_name), "bot") > 0 OR INSTR(LOWER(browser_name), "headless") > 0 OR INSTR(LOWER(browser_name), "preview") > 0)';
63+
6064
protected readonly MOBILE_TEST =
6165
'(INSTR(browser_name, "Mobile") > 0 OR device_name = "iPhone" OR device_name = "iPad" OR os_name = "iOS" OR os_name = "Android")';
6266

@@ -278,6 +282,21 @@ export default class GoogleCloudBigQuery extends BaseDatabase {
278282
params: { page: queryOptions.filter_options.page },
279283
}
280284
: undefined;
285+
const getTrafficType = () => {
286+
if (queryOptions.filter_options.traffic_type === WebTrafficType.BOT) {
287+
return {
288+
where: this.BOT_TEST,
289+
params: { traffic_type: queryOptions.filter_options.traffic_type },
290+
};
291+
} else if (queryOptions.filter_options.traffic_type === WebTrafficType.VISITOR) {
292+
return {
293+
where: 'NOT ' + this.BOT_TEST,
294+
params: { traffic_type: queryOptions.filter_options.traffic_type },
295+
};
296+
} else {
297+
return undefined;
298+
}
299+
};
281300
const getMobile = () => {
282301
if (
283302
typeof queryOptions.filter_options.mobile === 'boolean' &&
@@ -379,6 +398,7 @@ export default class GoogleCloudBigQuery extends BaseDatabase {
379398
getReferrer(),
380399
getReferrerTld(),
381400
getMobile(),
401+
getTrafficType(),
382402
getBrowser(),
383403
getBrowserVersion(),
384404
getScreenSize(),

api/src/backends/databases/MongoDb.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import BaseConfig from '../configuration/abstractions/BaseConfig';
1717
import GQLError from '../../errors/GQLError';
1818
import userMessages from '../../errors/UserMessages';
1919
import BaseLogger from '../logging/abstractions/BaseLogger';
20+
import { WebTrafficType } from '../../enums/WebTrafficType';
2021

2122
@injectable()
2223
export default class MongoDb extends BaseDatabase {
@@ -34,6 +35,12 @@ export default class MongoDb extends BaseDatabase {
3435
['os_name', /android/i],
3536
];
3637

38+
protected readonly BOT_TEST: [string, RegExp][] = [
39+
['browser_name', /bot/i],
40+
['browser_name', /headless/i],
41+
['browser_name', /preview/i],
42+
];
43+
3744
private getAsMobileAggregationRegex() {
3845
return this.MOBILE_TEST.map(([input, regex]) => {
3946
return {
@@ -51,6 +58,12 @@ export default class MongoDb extends BaseDatabase {
5158
}));
5259
}
5360

61+
private getAsBotFilter() {
62+
return this.BOT_TEST.map(([input, regex]) => ({
63+
[input]: regex,
64+
}));
65+
}
66+
5467
protected async getCollection(entity: App | IngestEndpoint): Promise<Collection> {
5568
const entityUsageIngestEndpointEnvironmentId =
5669
this.getEntityUsageIngestEndpointEnvironmentId(entity);
@@ -215,6 +228,19 @@ export default class MongoDb extends BaseDatabase {
215228
return undefined;
216229
}
217230
};
231+
const getTrafficType = () => {
232+
if (queryOptions.filter_options.traffic_type === WebTrafficType.BOT) {
233+
return {
234+
$or: this.getAsBotFilter(),
235+
};
236+
} else if (queryOptions.filter_options.traffic_type === WebTrafficType.VISITOR) {
237+
return {
238+
$nor: this.getAsBotFilter(),
239+
};
240+
} else {
241+
return undefined;
242+
}
243+
};
218244
const getBrowser = () =>
219245
MongoDb.getFilterObjectFromStringFilterOption(queryOptions, 'browser', 'browser_name');
220246
const getBrowserVersion = () =>
@@ -265,6 +291,7 @@ export default class MongoDb extends BaseDatabase {
265291
getReferrer(),
266292
getReferrerTld(),
267293
getMobile(),
294+
getTrafficType(),
268295
getBrowser(),
269296
getBrowserVersion(),
270297
getScreenSize(),

api/src/backends/databases/abstractions/BaseDatabase.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import IngestEndpoint from '../../../mongo/models/data/IngestEndpoint';
44
import { ObjectId } from 'mongodb';
55
import GenericError from '../../../errors/GenericError';
66
import { LogPriority } from '../../../enums/LogPriority';
7+
import { WebTrafficType } from '../../../enums/WebTrafficType';
78

89
export interface BaseQueryOptions {
910
time_slice: string;
@@ -38,6 +39,7 @@ export interface AppQueryOptions extends BaseQueryOptions {
3839
browser_version?: string;
3940
screen_size?: string;
4041
os?: string;
42+
traffic_type?: WebTrafficType;
4143
event?: string;
4244
event_group?: string;
4345
custom_release_id?: string;

api/src/enums/WebTrafficType.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export enum WebTrafficType {
2+
VISITOR = 'VISITOR',
3+
BOT = 'BOT',
4+
}

api/src/gql/TypeDefRegister.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import { Mode } from '../enums/Mode';
3939
import { InputType } from '../../../common/enums/InputType';
4040
import { TypeIcon } from '../../../common/enums/TypeIcon';
4141
import { IngestSchemaWizard } from '../enums/IngestSchemaWizard';
42+
import { WebTrafficType } from '../enums/WebTrafficType';
4243

4344
@injectable()
4445
export default class TypeDefRegister {
@@ -233,6 +234,12 @@ export default class TypeDefRegister {
233234
value: String!
234235
}
235236
237+
"""
238+
A set of supported \`WebTrafficType\` types.
239+
"""
240+
enum WebTrafficType
241+
${TypeDefRegister.enumToGQL(WebTrafficType)}
242+
236243
"""
237244
A set of supported \`IngestSchemaWizard\` types.
238245
"""

api/src/managers/tag/AppManager.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ export default class AppManager extends Manager<App> {
148148
referrer_tld: String
149149
page: String
150150
mobile: Boolean
151+
traffic_type: WebTrafficType
151152
browser: String
152153
browser_version: String
153154
screen_size: String

ui/src/components/molecules/ChartPageContainer/AppAnalyticsPageContainer.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import ChartEventSelector from '../../../lazyComponents/ChartEventSelector';
55
import { AppAnalyticsContentProps } from '../../../types/props/AppAnalyticsContentProps';
66
import ChartPageContainer, { ChartPageContainerProps, extractFilters } from './ChartPageContainer';
77
import ChartBaseFilterSelector from '../../../lazyComponents/ChartBaseFilterSelector';
8+
import { ChartWebTrafficTypeFilterSelector } from '../../organisms/ChartWebTrafficTypeFilterSelector';
89

910
export type AppAnalyticsPageContainerProps = AppAnalyticsContentProps & {
1011
children: ReactNode;
@@ -21,6 +22,7 @@ const AppAnalyticsPageContainer: FC<AppAnalyticsPageContainerProps> = (
2122
<>
2223
<ChartBaseFilterSelector {...appDashboardContentProps} />
2324
<ChartEventSelector {...appDashboardContentProps} />
25+
<ChartWebTrafficTypeFilterSelector {...appDashboardContentProps} />
2426
</>
2527
),
2628
rightHeaderBlock: (
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { FC } from 'react';
2+
import { FormControl, InputLabel, MenuItem, Select, SelectChangeEvent, Theme } from '@mui/material';
3+
import { SxProps } from '@mui/system';
4+
import { AppAnalyticsContentProps } from '../../types/props/AppAnalyticsContentProps';
5+
import { AppErrorContentProps } from '../../types/props/AppErrorContentProps';
6+
import { WebTrafficType } from '../../gql/generated/globalTypes';
7+
import { snakeToTitleCase } from '../../utils/TextUtils';
8+
9+
export const ChartWebTrafficTypeFilterSelector: FC<
10+
AppAnalyticsContentProps | AppErrorContentProps
11+
> = (props: AppAnalyticsContentProps | AppErrorContentProps) => {
12+
const { appQueryOptions, setFilter } = props;
13+
const currentWebTrafficType = appQueryOptions.filter_options.traffic_type ?? ' ';
14+
15+
const handleWebTrafficTypeFilterChange = (e: SelectChangeEvent) => {
16+
if (e.target.value === ' ') {
17+
setFilter('traffic_type', undefined);
18+
} else {
19+
setFilter('traffic_type', e.target.value as string);
20+
}
21+
};
22+
23+
const selectRoot: SxProps<Theme> = {
24+
'&:focus': {
25+
backgroundColor: '#ffffff',
26+
},
27+
width: '130px',
28+
};
29+
30+
const selectContainer: SxProps<Theme> = {
31+
marginRight: 2,
32+
marginBottom: 1,
33+
};
34+
35+
return (
36+
<>
37+
<FormControl variant="outlined" size="small" sx={selectContainer}>
38+
<InputLabel id="bot-label">Traffic Type</InputLabel>
39+
<Select
40+
labelId="bot-label"
41+
sx={selectRoot}
42+
value={currentWebTrafficType}
43+
onChange={handleWebTrafficTypeFilterChange}
44+
label="Traffic Type"
45+
>
46+
<MenuItem value={' '}>All Traffic</MenuItem>
47+
{Object.keys(WebTrafficType).map((key) => (
48+
<MenuItem key={key} value={key}>
49+
{snakeToTitleCase(key)} Traffic
50+
</MenuItem>
51+
))}
52+
</Select>
53+
</FormControl>
54+
</>
55+
);
56+
};

ui/src/gql/generated/globalTypes.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,14 @@ export enum VarType {
381381
TIMESTAMP = "TIMESTAMP",
382382
}
383383

384+
/**
385+
* A set of supported `WebTrafficType` types.
386+
*/
387+
export enum WebTrafficType {
388+
BOT = "BOT",
389+
VISITOR = "VISITOR",
390+
}
391+
384392
/**
385393
* In order to use AWS Kinesis as your storage engine, you will need to create an
386394
* AWS account and create a new service account for Scale8. Please see our
@@ -562,6 +570,7 @@ export interface AppQueryFilterOptions {
562570
referrer_tld?: string | null;
563571
page?: string | null;
564572
mobile?: boolean | null;
573+
traffic_type?: WebTrafficType | null;
565574
browser?: string | null;
566575
browser_version?: string | null;
567576
screen_size?: string | null;

0 commit comments

Comments
 (0)