@useairfoil/producer-google-analytics 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,19 +7,18 @@ import { amount, common, count, currencyCode, dimension } from "./shared";
7
7
 
8
8
  export const DailyOverviewSchema = Schema.Struct({
9
9
  ...common,
10
- active_users: count(30, "Users who engaged on the day."),
11
- new_users: count(31, "Users who came for the first time."),
12
- total_users: count(32, "Users who logged any event."),
13
- sessions: count(33, "Sessions that started."),
10
+ active_users: count("Users who engaged on the day."),
11
+ new_users: count("Users who came for the first time."),
12
+ total_users: count("Users who logged any event."),
13
+ sessions: count("Sessions that started."),
14
14
  engaged_sessions: count(
15
- 34,
16
15
  "Sessions that lasted 10 seconds or more, or had a key event or 2 page views.",
17
16
  ),
18
- screen_page_views: count(35, "Page and screen views."),
19
- event_count: count(36, "Events."),
20
- key_events: amount(37, "Key events. Can be fractional after attribution."),
21
- total_revenue: amount(38, "Revenue from purchases, subscriptions, and ads."),
22
- user_engagement_duration: amount(39, "Seconds the site or app was in the foreground."),
17
+ screen_page_views: count("Page and screen views."),
18
+ event_count: count("Events."),
19
+ key_events: amount("Key events. Can be fractional after attribution."),
20
+ total_revenue: amount("Revenue from purchases, subscriptions, and ads."),
21
+ user_engagement_duration: amount("Seconds the site or app was in the foreground."),
23
22
  currency_code: currencyCode,
24
23
  })
25
24
  .annotate({ description: "Google Analytics totals per day." })
@@ -29,19 +28,19 @@ export type DailyOverview = Schema.Schema.Type<typeof DailyOverviewSchema>;
29
28
 
30
29
  export const TrafficSourceSchema = Schema.Struct({
31
30
  ...common,
32
- session_source: dimension(10, "Source of the session, such as google."),
33
- session_medium: dimension(11, "Medium of the session, such as organic."),
34
- session_campaign_name: dimension(12, "Campaign of the session."),
35
- session_default_channel_group: dimension(13, "Default channel group of the session."),
36
- sessions: count(30, "Sessions that started."),
37
- engaged_sessions: count(31, "Engaged sessions."),
38
- active_users: count(32, "Users who engaged."),
39
- new_users: count(33, "Users who came for the first time."),
40
- screen_page_views: count(34, "Page and screen views."),
41
- event_count: count(35, "Events."),
42
- key_events: amount(36, "Key events."),
43
- total_revenue: amount(37, "Revenue from purchases, subscriptions, and ads."),
44
- user_engagement_duration: amount(38, "Seconds the site or app was in the foreground."),
31
+ session_source: dimension("Source of the session, such as google."),
32
+ session_medium: dimension("Medium of the session, such as organic."),
33
+ session_campaign_name: dimension("Campaign of the session."),
34
+ session_default_channel_group: dimension("Default channel group of the session."),
35
+ sessions: count("Sessions that started."),
36
+ engaged_sessions: count("Engaged sessions."),
37
+ active_users: count("Users who engaged."),
38
+ new_users: count("Users who came for the first time."),
39
+ screen_page_views: count("Page and screen views."),
40
+ event_count: count("Events."),
41
+ key_events: amount("Key events."),
42
+ total_revenue: amount("Revenue from purchases, subscriptions, and ads."),
43
+ user_engagement_duration: amount("Seconds the site or app was in the foreground."),
45
44
  currency_code: currencyCode,
46
45
  })
47
46
  .annotate({ description: "Google Analytics sessions per day and traffic source." })
@@ -51,19 +50,16 @@ export type TrafficSource = Schema.Schema.Type<typeof TrafficSourceSchema>;
51
50
 
52
51
  export const UserAcquisitionSchema = Schema.Struct({
53
52
  ...common,
54
- first_user_source: dimension(10, "Source that first brought the user."),
55
- first_user_medium: dimension(11, "Medium that first brought the user."),
56
- first_user_campaign_name: dimension(12, "Campaign that first brought the user."),
57
- first_user_default_channel_group: dimension(
58
- 13,
59
- "Default channel group that first brought the user.",
60
- ),
61
- new_users: count(30, "Users who came for the first time."),
62
- active_users: count(31, "Users who engaged."),
63
- sessions: count(32, "Sessions that started."),
64
- engaged_sessions: count(33, "Engaged sessions."),
65
- key_events: amount(34, "Key events."),
66
- total_revenue: amount(35, "Revenue from purchases, subscriptions, and ads."),
53
+ first_user_source: dimension("Source that first brought the user."),
54
+ first_user_medium: dimension("Medium that first brought the user."),
55
+ first_user_campaign_name: dimension("Campaign that first brought the user."),
56
+ first_user_default_channel_group: dimension("Default channel group that first brought the user."),
57
+ new_users: count("Users who came for the first time."),
58
+ active_users: count("Users who engaged."),
59
+ sessions: count("Sessions that started."),
60
+ engaged_sessions: count("Engaged sessions."),
61
+ key_events: amount("Key events."),
62
+ total_revenue: amount("Revenue from purchases, subscriptions, and ads."),
67
63
  currency_code: currencyCode,
68
64
  })
69
65
  .annotate({ description: "Google Analytics users per day and first traffic source." })
@@ -73,14 +69,14 @@ export type UserAcquisition = Schema.Schema.Type<typeof UserAcquisitionSchema>;
73
69
 
74
70
  export const PageSchema = Schema.Struct({
75
71
  ...common,
76
- host_name: dimension(10, "Host name, such as www.example.com."),
77
- page_path: dimension(11, "Page path, without the query string."),
78
- page_title: dimension(12, "Page title."),
79
- screen_page_views: count(30, "Page and screen views."),
80
- active_users: count(31, "Users who engaged."),
81
- event_count: count(32, "Events."),
82
- key_events: amount(33, "Key events."),
83
- user_engagement_duration: amount(34, "Seconds the page was in the foreground."),
72
+ host_name: dimension("Host name, such as www.example.com."),
73
+ page_path: dimension("Page path, without the query string."),
74
+ page_title: dimension("Page title."),
75
+ screen_page_views: count("Page and screen views."),
76
+ active_users: count("Users who engaged."),
77
+ event_count: count("Events."),
78
+ key_events: amount("Key events."),
79
+ user_engagement_duration: amount("Seconds the page was in the foreground."),
84
80
  })
85
81
  .annotate({ description: "Google Analytics page views per day and page." })
86
82
  .pipe(Iceberg.identifier("id"));
@@ -89,13 +85,13 @@ export type Page = Schema.Schema.Type<typeof PageSchema>;
89
85
 
90
86
  export const LandingPageSchema = Schema.Struct({
91
87
  ...common,
92
- landing_page: dimension(10, "First page path of the session, without the query string."),
93
- sessions: count(30, "Sessions that started."),
94
- engaged_sessions: count(31, "Engaged sessions."),
95
- active_users: count(32, "Users who engaged."),
96
- new_users: count(33, "Users who came for the first time."),
97
- key_events: amount(34, "Key events."),
98
- total_revenue: amount(35, "Revenue from purchases, subscriptions, and ads."),
88
+ landing_page: dimension("First page path of the session, without the query string."),
89
+ sessions: count("Sessions that started."),
90
+ engaged_sessions: count("Engaged sessions."),
91
+ active_users: count("Users who engaged."),
92
+ new_users: count("Users who came for the first time."),
93
+ key_events: amount("Key events."),
94
+ total_revenue: amount("Revenue from purchases, subscriptions, and ads."),
99
95
  currency_code: currencyCode,
100
96
  })
101
97
  .annotate({ description: "Google Analytics sessions per day and landing page." })
@@ -105,11 +101,11 @@ export type LandingPage = Schema.Schema.Type<typeof LandingPageSchema>;
105
101
 
106
102
  export const EventSchema = Schema.Struct({
107
103
  ...common,
108
- event_name: dimension(10, "Event name, such as page_view."),
109
- event_count: count(30, "Events."),
110
- total_users: count(31, "Users who logged the event."),
111
- event_value: amount(32, "Sum of the event's value parameter."),
112
- key_events: amount(33, "Key events."),
104
+ event_name: dimension("Event name, such as page_view."),
105
+ event_count: count("Events."),
106
+ total_users: count("Users who logged the event."),
107
+ event_value: amount("Sum of the event's value parameter."),
108
+ key_events: amount("Key events."),
113
109
  })
114
110
  .annotate({ description: "Google Analytics events per day and event name." })
115
111
  .pipe(Iceberg.identifier("id"));
@@ -118,15 +114,15 @@ export type Event = Schema.Schema.Type<typeof EventSchema>;
118
114
 
119
115
  export const LocationSchema = Schema.Struct({
120
116
  ...common,
121
- country: dimension(10, "Country of the user."),
122
- region: dimension(11, "Region of the user."),
123
- city: dimension(12, "City of the user."),
124
- active_users: count(30, "Users who engaged."),
125
- new_users: count(31, "Users who came for the first time."),
126
- sessions: count(32, "Sessions that started."),
127
- engaged_sessions: count(33, "Engaged sessions."),
128
- key_events: amount(34, "Key events."),
129
- total_revenue: amount(35, "Revenue from purchases, subscriptions, and ads."),
117
+ country: dimension("Country of the user."),
118
+ region: dimension("Region of the user."),
119
+ city: dimension("City of the user."),
120
+ active_users: count("Users who engaged."),
121
+ new_users: count("Users who came for the first time."),
122
+ sessions: count("Sessions that started."),
123
+ engaged_sessions: count("Engaged sessions."),
124
+ key_events: amount("Key events."),
125
+ total_revenue: amount("Revenue from purchases, subscriptions, and ads."),
130
126
  currency_code: currencyCode,
131
127
  })
132
128
  .annotate({ description: "Google Analytics users per day and location." })
@@ -136,16 +132,16 @@ export type Location = Schema.Schema.Type<typeof LocationSchema>;
136
132
 
137
133
  export const DeviceSchema = Schema.Struct({
138
134
  ...common,
139
- device_category: dimension(10, "Device category: desktop, mobile, or tablet."),
140
- platform: dimension(11, "Platform: web, iOS, or Android."),
141
- operating_system: dimension(12, "Operating system."),
142
- browser: dimension(13, "Browser."),
143
- active_users: count(30, "Users who engaged."),
144
- new_users: count(31, "Users who came for the first time."),
145
- sessions: count(32, "Sessions that started."),
146
- engaged_sessions: count(33, "Engaged sessions."),
147
- screen_page_views: count(34, "Page and screen views."),
148
- key_events: amount(35, "Key events."),
135
+ device_category: dimension("Device category: desktop, mobile, or tablet."),
136
+ platform: dimension("Platform: web, iOS, or Android."),
137
+ operating_system: dimension("Operating system."),
138
+ browser: dimension("Browser."),
139
+ active_users: count("Users who engaged."),
140
+ new_users: count("Users who came for the first time."),
141
+ sessions: count("Sessions that started."),
142
+ engaged_sessions: count("Engaged sessions."),
143
+ screen_page_views: count("Page and screen views."),
144
+ key_events: amount("Key events."),
149
145
  })
150
146
  .annotate({ description: "Google Analytics users per day and device." })
151
147
  .pipe(Iceberg.identifier("id"));
@@ -154,14 +150,14 @@ export type Device = Schema.Schema.Type<typeof DeviceSchema>;
154
150
 
155
151
  export const EcommerceItemSchema = Schema.Struct({
156
152
  ...common,
157
- item_id: dimension(10, "Item ID."),
158
- item_name: dimension(11, "Item name."),
159
- item_brand: dimension(12, "Item brand."),
160
- item_category: dimension(13, "First item category."),
161
- items_viewed: count(30, "Units viewed."),
162
- items_added_to_cart: count(31, "Units added to carts."),
163
- items_purchased: count(32, "Units purchased."),
164
- item_revenue: amount(33, "Revenue from the item, without tax and shipping."),
153
+ item_id: dimension("Item ID."),
154
+ item_name: dimension("Item name."),
155
+ item_brand: dimension("Item brand."),
156
+ item_category: dimension("First item category."),
157
+ items_viewed: count("Units viewed."),
158
+ items_added_to_cart: count("Units added to carts."),
159
+ items_purchased: count("Units purchased."),
160
+ item_revenue: amount("Revenue from the item, without tax and shipping."),
165
161
  currency_code: currencyCode,
166
162
  })
167
163
  .annotate({
@@ -1,9 +1,5 @@
1
- import { Iceberg } from "@useairfoil/connector-kit";
2
1
  import { Schema, SchemaTransformation } from "effect";
3
2
 
4
- export const field = (fieldId: number, description: string) =>
5
- Iceberg.field(fieldId, { description });
6
-
7
3
  /** The API's `YYYYMMDD` as `YYYY-MM-DD`. */
8
4
  export const isoDay = (date: string) => `${date.slice(0, 4)}-${date.slice(4, 6)}-${date.slice(6)}`;
9
5
 
@@ -18,25 +14,33 @@ const ReportDate = Schema.String.check(Schema.isPattern(/^\d{8}$/)).pipe(
18
14
  );
19
15
 
20
16
  /** `TYPE_INTEGER` metrics, as an Iceberg long. */
21
- export const count = (fieldId: number, description: string) =>
22
- Schema.BigIntFromString.pipe(field(fieldId, description));
17
+ export const count = (description: string) =>
18
+ Schema.BigIntFromString.pipe(Schema.annotateKey({ description }));
23
19
 
24
20
  /** Float, currency, and seconds metrics, as an Iceberg double. */
25
- export const amount = (fieldId: number, description: string) =>
26
- Schema.FiniteFromString.pipe(field(fieldId, description));
21
+ export const amount = (description: string) =>
22
+ Schema.FiniteFromString.pipe(Schema.annotateKey({ description }));
27
23
 
28
24
  /** Dimension values are never null. GA uses `(not set)` and `(other)` instead. */
29
- export const dimension = (fieldId: number, description: string) =>
30
- Schema.String.pipe(field(fieldId, description));
25
+ export const dimension = (description: string) =>
26
+ Schema.String.pipe(Schema.annotateKey({ description }));
31
27
 
32
28
  /** `version` is the fetch time, since report rows have no update time. */
33
29
  export const common = {
34
- id: Schema.String.pipe(field(1, "JSON array of the property ID, date, and dimension values.")),
35
- version: Schema.Date.pipe(field(2, "Time the row was read from Google Analytics.")),
36
- property_id: Schema.String.pipe(field(3, "GA4 property ID.")),
37
- date: ReportDate.pipe(field(4, "Day in the property's time zone, as YYYY-MM-DD.")),
30
+ id: Schema.String.pipe(
31
+ Schema.annotateKey({
32
+ description: "JSON array of the property ID, date, and dimension values.",
33
+ }),
34
+ ),
35
+ version: Schema.Date.pipe(
36
+ Schema.annotateKey({ description: "Time the row was read from Google Analytics." }),
37
+ ),
38
+ property_id: Schema.String.pipe(Schema.annotateKey({ description: "GA4 property ID." })),
39
+ date: ReportDate.pipe(
40
+ Schema.annotateKey({ description: "Day in the property's time zone, as YYYY-MM-DD." }),
41
+ ),
38
42
  };
39
43
 
40
44
  export const currencyCode = Schema.String.pipe(
41
- field(50, "Currency of the revenue fields, the property's currency."),
45
+ Schema.annotateKey({ description: "Currency of the revenue fields, the property's currency." }),
42
46
  );
package/src/start.ts CHANGED
@@ -32,7 +32,7 @@ export const startCommand = Command.make("start", {}, () =>
32
32
  const entrypoint = yield* GoogleAnalyticsConnector.GoogleAnalyticsConnector;
33
33
 
34
34
  return yield* ConnectorApp.start(entrypoint, { port }).pipe(
35
- Effect.provide(Ingestor.layerWingsConfig(entrypoint)),
35
+ Effect.provide(Ingestor.layerArrowFlightConfig(entrypoint)),
36
36
  );
37
37
  }).pipe(
38
38
  Effect.annotateLogs({ component: "google-analytics" }),
@@ -46,4 +46,4 @@ export const startCommand = Command.make("start", {}, () =>
46
46
  ),
47
47
  Effect.provide(RuntimeConfig.layerHosted()),
48
48
  ),
49
- ).pipe(Command.withDescription("Run the production connector against Wings"));
49
+ ).pipe(Command.withDescription("Run the connector"));
@@ -1 +0,0 @@
1
- {"version":3,"file":"src-Cu0LuOTi.mjs","names":["make","layer","make","GoogleAnalyticsAuth.GoogleAnalyticsAuth","layer","GoogleAnalyticsAuth.layer","layerConfig","GoogleAnalyticsClient.GoogleAnalyticsClient","GoogleAnalyticsClient.layer"],"sources":["../src/client/auth.ts","../src/schemas/api.ts","../src/client/client.ts","../src/schemas/shared.ts","../src/resources/reports/read.ts","../src/resources/reports/sources.ts","../src/resources/reports/resource.ts","../src/schemas/reports.ts","../src/resources/reports/specs.ts","../src/connector.ts"],"sourcesContent":["import { Auth, ConnectorError, Telemetry } from \"@useairfoil/connector-kit\";\nimport { Clock, Context, Duration, Effect, Layer, Redacted, Schedule, Schema } from \"effect\";\nimport { HttpClient, HttpClientRequest, HttpClientResponse } from \"effect/http\";\nimport { createPrivateKey, sign } from \"node:crypto\";\n\nimport type { GoogleAnalyticsConfig } from \"../manifest\";\n\ntype GoogleAnalyticsAuthConfig = Pick<GoogleAnalyticsConfig, \"clientEmail\" | \"privateKey\">;\n\nexport type GoogleAnalyticsAuthService = Auth.TokenCache<ConnectorError>;\n\nexport class GoogleAnalyticsAuth extends Context.Service<\n GoogleAnalyticsAuth,\n GoogleAnalyticsAuthService\n>()(\"@useairfoil/producer-google-analytics/GoogleAnalyticsAuth\") {}\n\nexport const tokenUrl = \"https://oauth2.googleapis.com/token\";\nexport const scope = \"https://www.googleapis.com/auth/analytics.readonly\";\n\nconst TokenResponseSchema = Schema.Struct({\n access_token: Schema.String,\n expires_in: Schema.Number,\n});\n\nconst TokenErrorSchema = Schema.Struct({\n error: Schema.optional(Schema.String),\n error_description: Schema.optional(Schema.String),\n});\n\nconst base64Url = (value: string | Buffer): string => Buffer.from(value).toString(\"base64url\");\n\n/** Signs the service account JWT with RS256. Google allows at most 1 hour. */\nexport const makeAssertion = (options: {\n readonly clientEmail: string;\n readonly privateKey: string;\n readonly nowMillis: number;\n}): Effect.Effect<string, ConnectorError> =>\n Effect.try({\n try: () => {\n const now = Math.floor(options.nowMillis / 1000);\n const header = base64Url(JSON.stringify({ alg: \"RS256\", typ: \"JWT\" }));\n const claims = base64Url(\n JSON.stringify({\n iss: options.clientEmail,\n scope,\n aud: tokenUrl,\n iat: now,\n exp: now + 3600,\n }),\n );\n const signingInput = `${header}.${claims}`;\n // Env files often keep the PEM on one line with escaped newlines.\n const key = createPrivateKey(options.privateKey.replace(/\\\\n/g, \"\\n\"));\n return `${signingInput}.${base64Url(sign(\"sha256\", Buffer.from(signingInput), key))}`;\n },\n catch: (cause) =>\n new ConnectorError({ message: \"Invalid Google service account private key\", cause }),\n });\n\nconst tokenError = (response: HttpClientResponse.HttpClientResponse) =>\n HttpClientResponse.schemaBodyJson(TokenErrorSchema)(response).pipe(\n Effect.map((body) => body.error),\n Effect.orElseSucceed(() => undefined),\n Effect.flatMap((error) =>\n Effect.fail(\n new ConnectorError({\n message:\n error === \"invalid_grant\" || error === \"invalid_client\"\n ? \"Google rejected the service account key\"\n : `Google returned ${response.status} for the OAuth token`,\n }),\n ),\n ),\n );\n\nexport const make = Effect.fnUntraced(function* (config: GoogleAnalyticsAuthConfig) {\n const client = (yield* HttpClient.HttpClient).pipe(\n HttpClient.retryTransient({\n schedule: Schedule.exponential(\"250 millis\").pipe(Schedule.jittered),\n times: 3,\n }),\n );\n\n return yield* Auth.makeTokenCache({\n acquire: Effect.gen(function* () {\n const assertion = yield* makeAssertion({\n clientEmail: config.clientEmail,\n privateKey: Redacted.value(config.privateKey),\n nowMillis: yield* Clock.currentTimeMillis,\n });\n const response = yield* client\n .execute(\n HttpClientRequest.post(tokenUrl).pipe(\n HttpClientRequest.acceptJson,\n HttpClientRequest.bodyUrlParams({\n grant_type: \"urn:ietf:params:oauth:grant-type:jwt-bearer\",\n assertion,\n }),\n ),\n )\n .pipe(\n Effect.mapError(\n (cause) => new ConnectorError({ message: \"Google OAuth token request failed\", cause }),\n ),\n );\n const body = yield* HttpClientResponse.matchStatus(response, {\n \"2xx\": (ok) =>\n HttpClientResponse.schemaBodyJson(TokenResponseSchema)(ok).pipe(\n Effect.mapError(\n (cause) =>\n new ConnectorError({ message: \"Invalid Google OAuth token response\", cause }),\n ),\n ),\n orElse: tokenError,\n });\n return {\n value: Redacted.make(body.access_token),\n expiresIn: Duration.seconds(body.expires_in),\n };\n }).pipe(\n Effect.tapError((error) => Telemetry.annotateError(\"api_auth\", error)),\n Effect.withSpan(Telemetry.SpanName.apiFetch, {\n kind: \"client\",\n attributes: { [Telemetry.Attr.apiPath]: \"/token\" },\n }),\n ),\n });\n});\n\nexport const layer = (\n config: GoogleAnalyticsAuthConfig,\n): Layer.Layer<GoogleAnalyticsAuth, never, HttpClient.HttpClient> =>\n Layer.effect(GoogleAnalyticsAuth)(make(config));\n","import { Schema } from \"effect\";\n\nconst ValueSchema = Schema.Struct({ value: Schema.String });\n\n/** `POST properties/{id}:runReport`. Every value comes as a string. */\nexport const RunReportResponseSchema = Schema.Struct({\n dimensionHeaders: Schema.Array(Schema.Struct({ name: Schema.String })),\n metricHeaders: Schema.Array(Schema.Struct({ name: Schema.String })),\n rows: Schema.optional(\n Schema.Array(\n Schema.Struct({\n dimensionValues: Schema.Array(ValueSchema),\n metricValues: Schema.Array(ValueSchema),\n }),\n ),\n ),\n // Left out when the report is empty.\n rowCount: Schema.optional(Schema.Int),\n metadata: Schema.Struct({\n timeZone: Schema.String,\n currencyCode: Schema.optional(Schema.String),\n dataLossFromOtherRow: Schema.optional(Schema.Boolean),\n subjectToThresholding: Schema.optional(Schema.Boolean),\n samplingMetadatas: Schema.optional(Schema.Array(Schema.Unknown)),\n dataTruncationReasons: Schema.optional(Schema.Array(Schema.Unknown)),\n }),\n});\n\nexport type RunReportResponse = Schema.Schema.Type<typeof RunReportResponseSchema>;\n\nexport const ErrorBodySchema = Schema.Struct({\n error: Schema.Struct({\n code: Schema.optional(Schema.Int),\n message: Schema.optional(Schema.String),\n status: Schema.optional(Schema.String),\n }),\n});\n","import { ConnectorError, Metrics, Telemetry } from \"@useairfoil/connector-kit\";\nimport {\n Config,\n Context,\n Data,\n DateTime,\n Duration,\n Effect,\n Exit,\n Layer,\n Redacted,\n Schedule,\n Semaphore,\n} from \"effect\";\nimport { HttpClient, HttpClientRequest, HttpClientResponse } from \"effect/http\";\n\nimport { type GoogleAnalyticsConfig, manifest } from \"../manifest\";\nimport { ErrorBodySchema, type RunReportResponse, RunReportResponseSchema } from \"../schemas/api\";\nimport * as GoogleAnalyticsAuth from \"./auth\";\n\nexport const DATA_API_VERSION = \"v1beta\";\n\nconst baseUrl = `https://analyticsdata.googleapis.com/${DATA_API_VERSION}`;\nconst maxRetries = 5;\nconst retryBaseDelay = Duration.seconds(1);\nconst requestTimeout = Duration.minutes(2);\n// Token quotas refill within the hour, or at midnight Pacific for the daily one.\n// Keep waiting; a failed source stays stopped until restart.\nconst limitRetry = Schedule.min([Schedule.exponential(\"1 minute\"), Schedule.spaced(\"10 minutes\")]);\n// A standard property allows 10 requests at once. Leave room for other tools.\nconst maxConcurrent = 5;\n\nexport type ReportRequest = {\n readonly dimensions: ReadonlyArray<string>;\n readonly metrics: ReadonlyArray<string>;\n /** `YYYY-MM-DD` in the property's time zone. */\n readonly startDate: string;\n readonly endDate: string;\n readonly limit: number;\n readonly offset: number;\n};\n\nexport type GoogleAnalyticsClientService = {\n /** `name` labels the span and errors, such as the resource name. */\n readonly runReport: (\n name: string,\n request: ReportRequest,\n ) => Effect.Effect<RunReportResponse, ConnectorError>;\n /** The property's time zone. Report dates are days in this zone. */\n readonly timeZone: Effect.Effect<DateTime.TimeZone, ConnectorError>;\n};\n\nexport class GoogleAnalyticsClient extends Context.Service<\n GoogleAnalyticsClient,\n GoogleAnalyticsClientService\n>()(\"@useairfoil/producer-google-analytics/GoogleAnalyticsClient\") {}\n\nclass QuotaExhausted extends Data.TaggedError(\"QuotaExhausted\")<{ readonly message: string }> {}\n\nconst statusError = (response: HttpClientResponse.HttpClientResponse, name: string) =>\n HttpClientResponse.schemaBodyJson(ErrorBodySchema)(response).pipe(\n Effect.map((body) => body.error),\n Effect.orElseSucceed(() => undefined),\n Effect.flatMap(\n (error): Effect.Effect<never, ConnectorError | QuotaExhausted> =>\n response.status === 429\n ? Effect.fail(\n new QuotaExhausted({\n message: `Google Analytics quota used up for ${name}${error?.message ? `: ${error.message}` : \"\"}`,\n }),\n )\n : Effect.fail(\n new ConnectorError({\n message: `Google Analytics API returned ${response.status}${error?.status ? ` ${error.status}` : \"\"} for ${name}${error?.message ? `: ${error.message}` : \"\"}`,\n }),\n ),\n ),\n Effect.tapError((error) => Telemetry.annotateError(\"api_status\", error)),\n );\n\nexport const make = Effect.fnUntraced(function* (\n config: Pick<GoogleAnalyticsConfig, \"propertyId\">,\n) {\n const auth = yield* GoogleAnalyticsAuth.GoogleAnalyticsAuth;\n const semaphore = yield* Semaphore.make(maxConcurrent);\n const client = (yield* HttpClient.HttpClient).pipe(\n HttpClient.mapRequest(HttpClientRequest.acceptJson),\n HttpClient.mapRequestEffect((request) =>\n auth.get.pipe(\n Effect.map((token) => HttpClientRequest.bearerToken(request, Redacted.value(token))),\n ),\n ),\n // Short retries cover 5xx and the concurrent request limit. 5xx count\n // against a small hourly server error quota, so keep the cap.\n HttpClient.retryTransient({\n schedule: Metrics.retrySchedule({\n connector: manifest.name,\n baseDelay: retryBaseDelay,\n times: maxRetries,\n }),\n }),\n );\n const url = `${baseUrl}/properties/${config.propertyId}:runReport`;\n\n const runReport = (name: string, request: ReportRequest) =>\n Effect.gen(function* () {\n const response = yield* client\n .execute(\n HttpClientRequest.post(url).pipe(\n HttpClientRequest.bodyJsonUnsafe({\n dateRanges: [{ startDate: request.startDate, endDate: request.endDate }],\n dimensions: request.dimensions.map((dimension) => ({ name: dimension })),\n metrics: request.metrics.map((metric) => ({ name: metric })),\n // Sort by every dimension so offset pages stay stable.\n orderBys: request.dimensions.map((dimension) => ({\n dimension: { dimensionName: dimension },\n })),\n limit: request.limit,\n offset: request.offset,\n }),\n ),\n )\n .pipe(\n Effect.tapError((error) => Telemetry.annotateError(\"api_http\", error)),\n Effect.mapError(\n (error) =>\n new ConnectorError({\n message: `Google Analytics API request failed for ${name}`,\n cause: error,\n }),\n ),\n );\n return yield* HttpClientResponse.matchStatus(response, {\n \"2xx\": (ok) =>\n HttpClientResponse.schemaBodyJson(RunReportResponseSchema)(ok).pipe(\n Effect.tapError((error) => Telemetry.annotateError(\"api_decode\", error)),\n Effect.mapError(\n (error) =>\n new ConnectorError({\n message: `Google Analytics API response for ${name} does not match the schema`,\n cause: error,\n }),\n ),\n ),\n orElse: (failed) => statusError(failed, name),\n });\n }).pipe(\n Effect.timeoutOrElse({\n duration: requestTimeout,\n orElse: () =>\n Effect.fail(\n new ConnectorError({ message: `Google Analytics API request timed out for ${name}` }),\n ),\n }),\n // The timeout starts once a permit is free, so queue time doesn't count.\n semaphore.withPermits(1),\n Effect.tapErrorTag(\"QuotaExhausted\", (error) =>\n Effect.logWarning(`${error.message}. Waiting to try again.`),\n ),\n Effect.retry({\n schedule: limitRetry,\n while: (error) => error._tag === \"QuotaExhausted\",\n }),\n Effect.catchTag(\"QuotaExhausted\", (error) =>\n Effect.fail(new ConnectorError({ message: error.message })),\n ),\n Effect.withSpan(Telemetry.SpanName.apiFetch, {\n kind: \"client\",\n attributes: { [Telemetry.Attr.apiPath]: `runReport:${name}` },\n }),\n );\n\n // Every report carries the zone; one small report reads it. Failures aren't\n // kept, and the hour picks up a changed zone.\n const timeZone = yield* Effect.cachedWithTTL(\n runReport(\"time_zone\", {\n dimensions: [\"date\"],\n metrics: [\"sessions\"],\n startDate: \"yesterday\",\n endDate: \"yesterday\",\n limit: 1,\n offset: 0,\n }).pipe(\n Effect.flatMap((response) =>\n DateTime.zoneMakeNamedEffect(response.metadata.timeZone).pipe(\n Effect.mapError(\n (cause) =>\n new ConnectorError({\n message: `Unknown Google Analytics time zone ${response.metadata.timeZone}`,\n cause,\n }),\n ),\n ),\n ),\n ),\n (exit) => (Exit.isSuccess(exit) ? Duration.hours(1) : Duration.zero),\n );\n\n return GoogleAnalyticsClient.of({ runReport, timeZone });\n});\n\nexport const layer = (\n config: GoogleAnalyticsConfig,\n): Layer.Layer<GoogleAnalyticsClient, never, HttpClient.HttpClient> =>\n Layer.effect(GoogleAnalyticsClient)(make(config)).pipe(\n Layer.provide(GoogleAnalyticsAuth.layer(config)),\n );\n\nexport const layerConfig = (\n config: Config.Wrap<GoogleAnalyticsConfig>,\n): Layer.Layer<GoogleAnalyticsClient, Config.ConfigError, HttpClient.HttpClient> =>\n Layer.unwrap(Config.unwrap(config).pipe(Effect.map(layer)));\n","import { Iceberg } from \"@useairfoil/connector-kit\";\nimport { Schema, SchemaTransformation } from \"effect\";\n\nexport const field = (fieldId: number, description: string) =>\n Iceberg.field(fieldId, { description });\n\n/** The API's `YYYYMMDD` as `YYYY-MM-DD`. */\nexport const isoDay = (date: string) => `${date.slice(0, 4)}-${date.slice(4, 6)}-${date.slice(6)}`;\n\nconst ReportDate = Schema.String.check(Schema.isPattern(/^\\d{8}$/)).pipe(\n Schema.decodeTo(\n Schema.String,\n SchemaTransformation.transform({\n decode: isoDay,\n encode: (date) => date.replaceAll(\"-\", \"\"),\n }),\n ),\n);\n\n/** `TYPE_INTEGER` metrics, as an Iceberg long. */\nexport const count = (fieldId: number, description: string) =>\n Schema.BigIntFromString.pipe(field(fieldId, description));\n\n/** Float, currency, and seconds metrics, as an Iceberg double. */\nexport const amount = (fieldId: number, description: string) =>\n Schema.FiniteFromString.pipe(field(fieldId, description));\n\n/** Dimension values are never null. GA uses `(not set)` and `(other)` instead. */\nexport const dimension = (fieldId: number, description: string) =>\n Schema.String.pipe(field(fieldId, description));\n\n/** `version` is the fetch time, since report rows have no update time. */\nexport const common = {\n id: Schema.String.pipe(field(1, \"JSON array of the property ID, date, and dimension values.\")),\n version: Schema.Date.pipe(field(2, \"Time the row was read from Google Analytics.\")),\n property_id: Schema.String.pipe(field(3, \"GA4 property ID.\")),\n date: ReportDate.pipe(field(4, \"Day in the property's time zone, as YYYY-MM-DD.\")),\n};\n\nexport const currencyCode = Schema.String.pipe(\n field(50, \"Currency of the revenue fields, the property's currency.\"),\n);\n","import { ConnectorError } from \"@useairfoil/connector-kit\";\nimport { DateTime, Effect, Option, Schema } from \"effect\";\n\nimport type { GoogleAnalyticsClientService } from \"../../client/client\";\nimport type { RunReportResponse } from \"../../schemas/api\";\nimport type { ReportSpec } from \"./spec\";\n\nimport { isoDay } from \"../../schemas/shared\";\n\n// The API returns at most 250,000 rows a request. Smaller pages keep memory low.\nconst pageSize = 100_000;\n\nexport type ReportPage<Row> = {\n readonly rows: ReadonlyArray<Row>;\n /** Offset of the next page of the same day. */\n readonly next: Option.Option<number>;\n};\n\ntype ReportRow = NonNullable<RunReportResponse[\"rows\"]>[number];\n\n/** `[column, apiName]` pairs, in request order. */\nconst pairs = (columns: Readonly<Record<string, string | undefined>>) =>\n Object.entries(columns).flatMap(([column, name]) =>\n name === undefined ? [] : [[column, name] as const],\n );\n\nconst valuesByName = (\n headers: ReadonlyArray<{ readonly name: string }>,\n values: ReadonlyArray<{ readonly value: string }>,\n) => new Map(headers.map((header, index) => [header.name, values[index]?.value]));\n\n/** Logs the response flags that mean some data is hidden or estimated. */\nconst warnDataLoss = (name: string, day: string, metadata: RunReportResponse[\"metadata\"]) => {\n const flags = {\n otherRow: metadata.dataLossFromOtherRow === true,\n thresholded: metadata.subjectToThresholding === true,\n sampled: (metadata.samplingMetadatas?.length ?? 0) > 0,\n truncated: (metadata.dataTruncationReasons?.length ?? 0) > 0,\n };\n return Object.values(flags).some(Boolean)\n ? Effect.logWarning(\"Google Analytics report is incomplete\").pipe(\n Effect.annotateLogs({ report: name, day, ...flags }),\n )\n : Effect.void;\n};\n\nexport const readDay = <Row extends object>(\n client: GoogleAnalyticsClientService,\n propertyId: string,\n spec: ReportSpec<Row>,\n options: { readonly day: string; readonly offset: number; readonly limit?: number },\n): Effect.Effect<ReportPage<Row>, ConnectorError> =>\n Effect.gen(function* () {\n const dimensions = pairs(spec.dimensions);\n const metrics = pairs(spec.metrics);\n const columns = [...dimensions, ...metrics];\n const response = yield* client.runReport(spec.name, {\n dimensions: [\"date\", ...dimensions.map(([, name]) => name)],\n metrics: metrics.map(([, name]) => name),\n startDate: options.day,\n endDate: options.day,\n limit: options.limit ?? pageSize,\n offset: options.offset,\n });\n yield* warnDataLoss(spec.name, options.day, response.metadata);\n const version = yield* DateTime.nowAsDate;\n const decodeRow = Schema.decodeUnknownEffect(spec.rowSchema);\n\n const toRow = (row: ReportRow) => {\n const values = new Map([\n ...valuesByName(response.dimensionHeaders, row.dimensionValues),\n ...valuesByName(response.metricHeaders, row.metricValues),\n ]);\n const date = values.get(\"date\") ?? \"\";\n return decodeRow({\n id: JSON.stringify([\n propertyId,\n isoDay(date),\n ...dimensions.map(([, name]) => values.get(name)),\n ]),\n version,\n property_id: propertyId,\n date,\n // Tables without revenue leave it out.\n currency_code: response.metadata.currencyCode,\n ...Object.fromEntries(columns.map(([column, name]) => [column, values.get(name)])),\n }).pipe(\n Effect.mapError(\n (cause) =>\n new ConnectorError({\n message: `Unexpected Google Analytics row in ${spec.name} for ${options.day}`,\n cause,\n }),\n ),\n );\n };\n\n const rows = yield* Effect.forEach(response.rows ?? [], toRow);\n const read = options.offset + rows.length;\n return {\n rows,\n next: rows.length > 0 && read < (response.rowCount ?? 0) ? Option.some(read) : Option.none(),\n };\n });\n","import { ConnectorError, Cursor, Fetch } from \"@useairfoil/connector-kit\";\nimport { DateTime, Duration, Effect, Option, Schema } from \"effect\";\n\nimport type { GoogleAnalyticsClientService } from \"../../client/client\";\nimport type { GoogleAnalyticsConfig } from \"../../manifest\";\nimport type { ReportSpec } from \"./spec\";\n\nimport { readDay } from \"./read\";\n\n// Daily data is ready about 12 hours after the day ends, so polling more often\n// mostly reads the same numbers.\nconst changesInterval = Duration.hours(6);\n\ntype ReportConfig = Pick<GoogleAnalyticsConfig, \"propertyId\" | \"startDate\" | \"lookbackDays\">;\n\nconst Day = Schema.String.check(Schema.isPattern(/^\\d{4}-\\d{2}-\\d{2}$/));\n\nconst addDays = (day: string, days: number) =>\n DateTime.formatIsoDateUtc(DateTime.add(DateTime.makeUnsafe(day), { days }));\n\n/** The calendar day of `time` in the property's time zone. */\nconst dayIn = (zone: DateTime.TimeZone, time: DateTime.DateTime) =>\n DateTime.formatIsoDate(DateTime.setZone(time, zone));\n\nconst minDay = (a: string, b: string) => (a < b ? a : b);\n\n/** Reports cover whole days, so backfill ends the day before the cutoff. */\nconst lastBackfillDay = (zone: DateTime.TimeZone, cutoff: DateTime.Utc) =>\n addDays(dayIn(zone, cutoff), -1);\n\n// The API rejects earlier days.\nconst firstDay = \"2015-08-14\";\n\nconst startDay = (config: ReportConfig, end: string) =>\n Option.match(config.startDate, {\n onNone: () =>\n Effect.succeed(\n DateTime.formatIsoDateUtc(DateTime.add(DateTime.makeUnsafe(end), { years: -2 })),\n ),\n onSome: (startDate) =>\n Schema.decodeUnknownEffect(Day)(startDate).pipe(\n Effect.mapError(\n (cause) => new ConnectorError({ message: \"GA_START_DATE must be YYYY-MM-DD\", cause }),\n ),\n ),\n }).pipe(Effect.map((day) => (day < firstDay ? firstDay : day)));\n\nconst cursorError = (cause: Schema.SchemaError) =>\n new ConnectorError({ message: \"Invalid Google Analytics cursor\", cause });\n\nconst PageCursorSchema = Schema.fromJsonString(Schema.Struct({ day: Day, offset: Schema.Int }));\n\n/**\n * `through` is the last day fully read. `pending` is set while a run is split:\n * the day and offset to go on from, and the last day of that run.\n */\nconst ChangesCursorSchema = Schema.Struct({\n through: Day,\n pending: Schema.optional(Schema.Struct({ day: Day, end: Day, offset: Schema.Int })),\n});\n\ntype ChangesCursor = Schema.Schema.Type<typeof ChangesCursorSchema>;\n\n// The first cursor is the backfill cutoff, a plain ISO time.\nconst ChangesCursorCodec = Schema.Union([\n Schema.fromJsonString(ChangesCursorSchema),\n Schema.DateTimeUtcFromString,\n]);\n\nexport const reportCheck = <Row extends object>(\n client: GoogleAnalyticsClientService,\n config: ReportConfig,\n spec: ReportSpec<Row>,\n) =>\n Effect.gen(function* () {\n const zone = yield* client.timeZone;\n const yesterday = addDays(dayIn(zone, yield* DateTime.now), -1);\n yield* startDay(config, yesterday);\n yield* readDay(client, config.propertyId, spec, { day: yesterday, offset: 0, limit: 1 });\n });\n\nexport const reportBackfill = <Row extends object>(\n client: GoogleAnalyticsClientService,\n config: ReportConfig,\n spec: ReportSpec<Row>,\n) =>\n Fetch.page({\n pageCursor: Cursor.string(),\n cutoff: Cursor.isoDateTime(),\n fetch: ({ pageCursor, cutoff }) =>\n Effect.gen(function* () {\n const zone = yield* client.timeZone;\n const cutoffTime = yield* Schema.decodeUnknownEffect(Schema.DateTimeUtcFromString)(\n String(cutoff),\n ).pipe(Effect.mapError(cursorError));\n const end = lastBackfillDay(zone, cutoffTime);\n const at =\n pageCursor === undefined\n ? { day: yield* startDay(config, end), offset: 0 }\n : yield* Schema.decodeUnknownEffect(PageCursorSchema)(String(pageCursor)).pipe(\n Effect.mapError(cursorError),\n );\n if (at.day > end) return { rows: [], hasMore: false };\n\n const page = yield* readDay(client, config.propertyId, spec, at);\n const next = Option.match(page.next, {\n onSome: (offset) => ({ day: at.day, offset }),\n onNone: () => ({ day: addDays(at.day, 1), offset: 0 }),\n });\n if (next.day > end) return { rows: page.rows, hasMore: false };\n return {\n rows: page.rows,\n hasMore: true,\n nextPageCursor: yield* Schema.encodeEffect(PageCursorSchema)(next).pipe(\n Effect.mapError(cursorError),\n ),\n };\n }),\n });\n\n/**\n * Reads the last `lookbackDays` again on every run, since Google keeps\n * updating recent days. After downtime it also reads the days it missed.\n */\nexport const reportChanges = <Row extends object>(\n client: GoogleAnalyticsClientService,\n config: ReportConfig,\n spec: ReportSpec<Row>,\n) =>\n Fetch.changes({\n cursor: Cursor.string(),\n interval: changesInterval,\n fetch: ({ cursor }) =>\n Effect.gen(function* () {\n const zone = yield* client.timeZone;\n const decoded = yield* Schema.decodeUnknownEffect(ChangesCursorCodec)(String(cursor)).pipe(\n Effect.mapError(cursorError),\n );\n const state: ChangesCursor = DateTime.isDateTime(decoded)\n ? { through: lastBackfillDay(zone, decoded) }\n : decoded;\n const yesterday = addDays(dayIn(zone, yield* DateTime.now), -1);\n const window = state.pending ?? {\n day: minDay(addDays(state.through, 1), addDays(yesterday, 1 - config.lookbackDays)),\n end: yesterday,\n offset: 0,\n };\n\n const page = yield* readDay(client, config.propertyId, spec, window);\n const pending = Option.match(page.next, {\n onSome: (offset) => Option.some({ ...window, offset }),\n onNone: () =>\n window.day < window.end\n ? Option.some({ ...window, day: addDays(window.day, 1), offset: 0 })\n : Option.none(),\n });\n const next: ChangesCursor = Option.match(pending, {\n onSome: (pending) => ({ through: state.through, pending }),\n onNone: () => ({ through: window.end }),\n });\n return {\n rows: page.rows,\n cursor: yield* Schema.encodeEffect(Schema.fromJsonString(ChangesCursorSchema))(next).pipe(\n Effect.mapError(cursorError),\n ),\n hasMore: Option.isSome(pending),\n };\n }),\n });\n","import type { ConnectorError, Identified } from \"@useairfoil/connector-kit\";\nimport type { Effect, Schema } from \"effect\";\n\nimport type { GoogleAnalyticsClientService } from \"../../client/client\";\nimport type { GoogleAnalyticsConfig } from \"../../manifest\";\nimport type { ReportSpec } from \"./spec\";\n\nimport { reportBackfill, reportChanges, reportCheck } from \"./sources\";\n\n/** What `Resource.entity` takes for a report. */\nexport type ReportEntity<Row extends object> = {\n readonly name: string;\n readonly rowSchema: Schema.Decoder<Row> & Identified<\"id\">;\n readonly version: \"version\";\n readonly check: Effect.Effect<void, ConnectorError>;\n readonly backfill: ReturnType<typeof reportBackfill<Row>>;\n readonly changes: ReturnType<typeof reportChanges<Row>>;\n};\n\nexport const reportEntity = <Row extends object>(\n client: GoogleAnalyticsClientService,\n config: GoogleAnalyticsConfig,\n spec: ReportSpec<Row>,\n): ReportEntity<Row> => ({\n name: spec.name,\n rowSchema: spec.rowSchema,\n version: \"version\",\n check: reportCheck(client, config, spec),\n backfill: reportBackfill(client, config, spec),\n changes: reportChanges(client, config, spec),\n});\n","import { Iceberg } from \"@useairfoil/connector-kit\";\nimport { Schema } from \"effect\";\n\nimport { amount, common, count, currencyCode, dimension } from \"./shared\";\n\n// User counts are distinct users, so they don't add up across rows or days.\n\nexport const DailyOverviewSchema = Schema.Struct({\n ...common,\n active_users: count(30, \"Users who engaged on the day.\"),\n new_users: count(31, \"Users who came for the first time.\"),\n total_users: count(32, \"Users who logged any event.\"),\n sessions: count(33, \"Sessions that started.\"),\n engaged_sessions: count(\n 34,\n \"Sessions that lasted 10 seconds or more, or had a key event or 2 page views.\",\n ),\n screen_page_views: count(35, \"Page and screen views.\"),\n event_count: count(36, \"Events.\"),\n key_events: amount(37, \"Key events. Can be fractional after attribution.\"),\n total_revenue: amount(38, \"Revenue from purchases, subscriptions, and ads.\"),\n user_engagement_duration: amount(39, \"Seconds the site or app was in the foreground.\"),\n currency_code: currencyCode,\n})\n .annotate({ description: \"Google Analytics totals per day.\" })\n .pipe(Iceberg.identifier(\"id\"));\n\nexport type DailyOverview = Schema.Schema.Type<typeof DailyOverviewSchema>;\n\nexport const TrafficSourceSchema = Schema.Struct({\n ...common,\n session_source: dimension(10, \"Source of the session, such as google.\"),\n session_medium: dimension(11, \"Medium of the session, such as organic.\"),\n session_campaign_name: dimension(12, \"Campaign of the session.\"),\n session_default_channel_group: dimension(13, \"Default channel group of the session.\"),\n sessions: count(30, \"Sessions that started.\"),\n engaged_sessions: count(31, \"Engaged sessions.\"),\n active_users: count(32, \"Users who engaged.\"),\n new_users: count(33, \"Users who came for the first time.\"),\n screen_page_views: count(34, \"Page and screen views.\"),\n event_count: count(35, \"Events.\"),\n key_events: amount(36, \"Key events.\"),\n total_revenue: amount(37, \"Revenue from purchases, subscriptions, and ads.\"),\n user_engagement_duration: amount(38, \"Seconds the site or app was in the foreground.\"),\n currency_code: currencyCode,\n})\n .annotate({ description: \"Google Analytics sessions per day and traffic source.\" })\n .pipe(Iceberg.identifier(\"id\"));\n\nexport type TrafficSource = Schema.Schema.Type<typeof TrafficSourceSchema>;\n\nexport const UserAcquisitionSchema = Schema.Struct({\n ...common,\n first_user_source: dimension(10, \"Source that first brought the user.\"),\n first_user_medium: dimension(11, \"Medium that first brought the user.\"),\n first_user_campaign_name: dimension(12, \"Campaign that first brought the user.\"),\n first_user_default_channel_group: dimension(\n 13,\n \"Default channel group that first brought the user.\",\n ),\n new_users: count(30, \"Users who came for the first time.\"),\n active_users: count(31, \"Users who engaged.\"),\n sessions: count(32, \"Sessions that started.\"),\n engaged_sessions: count(33, \"Engaged sessions.\"),\n key_events: amount(34, \"Key events.\"),\n total_revenue: amount(35, \"Revenue from purchases, subscriptions, and ads.\"),\n currency_code: currencyCode,\n})\n .annotate({ description: \"Google Analytics users per day and first traffic source.\" })\n .pipe(Iceberg.identifier(\"id\"));\n\nexport type UserAcquisition = Schema.Schema.Type<typeof UserAcquisitionSchema>;\n\nexport const PageSchema = Schema.Struct({\n ...common,\n host_name: dimension(10, \"Host name, such as www.example.com.\"),\n page_path: dimension(11, \"Page path, without the query string.\"),\n page_title: dimension(12, \"Page title.\"),\n screen_page_views: count(30, \"Page and screen views.\"),\n active_users: count(31, \"Users who engaged.\"),\n event_count: count(32, \"Events.\"),\n key_events: amount(33, \"Key events.\"),\n user_engagement_duration: amount(34, \"Seconds the page was in the foreground.\"),\n})\n .annotate({ description: \"Google Analytics page views per day and page.\" })\n .pipe(Iceberg.identifier(\"id\"));\n\nexport type Page = Schema.Schema.Type<typeof PageSchema>;\n\nexport const LandingPageSchema = Schema.Struct({\n ...common,\n landing_page: dimension(10, \"First page path of the session, without the query string.\"),\n sessions: count(30, \"Sessions that started.\"),\n engaged_sessions: count(31, \"Engaged sessions.\"),\n active_users: count(32, \"Users who engaged.\"),\n new_users: count(33, \"Users who came for the first time.\"),\n key_events: amount(34, \"Key events.\"),\n total_revenue: amount(35, \"Revenue from purchases, subscriptions, and ads.\"),\n currency_code: currencyCode,\n})\n .annotate({ description: \"Google Analytics sessions per day and landing page.\" })\n .pipe(Iceberg.identifier(\"id\"));\n\nexport type LandingPage = Schema.Schema.Type<typeof LandingPageSchema>;\n\nexport const EventSchema = Schema.Struct({\n ...common,\n event_name: dimension(10, \"Event name, such as page_view.\"),\n event_count: count(30, \"Events.\"),\n total_users: count(31, \"Users who logged the event.\"),\n event_value: amount(32, \"Sum of the event's value parameter.\"),\n key_events: amount(33, \"Key events.\"),\n})\n .annotate({ description: \"Google Analytics events per day and event name.\" })\n .pipe(Iceberg.identifier(\"id\"));\n\nexport type Event = Schema.Schema.Type<typeof EventSchema>;\n\nexport const LocationSchema = Schema.Struct({\n ...common,\n country: dimension(10, \"Country of the user.\"),\n region: dimension(11, \"Region of the user.\"),\n city: dimension(12, \"City of the user.\"),\n active_users: count(30, \"Users who engaged.\"),\n new_users: count(31, \"Users who came for the first time.\"),\n sessions: count(32, \"Sessions that started.\"),\n engaged_sessions: count(33, \"Engaged sessions.\"),\n key_events: amount(34, \"Key events.\"),\n total_revenue: amount(35, \"Revenue from purchases, subscriptions, and ads.\"),\n currency_code: currencyCode,\n})\n .annotate({ description: \"Google Analytics users per day and location.\" })\n .pipe(Iceberg.identifier(\"id\"));\n\nexport type Location = Schema.Schema.Type<typeof LocationSchema>;\n\nexport const DeviceSchema = Schema.Struct({\n ...common,\n device_category: dimension(10, \"Device category: desktop, mobile, or tablet.\"),\n platform: dimension(11, \"Platform: web, iOS, or Android.\"),\n operating_system: dimension(12, \"Operating system.\"),\n browser: dimension(13, \"Browser.\"),\n active_users: count(30, \"Users who engaged.\"),\n new_users: count(31, \"Users who came for the first time.\"),\n sessions: count(32, \"Sessions that started.\"),\n engaged_sessions: count(33, \"Engaged sessions.\"),\n screen_page_views: count(34, \"Page and screen views.\"),\n key_events: amount(35, \"Key events.\"),\n})\n .annotate({ description: \"Google Analytics users per day and device.\" })\n .pipe(Iceberg.identifier(\"id\"));\n\nexport type Device = Schema.Schema.Type<typeof DeviceSchema>;\n\nexport const EcommerceItemSchema = Schema.Struct({\n ...common,\n item_id: dimension(10, \"Item ID.\"),\n item_name: dimension(11, \"Item name.\"),\n item_brand: dimension(12, \"Item brand.\"),\n item_category: dimension(13, \"First item category.\"),\n items_viewed: count(30, \"Units viewed.\"),\n items_added_to_cart: count(31, \"Units added to carts.\"),\n items_purchased: count(32, \"Units purchased.\"),\n item_revenue: amount(33, \"Revenue from the item, without tax and shipping.\"),\n currency_code: currencyCode,\n})\n .annotate({\n description: \"Google Analytics ecommerce items per day. Empty without ecommerce events.\",\n })\n .pipe(Iceberg.identifier(\"id\"));\n\nexport type EcommerceItem = Schema.Schema.Type<typeof EcommerceItemSchema>;\n","import type { ReportSpec } from \"./spec\";\n\nimport {\n type DailyOverview,\n DailyOverviewSchema,\n type Device,\n DeviceSchema,\n type EcommerceItem,\n EcommerceItemSchema,\n type Event,\n EventSchema,\n type LandingPage,\n LandingPageSchema,\n type Location,\n LocationSchema,\n type Page,\n PageSchema,\n type TrafficSource,\n TrafficSourceSchema,\n type UserAcquisition,\n UserAcquisitionSchema,\n} from \"../../schemas/reports\";\n\n// Every pair here is one Google shows together in its own reports. Check new\n// ones with `checkCompatibility` first.\n\nexport const dailyOverview: ReportSpec<DailyOverview> = {\n name: \"daily_overview\",\n dimensions: {},\n metrics: {\n active_users: \"activeUsers\",\n new_users: \"newUsers\",\n total_users: \"totalUsers\",\n sessions: \"sessions\",\n engaged_sessions: \"engagedSessions\",\n screen_page_views: \"screenPageViews\",\n event_count: \"eventCount\",\n key_events: \"keyEvents\",\n total_revenue: \"totalRevenue\",\n user_engagement_duration: \"userEngagementDuration\",\n },\n rowSchema: DailyOverviewSchema,\n};\n\nexport const trafficSources: ReportSpec<TrafficSource> = {\n name: \"traffic_sources\",\n dimensions: {\n session_source: \"sessionSource\",\n session_medium: \"sessionMedium\",\n session_campaign_name: \"sessionCampaignName\",\n session_default_channel_group: \"sessionDefaultChannelGroup\",\n },\n metrics: {\n sessions: \"sessions\",\n engaged_sessions: \"engagedSessions\",\n active_users: \"activeUsers\",\n new_users: \"newUsers\",\n screen_page_views: \"screenPageViews\",\n event_count: \"eventCount\",\n key_events: \"keyEvents\",\n total_revenue: \"totalRevenue\",\n user_engagement_duration: \"userEngagementDuration\",\n },\n rowSchema: TrafficSourceSchema,\n};\n\nexport const userAcquisition: ReportSpec<UserAcquisition> = {\n name: \"user_acquisition\",\n dimensions: {\n first_user_source: \"firstUserSource\",\n first_user_medium: \"firstUserMedium\",\n first_user_campaign_name: \"firstUserCampaignName\",\n first_user_default_channel_group: \"firstUserDefaultChannelGroup\",\n },\n metrics: {\n new_users: \"newUsers\",\n active_users: \"activeUsers\",\n sessions: \"sessions\",\n engaged_sessions: \"engagedSessions\",\n key_events: \"keyEvents\",\n total_revenue: \"totalRevenue\",\n },\n rowSchema: UserAcquisitionSchema,\n};\n\nexport const pages: ReportSpec<Page> = {\n name: \"pages\",\n dimensions: { host_name: \"hostName\", page_path: \"pagePath\", page_title: \"pageTitle\" },\n metrics: {\n screen_page_views: \"screenPageViews\",\n active_users: \"activeUsers\",\n event_count: \"eventCount\",\n key_events: \"keyEvents\",\n user_engagement_duration: \"userEngagementDuration\",\n },\n rowSchema: PageSchema,\n};\n\nexport const landingPages: ReportSpec<LandingPage> = {\n name: \"landing_pages\",\n dimensions: { landing_page: \"landingPage\" },\n metrics: {\n sessions: \"sessions\",\n engaged_sessions: \"engagedSessions\",\n active_users: \"activeUsers\",\n new_users: \"newUsers\",\n key_events: \"keyEvents\",\n total_revenue: \"totalRevenue\",\n },\n rowSchema: LandingPageSchema,\n};\n\nexport const events: ReportSpec<Event> = {\n name: \"events\",\n dimensions: { event_name: \"eventName\" },\n metrics: {\n event_count: \"eventCount\",\n total_users: \"totalUsers\",\n event_value: \"eventValue\",\n key_events: \"keyEvents\",\n },\n rowSchema: EventSchema,\n};\n\nexport const locations: ReportSpec<Location> = {\n name: \"locations\",\n dimensions: { country: \"country\", region: \"region\", city: \"city\" },\n metrics: {\n active_users: \"activeUsers\",\n new_users: \"newUsers\",\n sessions: \"sessions\",\n engaged_sessions: \"engagedSessions\",\n key_events: \"keyEvents\",\n total_revenue: \"totalRevenue\",\n },\n rowSchema: LocationSchema,\n};\n\nexport const devices: ReportSpec<Device> = {\n name: \"devices\",\n dimensions: {\n device_category: \"deviceCategory\",\n platform: \"platform\",\n operating_system: \"operatingSystem\",\n browser: \"browser\",\n },\n metrics: {\n active_users: \"activeUsers\",\n new_users: \"newUsers\",\n sessions: \"sessions\",\n engaged_sessions: \"engagedSessions\",\n screen_page_views: \"screenPageViews\",\n key_events: \"keyEvents\",\n },\n rowSchema: DeviceSchema,\n};\n\nexport const ecommerceItems: ReportSpec<EcommerceItem> = {\n name: \"ecommerce_items\",\n dimensions: {\n item_id: \"itemId\",\n item_name: \"itemName\",\n item_brand: \"itemBrand\",\n item_category: \"itemCategory\",\n },\n metrics: {\n items_viewed: \"itemsViewed\",\n items_added_to_cart: \"itemsAddedToCart\",\n items_purchased: \"itemsPurchased\",\n item_revenue: \"itemRevenue\",\n },\n rowSchema: EcommerceItemSchema,\n};\n","import { Connector, Resource } from \"@useairfoil/connector-kit\";\nimport { Config, Context, Effect, Layer } from \"effect\";\n\nimport type { GoogleAnalyticsConfig } from \"./manifest\";\n\nimport * as GoogleAnalyticsClient from \"./client/client\";\nimport { reportEntity } from \"./resources/reports/resource\";\nimport {\n dailyOverview,\n devices,\n ecommerceItems,\n events,\n landingPages,\n locations,\n pages,\n trafficSources,\n userAcquisition,\n} from \"./resources/reports/specs\";\nexport { GoogleAnalyticsConfigDef, manifest } from \"./manifest\";\nexport type { GoogleAnalyticsConfig } from \"./manifest\";\n\nexport const make = Effect.fnUntraced(function* (config: GoogleAnalyticsConfig) {\n const client = yield* GoogleAnalyticsClient.GoogleAnalyticsClient;\n\n return Connector.define({\n name: \"producer-google-analytics\",\n title: \"Google Analytics\",\n resources: [\n Resource.entity(reportEntity(client, config, dailyOverview)),\n Resource.entity(reportEntity(client, config, trafficSources)),\n Resource.entity(reportEntity(client, config, userAcquisition)),\n Resource.entity(reportEntity(client, config, pages)),\n Resource.entity(reportEntity(client, config, landingPages)),\n Resource.entity(reportEntity(client, config, events)),\n Resource.entity(reportEntity(client, config, locations)),\n Resource.entity(reportEntity(client, config, devices)),\n Resource.entity(reportEntity(client, config, ecommerceItems)),\n ],\n });\n});\n\nexport type GoogleAnalyticsConnectorRuntime = Effect.Success<ReturnType<typeof make>>;\n\nexport class GoogleAnalyticsConnector extends Context.Service<\n GoogleAnalyticsConnector,\n GoogleAnalyticsConnectorRuntime\n>()(\"@useairfoil/producer-google-analytics/GoogleAnalyticsConnector\") {}\n\nexport const layer = (config: GoogleAnalyticsConfig) =>\n Layer.effect(GoogleAnalyticsConnector)(\n make(config).pipe(Effect.annotateLogs({ component: \"google-analytics\" })),\n ).pipe(Layer.provide(GoogleAnalyticsClient.layer(config)));\n\nexport const layerConfig = (config: Config.Wrap<GoogleAnalyticsConfig>) =>\n Layer.unwrap(Config.unwrap(config).pipe(Effect.map(layer)));\n"],"mappings":"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"}