Skip to content

Commit 911ff67

Browse files
committed
Merge remote-tracking branch 'origin/main' into overwrite-timestamp
1 parent 3ca2962 commit 911ff67

File tree

13 files changed

+352
-93
lines changed

13 files changed

+352
-93
lines changed
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import React from "react"
2+
import Chip from "@mui/material/Chip";
3+
import Divider from '@mui/material/Divider';
4+
import { styled } from "@mui/material/styles"
5+
import Typography from "@mui/material/Typography"
6+
import TextField from "@mui/material/TextField"
7+
import Grid from "@mui/material/Grid"
8+
9+
import LinkedTextField from "@/components/LinkedTextField"
10+
import ExternalLink from "@/components/ExternalLink"
11+
import { Label } from "./types"
12+
13+
const Root = styled("div")(({ theme }) => ({
14+
marginTop: theme.spacing(3),
15+
}))
16+
17+
interface GeographicInformationProps {
18+
user_location_city: string | undefined
19+
setUserLocationCity: (value: string) => void
20+
user_location_region_id: string | undefined
21+
setUserLocationRegionId: (value: string) => void
22+
user_location_country_id: string | undefined
23+
setUserLocationCountryId: (value: string) => void
24+
user_location_subcontinent_id: string | undefined
25+
setUserLocationSubcontinentId: (value: string) => void
26+
user_location_continent_id: string | undefined
27+
setUserLocationContinentId: (value: string) => void
28+
ip_override: string | undefined
29+
setIpOverride: (value: string) => void
30+
}
31+
32+
const GeographicInformation: React.FC<GeographicInformationProps> = ({
33+
user_location_city,
34+
setUserLocationCity,
35+
user_location_region_id,
36+
setUserLocationRegionId,
37+
user_location_country_id,
38+
setUserLocationCountryId,
39+
user_location_subcontinent_id,
40+
setUserLocationSubcontinentId,
41+
user_location_continent_id,
42+
setUserLocationContinentId,
43+
ip_override,
44+
setIpOverride,
45+
}) => {
46+
return (
47+
<Root>
48+
<Divider><Chip label="GEOGRAPHIC INFORMATION" size="small" /></Divider>
49+
<Typography variant="h6">User Location</Typography>
50+
<Typography>
51+
See the{" "}
52+
<ExternalLink href="https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference#user_location">
53+
documentation
54+
</ExternalLink>{" "}
55+
for more information about user location attributes.
56+
</Typography>
57+
<Grid container spacing={1}>
58+
<Grid item xs={12} sm={6}>
59+
<TextField
60+
fullWidth
61+
label={Label.City}
62+
id={Label.City}
63+
variant="outlined"
64+
size="small"
65+
value={user_location_city || ""}
66+
onChange={e => setUserLocationCity(e.target.value)}
67+
helperText="The city name, e.g., Mountain View"
68+
/>
69+
</Grid>
70+
<Grid item xs={12} sm={6}>
71+
<TextField
72+
fullWidth
73+
label={Label.RegionId}
74+
id={Label.RegionId}
75+
variant="outlined"
76+
size="small"
77+
value={user_location_region_id || ""}
78+
onChange={e => setUserLocationRegionId(e.target.value)}
79+
helperText="The country and subdivision, e.g., US-CA"
80+
/>
81+
</Grid>
82+
<Grid item xs={12} sm={6}>
83+
<TextField
84+
fullWidth
85+
label={Label.CountryId}
86+
id={Label.CountryId}
87+
variant="outlined"
88+
size="small"
89+
value={user_location_country_id || ""}
90+
onChange={e => setUserLocationCountryId(e.target.value)}
91+
helperText="The country code, e.g., US"
92+
/>
93+
</Grid>
94+
<Grid item xs={12} sm={6}>
95+
<TextField
96+
fullWidth
97+
label={Label.ContinentId}
98+
id={Label.ContinentId}
99+
variant="outlined"
100+
size="small"
101+
value={user_location_continent_id || ""}
102+
onChange={e => setUserLocationContinentId(e.target.value)}
103+
helperText="The continent code, e.g., 019"
104+
/>
105+
</Grid>
106+
<Grid item xs={12} sm={6}>
107+
<TextField
108+
fullWidth
109+
label={Label.SubcontinentId}
110+
id={Label.SubcontinentId}
111+
variant="outlined"
112+
size="small"
113+
value={user_location_subcontinent_id || ""}
114+
onChange={e => setUserLocationSubcontinentId(e.target.value)}
115+
helperText="The subcontinent code, e.g., 021"
116+
/>
117+
</Grid>
118+
</Grid>
119+
<Typography variant="h6">IP Override</Typography>
120+
<Typography>
121+
Provide an IP address to derive the user's geographic location. If
122+
both an IP override and user location are provided, user location will
123+
be used.
124+
</Typography>
125+
<LinkedTextField
126+
label={Label.IpOverride}
127+
id={Label.IpOverride}
128+
linkTitle="See ip_override on devsite."
129+
href="https://developers.google.com/analytics/devguides/collection/protocol/ga4/reference#ip_override"
130+
value={ip_override || ""}
131+
onChange={setIpOverride}
132+
helperText="The IP address of the user."
133+
/>
134+
</Root>
135+
)
136+
}
137+
138+
export default GeographicInformation

src/components/ga4/EventBuilder/TimestampPicker.spec.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ import * as React from "react"
22
import { render, fireEvent, screen } from "@testing-library/react"
33
import userEvent from "@testing-library/user-event"
44
import "@testing-library/jest-dom"
5-
import TimestampPicker from "./TimestampPicker"
6-
import { UseFirebaseCtx, Label } from "."
5+
import TimestampPicker from './TimestampPicker';
6+
import { UseFirebaseCtx } from '.';
7+
import { Label } from './types';
78

89
describe("TimestampPicker", () => {
910
const setTimestamp = jest.fn()
@@ -198,4 +199,3 @@ describe("TimestampPicker", () => {
198199
jest.restoreAllMocks()
199200
})
200201
})
201-

src/components/ga4/EventBuilder/TimestampPicker.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider"
1313
import dayjs from "dayjs"
1414
import utc from "dayjs/plugin/utc"
1515
import timezone from "dayjs/plugin/timezone"
16-
import { Label, UseFirebaseCtx } from "."
16+
import { UseFirebaseCtx } from "."
17+
import { Label } from "./types"
1718
import LinkedTextField from "@/components/LinkedTextField"
1819
import TimezoneSelect from "./TimezoneSelect"
1920
import { TimestampScope } from "@/constants"

src/components/ga4/EventBuilder/TimezoneSelect.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as React from "react"
22
import { Autocomplete, TextField } from "@mui/material"
3-
import { Label } from "."
3+
import { Label } from "./types"
44

55
const timezones = Intl.supportedValuesOf("timeZone")
66

src/components/ga4/EventBuilder/ValidateEvent/index.spec.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,14 @@ const renderComponent = (props: Partial<ValidateEventProps> = {}) => {
5353
payloadObj: [],
5454
api_secret: "secret123",
5555
clientIds: {},
56+
ip_override: "",
57+
user_location: {
58+
city: "Mountain View",
59+
region_id: "CA",
60+
country_id: "US",
61+
subcontinent_id: "021",
62+
continent_id: "019"
63+
}
5664
}
5765

5866
return render(

src/components/ga4/EventBuilder/ValidateEvent/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ import PrettyJson from "@/components/PrettyJson"
2929
import usePayload from "./usePayload"
3030
import { ValidationMessage } from "../types"
3131
import Spinner from "@/components/Spinner"
32-
import { EventCtx, Label } from ".."
32+
import { EventCtx } from ".."
33+
import { Label } from "../types"
3334
import { Box, Card } from "@mui/material"
3435
import { green, red } from "@mui/material/colors"
3536
import WithHelpText from "@/components/WithHelpText"

src/components/ga4/EventBuilder/ValidateEvent/schemas/baseContent.spec.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,4 +142,16 @@ describe("baseContentSchema", () => {
142142

143143
expect(validator.isValid(validInput)).toEqual(false)
144144
})
145+
146+
describe("with ip_override", () => {
147+
test("is valid with a valid IPv4 address", () => {
148+
const validInput = {
149+
events: [{ name: "something", params: {} }],
150+
ip_override: "127.0.0.1",
151+
}
152+
const validator = new Validator(baseContentSchema)
153+
expect(validator.isValid(validInput)).toEqual(true)
154+
})
155+
})
156+
145157
})

src/components/ga4/EventBuilder/ValidateEvent/schemas/baseContent.ts

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,34 @@
22

33
import { userPropertiesSchema } from './userProperties'
44
import { eventsSchema } from './events'
5+
import { userLocationSchema } from "./userLocation"
56

67
export const baseContentSchema = {
7-
"type": "object",
8-
"required": ["events"],
9-
"additionalProperties": false,
10-
"properties": {
11-
"app_instance_id": {
12-
"type": "string",
13-
"format": "app_instance_id"
14-
},
15-
"client_id": {
16-
"type": "string",
17-
},
18-
"user_id": {
19-
"type": "string"
20-
},
21-
"timestamp_micros": {
22-
// "type": "number"
23-
},
24-
"user_properties": userPropertiesSchema,
25-
"non_personalized_ads": {
26-
"type": "boolean"
27-
},
28-
"events": eventsSchema,
29-
}
8+
type: "object",
9+
required: ["events"],
10+
additionalProperties: false,
11+
properties: {
12+
app_instance_id: {
13+
type: "string",
14+
format: "app_instance_id",
15+
},
16+
client_id: {
17+
type: "string",
18+
},
19+
user_id: {
20+
type: "string",
21+
},
22+
timestamp_micros: {
23+
// "type": "number"
24+
},
25+
user_properties: userPropertiesSchema,
26+
non_personalized_ads: {
27+
type: "boolean",
28+
},
29+
events: eventsSchema,
30+
user_location: userLocationSchema,
31+
ip_override: {
32+
type: "string",
33+
},
34+
},
3035
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// User location schema
2+
3+
export const userLocationSchema = {
4+
type: "object",
5+
additionalProperties: false,
6+
properties: {
7+
city: {
8+
type: "string",
9+
},
10+
region_id: {
11+
type: "string",
12+
},
13+
country_id: {
14+
type: "string",
15+
},
16+
subcontinent_id: {
17+
type: "string",
18+
},
19+
continent_id: {
20+
type: "string",
21+
},
22+
},
23+
}

src/components/ga4/EventBuilder/ValidateEvent/usePayload.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,9 @@ const usePayload = (): {} => {
7575
clientIds,
7676
type,
7777
useTextBox,
78-
payloadObj
78+
payloadObj,
79+
ip_override,
80+
user_location,
7981
} = useContext(EventCtx)!
8082

8183
const eventName = useMemo(() => {
@@ -97,22 +99,34 @@ const usePayload = (): {} => {
9799
[items]
98100
)
99101

100-
const params = useMemo(() => parameters.reduce(objectify, itemsParameter), [
101-
parameters,
102-
itemsParameter,
103-
])
102+
const params = useMemo(
103+
() => parameters.reduce(objectify, itemsParameter),
104+
[parameters, itemsParameter]
105+
)
104106

105107
const user_properties = useMemo(
106108
() => userProperties.reduce(objectifyUserProperties, {}),
107109
[userProperties]
108110
)
109111

112+
const user_location_info = useMemo(() => {
113+
if (user_location === undefined) {
114+
return undefined
115+
}
116+
const cleaned_location = removeUndefined(user_location)
117+
if (Object.keys(cleaned_location).length === 0) {
118+
return undefined
119+
}
120+
return cleaned_location
121+
}, [user_location])
122+
110123
let payload = useMemo(() => {
111124
return {
112125
...removeUndefined(clientIds),
113126
...removeUndefined({ timestamp_micros }),
114127
...removeUndefined({ non_personalized_ads }),
115128
...removeUndefined(removeEmptyObject({ user_properties })),
129+
...removeUndefined({ ip_override, user_location: user_location_info }),
116130
events: [
117131
{ name: eventName, ...(parameters.length > 0 ? { params } : {}) },
118132
],
@@ -125,6 +139,8 @@ const usePayload = (): {} => {
125139
params,
126140
timestamp_micros,
127141
user_properties,
142+
ip_override,
143+
user_location_info,
128144
])
129145

130146
if (useTextBox) {

0 commit comments

Comments
 (0)