diff --git a/.changeset/sixty-suns-brush.md b/.changeset/sixty-suns-brush.md new file mode 100644 index 0000000..367af49 --- /dev/null +++ b/.changeset/sixty-suns-brush.md @@ -0,0 +1,5 @@ +--- +'@shopify/shopify-app-session-storage-redis': patch +--- + +Add event handlers to redis client to prevent crashing on disconnect event. Fixes #129, #160 (Thanks to @davidhollenbeckx for linking to issue and solution.) diff --git a/packages/shopify-app-session-storage-redis/src/__tests__/redis.conf b/packages/shopify-app-session-storage-redis/src/__tests__/redis.conf index 1ad55b7..88b5069 100644 --- a/packages/shopify-app-session-storage-redis/src/__tests__/redis.conf +++ b/packages/shopify-app-session-storage-redis/src/__tests__/redis.conf @@ -1 +1,2 @@ user shopify on +@all allkeys >passify +timeout 2 diff --git a/packages/shopify-app-session-storage-redis/src/__tests__/redis.test.ts b/packages/shopify-app-session-storage-redis/src/__tests__/redis.test.ts index 4b94eb7..25c2ade 100644 --- a/packages/shopify-app-session-storage-redis/src/__tests__/redis.test.ts +++ b/packages/shopify-app-session-storage-redis/src/__tests__/redis.test.ts @@ -35,6 +35,10 @@ describe('RedisSessionStorage', () => { await wait(10000); client = createClient({url: dbURL.toString()}); + client.on('error', (err) => {}); + client.on('connect', () => {}); + client.on('reconnecting', () => {}); + client.on('ready', () => {}); await client.connect(); }); @@ -161,6 +165,32 @@ describe('RedisSessionStorage', () => { storageClone1.disconnect(); storageClone2.disconnect(); }); + + it(`reconnects after a timeout`, async () => { + const storage = new RedisSessionStorage(dbURL); + await storage.ready; + + const sessionId = 'timeout_connection_test'; + const session = new Session({ + id: sessionId, + shop: 'shop1.myshopify.com', + state: 'state', + isOnline: false, + scope: ['test_scope'].toString(), + accessToken: 'abcde-12345-123', + }); + + await storage.storeSession(session); + + // Wait for the redis client to disconnect + await wait(2500); + + const storedSession = await storage.loadSession(sessionId); + expect(storedSession).toBeDefined(); + expect(storedSession?.id).toBe(sessionId); + + await storage.disconnect(); + }); }); async function initWithNonSessionData(client: RedisClient) { diff --git a/packages/shopify-app-session-storage-redis/src/redis-connection.ts b/packages/shopify-app-session-storage-redis/src/redis-connection.ts index cfe8f6f..7eb4f40 100644 --- a/packages/shopify-app-session-storage-redis/src/redis-connection.ts +++ b/packages/shopify-app-session-storage-redis/src/redis-connection.ts @@ -7,18 +7,9 @@ export class RedisConnection implements DBConnection { sessionStorageIdentifier: string; private client: RedisClient; - constructor( - dbUrl: string, - options: RedisClientOptions, - keyPrefix: string, - onError?: (...args: any[]) => void, - ) { + constructor(dbUrl: string, options: RedisClientOptions, keyPrefix: string) { this.init(dbUrl, options); this.sessionStorageIdentifier = keyPrefix; - - if (onError) { - this.client.on('error', onError); - } } query(_query: string, _params: any[]): Promise { @@ -62,5 +53,12 @@ export class RedisConnection implements DBConnection { ...options, url: dbUrl, }); + + this.client.on('error', this.eventHandler); + this.client.on('connect', this.eventHandler); + this.client.on('reconnecting', this.eventHandler); + this.client.on('ready', this.eventHandler); } + + private eventHandler = (..._args: any[]) => {}; }