Skip to content

Commit bb02f04

Browse files
committed
Add runtime validation of the api responses from cloudflare
1 parent 064dc2b commit bb02f04

File tree

14 files changed

+11051
-380
lines changed

14 files changed

+11051
-380
lines changed
File renamed without changes.

.gitignore

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
node_modules
22
.env
33
.dev.vars
4-
.wrangler
5-
worker-configuration.d.ts
4+
.wrangler

.vscode/settings.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
{
22
"files.associations": {
33
"wrangler.json": "jsonc"
4-
}
4+
},
5+
"typescript.tsdk": "node_modules/typescript/lib",
6+
"typescript.experimental.useTsgo": true,
7+
"typescript.native-preview.trace.server": "messages"
58
}

datadog-dashboard.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
{
2626
"name": "query1",
2727
"data_source": "metrics",
28-
"query": "sum:cloudflare.containers.instances.total.max{*}"
28+
"query": "sum:cloudflare.containers.instances.max{$application_name}"
2929
}
3030
],
3131
"response_format": "timeseries",
@@ -68,27 +68,27 @@
6868
{
6969
"name": "query1",
7070
"data_source": "metrics",
71-
"query": "sum:cloudflare.containers.instances.total.healthy{*}"
71+
"query": "sum:cloudflare.containers.instances.healthy{$application_name}"
7272
},
7373
{
7474
"name": "query2",
7575
"data_source": "metrics",
76-
"query": "sum:cloudflare.containers.instances.total.starting{*}"
76+
"query": "sum:cloudflare.containers.instances.starting{$application_name}"
7777
},
7878
{
7979
"name": "query3",
8080
"data_source": "metrics",
81-
"query": "sum:cloudflare.containers.instances.total.scheduling{*}"
81+
"query": "sum:cloudflare.containers.instances.scheduling{$application_name}"
8282
},
8383
{
8484
"name": "query4",
8585
"data_source": "metrics",
86-
"query": "sum:cloudflare.containers.instances.total.failed{*}"
86+
"query": "sum:cloudflare.containers.instances.failed{$application_name}"
8787
},
8888
{
8989
"name": "query5",
9090
"data_source": "metrics",
91-
"query": "sum:cloudflare.containers.instances.total.stopped{*}"
91+
"query": "sum:cloudflare.containers.instances.stopped{$application_name}"
9292
}
9393
],
9494
"response_format": "timeseries",

package-lock.json

Lines changed: 61 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,9 @@
2929
"typescript": "^5.0.4",
3030
"vitest": "~3.0.0",
3131
"wrangler": "^4.53.0"
32+
},
33+
"dependencies": {
34+
"p-all": "^5.0.1",
35+
"zod": "^4.1.13"
3236
}
3337
}

src/api/cloudflare.ts

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,32 @@
1-
import type {
1+
import { z } from "zod/v4";
2+
import {
23
CloudchamberMetricsResponse,
34
Container,
4-
MetricsGroup,
5+
type MetricsGroup,
56
} from "../types";
67

78
export interface CloudflareApiConfig {
89
accountId: string;
910
apiToken: string;
1011
}
1112

12-
export interface ContainersListResponse {
13-
success: boolean;
14-
result: Container[];
15-
errors: unknown[];
16-
messages: unknown[];
17-
}
13+
const ContainersListResponseSchema = z.object({
14+
success: z.boolean(),
15+
result: z.array(Container),
16+
errors: z.array(z.unknown()).optional(),
17+
messages: z.array(z.unknown()).optional(),
18+
});
1819

1920
export interface GraphQLResponse<T> {
2021
data: T | null;
2122
errors: { message: string; path: string[] | null }[] | null;
2223
}
2324

2425
const CONTAINERS_METRICS_QUERY = `
25-
query GetCloudchamberMetrics($accountTag: string!, $datetimeStart: Time, $datetimeEnd: Time, $applicationId: string) {
26+
query GetCloudchamberMetrics($accountTag: string!, $datetimeStart: Time, $datetimeEnd: Time, $applicationIds: [string!]) {
2627
viewer {
2728
accounts(filter: {accountTag: $accountTag}) {
28-
cloudchamberMetricsAdaptiveGroups(limit: 10000, filter: {applicationId: $applicationId, datetimeMinute_geq: $datetimeStart, datetimeMinute_leq: $datetimeEnd}) {
29+
cloudchamberMetricsAdaptiveGroups(limit: 10000, filter: {applicationId_in: $applicationIds, datetimeMinute_geq: $datetimeStart, datetimeMinute_leq: $datetimeEnd}) {
2930
avg {
3031
memory
3132
cpuLoad
@@ -91,7 +92,7 @@ export class CloudflareApi {
9192
);
9293
}
9394

94-
const data = (await response.json()) as ContainersListResponse;
95+
const data = ContainersListResponseSchema.parse(await response.json());
9596

9697
if (!data.success) {
9798
throw new Error(`API error: ${JSON.stringify(data.errors)}`);
@@ -101,13 +102,13 @@ export class CloudflareApi {
101102
}
102103

103104
/**
104-
* Get metrics for a specific container application
105-
* @param applicationId - The container application ID
105+
* Get metrics for multiple container applications
106+
* @param applicationIds - Array of container application IDs
106107
* @param startTime - Start of the time range (defaults to 5 minutes ago)
107108
* @param endTime - End of the time range (defaults to now)
108109
*/
109110
async getContainerMetrics(
110-
applicationId: string,
111+
applicationIds: string[],
111112
startTime?: Date,
112113
endTime?: Date,
113114
): Promise<MetricsGroup[]> {
@@ -118,7 +119,7 @@ export class CloudflareApi {
118119
accountTag: this.config.accountId,
119120
datetimeStart: start.toISOString(),
120121
datetimeEnd: now.toISOString(),
121-
applicationId,
122+
applicationIds,
122123
};
123124

124125
const response = await fetch(this.graphqlUrl, {
@@ -136,16 +137,17 @@ export class CloudflareApi {
136137
);
137138
}
138139

139-
const data = (await response.json()) as CloudchamberMetricsResponse;
140+
const data = CloudchamberMetricsResponse.parse(await response.json());
140141

141142
if (data.errors && data.errors.length > 0) {
142143
throw new Error(`GraphQL errors: ${JSON.stringify(data.errors)}`);
143144
}
144145

145146
const groups =
146147
data.data?.viewer?.accounts?.[0]?.cloudchamberMetricsAdaptiveGroups ?? [];
148+
147149
console.log("Fetched container metrics", {
148-
applicationId,
150+
applicationCount: applicationIds.length,
149151
groupCount: groups.length,
150152
});
151153

src/env.d.ts

Lines changed: 0 additions & 7 deletions
This file was deleted.

src/index.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,5 @@
11
export { MetricsExporterWorkflow } from "./workflow";
22

3-
export interface Env {
4-
CLOUDFLARE_API_TOKEN: string;
5-
CLOUDFLARE_ACCOUNT_ID: string;
6-
DATADOG_API_KEY: string;
7-
DATADOG_SITE?: string;
8-
METRICS_WORKFLOW: Workflow;
9-
}
10-
113
const REQUIRED_ENV_VARS = [
124
"CLOUDFLARE_API_TOKEN",
135
"CLOUDFLARE_ACCOUNT_ID",

0 commit comments

Comments
 (0)