Skip to content

Commit

Permalink
Merge pull request #7 from Teneff/bugfix
Browse files Browse the repository at this point in the history
Fixes #6
  • Loading branch information
Teneff committed May 16, 2024
2 parents d8f5161 + cdcecfa commit 3c0dc42
Show file tree
Hide file tree
Showing 11 changed files with 312 additions and 313 deletions.
18 changes: 13 additions & 5 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,27 +1,35 @@
{
"env": {
"es2021": true,
"es2020": true,
"node": true
},
"extends": ["plugin:@typescript-eslint/recommended"],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": ["tsconfig.jest.json"],
"ecmaVersion": 12,
"sourceType": "module"
"project": ["tsconfig.build.json"]
},
"plugins": ["@typescript-eslint"],
"rules": {
"@typescript-eslint/no-explicit-any": "off"
},
"overrides": [
{
"rules": {
"@typescript-eslint/no-explicit-any": "off"
},
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": ["tsconfig.jest.json"]
},
"files": [
"**/__tests__/**/*.[jt]s?(x)",
"**/?(*.)+(spec|test).[tj]s?(x)"
],
"plugins": ["jest"],
"extends": ["plugin:@typescript-eslint/recommended", "plugin:jest/recommended"]
"extends": [
"plugin:@typescript-eslint/recommended",
"plugin:jest/recommended"
]
}
]
}
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
"source.fixAll.eslint": "always"
},
"typescript.tsdk": "node_modules/typescript/lib"
}
29 changes: 25 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,18 +88,39 @@ class Example
```

### v1.1.0

Adds support for unknown errors

```javascript
import withRetry, { UnknownError } from '@teneff/with-retry'
import withRetry, { UnknownError } from "@teneff/with-retry";

function resolvesPromiseWithNonError() {
return Promise.reject('a string')
return Promise.reject("a string");
}

await withRetry({
errors: UnknownError
})(resolvesPromiseWithNonError)()
errors: UnknownError,
})(resolvesPromiseWithNonError)();
```

### v1.1.2

Fixes issue [#6](https://github.com/Teneff/withRetry/issues/6) preserving class context

```typescript
class Example {
private mockCallback = jest
.fn()
.mockRejectedValueOnce(new Error(`[${num}] mock error`))
.mockResolvedValue(`[${num}] success`);

@withRetry({
maxCalls: 4,
})
getData2(): Promise<string> {
return this.mockCallback("arg1", "arg2");
}
}
```

[got]: http://npmjs.com/package/got
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
{
"name": "@teneff/with-retry",
"version": "1.1.1",
"version": "1.1.2",
"description": "Decorator for retrying async operations",
"main": "index.js",
"types": "index.d.ts",
"scripts": {
"clean": "rimraf dist",
"build": "tsc",
"build": "tsc -p tsconfig.build.json",
"lint": "eslint src",
"test": "jest",
"typecheck": "yarn build --noEmit -p tsconfig.jest.json",
"codecov": "codecov",
"postbuild": "cp package.json dist; cp README.md dist"
},
Expand Down
55 changes: 46 additions & 9 deletions src/decorator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,64 @@ import withRetry from "./decorator";

const mockCallback = jest
.fn<Promise<string>, string[]>()
.mockRejectedValueOnce(new Error("mock error"))
.mockRejectedValueOnce(new Error("mock error"))
.mockRejectedValueOnce(new Error("mock error"))
.mockResolvedValue("success");
.mockRejectedValueOnce(new Error("[1] mock error"))
.mockRejectedValueOnce(new Error("[1] mock error"))
.mockRejectedValueOnce(new Error("[1] mock error"))
.mockResolvedValue("[1] success");

class Example {
readonly mockCallback: jest.Mock<Promise<string>, string[], any>;

constructor(public readonly num: number) {
this.mockCallback = jest
.fn<Promise<string>, string[]>()
.mockRejectedValueOnce(new Error(`[${num}] mock error`))
.mockResolvedValue(`[${num}] success`);
}

@withRetry({
maxCalls: 4,
})
getData(): Promise<string> {
return mockCallback("arg1", "arg2");
}

@withRetry({
maxCalls: 4,
})
getData2(): Promise<string> {
return this.mockCallback("arg1", "arg2");
}
}

describe("decorator", () => {
let result: Promise<string>;
beforeAll(() => {
result = new Example().getData();
describe("Example1", () => {
let result: Promise<string>;
beforeAll(() => {
result = new Example(1).getData();
});

it("should equal success", () => {
return expect(result).resolves.toEqual("[1] success");
});
});

it("should equal success", () => {
return expect(result).resolves.toEqual("success");
describe("Example2", () => {
const exmaple = new Example(2);
let result: string;

beforeAll(async () => {
try {
result = await exmaple.getData2();
} catch {}
});

it("should preserve this reference", () => {
return expect(result).toEqual("[2] success");
});

it("should have called mockCallback 2 times", () => {
expect(exmaple.mockCallback).toHaveBeenCalledTimes(2);
});
});
});
15 changes: 8 additions & 7 deletions src/decorator.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import withRetry from "./withRetry";

export default function (...args: Parameters<typeof withRetry>) {
return function <T, K extends keyof T>(
target: T,
export default function <T>(...args: Parameters<typeof withRetry>) {
return function <K extends string>(
target: { [key in K]: (...args: any[]) => Promise<T> },
propertyKey: K,
descriptor: PropertyDescriptor
): void {
Object.assign(descriptor, {
value: withRetry(...args)(descriptor.value),
descriptor?: TypedPropertyDescriptor<() => Promise<T>>
): any {
const method = target[propertyKey];
return Object.assign({}, descriptor, {
value: withRetry(...args)(method),
});
};
}
7 changes: 4 additions & 3 deletions src/withRetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,13 @@ export default function withRetry(
let { maxCalls } = settings;
const errors: Error[] = [];

return async (
return async function (
this: any,
...args: Parameters<typeof callback>
): ReturnType<typeof callback> => {
): ReturnType<typeof callback> {
do {
try {
return await callback(...args);
return await callback.apply(this, args);
} catch (error) {
const e = error instanceof Error ? error : new UnknownError(error);

Expand Down
12 changes: 12 additions & 0 deletions tsconfig.build.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"experimentalDecorators": true /* Enables experimental support for ES7 decorators. */,
"emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */,
},
"exclude": [
"**/*.spec.*",
"**/*.test.*",
"**/__*__/**/*"
]
}
1 change: 1 addition & 0 deletions tsconfig.jest.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
"emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */
},
"extends": "./tsconfig.json",
"include": ["src"],
"exclude": []
}
11 changes: 3 additions & 8 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

/* Basic Options */
// "incremental": true, /* Enable incremental compilation */
"target": "ES2017" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
"target": "ES2020" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
"module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
// "lib": [], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */
Expand Down Expand Up @@ -61,17 +61,12 @@
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */

/* Experimental Options */
// "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */,
// "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */,
"experimentalDecorators": true /* Enables experimental support for ES7 decorators. */,
"emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */,

/* Advanced Options */
"skipLibCheck": true /* Skip type checking of declaration files. */,
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
},
"include": ["src/**/*"],
"exclude": [
"**/*.spec.*",
"**/*.test.*",
"**/__*__/**/*"
]
}
Loading

0 comments on commit 3c0dc42

Please sign in to comment.