diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3a956cc..870bb29 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: strategy: matrix: - node-version: [10.x, 12.x, 14.x] + node-version: [12.x, 14.x] steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml new file mode 100644 index 0000000..20c7ce8 --- /dev/null +++ b/.github/workflows/npm-publish.yml @@ -0,0 +1,39 @@ +# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created +# For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages + +name: Node.js Package + +on: + release: + types: [created] + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [12.x] + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: ${{ matrix.node-version }} + - run: npm ci + - run: npm run build + - run: npm test + + publish-npm: + needs: build + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [12.x] + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: ${{ matrix.node-version }} + registry-url: https://registry.npmjs.org/ + - run: npm publish + env: + NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} \ No newline at end of file diff --git a/jest.config.js b/jest.config.js index 70e9c23..cecbca6 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,15 +1,16 @@ module.exports = { - moduleFileExtensions: ["ts", "js", "json"], + moduleFileExtensions: ['ts', 'js', 'json'], transform: { - "^.+\\.(ts)$": "ts-jest" + '^.+\\.(ts)$': 'ts-jest', }, globals: { - "ts-jest": { - tsconfig: "tsconfig.json" - } + 'ts-jest': { + tsconfig: 'tsconfig.json', + }, }, - testMatch: ["**/*.test.ts"], - testPathIgnorePatterns: ["/node_modules/", "/dist/", "/lib/"], + testMatch: ['**/*.test.ts'], + testPathIgnorePatterns: ['/node_modules/', '/dist/', '/lib/'], collectCoverage: true, + 'coverageReporters': ['json', 'html'], verbose: true, }; diff --git a/package.json b/package.json index 50bdd23..b06bde0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "typescript-retry-decorator", - "version": "1.5.1", + "version": "2.0.0", "description": "A simple retry decorator for typescript with no dependency.", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/src/retry.decorator.test.ts b/src/retry.decorator.test.ts index fe6ea71..34a41f5 100644 --- a/src/retry.decorator.test.ts +++ b/src/retry.decorator.test.ts @@ -62,18 +62,22 @@ describe('Retry Test', () => { test('normal retry', async () => { const calledSpy = jest.spyOn(testClass, 'called'); - calledSpy.mockRejectedValueOnce('bad'); - calledSpy.mockResolvedValueOnce('good'); + calledSpy.mockRejectedValueOnce(new Error('rejected')); + calledSpy.mockResolvedValueOnce('fulfilled'); await testClass.testMethod(); expect(calledSpy).toHaveBeenCalledTimes(2); }); test('exceed max retry', async () => { const calledSpy = jest.spyOn(testClass, 'called'); - calledSpy.mockRejectedValue('bad'); + const errorMsg = 'rejected'; + calledSpy.mockRejectedValue(new Error(errorMsg)); try { await testClass.testMethod(); - } catch (e) {} + } catch (e) { + expect(e).not.toBeUndefined(); + expect(e.message.includes(errorMsg)); + } expect(calledSpy).toHaveBeenCalledTimes(3); }); diff --git a/src/retry.decorator.ts b/src/retry.decorator.ts index b9bedb4..9ea4912 100644 --- a/src/retry.decorator.ts +++ b/src/retry.decorator.ts @@ -25,9 +25,9 @@ export function Retryable(options: RetryOptions): Function { try { return await retryAsync.apply(this, [originalFn, args, options.maxAttempts, options.backOff]); } catch (e) { - if (e.message === 'maxAttempts') { - e.code = '429'; - e.message = `Failed for '${propertyKey}' for ${options.maxAttempts} times.`; + if (e instanceof MaxAttemptsError) { + const msgPrefix = `Failed for '${propertyKey}' for ${options.maxAttempts} times.`; + e.message = e.message ? `${msgPrefix} Original Error: ${e.message}` : msgPrefix; } throw e; } @@ -41,7 +41,7 @@ export function Retryable(options: RetryOptions): Function { } catch (e) { if (--maxAttempts < 0) { e?.message && console.error(e.message); - throw new Error('maxAttempts'); + throw new MaxAttemptsError(e?.message); } if (!canRetry(e)) { throw e; @@ -74,6 +74,15 @@ export function Retryable(options: RetryOptions): Function { } } +export class MaxAttemptsError extends Error { + code = '429' + /* if target is ES5, need the 'new.target.prototype' + constructor(msg?: string) { + super(msg) + Object.setPrototypeOf(this, new.target.prototype) + } */ +} + export interface RetryOptions { maxAttempts: number; backOffPolicy?: BackOffPolicy; diff --git a/tsconfig.json b/tsconfig.json index 675f12b..c5dde11 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "module": "commonjs", - "target": "ES5", + "target": "ES2015", "noImplicitAny": true, "declaration": true, "moduleResolution": "node",