Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Javascript Node CircleCI 2.0 configuration file
#
# Check https://circleci.com/docs/2.0/language-javascript/ for more details
#
version: 2
jobs:
build:
working_directory: ~/node-catchie
docker:
- image: circleci/node:8.9
# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images
# documented at https://circleci.com/docs/2.0/circleci-images/
steps:
- checkout
- run:
name: Install make
command: sudo apt-get update && sudo apt-get -y install gcc make

# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ .Branch }}-{{ checksum "package.json" }}
- v1-dependencies-{{ .Branch }}
# fallback to using the latest cache if no exact match is found
- v1-dependencies
- run:
name: Install dependencies
command: make deps
- save_cache:
paths:
- node_modules
key: v1-dependencies-{{ .Branch }}-{{ checksum "package.json" }}
- run:
name: Test project
command: make test

# Special step used to persist a temporary file to be used by another job in the workflow
- persist_to_workspace:
root: ~/node-catchie
paths:
- coverage

sonarqube:
working_directory: ~/node-catchie
docker:
# Sonarqube need OpenJDK 8 to run his analysis correctly
- image: circleci/openjdk:8-jdk-browsers
steps:
- checkout
# Special step used to attach the workflow’s workspace to the current container
# Retrieve coverage's folder for sonarqube
- attach_workspace:
at: ~/node-catchie

# Sonarqube need to have node installed to run his analysis
- run:
name: Install node
command: |
sudo apt install curl
curl -sL https://deb.nodesource.com/setup_10.x | sudo bash -
sudo apt install nodejs
- run:
name: Sonar analysis
command: make sonar

workflows:
version: 2
build_and_test:
jobs:
- build
- sonarqube:
# sonarqube's job waiting for build's job before to run
requires:
- build
3 changes: 3 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ lib-cov
coverage
.sonar/

# Nyc coverage directory
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ pids
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
# Coverage directory used by tools like istanbul/nyc or sonar
coverage
.sonar/
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
Expand Down
3 changes: 2 additions & 1 deletion .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ pids
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
# Coverage directory used by tools like istanbul or nyc
coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
Expand Down
28 changes: 15 additions & 13 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,27 +1,29 @@
.PHONY: test

deps:
npm i
npm install

lint:
node_modules/.bin/eslint .
run:
npm start

test:
make lint
make cover
lint:
npm run lint

cover:
node_modules/.bin/istanbul cover node_modules/.bin/_mocha -- test --recursive --timeout=10000
npm run cover

test:
npm run test

sonar:
sed '/sonar.projectVersion/d' ./sonar-project.properties > tmp && mv tmp sonar-project.properties
echo sonar.projectVersion=`cat package.json | python -c "import json,sys;obj=json.load(sys.stdin);print obj['version'];"` >> sonar-project.properties
wget https://sonarsource.bintray.com/Distribution/sonar-scanner-cli/sonar-scanner-2.8.zip
unzip sonar-scanner-2.8.zip
ifdef CI_PULL_REQUEST
@sonar-scanner-2.8/bin/sonar-runner -e -Dsonar.analysis.mode=preview -Dsonar.github.pullRequest=${shell basename $(CI_PULL_REQUEST)} -Dsonar.github.repository=$(REPO_SLUG) -Dsonar.github.oauth=$(GITHUB_TOKEN) -Dsonar.login=$(SONAR_LOGIN) -Dsonar.password=$(SONAR_PASS) -Dsonar.host.url=$(SONAR_HOST_URL)
wget https://s3.eu-central-1.amazonaws.com/dialonce-cdn/utilities/sonar-scanner-cli.zip
unzip sonar-scanner-*
ifdef CIRCLE_PULL_REQUEST
@sonar-scanner/bin/sonar-scanner -e -Dsonar.analysis.mode=preview -Dsonar.github.pullRequest=${shell basename $(CIRCLE_PULL_REQUEST)} -Dsonar.github.repository=$(REPO_SLUG) -Dsonar.github.oauth=$(GITHUB_TOKEN) -Dsonar.login=$(SONAR_LOGIN) -Dsonar.password=$(SONAR_PASS) -Dsonar.host.url=$(SONAR_HOST_URL)
endif
ifeq ($(CIRCLE_BRANCH),develop)
@sonar-scanner-2.8/bin/sonar-runner -e -Dsonar.analysis.mode=publish -Dsonar.host.url=$(SONAR_HOST_URL) -Dsonar.login=$(SONAR_LOGIN) -Dsonar.password=$(SONAR_PASS)
@sonar-scanner/bin/sonar-scanner -e -Dsonar.analysis.mode=publish -Dsonar.host.url=$(SONAR_HOST_URL) -Dsonar.login=$(SONAR_LOGIN) -Dsonar.password=$(SONAR_PASS)
endif
rm -rf sonar-scanner-2.8 sonar-scanner-2.8.zip
rm -rf sonar-scanner*
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
## Catchie
## node-catchie

[![CircleCI](https://circleci.com/gh/dial-once/node-catchie.svg?style=svg)](https://circleci.com/gh/dial-once/node-catchie)
[![Sonar](http://proxy.dialonce.net/sonar/api/badges/gate?key=node-node-catchie)](http://sonar.dialonce.net/dashboard?id=node-node-catchie)
[![Sonar](http://proxy.dialonce.net/sonar/api/badges/measure?key=node-node-catchie&metric=ncloc)](http://sonar.dialonce.net/dashboard?id=node-node-catchie)
[![Sonar](http://proxy.dialonce.net/sonar/api/badges/measure?key=node-node-catchie&metric=coverage)](http://sonar.dialonce.net/dashboard?id=node-node-catchie)
[![Sonar](http://proxy.dialonce.net/sonar/api/badges/measure?key=node-node-catchie&metric=code_smells)](http://proxy.dialonce.net/sonar/api/badges/measure?key=node-node-catchie&metric=coverage)
[![Sonar](http://proxy.dialonce.net/sonar/api/badges/measure?key=node-node-catchie&metric=bugs)](http://sonar.dialonce.net/dashboard?id=node-node-catchie)
[![Sonar](http://proxy.dialonce.net/sonar/api/badges/measure?key=node-node-catchie&metric=sqale_debt_ratio)](http://sonar.dialonce.net/dashboard?id=node-node-catchie)


Repeats the provided function (sync and async) n times if an error occurs during its execution.

Expand All @@ -24,7 +32,7 @@ By default, a __console__ will be used as a logger
You can also silence the module and not let it log info about retries if you pass the boolean value:
```js
// 2nd parameter is silence { boolean }
const catchie = require('catchie')(new winston.Logger(), true);
const catchie = require('catchie')(new winston.Logger(), true);
```

## Usage
Expand Down
21 changes: 12 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
"description": "Retries a provided function n times on error",
"main": "src/index.js",
"scripts": {
"test": "node_modules/.bin/eslint . && node_modules/.bin/_mocha --recursive test",
"start": "node src/index.js",
"cover": "node_modules/.bin/istanbul cover node_modules/.bin/_mocha -- test --recursive"
"lint": "./node_modules/.bin/eslint .",
"cover": "CONSOLE_LOGGING=false ./node_modules/.bin/nyc --reporter=lcov ./node_modules/.bin/_mocha test --recursive --timeout=10000 --exit",
"test": "npm run lint && npm run cover"
},
"repository": {
"type": "git",
Expand All @@ -26,14 +27,16 @@
"url": "https://github.com/dial-once/node-catchie/issues"
},
"homepage": "https://github.com/dial-once/node-catchie#readme",
"dependencies": {
"sinon": "^2.3.1"
},
"devDependencies": {
"eslint": "^3.19.0",
"eslint-config-airbnb": "^15.0.1",
"eslint-plugin-import": "^2.3.0",
"eslint-plugin-jsx-a11y": "^5.0.3",
"eslint-plugin-react": "^7.0.1",
"istanbul": "^0.4.5",
"eslint": "^5.4.0",
"eslint-config-airbnb": "^17.1.0",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-jsx-a11y": "^6.1.1",
"eslint-plugin-react": "^7.11.1",
"mocha": "^5.2.0",
"sinon": "^2.3.1"
"nyc": "^12.0.2"
}
}
1 change: 0 additions & 1 deletion sonar-project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ sonar.projectName=node-catchie

sonar.sources=src
sonar.tests=test
sonar.language=js

sonar.dynamicAnalysis=reuseReports
sonar.javascript.jstest.reportsPath=coverage
Expand Down
94 changes: 48 additions & 46 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
let defaultLogger = console;

/**
@class Catchie
Consume errors and retry the function execution given times
/* eslint-disable no-use-before-define */

Supported environment variables:
CATCHIE_MAX_RETRY - cap amount of function retry
**/
/**
* @class Catchie
* Consume errors and retry the function execution given times
*
* Supported environment variables:
* CATCHIE_MAX_RETRY - cap amount of function retry
* */
class Catchie {
/**
@constructor
Construct instance of the module
@param logger {Object} - logger instnace
@param silent {boolean} - log or not the info about retries
**/
* @constructor
* Construct instance of the module
* @param logger {Object} - logger instnace
* @param silent {boolean} - log or not the info about retries
* */
constructor(logger, silent) {
this.logger = logger || defaultLogger;
this.successCount = 0;
Expand All @@ -22,39 +24,39 @@ class Catchie {
}

/**
@function clear
Reset the values of successCount and failureCount
**/
* @function clear
* Reset the values of successCount and failureCount
* */
clear() {
this.successCount = 0;
this.failureCount = 0;
}

/**
@function callCount
Get the total amount of given function calls
@return {number} - total amount of function calls
**/
* @function callCount
* Get the total amount of given function calls
* @return {number} - total amount of function calls
* */
get callCount() {
return this.successCount + this.failureCount;
}

/**
@function retry
Execute the function given amount of times
@param fn { Function } - a function returning value / Promise
@retryCount { string|number } - amount of times to repeat a function on error
@return
{ any } - if function is sync
{ Promise } - if function is async
@throws if after the retryCount times of function execution the function still ended up with an error
@throws if fn param is not a function
**/
* @function retry
* Execute the function given amount of times
* @param fn { Function } - a function returning value / Promise
* @retryCount { string|number } - amount of times to repeat a function on error
* @return
* { any } - if function is sync
* { Promise } - if function is async
* @throws if after the retryCount times of function execution the function still ended up with an error
* @throws if fn param is not a function
* */
retry(fn, retryCount = 1) {
this.clear();
let timesToRetry = parseInt(retryCount || process.env.CATCHIE_MAX_RETRY, 10);

if (isNaN(timesToRetry)) {
if (Number.isNaN(timesToRetry)) {
timesToRetry = 0;
}

Expand All @@ -73,7 +75,7 @@ class Catchie {
if (!this.silent) {
this.logger.info(`Retrying function due to ${e} error`);
}
return repeatable(...args); // eslint-disable-line
return repeatable(...args);
};

const repeatable = () => {
Expand All @@ -84,21 +86,21 @@ class Catchie {
if (value instanceof Promise) {
// for promises a plain-old-catch does not work
return Promise.resolve()
.then(() => value)
.then((result) => {
this.successCount ++;
return result;
})
.catch((e) => {
this.failureCount ++;
return loopCheck(e, arguments);
});
.then(() => value)
.then((result) => {
this.successCount++;
return result;
})
.catch((e) => {
this.failureCount++;
return loopCheck(e, arguments);
});
}
// if did not throw -> return either a Promise or an actual value
this.successCount ++;
this.successCount++;
return value;
} catch (e) {
this.failureCount ++;
this.failureCount++;
return loopCheck(e, arguments);
}
};
Expand All @@ -108,11 +110,11 @@ class Catchie {
}

/**
@function
@param loggerInstance {object} - instance of a logger to use
@param silet {boolean} - log or not log the info about retries
@return {Object} - new instance of the module
**/
* @function
* @param loggerInstance {object} - instance of a logger to use
* @param silet {boolean} - log or not log the info about retries
* @return {Object} - new instance of the module
* */
module.exports = (loggerInstance, silent = false) => {
if (![null, undefined].includes(loggerInstance)) {
defaultLogger = loggerInstance;
Expand Down
Loading