From 04c816dbd7df4d1bfc78fe1ffbe793b5426cbd88 Mon Sep 17 00:00:00 2001 From: Tyler Arehart Date: Sat, 6 Jan 2024 11:57:07 -0800 Subject: [PATCH] Retrying poller initialization. --- README.md | 5 +++-- __tests__/poller.test.ts | 42 ++++++++++++++++++++++++++++++++++++++++ package.json | 4 ++-- src/poller.ts | 6 ++++-- 4 files changed, 51 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 05baa10..c2fd8d3 100644 --- a/README.md +++ b/README.md @@ -3,14 +3,15 @@ [![npm version][npm-badge]][npm-url] [![Build Status - GitHub Actions][gha-badge]][gha-ci] -A wrapper around @aws-sdk/client-appconfigdata to provide background polling and caching. +This library polls AWS AppConfig on a background thread so your app has instant access to a cached version of your configuration. +It relies on the official @aws-sdk/client-appconfigdata library to fetch the data. Although AWS seems to recommend their [simplified retrieval methods](https://docs.aws.amazon.com/appconfig/latest/userguide/appconfig-retrieving-simplified-methods.html) for fetching AppConfig, i.e. running an agent in a separate process, you may prefer to use this library: - You don't need to set up a lambda extension. - You easily get parity between your local development server and prod. - The parsed version of your config is conveniently cached. -- You get convenient reporting of transient errors while the library works in the background to recover. +- You get reporting of transient errors while the library works in the background to recover. ## Usage diff --git a/__tests__/poller.test.ts b/__tests__/poller.test.ts index 18a2803..dd39762 100644 --- a/__tests__/poller.test.ts +++ b/__tests__/poller.test.ts @@ -180,4 +180,46 @@ describe('Poller', () => { standardConfig.configParser(configValue2), ); }); + + it('Retries start session if first one fails', async () => { + const configValue = 'worked once'; + + appConfigClientMock.on(StartConfigurationSessionCommand) + .rejectsOnce({ + message: 'Failed to start', + }) + .resolves({ + InitialConfigurationToken: 'initialToken', + }); + + appConfigClientMock + .on(GetLatestConfigurationCommand) + .resolves({ + Configuration: Uint8ArrayBlobAdapter.fromString(configValue), + }); + + const dataClient = new AppConfigDataClient(); + + poller = new Poller({ + dataClient: dataClient, + ...standardConfig, + }); + + const initialResponse = await poller.start(); + expect(initialResponse.error).toBeDefined(); + expect(initialResponse.isInitiallySuccessful).toBeFalsy(); + + const latest = poller.getConfigurationString(); + + expect(latest.latestValue).toBeUndefined(); + expect(latest.errorCausingStaleValue).toBeUndefined(); + + await wait(standardConfig.pollIntervalSeconds * 1000 + 100); + + const updated = poller.getConfigurationObject(); + expect(updated.errorCausingStaleValue).toBeUndefined(); + expect(updated.latestValue).toEqual( + standardConfig.configParser(configValue), + ); + }); }); diff --git a/package.json b/package.json index fe68c82..e80e21c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "aws-appconfig-poller", - "version": "0.0.4", + "version": "0.0.5", "description": "A wrapper around @aws-sdk/client-appconfigdata to provide background polling and caching.", "repository": { "type": "git", @@ -41,7 +41,7 @@ "test": "jest --coverage", "prettier": "prettier --config .prettierrc --write .", "test:watch": "jest --watch", - "infinite": "tsc && node build/examples/infinitePoller.js" + "infinite": "tsc && node dist/examples/infinitePoller.js" }, "author": "Tyler Arehart ", "license": "MIT", diff --git a/src/poller.ts b/src/poller.ts index 4f68d83..f5708d7 100644 --- a/src/poller.ts +++ b/src/poller.ts @@ -28,8 +28,9 @@ export interface Outcome { type PollingPhase = 'ready' | 'starting' | 'active' | 'stopped'; +const DEFAULT_POLL_INTERVAL_SECONDS = 60; + export class Poller { - private readonly DEFAULT_POLL_INTERVAL_SECONDS = 60; private readonly config: PollerConfig; @@ -130,6 +131,7 @@ export class Poller { return await this.fetchLatestConfiguration(); } catch (e) { + this.timeoutHandle = setTimeout(this.startPolling.bind(this), this.config.pollIntervalSeconds || DEFAULT_POLL_INTERVAL_SECONDS) return { isInitiallySuccessful: false, error: e, @@ -248,7 +250,7 @@ export class Poller { return ( this.config.pollIntervalSeconds || awsSuggestedSeconds || - this.DEFAULT_POLL_INTERVAL_SECONDS + DEFAULT_POLL_INTERVAL_SECONDS ); } }