-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.ts
179 lines (150 loc) · 4.34 KB
/
main.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
import {
CompressionTypes,
Kafka,
Message,
RecordMetadata,
} from "npm:kafkajs@2.2.4";
const HTTP_CODES = {
OK: 200,
BAD_REQUEST: 400,
INTERNAL_SERVER_ERROR: 500,
};
const username = Deno.env.get("KAFKA_USERNAME");
const password = Deno.env.get("KAFKA_PASSWORD");
const broker = Deno.env.get("KAFKA_BROKER");
const SASL_MECHANISM = "scram-sha-256";
function InstructionsPage() {
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Roll Dice</title>
<script>
function updateAction() {
const username = document.getElementById("username").value;
const form = document.querySelector("form");
form.action = "/roll/" + username;
}
</script>
</head>
<body>
<h1>Roll Dice</h1>
<form action="/roll/anonymous">
<input type="text" id="username" onchange="updateAction()" placeholder="Enter your username" />
<button type="submit">Roll the dice</a>
<form>
</body>
</html>
`.trim();
}
type DiceRollEvent = {
roll: number;
username: string;
};
if (!username) {
throw new Error("Please set the 'KAFKA_USERNAME' environment variable");
}
if (!password) {
throw new Error("Please set the 'KAFKA_PASSWORD' environment variable");
}
if (!broker) {
throw new Error("Please set the 'KAFKA_BROKER' environment variable");
}
const redpanda = new Kafka({
brokers: [broker],
ssl: {},
sasl: {
mechanism: SASL_MECHANISM,
username,
password,
},
});
const producer = redpanda.producer();
type EasyMsg = {
key: string;
value: Record<string, unknown>;
};
const sendMessage = async (
topic: string,
msg: EasyMsg,
): Promise<RecordMetadata[] | void> => {
// Messages with the same key are sent to the same topic partition for
// guaranteed ordering
await producer.connect();
const messages: Array<Message> = [
{
key: msg.key,
value: JSON.stringify(msg.value),
},
];
const recordMetadata = await producer
.send({
topic,
compression: CompressionTypes.GZIP,
messages,
})
.catch((e: Error) => {
console.error(`Unable to send message: ${e.message}`, e);
});
if (recordMetadata) return recordMetadata;
};
// roll dice
const rollDice = (sides = 6) => {
return Math.floor(Math.random() * sides) + 1;
};
Deno.serve(async (req: Request) => {
// Deno cloud populated region of the world
const region = Deno.env.get("DENO_REGION") || "unknown";
// the URL of the current HTTP Request
const url = new URL(req.url);
// everything segment after the hostname
const segments: Array<string> = url.pathname.split("/"); // ideally ["roll", "username"];
const action = segments[1]; // ideally "roll"
const username = segments[2]; // ideally "username"
const date = new Date().toISOString(); // eg. "2021-09-01T12:00:00.000Z"
// the user doesn't know what they're doing, show instructions
const shouldDisplayInstructions = segments.length !== 3 || action !== "roll";
if (shouldDisplayInstructions) {
return new Response(InstructionsPage(), {
headers: {
"content-type": "text/html",
},
status: HTTP_CODES.BAD_REQUEST,
});
}
// things seem good - get a random number between 1 and 6
const roll = rollDice();
// create a JSON object, the body of the Kafka Message
const value: DiceRollEvent = { roll, username }; // eg. "{"roll": 4, "username": "alice"}"
// send the message to the Kafka "roll" topic
const TOPIC_NAME = "roll";
const kafka_record = await sendMessage(TOPIC_NAME, {
key: region,
value,
});
// after sending the message, disconnect the producer
await producer.disconnect();
// if there is a record from kafka, this will be `true`
const kafka_success = !!kafka_record;
// create a response body to send to the user who made the request
const responseBody = {
username,
roll,
region,
date,
kafka_success,
kafka_record,
};
// the status of the request should be "OK" if the Kafka message was stored successfully
// otherwise, the status should be "INTERNAL_SERVER_ERROR"
const status = kafka_success
? HTTP_CODES.OK
: HTTP_CODES.INTERNAL_SERVER_ERROR;
return new Response(JSON.stringify(responseBody, null, 2), {
headers: {
"content-type": "application/json",
},
status,
});
});