Skip to content

Commit 70bc178

Browse files
authored
DX-1023: Add regex for base url (#1157)
* fix: add errors to exports * feat: validate url for https * fix: make url error more verbose * fix: fmt * fix: add regex explanation * fix: update bun.lockb * fix: update cloudflare-workers redis dependency * fix: bump node version in nextjs deployed tests tests were failing because they got node end-of-life errors at vercel * fix: update url message
1 parent 6b895d1 commit 70bc178

File tree

17 files changed

+101
-77
lines changed

17 files changed

+101
-77
lines changed

.github/workflows/tests.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ jobs:
134134
- name: Setup node
135135
uses: actions/setup-node@v3
136136
with:
137-
node-version: 18
137+
node-version: 20
138138

139139
- name: Setup Bun
140140
uses: oven-sh/setup-bun@v1
@@ -169,7 +169,7 @@ jobs:
169169
- name: Setup node
170170
uses: actions/setup-node@v3
171171
with:
172-
node-version: 18
172+
node-version: 20
173173

174174
- name: Setup Bun
175175
uses: oven-sh/setup-bun@v1

bun.lockb

0 Bytes
Binary file not shown.

examples/cloudflare-workers/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@
1212
"wrangler": "^2.20.0"
1313
},
1414
"dependencies": {
15-
"@upstash/redis": "link:../../dist"
15+
"@upstash/redis": "latest"
1616
}
1717
}

pkg/auto-pipeline.test.ts

Lines changed: 20 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ describe("Auto pipeline", () => {
1717

1818
const redis = Redis.fromEnv({
1919
latencyLogging: false,
20-
enableAutoPipelining: true
20+
enableAutoPipelining: true,
2121
});
2222
// @ts-expect-error pipelineCounter is not in type but accessible
2323
expect(redis.pipelineCounter).toBe(0);
@@ -152,10 +152,9 @@ describe("Auto pipeline", () => {
152152
});
153153

154154
test("should group async requests with sync requests", async () => {
155-
156155
const redis = Redis.fromEnv({
157156
latencyLogging: false,
158-
enableAutoPipelining: true
157+
enableAutoPipelining: true,
159158
});
160159
// @ts-expect-error pipelineCounter is not in type but accessible
161160
expect(redis.pipelineCounter).toBe(0);
@@ -178,10 +177,9 @@ describe("Auto pipeline", () => {
178177
});
179178

180179
test("should execute a pipeline for each consecutive awaited command", async () => {
181-
182180
const redis = Redis.fromEnv({
183181
latencyLogging: false,
184-
enableAutoPipelining: true
182+
enableAutoPipelining: true,
185183
});
186184
// @ts-expect-error pipelineCounter is not in type but accessible
187185
expect(redis.pipelineCounter).toBe(0);
@@ -204,10 +202,9 @@ describe("Auto pipeline", () => {
204202
});
205203

206204
test("should execute a single pipeline for several commands inside Promise.all", async () => {
207-
208205
const redis = Redis.fromEnv({
209206
latencyLogging: false,
210-
enableAutoPipelining: true
207+
enableAutoPipelining: true,
211208
});
212209
// @ts-expect-error pipelineCounter is not in type but accessible
213210
expect(redis.pipelineCounter).toBe(0);
@@ -222,14 +219,12 @@ describe("Auto pipeline", () => {
222219
// @ts-expect-error pipelineCounter is not in type but accessible
223220
expect(redis.pipelineCounter).toBe(1);
224221
expect(resArray).toEqual(["OK", 1, 2, "OK", "bar"]);
225-
226222
});
227223

228224
test("should be able to utilize only redis functions 'use' like usual", async () => {
229-
230225
const redis = Redis.fromEnv({
231226
latencyLogging: false,
232-
enableAutoPipelining: true
227+
enableAutoPipelining: true,
233228
});
234229
// @ts-expect-error pipelineCounter is not in type but accessible
235230
expect(redis.pipelineCounter).toBe(0);
@@ -239,7 +234,7 @@ describe("Auto pipeline", () => {
239234
state = true;
240235
return await next(req);
241236
});
242-
237+
243238
// @ts-expect-error pipelineCounter is not in type but accessible
244239
expect(redis.pipelineCounter).toBe(0);
245240

@@ -252,59 +247,56 @@ describe("Auto pipeline", () => {
252247
});
253248

254249
test("should be able to utilize only redis functions 'multi' and 'pipeline' like usual", async () => {
255-
256250
const redis = Redis.fromEnv({
257251
latencyLogging: false,
258-
enableAutoPipelining: true
252+
enableAutoPipelining: true,
259253
});
260254
// @ts-expect-error pipelineCounter is not in type but accessible
261255
expect(redis.pipelineCounter).toBe(0);
262256

263257
const pipe = redis.pipeline();
264258
pipe.incr("voila");
265259
pipe.incr("voila");
266-
const result = await pipe.exec()
267-
expect(result).toEqual([1, 2])
268-
260+
const result = await pipe.exec();
261+
expect(result).toEqual([1, 2]);
262+
269263
// @ts-expect-error pipelineCounter is not in type but accessible
270264
expect(redis.pipelineCounter).toBe(0);
271265

272266
const transaction = redis.multi();
273267
transaction.incr("et voila");
274268
transaction.incr("et voila");
275-
const result_2 = await transaction.exec()
276-
expect(result_2).toEqual([1, 2])
277-
269+
const result_2 = await transaction.exec();
270+
expect(result_2).toEqual([1, 2]);
271+
278272
// @ts-expect-error pipelineCounter is not in type but accessible
279273
expect(redis.pipelineCounter).toBe(0);
280274
});
281275

282276
test("should be able to utilize only redis functions 'createScript' like usual", async () => {
283-
284277
const redis = Redis.fromEnv({
285278
latencyLogging: false,
286-
enableAutoPipelining: true
279+
enableAutoPipelining: true,
287280
});
288281
// @ts-expect-error pipelineCounter is not in type but accessible
289282
expect(redis.pipelineCounter).toBe(0);
290283

291284
const script = redis.createScript("return ARGV[1];");
292-
285+
293286
// @ts-expect-error pipelineCounter is not in type but accessible
294287
expect(redis.pipelineCounter).toBe(0);
295288

296289
const res = await script.eval([], ["Hello World"]);
297290
expect(res).toEqual("Hello World");
298-
291+
299292
// @ts-expect-error pipelineCounter is not in type but accessible
300293
expect(redis.pipelineCounter).toBe(1);
301294
});
302295

303296
test("should handle JSON commands correctly", async () => {
304-
305297
const redis = Redis.fromEnv({
306298
latencyLogging: false,
307-
enableAutoPipelining: true
299+
enableAutoPipelining: true,
308300
});
309301

310302
// @ts-expect-error pipelineCounter is not in type but accessible
@@ -317,19 +309,11 @@ describe("Auto pipeline", () => {
317309
redis.json.get("baz1"),
318310
redis.json.del("baz1"),
319311
redis.json.get("baz1"),
320-
])
312+
]);
321313

322314
// @ts-expect-error pipelineCounter is not in type but accessible
323315
expect(redis.pipelineCounter).toBe(1);
324316

325-
expect(res).toEqual([
326-
"OK",
327-
"OK",
328-
"bar",
329-
{ hello: "world" },
330-
1,
331-
null
332-
])
333-
})
317+
expect(res).toEqual(["OK", "OK", "bar", { hello: "world" }, 1, null]);
318+
});
334319
});
335-

pkg/auto-pipeline.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,21 @@ export function createAutoPipelineProxy(_redis: Redis, json?: boolean): Redis {
1616
}
1717

1818
return new Proxy(redis, {
19-
get: (redis, command: "pipelineCounter" | keyof Pipeline | redisOnly ) => {
19+
get: (redis, command: "pipelineCounter" | keyof Pipeline | redisOnly) => {
2020
// return pipelineCounter of autoPipelineExecutor
2121
if (command === "pipelineCounter") {
2222
return redis.autoPipelineExecutor.pipelineCounter;
2323
}
2424

2525
if (command === "json") {
2626
return createAutoPipelineProxy(redis, true);
27-
};
28-
27+
}
28+
2929
const commandInRedisButNotPipeline =
3030
command in redis && !(command in redis.autoPipelineExecutor.pipeline);
3131

3232
if (commandInRedisButNotPipeline) {
33-
return redis[command as redisOnly];
33+
return redis[command as redisOnly];
3434
}
3535

3636
// If the method is a function on the pipeline, wrap it with the executor logic
@@ -39,7 +39,7 @@ export function createAutoPipelineProxy(_redis: Redis, json?: boolean): Redis {
3939
// pass the function as a callback
4040
return redis.autoPipelineExecutor.withAutoPipeline((pipeline) => {
4141
if (json) {
42-
(pipeline.json[command as keyof Pipeline["json"]] as Function)(...args)
42+
(pipeline.json[command as keyof Pipeline["json"]] as Function)(...args);
4343
} else {
4444
(pipeline[command as keyof Pipeline] as Function)(...args);
4545
}
@@ -94,7 +94,7 @@ class AutoPipelineExecutor {
9494
}
9595

9696
private async deferExecution() {
97-
await Promise.resolve()
98-
return await Promise.resolve()
97+
await Promise.resolve();
98+
return await Promise.resolve();
9999
}
100-
}
100+
}

pkg/commands/scan.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,9 @@ export class ScanCommand extends Command<[string, string[]], [string, string[]]>
2424
if (opts?.type && opts.type.length > 0) {
2525
command.push("type", opts.type);
2626
}
27-
super(
28-
command,
29-
{
30-
deserialize: deserializeScanResponse,
31-
...cmdOpts,
32-
}
33-
);
27+
super(command, {
28+
deserialize: deserializeScanResponse,
29+
...cmdOpts,
30+
});
3431
}
3532
}

pkg/commands/sscan.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,9 @@ export class SScanCommand extends Command<
2121
command.push("count", opts.count);
2222
}
2323

24-
super(
25-
command,
26-
{
27-
deserialize: deserializeScanResponse,
28-
...cmdOpts,
29-
}
30-
);
24+
super(command, {
25+
deserialize: deserializeScanResponse,
26+
...cmdOpts,
27+
});
3128
}
3229
}

pkg/commands/zscan.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,9 @@ export class ZScanCommand extends Command<
2121
command.push("count", opts.count);
2222
}
2323

24-
super(
25-
command,
26-
{
27-
deserialize: deserializeScanResponse,
28-
...cmdOpts,
29-
}
30-
);
24+
super(command, {
25+
deserialize: deserializeScanResponse,
26+
...cmdOpts,
27+
});
3128
}
3229
}

pkg/error.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,10 @@ export class UpstashError extends Error {
77
this.name = "UpstashError";
88
}
99
}
10+
11+
export class UrlError extends Error {
12+
constructor(url: string) {
13+
super(`Upstash Redis client was passed an invalid URL. You should pass the URL together with https. Received: "${url}". `);
14+
this.name = "UrlError";
15+
}
16+
}

pkg/http.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { describe, expect, test } from "bun:test";
22
import { HttpClient } from "./http";
33

4+
import { UrlError } from "./error";
45
import { newHttpClient } from "./test-utils";
56
test("remove trailing slash from urls", () => {
67
const client = new HttpClient({ baseUrl: "https://example.com/" });
@@ -48,3 +49,27 @@ describe("Abort", () => {
4849
expect((await body).result).toEqual("Abort works!");
4950
});
5051
});
52+
53+
describe("should reject invalid urls", () => {
54+
test("should reject when https is missing", () => {
55+
try {
56+
new HttpClient({ baseUrl: "eu1-flying-whale-434343.upstash.io" });
57+
} catch (error) {
58+
expect(error instanceof UrlError).toBeTrue();
59+
return;
60+
}
61+
throw new Error("Test error. Should have raised when initializing client");
62+
});
63+
64+
test("should allow when http is used", () => {
65+
new HttpClient({
66+
baseUrl: "http://eu1-flying-whale-434343.upstash.io",
67+
});
68+
});
69+
70+
test("should allow when https is used", () => {
71+
new HttpClient({
72+
baseUrl: "https://eu1-flying-whale-434343.upstash.io",
73+
});
74+
});
75+
});

0 commit comments

Comments
 (0)