From 6ab0fb8dcf9057464d33a9652be216179775df88 Mon Sep 17 00:00:00 2001 From: CahidArda Date: Tue, 8 Oct 2024 14:28:21 +0300 Subject: [PATCH 1/5] fix: replace throw on missing env with warning --- pkg/error.ts | 2 +- pkg/http.ts | 12 +++++++++++- platforms/cloudflare.ts | 31 +++++++++++++++++------------ platforms/fastly.ts | 22 ++++++++++++++------- platforms/nodejs.ts | 40 +++++++++++++++++++++----------------- platforms/platform.test.ts | 36 ++++++++++++++++++++++++++++++++++ 6 files changed, 104 insertions(+), 39 deletions(-) create mode 100644 platforms/platform.test.ts diff --git a/pkg/error.ts b/pkg/error.ts index 7e27c36d..02a0b9f9 100644 --- a/pkg/error.ts +++ b/pkg/error.ts @@ -11,7 +11,7 @@ export class UpstashError extends Error { export class UrlError extends Error { constructor(url: string) { super( - `Upstash Redis client was passed an invalid URL. You should pass the URL together with https. Received: "${url}". ` + `Upstash Redis client was passed an invalid URL. You should pass a URL starting with https. Received: "${url}". ` ); this.name = "UrlError"; } diff --git a/pkg/http.ts b/pkg/http.ts index 72c6e319..fd020f6a 100644 --- a/pkg/http.ts +++ b/pkg/http.ts @@ -127,6 +127,7 @@ export class HttpClient implements Requester { }; public readYourWrites: boolean; public upstashSyncToken = ""; + private hasCredentials: boolean; public readonly retry: { attempts: number; @@ -157,7 +158,7 @@ export class HttpClient implements Requester { * - `$` asserts the position at the end of the string. */ const urlRegex = /^https?:\/\/[^\s#$./?].\S*$/; - if (!urlRegex.test(this.baseUrl)) { + if (this.baseUrl && !urlRegex.test(this.baseUrl)) { throw new UrlError(this.baseUrl); } @@ -167,6 +168,8 @@ export class HttpClient implements Requester { ...config.headers, }; + this.hasCredentials = Boolean(this.baseUrl && this.headers.authorization.split(" ")[1]); + if (this.options.responseEncoding === "base64") { this.headers["Upstash-Encoding"] = "base64"; } @@ -206,6 +209,13 @@ export class HttpClient implements Requester { backend: this.options.backend, }; + if (!this.hasCredentials) { + throw new Error( + "[Upstash Redis] Redis client was initialized without url or token." + + " Failed to execute command." + ); + } + /** * We've recieved a new `upstash-sync-token` in the previous response. We use it in the next request to observe the effects of previous requests. */ diff --git a/platforms/cloudflare.ts b/platforms/cloudflare.ts index 9a705c14..19e25f1b 100644 --- a/platforms/cloudflare.ts +++ b/platforms/cloudflare.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ import type { RequesterConfig } from "../pkg/http"; import { HttpClient } from "../pkg/http"; import * as core from "../pkg/redis"; @@ -56,27 +55,35 @@ export class Redis extends core.Redis { */ constructor(config: RedisConfigCloudflare, env?: Env) { if (!config.url) { - throw new Error( + console.warn( `[Upstash Redis] The 'url' property is missing or undefined in your Redis config.` ); } if (!config.token) { - throw new Error( + console.warn( `[Upstash Redis] The 'token' property is missing or undefined in your Redis config.` ); } - if (config.url.startsWith(" ") || config.url.endsWith(" ") || /\r|\n/.test(config.url)) { - console.warn("The redis url contains whitespace or newline, which can cause errors!"); + if (config.url!.startsWith(" ") || config.url!.endsWith(" ") || /\r|\n/.test(config.url!)) { + console.warn( + "[Upstash Redis] The redis url contains whitespace or newline, which can cause errors!" + ); } - if (config.token.startsWith(" ") || config.token.endsWith(" ") || /\r|\n/.test(config.token)) { - console.warn("The redis token contains whitespace or newline, which can cause errors!"); + if ( + config.token!.startsWith(" ") || + config.token!.endsWith(" ") || + /\r|\n/.test(config.token!) + ) { + console.warn( + "[Upstash Redis] The redis token contains whitespace or newline, which can cause errors!" + ); } const client = new HttpClient({ retry: config.retry, - baseUrl: config.url, + baseUrl: config.url!, headers: { authorization: `Bearer ${config.token}` }, responseEncoding: config.responseEncoding, signal: config.signal, @@ -127,13 +134,13 @@ export class Redis extends core.Redis { const token = env?.UPSTASH_REDIS_REST_TOKEN ?? UPSTASH_REDIS_REST_TOKEN; if (!url) { - throw new Error( - "Unable to find environment variable: `UPSTASH_REDIS_REST_URL`. Please add it via `wrangler secret put UPSTASH_REDIS_REST_URL`" + console.warn( + "[Upstash Redis] Unable to find environment variable: `UPSTASH_REDIS_REST_URL`. Please add it via `wrangler secret put UPSTASH_REDIS_REST_URL`" ); } if (!token) { - throw new Error( - "Unable to find environment variable: `UPSTASH_REDIS_REST_TOKEN`. Please add it via `wrangler secret put UPSTASH_REDIS_REST_TOKEN`" + console.warn( + "[Upstash Redis] Unable to find environment variable: `UPSTASH_REDIS_REST_TOKEN`. Please add it via `wrangler secret put UPSTASH_REDIS_REST_TOKEN`" ); } return new Redis({ ...opts, url, token }, env); diff --git a/platforms/fastly.ts b/platforms/fastly.ts index 4ca91384..279e4577 100644 --- a/platforms/fastly.ts +++ b/platforms/fastly.ts @@ -53,26 +53,34 @@ export class Redis extends core.Redis { */ constructor(config: RedisConfigFastly) { if (!config.url) { - throw new Error( + console.warn( `[Upstash Redis] The 'url' property is missing or undefined in your Redis config.` ); } if (!config.token) { - throw new Error( + console.warn( `[Upstash Redis] The 'token' property is missing or undefined in your Redis config.` ); } - if (config.url.startsWith(" ") || config.url.endsWith(" ") || /\r|\n/.test(config.url)) { - console.warn("The redis url contains whitespace or newline, which can cause errors!"); + if (config.url!.startsWith(" ") || config.url!.endsWith(" ") || /\r|\n/.test(config.url!)) { + console.warn( + "[Upstash Redis] The redis url contains whitespace or newline, which can cause errors!" + ); } - if (config.token.startsWith(" ") || config.token.endsWith(" ") || /\r|\n/.test(config.token)) { - console.warn("The redis token contains whitespace or newline, which can cause errors!"); + if ( + config.token!.startsWith(" ") || + config.token!.endsWith(" ") || + /\r|\n/.test(config.token!) + ) { + console.warn( + "[Upstash Redis] The redis token contains whitespace or newline, which can cause errors!" + ); } const client = new HttpClient({ - baseUrl: config.url, + baseUrl: config.url!, retry: config.retry, headers: { authorization: `Bearer ${config.token}` }, options: { backend: config.backend }, diff --git a/platforms/nodejs.ts b/platforms/nodejs.ts index 9bfb4879..3e3b1840 100644 --- a/platforms/nodejs.ts +++ b/platforms/nodejs.ts @@ -102,37 +102,41 @@ export class Redis extends core.Redis { } if (!configOrRequester.url) { - throw new Error( + console.warn( `[Upstash Redis] The 'url' property is missing or undefined in your Redis config.` ); } if (!configOrRequester.token) { - throw new Error( + console.warn( `[Upstash Redis] The 'token' property is missing or undefined in your Redis config.` ); } if ( - configOrRequester.url.startsWith(" ") || - configOrRequester.url.endsWith(" ") || - /\r|\n/.test(configOrRequester.url) + configOrRequester.url!.startsWith(" ") || + configOrRequester.url!.endsWith(" ") || + /\r|\n/.test(configOrRequester.url!) ) { - console.warn("The redis url contains whitespace or newline, which can cause errors!"); + console.warn( + "[Upstash Redis] The redis url contains whitespace or newline, which can cause errors!" + ); } if ( - configOrRequester.token.startsWith(" ") || - configOrRequester.token.endsWith(" ") || - /\r|\n/.test(configOrRequester.token) + configOrRequester.token!.startsWith(" ") || + configOrRequester.token!.endsWith(" ") || + /\r|\n/.test(configOrRequester.token!) ) { - console.warn("The redis token contains whitespace or newline, which can cause errors!"); + console.warn( + "[Upstash Redis] The redis token contains whitespace or newline, which can cause errors!" + ); } const client = new HttpClient({ - baseUrl: configOrRequester.url, + baseUrl: configOrRequester.url!, retry: configOrRequester.retry, headers: { authorization: `Bearer ${configOrRequester.token}` }, - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + agent: configOrRequester.agent, responseEncoding: configOrRequester.responseEncoding, cache: configOrRequester.cache ?? "no-store", @@ -172,21 +176,21 @@ export class Redis extends core.Redis { */ static fromEnv(config?: Omit): Redis { // @ts-ignore process will be defined in node - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (process.env === undefined) { throw new TypeError( - 'Unable to get environment variables, `process.env` is undefined. If you are deploying to cloudflare, please import from "@upstash/redis/cloudflare" instead' + '[Upstash Redis] Unable to get environment variables, `process.env` is undefined. If you are deploying to cloudflare, please import from "@upstash/redis/cloudflare" instead' ); } - // @ts-ignore process will be defined in node const url = process.env.UPSTASH_REDIS_REST_URL; if (!url) { - throw new Error("Unable to find environment variable: `UPSTASH_REDIS_REST_URL`"); + console.warn("[Upstash Redis] Unable to find environment variable: `UPSTASH_REDIS_REST_URL`"); } - // @ts-ignore process will be defined in node const token = process.env.UPSTASH_REDIS_REST_TOKEN; if (!token) { - throw new Error("Unable to find environment variable: `UPSTASH_REDIS_REST_TOKEN`"); + console.warn( + "[Upstash Redis] Unable to find environment variable: `UPSTASH_REDIS_REST_TOKEN`" + ); } return new Redis({ ...config, url, token }); } diff --git a/platforms/platform.test.ts b/platforms/platform.test.ts new file mode 100644 index 00000000..917d8da6 --- /dev/null +++ b/platforms/platform.test.ts @@ -0,0 +1,36 @@ +import { describe, expect, test } from "bun:test"; +import { Redis as NodeRedis } from "./nodejs"; +import { Redis as CloudflareRedis } from "./cloudflare"; +import { Redis as FastlyRedis } from "./fastly"; + +describe("should allow creating a client without credentials but fail when requesting", () => { + test("nodejs", () => { + const redis = new NodeRedis({ url: undefined, token: undefined }); + + const throws = redis.get("foo"); + expect(throws).toThrow( + "[Upstash Redis] Redis client was initialized without url or token." + + " Failed to execute command." + ); + }); + + test("cloudflare", () => { + const redis = new CloudflareRedis({ url: undefined, token: undefined }); + + const throws = redis.get("foo"); + expect(throws).toThrow( + "[Upstash Redis] Redis client was initialized without url or token." + + " Failed to execute command." + ); + }); + + test("fastly", () => { + const redis = new FastlyRedis({ url: undefined, token: undefined, backend: "upstash-db" }); + + const throws = redis.get("foo"); + expect(throws).toThrow( + "[Upstash Redis] Redis client was initialized without url or token." + + " Failed to execute command." + ); + }); +}); From a59de0427feb49ffbd10fcc1674ad5ee0bbe3553 Mon Sep 17 00:00:00 2001 From: CahidArda Date: Tue, 8 Oct 2024 16:17:00 +0300 Subject: [PATCH 2/5] fix: check url/token only if they exist --- platforms/cloudflare.ts | 17 +++++++---------- platforms/fastly.ts | 19 ++++++++----------- platforms/nodejs.ts | 27 ++++++++++++--------------- 3 files changed, 27 insertions(+), 36 deletions(-) diff --git a/platforms/cloudflare.ts b/platforms/cloudflare.ts index 19e25f1b..c62e4920 100644 --- a/platforms/cloudflare.ts +++ b/platforms/cloudflare.ts @@ -58,22 +58,19 @@ export class Redis extends core.Redis { console.warn( `[Upstash Redis] The 'url' property is missing or undefined in your Redis config.` ); - } - - if (!config.token) { + } else if (config.url.startsWith(" ") || config.url.endsWith(" ") || /\r|\n/.test(config.url)) { console.warn( - `[Upstash Redis] The 'token' property is missing or undefined in your Redis config.` + "[Upstash Redis] The redis url contains whitespace or newline, which can cause errors!" ); } - if (config.url!.startsWith(" ") || config.url!.endsWith(" ") || /\r|\n/.test(config.url!)) { + if (!config.token) { console.warn( - "[Upstash Redis] The redis url contains whitespace or newline, which can cause errors!" + `[Upstash Redis] The 'token' property is missing or undefined in your Redis config.` ); - } - if ( - config.token!.startsWith(" ") || - config.token!.endsWith(" ") || + } else if ( + config.token.startsWith(" ") || + config.token.endsWith(" ") || /\r|\n/.test(config.token!) ) { console.warn( diff --git a/platforms/fastly.ts b/platforms/fastly.ts index 279e4577..59435174 100644 --- a/platforms/fastly.ts +++ b/platforms/fastly.ts @@ -56,23 +56,20 @@ export class Redis extends core.Redis { console.warn( `[Upstash Redis] The 'url' property is missing or undefined in your Redis config.` ); - } - - if (!config.token) { + } else if (config.url.startsWith(" ") || config.url.endsWith(" ") || /\r|\n/.test(config.url)) { console.warn( - `[Upstash Redis] The 'token' property is missing or undefined in your Redis config.` + "[Upstash Redis] The redis url contains whitespace or newline, which can cause errors!" ); } - if (config.url!.startsWith(" ") || config.url!.endsWith(" ") || /\r|\n/.test(config.url!)) { + if (!config.token) { console.warn( - "[Upstash Redis] The redis url contains whitespace or newline, which can cause errors!" + `[Upstash Redis] The 'token' property is missing or undefined in your Redis config.` ); - } - if ( - config.token!.startsWith(" ") || - config.token!.endsWith(" ") || - /\r|\n/.test(config.token!) + } else if ( + config.token.startsWith(" ") || + config.token.endsWith(" ") || + /\r|\n/.test(config.token) ) { console.warn( "[Upstash Redis] The redis token contains whitespace or newline, which can cause errors!" diff --git a/platforms/nodejs.ts b/platforms/nodejs.ts index 3e3b1840..1387d726 100644 --- a/platforms/nodejs.ts +++ b/platforms/nodejs.ts @@ -105,27 +105,24 @@ export class Redis extends core.Redis { console.warn( `[Upstash Redis] The 'url' property is missing or undefined in your Redis config.` ); - } - - if (!configOrRequester.token) { + } else if ( + configOrRequester.url.startsWith(" ") || + configOrRequester.url.endsWith(" ") || + /\r|\n/.test(configOrRequester.url) + ) { console.warn( - `[Upstash Redis] The 'token' property is missing or undefined in your Redis config.` + "[Upstash Redis] The redis url contains whitespace or newline, which can cause errors!" ); } - if ( - configOrRequester.url!.startsWith(" ") || - configOrRequester.url!.endsWith(" ") || - /\r|\n/.test(configOrRequester.url!) - ) { + if (!configOrRequester.token) { console.warn( - "[Upstash Redis] The redis url contains whitespace or newline, which can cause errors!" + `[Upstash Redis] The 'token' property is missing or undefined in your Redis config.` ); - } - if ( - configOrRequester.token!.startsWith(" ") || - configOrRequester.token!.endsWith(" ") || - /\r|\n/.test(configOrRequester.token!) + } else if ( + configOrRequester.token.startsWith(" ") || + configOrRequester.token.endsWith(" ") || + /\r|\n/.test(configOrRequester.token) ) { console.warn( "[Upstash Redis] The redis token contains whitespace or newline, which can cause errors!" From 2fda697c63b1d98d6a178e1087a283ebf8ba2b77 Mon Sep 17 00:00:00 2001 From: CahidArda Date: Tue, 8 Oct 2024 16:49:18 +0300 Subject: [PATCH 3/5] fix: only replace if baseUrl exists --- pkg/http.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/http.ts b/pkg/http.ts index fd020f6a..c413ec7e 100644 --- a/pkg/http.ts +++ b/pkg/http.ts @@ -146,7 +146,7 @@ export class HttpClient implements Requester { this.upstashSyncToken = ""; this.readYourWrites = config.readYourWrites ?? true; - this.baseUrl = config.baseUrl.replace(/\/$/, ""); + this.baseUrl = (config.baseUrl || "").replace(/\/$/, ""); /** * regex to check if the baseUrl starts with http:// or https:// From 45a689ef65e74c2070bce85cda4bf846b09b2643 Mon Sep 17 00:00:00 2001 From: CahidArda Date: Wed, 9 Oct 2024 09:05:53 +0300 Subject: [PATCH 4/5] fix: rm throw from client because of vercel pre-rendering, the error was thrown when turbo was used --- pkg/http.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/http.ts b/pkg/http.ts index c413ec7e..e01075ee 100644 --- a/pkg/http.ts +++ b/pkg/http.ts @@ -210,7 +210,7 @@ export class HttpClient implements Requester { }; if (!this.hasCredentials) { - throw new Error( + console.warn( "[Upstash Redis] Redis client was initialized without url or token." + " Failed to execute command." ); From 9e41a272cf8b694ee462f29f5621722d84c51270 Mon Sep 17 00:00:00 2001 From: CahidArda Date: Wed, 9 Oct 2024 18:02:56 +0300 Subject: [PATCH 5/5] fix: rm platform tests --- platforms/platform.test.ts | 36 ------------------------------------ 1 file changed, 36 deletions(-) delete mode 100644 platforms/platform.test.ts diff --git a/platforms/platform.test.ts b/platforms/platform.test.ts deleted file mode 100644 index 917d8da6..00000000 --- a/platforms/platform.test.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { describe, expect, test } from "bun:test"; -import { Redis as NodeRedis } from "./nodejs"; -import { Redis as CloudflareRedis } from "./cloudflare"; -import { Redis as FastlyRedis } from "./fastly"; - -describe("should allow creating a client without credentials but fail when requesting", () => { - test("nodejs", () => { - const redis = new NodeRedis({ url: undefined, token: undefined }); - - const throws = redis.get("foo"); - expect(throws).toThrow( - "[Upstash Redis] Redis client was initialized without url or token." + - " Failed to execute command." - ); - }); - - test("cloudflare", () => { - const redis = new CloudflareRedis({ url: undefined, token: undefined }); - - const throws = redis.get("foo"); - expect(throws).toThrow( - "[Upstash Redis] Redis client was initialized without url or token." + - " Failed to execute command." - ); - }); - - test("fastly", () => { - const redis = new FastlyRedis({ url: undefined, token: undefined, backend: "upstash-db" }); - - const throws = redis.get("foo"); - expect(throws).toThrow( - "[Upstash Redis] Redis client was initialized without url or token." + - " Failed to execute command." - ); - }); -});