Skip to content

Commit 4107f57

Browse files
feat: support analytics engine in local/remote dev (#6666)
This adds "support" for analytics engine datasets for `wrangler dev`. Specifically, it simply mocks the AE bindings so that they exist while developing (and don't throw when accessed). This does NOT add support in Pages, though we very well could do so in a similar way in a followup.
1 parent 67711c2 commit 4107f57

File tree

15 files changed

+185
-2
lines changed

15 files changed

+185
-2
lines changed

.changeset/long-rules-wait.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
"wrangler": minor
3+
---
4+
5+
feat: support analytics engine in local/remote dev
6+
7+
This adds "support" for analytics engine datasets for `wrangler dev`. Specifically, it simply mocks the AE bindings so that they exist while developing (and don't throw when accessed).
8+
9+
This does NOT add support in Pages, though we very well could do so in a similar way in a followup.

packages/wrangler/e2e/dev.test.ts

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,104 @@ describe("writes debug logs to hidden file", () => {
602602
});
603603
});
604604

605+
describe("analytics engine", () => {
606+
describe.each([
607+
{ cmd: "wrangler dev" },
608+
{ cmd: "wrangler dev --x-dev-env" },
609+
{ cmd: "wrangler dev --remote" },
610+
{ cmd: "wrangler dev --remote --x-dev-env" },
611+
])("mock analytics engine datasets: $cmd", ({ cmd }) => {
612+
describe("module worker", () => {
613+
it("analytics engine datasets are mocked in dev", async () => {
614+
const helper = new WranglerE2ETestHelper();
615+
await helper.seed({
616+
"wrangler.toml": dedent`
617+
name = "${workerName}"
618+
main = "src/index.ts"
619+
compatibility_date = "2024-08-08"
620+
621+
[[analytics_engine_datasets]]
622+
binding = "ANALYTICS_BINDING"
623+
dataset = "ANALYTICS_DATASET"
624+
`,
625+
"src/index.ts": dedent`
626+
export default {
627+
fetch(request, env) {
628+
// let's make an analytics call
629+
env.ANALYTICS_BINDING.writeDataPoint({
630+
'blobs': ["Seattle", "USA", "pro_sensor_9000"], // City, State
631+
'doubles': [25, 0.5],
632+
'indexes': ["a3cd45"]
633+
});
634+
// and return a response
635+
return new Response("successfully wrote datapoint from module worker");
636+
}
637+
}`,
638+
"package.json": dedent`
639+
{
640+
"name": "worker",
641+
"version": "0.0.0",
642+
"private": true
643+
}
644+
`,
645+
});
646+
const worker = helper.runLongLived(cmd);
647+
648+
const { url } = await worker.waitForReady();
649+
650+
const text = await fetchText(url);
651+
expect(text).toContain(
652+
`successfully wrote datapoint from module worker`
653+
);
654+
});
655+
});
656+
657+
describe("service worker", async () => {
658+
it("analytics engine datasets are mocked in dev", async () => {
659+
const helper = new WranglerE2ETestHelper();
660+
await helper.seed({
661+
"wrangler.toml": dedent`
662+
name = "${workerName}"
663+
main = "src/index.ts"
664+
compatibility_date = "2024-08-08"
665+
666+
[[analytics_engine_datasets]]
667+
binding = "ANALYTICS_BINDING"
668+
dataset = "ANALYTICS_DATASET"
669+
`,
670+
"src/index.ts": dedent`
671+
addEventListener("fetch", (event) => {
672+
// let's make an analytics call
673+
ANALYTICS_BINDING.writeDataPoint({
674+
blobs: ["Seattle", "USA", "pro_sensor_9000"], // City, State
675+
doubles: [25, 0.5],
676+
indexes: ["a3cd45"],
677+
});
678+
// and return a response
679+
event.respondWith(new Response("successfully wrote datapoint from service worker"));
680+
});
681+
`,
682+
"package.json": dedent`
683+
{
684+
"name": "worker",
685+
"version": "0.0.0",
686+
"private": true
687+
}
688+
`,
689+
});
690+
const worker = helper.runLongLived(cmd);
691+
692+
const { url } = await worker.waitForReady();
693+
694+
const text = await fetchText(url);
695+
expect(text).toContain(
696+
`successfully wrote datapoint from service worker`
697+
);
698+
});
699+
});
700+
});
701+
});
702+
605703
describe("zone selection", () => {
606704
it("defaults to a workers.dev preview", async () => {
607705
const helper = new WranglerE2ETestHelper();

packages/wrangler/src/__tests__/navigator-user-agent.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ describe("defineNavigatorUserAgent is respected", () => {
114114
additionalModules: [],
115115
moduleCollector: noopModuleCollector,
116116
serveLegacyAssetsFromWorker: false,
117+
mockAnalyticsEngineDatasets: [],
117118
doBindings: [],
118119
define: {},
119120
alias: {},
@@ -176,6 +177,7 @@ describe("defineNavigatorUserAgent is respected", () => {
176177
doBindings: [],
177178
define: {},
178179
alias: {},
180+
mockAnalyticsEngineDatasets: [],
179181
checkFetch: false,
180182
targetConsumer: "deploy",
181183
local: true,

packages/wrangler/src/api/startDevWorker/BundlerController.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ export class BundlerController extends Controller<BundlerControllerEventMap> {
115115
nodejsCompatMode: config.build.nodejsCompatMode,
116116
define: config.build.define,
117117
checkFetch: true,
118+
mockAnalyticsEngineDatasets:
119+
bindings.analytics_engine_datasets ?? [],
118120
alias: config.build.alias,
119121
legacyAssets: config.legacy?.legacyAssets,
120122
// enable the cache when publishing
@@ -227,6 +229,7 @@ export class BundlerController extends Controller<BundlerControllerEventMap> {
227229
noBundle: !config.build?.bundle,
228230
findAdditionalModules: config.build?.findAdditionalModules,
229231
durableObjects: bindings?.durable_objects ?? { bindings: [] },
232+
mockAnalyticsEngineDatasets: bindings.analytics_engine_datasets ?? [],
230233
local: !config.dev?.remote,
231234
// startDevWorker only applies to "dev"
232235
targetConsumer: "dev",

packages/wrangler/src/deploy/deploy.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,8 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
551551
checkFetch: false,
552552
alias: config.alias,
553553
legacyAssets: config.legacy_assets,
554+
// We do not mock AE datasets when deploying
555+
mockAnalyticsEngineDatasets: [],
554556
// enable the cache when publishing
555557
bypassAssetCache: false,
556558
// We want to know if the build is for development or publishing

packages/wrangler/src/deployment-bundle/bundle.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ export type BundleOptions = {
8686
define: Config["define"];
8787
alias: Config["alias"];
8888
checkFetch: boolean;
89+
mockAnalyticsEngineDatasets: Config["analytics_engine_datasets"];
8990
targetConsumer: "dev" | "deploy";
9091
testScheduled?: boolean;
9192
inject?: string[];
@@ -122,6 +123,7 @@ export async function bundleWorker(
122123
alias,
123124
define,
124125
checkFetch,
126+
mockAnalyticsEngineDatasets,
125127
legacyAssets,
126128
bypassAssetCache,
127129
targetConsumer,
@@ -148,6 +150,21 @@ export async function bundleWorker(
148150
// At this point, we take the opportunity to "wrap" the worker with middleware.
149151
const middlewareToLoad: MiddlewareLoader[] = [];
150152

153+
if (
154+
targetConsumer === "dev" &&
155+
mockAnalyticsEngineDatasets &&
156+
mockAnalyticsEngineDatasets.length > 0
157+
) {
158+
middlewareToLoad.push({
159+
name: "mock-analytics-engine",
160+
path: "templates/middleware/middleware-mock-analytics-engine.ts",
161+
config: {
162+
bindings: mockAnalyticsEngineDatasets.map(({ binding }) => binding),
163+
},
164+
supports: ["modules", "service-worker"],
165+
});
166+
}
167+
151168
if (
152169
targetConsumer === "dev" &&
153170
!process.env.WRANGLER_DISABLE_REQUEST_BODY_DRAINING

packages/wrangler/src/dev/dev.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,7 @@ function DevSession(props: DevSessionProps) {
606606
alias: props.alias,
607607
noBundle: props.noBundle,
608608
findAdditionalModules: props.findAdditionalModules,
609+
mockAnalyticsEngineDatasets: props.bindings.analytics_engine_datasets ?? [],
609610
legacyAssets: props.legacyAssetsConfig,
610611
durableObjects: props.bindings.durable_objects || { bindings: [] },
611612
local: props.local,

packages/wrangler/src/dev/start-server.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ export async function startDevServer(
243243
testScheduled: props.testScheduled,
244244
local: props.local,
245245
doBindings: props.bindings.durable_objects?.bindings ?? [],
246+
mockAnalyticsEngineDatasets: props.bindings.analytics_engine_datasets ?? [],
246247
projectRoot: props.projectRoot,
247248
defineNavigatorUserAgent: isNavigatorDefined(
248249
props.compatibilityDate,
@@ -416,6 +417,7 @@ async function runEsbuild({
416417
testScheduled,
417418
local,
418419
doBindings,
420+
mockAnalyticsEngineDatasets,
419421
projectRoot,
420422
defineNavigatorUserAgent,
421423
}: {
@@ -438,6 +440,7 @@ async function runEsbuild({
438440
testScheduled?: boolean;
439441
local: boolean;
440442
doBindings: DurableObjectBindings;
443+
mockAnalyticsEngineDatasets: Config["analytics_engine_datasets"];
441444
projectRoot: string | undefined;
442445
defineNavigatorUserAgent: boolean;
443446
}): Promise<EsbuildBundle> {
@@ -475,6 +478,7 @@ async function runEsbuild({
475478
nodejsCompatMode,
476479
define,
477480
checkFetch: true,
481+
mockAnalyticsEngineDatasets,
478482
alias,
479483
legacyAssets,
480484
// disable the cache in dev

packages/wrangler/src/dev/use-esbuild.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export type EsbuildBundleProps = {
5151
noBundle: boolean;
5252
findAdditionalModules: boolean | undefined;
5353
durableObjects: Config["durable_objects"];
54+
mockAnalyticsEngineDatasets: Config["analytics_engine_datasets"];
5455
local: boolean;
5556
targetConsumer: "dev" | "deploy";
5657
testScheduled: boolean;
@@ -78,6 +79,7 @@ export function runBuild(
7879
alias,
7980
noBundle,
8081
findAdditionalModules,
82+
mockAnalyticsEngineDatasets,
8183
durableObjects,
8284
local,
8385
targetConsumer,
@@ -103,6 +105,7 @@ export function runBuild(
103105
noBundle: boolean;
104106
findAdditionalModules: boolean | undefined;
105107
durableObjects: Config["durable_objects"];
108+
mockAnalyticsEngineDatasets: Config["analytics_engine_datasets"];
106109
local: boolean;
107110
targetConsumer: "dev" | "deploy";
108111
testScheduled: boolean;
@@ -183,6 +186,7 @@ export function runBuild(
183186
alias,
184187
define,
185188
checkFetch: true,
189+
mockAnalyticsEngineDatasets,
186190
legacyAssets,
187191
// disable the cache in dev
188192
bypassAssetCache: true,
@@ -264,6 +268,7 @@ export function useEsbuild({
264268
define,
265269
noBundle,
266270
findAdditionalModules,
271+
mockAnalyticsEngineDatasets,
267272
durableObjects,
268273
local,
269274
targetConsumer,
@@ -295,6 +300,7 @@ export function useEsbuild({
295300
noBundle,
296301
findAdditionalModules,
297302
durableObjects,
303+
mockAnalyticsEngineDatasets,
298304
local,
299305
targetConsumer,
300306
testScheduled,
@@ -327,6 +333,7 @@ export function useEsbuild({
327333
alias,
328334
define,
329335
legacyAssets,
336+
mockAnalyticsEngineDatasets,
330337
durableObjects,
331338
local,
332339
targetConsumer,

packages/wrangler/src/pages/functions/buildPlugin.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ export function buildPluginFromFunctions({
106106
],
107107
serveLegacyAssetsFromWorker: false,
108108
checkFetch: local,
109+
// TODO: mock AE datasets in Pages functions for dev
110+
mockAnalyticsEngineDatasets: [],
109111
targetConsumer: local ? "dev" : "deploy",
110112
forPages: true,
111113
local,

0 commit comments

Comments
 (0)