diff --git a/.eslintrc.json b/.eslintrc.json index aa47d6b..c5b77ae 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,38 +1,36 @@ { - "env": { - "node": true, - "mocha": true, - "commonjs": true - }, - "plugins": ["prettier", "simple-import-sort", "eslint-plugin-import"], - "extends": [ - "airbnb-base", - "prettier" - ], - "parserOptions": { - "ecmaVersion": 12 - }, - "rules": { - "simple-import-sort/imports": "error", - "no-underscore-dangle": "off", - "camelcase":"off", - "no-unused-vars":"warn", - "func-names": "off", - "no-param-reassign": "warn", - "consistent-return":"warn", - "no-restricted-syntax":"warn", - "global-require":"off", - "radix":"off", - "no-await-in-loop":"off", - "class-methods-use-this":"warn", - "no-return-await":"warn", - "no-new-wrappers": "warn", - "prefer-destructuring": "warn", - "no-shadow":"warn", - "import/order": "warn", - "no-unused-expressions": "warn", - "import/no-extraneous-dependencies": "warn" , - "import/no-unresolved": "warn", - "no-plusplus":"warn", - "no-continue":"off" } + "env": { + "node": true, + "mocha": true, + "commonjs": true + }, + "plugins": ["prettier", "simple-import-sort", "eslint-plugin-import"], + "extends": ["airbnb-base", "prettier"], + "parserOptions": { + "ecmaVersion": 12 + }, + "rules": { + "simple-import-sort/imports": "error", + "no-underscore-dangle": "off", + "camelcase": "off", + "no-unused-vars": "warn", + "func-names": "off", + "no-param-reassign": "warn", + "consistent-return": "warn", + "no-restricted-syntax": "warn", + "global-require": "off", + "radix": "off", + "no-await-in-loop": "off", + "class-methods-use-this": "warn", + "no-return-await": "warn", + "no-new-wrappers": "warn", + "prefer-destructuring": "warn", + "no-shadow": "warn", + "import/order": "warn", + "no-unused-expressions": "warn", + "import/no-extraneous-dependencies": "warn", + "import/no-unresolved": "warn", + "no-plusplus": "warn", + "no-continue": "off" + } } diff --git a/.github/workflows/treetracker-api-build-deploy-dev.yml b/.github/workflows/treetracker-api-build-deploy-dev.yml index db79960..cf629f9 100644 --- a/.github/workflows/treetracker-api-build-deploy-dev.yml +++ b/.github/workflows/treetracker-api-build-deploy-dev.yml @@ -26,10 +26,9 @@ jobs: - name: run ESLint run: npm run lint working-directory: ${{ env.project-directory }} -# - name: run api tests -# run: npm run test -# working-directory: ${{ env.project-directory }} - + # - name: run api tests + # run: npm run test + # working-directory: ${{ env.project-directory }} release: name: Release @@ -41,38 +40,38 @@ jobs: github.repository == "Greenstand/${{ github.event.repository.name }}" steps: - uses: actions/checkout@v2 - + - name: Use Node.js 16.x uses: actions/setup-node@v1 with: node-version: '16.x' - + - name: npm clean install run: npm ci working-directory: ${{ env.project-directory }} - + - run: npm i -g semantic-release @semantic-release/{git,exec,changelog} - + - run: semantic-release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - + - name: get-npm-version id: package-version uses: martinbeentjes/npm-get-version-action@master - + - name: Set up QEMU uses: docker/setup-qemu-action@v1 - + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 - + - name: Login to DockerHub uses: docker/login-action@v1 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - + - name: Build snapshot and push on merge id: docker_build_release uses: docker/build-push-action@v2 @@ -81,12 +80,12 @@ jobs: file: ./Dockerfile push: true tags: greenstand/${{ github.event.repository.name }}:${{ steps.package-version.outputs.current-version }} - + - id: export_bumped_version run: | export BUMPED_VERSION="${{ steps.package-version.outputs.current-version }}" echo "::set-output name=bumped_version::${BUMPED_VERSION}" - + outputs: bumped_version: ${{ steps.export_bumped_version.outputs.bumped_version }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 79681a0..7bde5c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,198 +1,172 @@ # [1.6.0](https://github.com/Greenstand/treetracker-earnings/compare/v1.5.0...v1.6.0) (2022-02-08) - ### Bug Fixes -* add authorization header to integration tests ([5270dda](https://github.com/Greenstand/treetracker-earnings/commit/5270dda55a3ee984c741ad24da8f988a45b5f76f)) - +- add authorization header to integration tests ([5270dda](https://github.com/Greenstand/treetracker-earnings/commit/5270dda55a3ee984c741ad24da8f988a45b5f76f)) ### Features -* **earnings patch:** include payment confirmed by field in processing single payment ([da0d7a0](https://github.com/Greenstand/treetracker-earnings/commit/da0d7a0c4bf551d28c34f07d943fbbc602da46f2)) +- **earnings patch:** include payment confirmed by field in processing single payment ([da0d7a0](https://github.com/Greenstand/treetracker-earnings/commit/da0d7a0c4bf551d28c34f07d943fbbc602da46f2)) # [1.5.0](https://github.com/Greenstand/treetracker-earnings/compare/v1.4.2...v1.5.0) (2022-02-03) - ### Features -* **batch update earnings:** set payment comfirmed by field ([2c52c90](https://github.com/Greenstand/treetracker-earnings/commit/2c52c90afe4457e2b1bd3665a2243a7ddea4905d)) -* **earnings model:** include id field in earnings model ([bd78348](https://github.com/Greenstand/treetracker-earnings/commit/bd78348bfa447bc9d1c31606a81282f18d0b2a3b)) -* **earnings model:** return concatenated first name and last name as grower name ([180b849](https://github.com/Greenstand/treetracker-earnings/commit/180b8493ed630c43dea2f33e0d342ea8fc231c62)) -* **earnings patch:** set confirmation method in processing single earning ([04e247a](https://github.com/Greenstand/treetracker-earnings/commit/04e247ab4592a9ba46addfc4f80fcaab5a6fab0c)) -* **earnings:** improve grower and phone filters ([9532d4a](https://github.com/Greenstand/treetracker-earnings/commit/9532d4ad3eb484d1c910a97307520cea09d3ca40)) -* **earnings:** undo including payment confirmed by in processing payments ([c64b1a5](https://github.com/Greenstand/treetracker-earnings/commit/c64b1a551138478a2ce5d0055f812b4b2c382e01)) -* **migrations:** add migration to change confirmed by data type to bigint ([5c2400c](https://github.com/Greenstand/treetracker-earnings/commit/5c2400ca9db264d85692fb05a7c23995085339f6)) +- **batch update earnings:** set payment comfirmed by field ([2c52c90](https://github.com/Greenstand/treetracker-earnings/commit/2c52c90afe4457e2b1bd3665a2243a7ddea4905d)) +- **earnings model:** include id field in earnings model ([bd78348](https://github.com/Greenstand/treetracker-earnings/commit/bd78348bfa447bc9d1c31606a81282f18d0b2a3b)) +- **earnings model:** return concatenated first name and last name as grower name ([180b849](https://github.com/Greenstand/treetracker-earnings/commit/180b8493ed630c43dea2f33e0d342ea8fc231c62)) +- **earnings patch:** set confirmation method in processing single earning ([04e247a](https://github.com/Greenstand/treetracker-earnings/commit/04e247ab4592a9ba46addfc4f80fcaab5a6fab0c)) +- **earnings:** improve grower and phone filters ([9532d4a](https://github.com/Greenstand/treetracker-earnings/commit/9532d4ad3eb484d1c910a97307520cea09d3ca40)) +- **earnings:** undo including payment confirmed by in processing payments ([c64b1a5](https://github.com/Greenstand/treetracker-earnings/commit/c64b1a551138478a2ce5d0055f812b4b2c382e01)) +- **migrations:** add migration to change confirmed by data type to bigint ([5c2400c](https://github.com/Greenstand/treetracker-earnings/commit/5c2400ca9db264d85692fb05a7c23995085339f6)) ## [1.4.2](https://github.com/Greenstand/treetracker-earnings/compare/v1.4.1...v1.4.2) (2022-01-18) - ### Bug Fixes -* include blank template fields ([3224abc](https://github.com/Greenstand/treetracker-earnings/commit/3224abc4b6251522078d59aa268af5800f4f2f28)) +- include blank template fields ([3224abc](https://github.com/Greenstand/treetracker-earnings/commit/3224abc4b6251522078d59aa268af5800f4f2f28)) ## [1.4.1](https://github.com/Greenstand/treetracker-earnings/compare/v1.4.0...v1.4.1) (2021-12-14) - ### Bug Fixes -* update sealed secret ([c9fd173](https://github.com/Greenstand/treetracker-earnings/commit/c9fd173b27fd5103cbbd5e40d7cf9e6a30193c2c)) +- update sealed secret ([c9fd173](https://github.com/Greenstand/treetracker-earnings/commit/c9fd173b27fd5103cbbd5e40d7cf9e6a30193c2c)) # [1.4.0](https://github.com/Greenstand/treetracker-earnings/compare/v1.3.1...v1.4.0) (2021-11-23) - ### Bug Fixes -* add external api call to get phone value for batch requests ([7826245](https://github.com/Greenstand/treetracker-earnings/commit/7826245a8fc8083ab92687de39adb5679da6bec9)) -* add filter params to next/prev links ([66596e0](https://github.com/Greenstand/treetracker-earnings/commit/66596e04f86c5de7e98bd508fd7762ee8e0400fe)) -* external api call issue ([86def0d](https://github.com/Greenstand/treetracker-earnings/commit/86def0dc3ab07fa81239d38ca9b35c911b7220d5)) -* returning correct query in the links ([6420713](https://github.com/Greenstand/treetracker-earnings/commit/642071385a11fddae676c3435791090c3f763768)) - +- add external api call to get phone value for batch requests ([7826245](https://github.com/Greenstand/treetracker-earnings/commit/7826245a8fc8083ab92687de39adb5679da6bec9)) +- add filter params to next/prev links ([66596e0](https://github.com/Greenstand/treetracker-earnings/commit/66596e04f86c5de7e98bd508fd7762ee8e0400fe)) +- external api call issue ([86def0d](https://github.com/Greenstand/treetracker-earnings/commit/86def0dc3ab07fa81239d38ca9b35c911b7220d5)) +- returning correct query in the links ([6420713](https://github.com/Greenstand/treetracker-earnings/commit/642071385a11fddae676c3435791090c3f763768)) ### Features -* return and add sorting by grower and funder ([a0d7a24](https://github.com/Greenstand/treetracker-earnings/commit/a0d7a24bdc0c17119daed9e4daa8e9bacf1a71cd)) +- return and add sorting by grower and funder ([a0d7a24](https://github.com/Greenstand/treetracker-earnings/commit/a0d7a24bdc0c17119daed9e4daa8e9bacf1a71cd)) ## [1.3.1](https://github.com/Greenstand/treetracker-earnings/compare/v1.3.0...v1.3.1) (2021-11-11) - ### Bug Fixes -* rename overlay dir ([814eed0](https://github.com/Greenstand/treetracker-earnings/commit/814eed0883d5bb29ad63d62566fffdcd1e33663d)) +- rename overlay dir ([814eed0](https://github.com/Greenstand/treetracker-earnings/commit/814eed0883d5bb29ad63d62566fffdcd1e33663d)) # [1.3.0](https://github.com/Greenstand/treetracker-earnings/compare/v1.2.0...v1.3.0) (2021-11-11) - ### Bug Fixes -* lint fix ([cc8eac1](https://github.com/Greenstand/treetracker-earnings/commit/cc8eac107d3c385c9285b4a3078ee574f42ea0a7)) -* remove schema creationg from migrations ([41479e6](https://github.com/Greenstand/treetracker-earnings/commit/41479e6e0ab4692d95479ea8e462e594d413e158)) - +- lint fix ([cc8eac1](https://github.com/Greenstand/treetracker-earnings/commit/cc8eac107d3c385c9285b4a3078ee574f42ea0a7)) +- remove schema creationg from migrations ([41479e6](https://github.com/Greenstand/treetracker-earnings/commit/41479e6e0ab4692d95479ea8e462e594d413e158)) ### Features -* add database secrets for test and prod ([5bcd255](https://github.com/Greenstand/treetracker-earnings/commit/5bcd255f3b8b5b9b2091642b80becad0d77b744e)) -* add Sierra Leone currency to enum ([513d285](https://github.com/Greenstand/treetracker-earnings/commit/513d285e8bae8aac767b7ff74c0fe828e7819f53)) +- add database secrets for test and prod ([5bcd255](https://github.com/Greenstand/treetracker-earnings/commit/5bcd255f3b8b5b9b2091642b80becad0d77b744e)) +- add Sierra Leone currency to enum ([513d285](https://github.com/Greenstand/treetracker-earnings/commit/513d285e8bae8aac767b7ff74c0fe828e7819f53)) # [1.2.0](https://github.com/Greenstand/treetracker-earnings/compare/v1.1.10...v1.2.0) (2021-11-04) - ### Bug Fixes -* add earnings schema to batch repository ([6563b9b](https://github.com/Greenstand/treetracker-earnings/commit/6563b9b99f92f37efb7369851e3a660225ff230a)) -* change earnings/batch GET to use streams ([0980970](https://github.com/Greenstand/treetracker-earnings/commit/0980970be784aa9135b41a8ceb86b1aa6e283b42)) -* rename column consolidation_id to consolidation_rule_id ([526c45b](https://github.com/Greenstand/treetracker-earnings/commit/526c45b036f0ede71f25f0846b8a6057d5b61a3e)) - +- add earnings schema to batch repository ([6563b9b](https://github.com/Greenstand/treetracker-earnings/commit/6563b9b99f92f37efb7369851e3a660225ff230a)) +- change earnings/batch GET to use streams ([0980970](https://github.com/Greenstand/treetracker-earnings/commit/0980970be784aa9135b41a8ceb86b1aa6e283b42)) +- rename column consolidation_id to consolidation_rule_id ([526c45b](https://github.com/Greenstand/treetracker-earnings/commit/526c45b036f0ede71f25f0846b8a6057d5b61a3e)) ### Features -* port earning/batch PATCH to using streams ([d109d10](https://github.com/Greenstand/treetracker-earnings/commit/d109d10f7df3087a71de730a5be1d4cc425ad701)) -* put add sorting support for id, amount, effective_payment_date, payment_system ([3c6536c](https://github.com/Greenstand/treetracker-earnings/commit/3c6536c547fdf3b288b27f0877fe2dba535be00f)) -* return total count ([748f993](https://github.com/Greenstand/treetracker-earnings/commit/748f99366fe567a3bf2583cba3a9b4bfd3dbe24e)) +- port earning/batch PATCH to using streams ([d109d10](https://github.com/Greenstand/treetracker-earnings/commit/d109d10f7df3087a71de730a5be1d4cc425ad701)) +- put add sorting support for id, amount, effective_payment_date, payment_system ([3c6536c](https://github.com/Greenstand/treetracker-earnings/commit/3c6536c547fdf3b288b27f0877fe2dba535be00f)) +- return total count ([748f993](https://github.com/Greenstand/treetracker-earnings/commit/748f99366fe567a3bf2583cba3a9b4bfd3dbe24e)) ## [1.1.10](https://github.com/Greenstand/treetracker-earnings/compare/v1.1.9...v1.1.10) (2021-11-03) - ### Bug Fixes -* update CORS settings ([98d8205](https://github.com/Greenstand/treetracker-earnings/commit/98d8205bc138bb0a8ab676579d32f5be6b4e57bd)) +- update CORS settings ([98d8205](https://github.com/Greenstand/treetracker-earnings/commit/98d8205bc138bb0a8ab676579d32f5be6b4e57bd)) ## [1.1.9](https://github.com/Greenstand/treetracker-earnings/compare/v1.1.8...v1.1.9) (2021-11-03) - ### Bug Fixes -* add authorization header to CORS ([302c4a0](https://github.com/Greenstand/treetracker-earnings/commit/302c4a00b4517106d62fb9bd20aeb0e7b5be9fe8)) +- add authorization header to CORS ([302c4a0](https://github.com/Greenstand/treetracker-earnings/commit/302c4a00b4517106d62fb9bd20aeb0e7b5be9fe8)) ## [1.1.8](https://github.com/Greenstand/treetracker-earnings/compare/v1.1.7...v1.1.8) (2021-11-02) - ### Bug Fixes -* cors ([64f5eca](https://github.com/Greenstand/treetracker-earnings/commit/64f5ecaf12eb0a132ec6beefd6d25e0d5d36e99a)) +- cors ([64f5eca](https://github.com/Greenstand/treetracker-earnings/commit/64f5ecaf12eb0a132ec6beefd6d25e0d5d36e99a)) ## [1.1.7](https://github.com/Greenstand/treetracker-earnings/compare/v1.1.6...v1.1.7) (2021-10-29) - ### Bug Fixes -* correct the mapping file ([de68150](https://github.com/Greenstand/treetracker-earnings/commit/de6815002fc2302897c3ba28cdd3d3248ec3f40c)) +- correct the mapping file ([de68150](https://github.com/Greenstand/treetracker-earnings/commit/de6815002fc2302897c3ba28cdd3d3248ec3f40c)) ## [1.1.6](https://github.com/Greenstand/treetracker-earnings/compare/v1.1.5...v1.1.6) (2021-10-29) - ### Bug Fixes -* open up CORS for dev ([71906ab](https://github.com/Greenstand/treetracker-earnings/commit/71906abbd1753c9628d5cbe00d5e5c9665447e03)) +- open up CORS for dev ([71906ab](https://github.com/Greenstand/treetracker-earnings/commit/71906abbd1753c9628d5cbe00d5e5c9665447e03)) ## [1.1.5](https://github.com/Greenstand/treetracker-earnings/compare/v1.1.4...v1.1.5) (2021-10-20) - ### Bug Fixes -* do not use public schema ([8980263](https://github.com/Greenstand/treetracker-earnings/commit/8980263e7fa5afcb389b7cb695ce9f2914f89d6e)) +- do not use public schema ([8980263](https://github.com/Greenstand/treetracker-earnings/commit/8980263e7fa5afcb389b7cb695ce9f2914f89d6e)) ## [1.1.4](https://github.com/Greenstand/treetracker-earnings/compare/v1.1.3...v1.1.4) (2021-10-20) - ### Bug Fixes -* correct name of secret ([b5b4a3a](https://github.com/Greenstand/treetracker-earnings/commit/b5b4a3a6c9695f8c2b8ae57916002a85040eae2a)) +- correct name of secret ([b5b4a3a](https://github.com/Greenstand/treetracker-earnings/commit/b5b4a3a6c9695f8c2b8ae57916002a85040eae2a)) ## [1.1.3](https://github.com/Greenstand/treetracker-earnings/compare/v1.1.2...v1.1.3) (2021-10-20) - ### Bug Fixes -* set up service name ([66c1817](https://github.com/Greenstand/treetracker-earnings/commit/66c1817e05fb859db151c109116b999e98cfb10a)) +- set up service name ([66c1817](https://github.com/Greenstand/treetracker-earnings/commit/66c1817e05fb859db151c109116b999e98cfb10a)) ## [1.1.2](https://github.com/Greenstand/treetracker-earnings/compare/v1.1.1...v1.1.2) (2021-10-20) - ### Bug Fixes -* correct namespace ([3f185be](https://github.com/Greenstand/treetracker-earnings/commit/3f185be35cf71b24051b9a8147b3e409f9dd6bbf)) +- correct namespace ([3f185be](https://github.com/Greenstand/treetracker-earnings/commit/3f185be35cf71b24051b9a8147b3e409f9dd6bbf)) ## [1.1.1](https://github.com/Greenstand/treetracker-earnings/compare/v1.1.0...v1.1.1) (2021-10-20) - ### Bug Fixes -* add namespace ([e91cf93](https://github.com/Greenstand/treetracker-earnings/commit/e91cf93ebe64d4da8a282c638424981a4577759b)) -* add service name ([7430fa3](https://github.com/Greenstand/treetracker-earnings/commit/7430fa3fd3d59a97e76b1200eccb697ad3e25f34)) -* update database secret ([fb6025c](https://github.com/Greenstand/treetracker-earnings/commit/fb6025c716528b8a751bf1c155e5ff648a761061)) +- add namespace ([e91cf93](https://github.com/Greenstand/treetracker-earnings/commit/e91cf93ebe64d4da8a282c638424981a4577759b)) +- add service name ([7430fa3](https://github.com/Greenstand/treetracker-earnings/commit/7430fa3fd3d59a97e76b1200eccb697ad3e25f34)) +- update database secret ([fb6025c](https://github.com/Greenstand/treetracker-earnings/commit/fb6025c716528b8a751bf1c155e5ff648a761061)) # [1.1.0](https://github.com/Greenstand/treetracker-earnings/compare/v1.0.1...v1.1.0) (2021-10-20) - ### Features -* update to node 16 alpine image ([f07148b](https://github.com/Greenstand/treetracker-earnings/commit/f07148b618dd9d5d54038f36094b9242ae9ad2da)) +- update to node 16 alpine image ([f07148b](https://github.com/Greenstand/treetracker-earnings/commit/f07148b618dd9d5d54038f36094b9242ae9ad2da)) ## [1.0.1](https://github.com/Greenstand/treetracker-earnings/compare/v1.0.0...v1.0.1) (2021-10-20) - ### Bug Fixes -* run npm ci without silent to debug ([f23eb61](https://github.com/Greenstand/treetracker-earnings/commit/f23eb6163a893a9589db96a4090c063df37c9da8)) +- run npm ci without silent to debug ([f23eb61](https://github.com/Greenstand/treetracker-earnings/commit/f23eb6163a893a9589db96a4090c063df37c9da8)) # 1.0.0 (2021-10-20) - ### Bug Fixes -* check husky ([0a96bc8](https://github.com/Greenstand/treetracker-earnings/commit/0a96bc8ef529e4af7b8fc334bec8acedb4298eb9)) -* configure project name in package.json ([97424b9](https://github.com/Greenstand/treetracker-earnings/commit/97424b97048c3684c6633f9bc83417af4f63e0e3)) -* disable test automation for now ([84bd19a](https://github.com/Greenstand/treetracker-earnings/commit/84bd19a3628cd82badf8f6a037c39beb44a648e6)) -* force deployment ([c944dfe](https://github.com/Greenstand/treetracker-earnings/commit/c944dfe545fce3b176838b53b49629455e6f97f9)) -* force deployment ([f747e41](https://github.com/Greenstand/treetracker-earnings/commit/f747e419d18fff81e8c83238b55e210a516b4989)) -* lint:fix applied ([9e61b7c](https://github.com/Greenstand/treetracker-earnings/commit/9e61b7c6aceea415e717262bafff4ab0d106f2ad)) -* resolve lint errors ([6024f90](https://github.com/Greenstand/treetracker-earnings/commit/6024f90899296055783a1b50f71a187a191142ea)) -* update workflows to use node 16 ([c8c62af](https://github.com/Greenstand/treetracker-earnings/commit/c8c62afec1210b27df17ee05eca289a8d0ba950f)) -* use main branch ([3b048d7](https://github.com/Greenstand/treetracker-earnings/commit/3b048d7ae69cb45b022852f3f68c24de8483cd52)) - +- check husky ([0a96bc8](https://github.com/Greenstand/treetracker-earnings/commit/0a96bc8ef529e4af7b8fc334bec8acedb4298eb9)) +- configure project name in package.json ([97424b9](https://github.com/Greenstand/treetracker-earnings/commit/97424b97048c3684c6633f9bc83417af4f63e0e3)) +- disable test automation for now ([84bd19a](https://github.com/Greenstand/treetracker-earnings/commit/84bd19a3628cd82badf8f6a037c39beb44a648e6)) +- force deployment ([c944dfe](https://github.com/Greenstand/treetracker-earnings/commit/c944dfe545fce3b176838b53b49629455e6f97f9)) +- force deployment ([f747e41](https://github.com/Greenstand/treetracker-earnings/commit/f747e419d18fff81e8c83238b55e210a516b4989)) +- lint:fix applied ([9e61b7c](https://github.com/Greenstand/treetracker-earnings/commit/9e61b7c6aceea415e717262bafff4ab0d106f2ad)) +- resolve lint errors ([6024f90](https://github.com/Greenstand/treetracker-earnings/commit/6024f90899296055783a1b50f71a187a191142ea)) +- update workflows to use node 16 ([c8c62af](https://github.com/Greenstand/treetracker-earnings/commit/c8c62afec1210b27df17ee05eca289a8d0ba950f)) +- use main branch ([3b048d7](https://github.com/Greenstand/treetracker-earnings/commit/3b048d7ae69cb45b022852f3f68c24de8483cd52)) ### Features -* add earnings batch endpoints ([c229c39](https://github.com/Greenstand/treetracker-earnings/commit/c229c397df44d9ae58be34329cb0351703791b19)) -* earnings get and patch ([9d9a1da](https://github.com/Greenstand/treetracker-earnings/commit/9d9a1da4c38f8cbcde6fed0d638d9a4f3d081d55)) -* tests added ([8215178](https://github.com/Greenstand/treetracker-earnings/commit/8215178d573df357cfebddcef434815dad14a5ea)) +- add earnings batch endpoints ([c229c39](https://github.com/Greenstand/treetracker-earnings/commit/c229c397df44d9ae58be34329cb0351703791b19)) +- earnings get and patch ([9d9a1da](https://github.com/Greenstand/treetracker-earnings/commit/9d9a1da4c38f8cbcde6fed0d638d9a4f3d081d55)) +- tests added ([8215178](https://github.com/Greenstand/treetracker-earnings/commit/8215178d573df357cfebddcef434815dad14a5ea)) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d1f6951..23e4289 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,4 +2,4 @@ Create your local git branch and rebase it from the shared master branch. Please make sure to rebuild your local database schemas using the migrations (as illustrated in the Database Setup section above) to capture any latest updates/changes. -When you are ready to submit a pull request from your local branch, please rebase your branch off of the shared master branch again to integrate any new updates in the codebase before submitting. Any developers joining the project should feel free to review any outstanding pull requests and assign themselves to any open tickets on the Issues list. +When you are ready to submit a pull request from your local branch, please rebase your branch off of the shared master branch again to integrate any new updates in the codebase before submitting. Any developers joining the project should feel free to review any outstanding pull requests and assign themselves to any open tickets on the Issues list. diff --git a/README.md b/README.md index f010c3f..68e596a 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,25 @@ # Name of this microservice - + Description of this microservice # Development toolkit -This repository was created from Greenstand's template for microservice projects. This means it comes with many development tools that we use for development and deployment. As a contributor to this repository, you should learn and use these tools. They are outlined below. +This repository was created from Greenstand's template for microservice projects. This means it comes with many development tools that we use for development and deployment. As a contributor to this repository, you should learn and use these tools. They are outlined below. ## db-migrate + ## Conventional Commits + ## husky + ## prettier / lint + ## github actions -## mocha +## mocha # Getting Started - + ## Project Setup Open terminal and navigate to a folder to install this project: @@ -24,14 +28,13 @@ Open terminal and navigate to a folder to install this project: git clone https://github.com/Greenstand/treetracker-repository-name.git ``` -Install all necessary dependencies: + +Install all necessary dependencies: ``` npm install ``` - - ### Database Setup This repository using db-migrate to manage database migrations for its schema. @@ -52,37 +55,34 @@ Documentation for db-migrate: https://db-migrate.readthedocs.io/en/latest/ This project use multiple layer structure to build the whole system. Similar with MVC structure: -![layers](/layers.png "layers") - +![layers](/layers.png 'layers') -* **Protocol layer** +- **Protocol layer** Wallet API offers RESTFul API interace based on HTTP protocol. We use Express to handle all HTTP requests. The Express-routers work like the controller role in MVC, they receive the requests and parameters from client, and translate it and dispatch tasks to appropriate business objects. Then receive the result from them, translate to the 'view', the JSON response, to client. -* **Service layer** +- **Service layer** -Both service layer and model layer are where all the business logic is located. Comparing to the Model , `service` object don't have state (stateless). +Both service layer and model layer are where all the business logic is located. Comparing to the Model , `service` object don't have state (stateless). Please put business logic code into service object when it is hard to put them into the `Model` object. Because we didn't use Factory or dependency injection to create object, so service layer also can be used as Factory to create `model` object. -* **Model layer** +- **Model layer** -The business model, major business logic is here. They are real object, in the perspective of object oriented programming: they have states, they have the method to do stuff. +The business model, major business logic is here. They are real object, in the perspective of object oriented programming: they have states, they have the method to do stuff. There are more discussion about this, check below selection. -* **Repository layer** +- **Repository layer** Repository is responsible for communicate with the real database, this isolation brings flexibility for us, for example, we can consider replace the implementation of the storage infrastructure in the future. All the SQL statements should be here. - - # How to test ## Unit test @@ -106,6 +106,7 @@ npm run test-integration ``` ## Database seeding test + In order to efficiently run our integration tests, we rely on automated database seeding/clearing functions to mock database entries. To test these functions, run: ``` @@ -135,7 +136,3 @@ npm run server-test ``` This command would run a API server locally, and seed some basic data into DB (the same with the data we used in the integration test). - - - - diff --git a/__tests__/api-tests/earnings-api.spec.js b/__tests__/api-tests/earnings-api.spec.js index 3cd5b0a..4ed8d01 100644 --- a/__tests__/api-tests/earnings-api.spec.js +++ b/__tests__/api-tests/earnings-api.spec.js @@ -10,8 +10,8 @@ const { } = require('./seed-data-creation'); const { GenericObject } = require('./generic-class'); -const jwtToken = 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidXNlck5hbWUiOiJhZG1pbiIsImZpcnN0TmFtZSI6IkFkbWluIiwibGFzdE5hbWUiOiJQYW5lbCIsInBhc3N3b3JkSGFzaCI6IjQ0ZDE0MmNkMjBhMWQxZTE1YzQxOWZhOThkZTc5Y2U5OTc0MWNjNDY4NjZhYzI2MjY4N2ViNDQ2MGM0Y2NiNDIzZTdjMzU2ZDlmYzQwYjMxYWVjZmU2ODYyMzYyNjMzOWNmZmQ4YTg5YzYxYjMyOTdlY2I1YzRiZTYzZDFkMDY5Iiwic2FsdCI6IkQ4OFdDcCIsImVtYWlsIjoiYWRtaW5AZ3JlZW5zdGFuZC5vcmciLCJhY3RpdmUiOnRydWUsImNyZWF0ZWRBdCI6IjIwMjAtMDgtMDNUMTg6NDc6NDcuMjU4WiIsImVuYWJsZWQiOnRydWUsInJvbGUiOls0NiwxLDJdLCJyb2xlTmFtZXMiOlsiRWFybmluZ3MgTWFuYWdlciIsIkFkbWluIiwiVHJlZSBNYW5hZ2VyIl0sInBvbGljeSI6eyJwb2xpY2llcyI6W3sibmFtZSI6Imxpc3RfZWFybmluZ3MiLCJkZXNjcmlwdGlvbiI6IkNhbiB2aWV3IGVhcm5pbmdzIn0seyJuYW1lIjoibWFuYWdlX2Vhcm5pbmdzIiwiZGVzY3JpcHRpb24iOiJDYW4gbW9kaWZ5L2V4cG9ydCBlYXJuaW5ncyJ9LHsibmFtZSI6InN1cGVyX3Blcm1pc3Npb24iLCJkZXNjcmlwdGlvbiI6IkNhbiBkbyBhbnl0aGluZyJ9LHsibmFtZSI6Imxpc3RfdXNlciIsImRlc2NyaXB0aW9uIjoiQ2FuIHZpZXcgYWRtaW4gdXNlcnMifSx7Im5hbWUiOiJtYW5hZ2VyX3VzZXIiLCJkZXNjcmlwdGlvbiI6IkNhbiBjcmVhdGUvbW9kaWZ5IGFkbWluIHVzZXIifSx7Im5hbWUiOiJsaXN0X3RyZWUiLCJkZXNjcmlwdGlvbiI6IkNhbiB2aWV3IHRyZWVzIn0seyJuYW1lIjoiYXBwcm92ZV90cmVlIiwiZGVzY3JpcHRpb24iOiJDYW4gYXBwcm92ZS9yZWplY3QgdHJlZXMifV19LCJpYXQiOjE2NDI4NzIyNjV9.JYWYUo7B6y2jNyoaaw9uA0jTU4AYIZLXg0oGwF3XoVE' - +const jwtToken = + 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwidXNlck5hbWUiOiJhZG1pbiIsImZpcnN0TmFtZSI6IkFkbWluIiwibGFzdE5hbWUiOiJQYW5lbCIsInBhc3N3b3JkSGFzaCI6IjQ0ZDE0MmNkMjBhMWQxZTE1YzQxOWZhOThkZTc5Y2U5OTc0MWNjNDY4NjZhYzI2MjY4N2ViNDQ2MGM0Y2NiNDIzZTdjMzU2ZDlmYzQwYjMxYWVjZmU2ODYyMzYyNjMzOWNmZmQ4YTg5YzYxYjMyOTdlY2I1YzRiZTYzZDFkMDY5Iiwic2FsdCI6IkQ4OFdDcCIsImVtYWlsIjoiYWRtaW5AZ3JlZW5zdGFuZC5vcmciLCJhY3RpdmUiOnRydWUsImNyZWF0ZWRBdCI6IjIwMjAtMDgtMDNUMTg6NDc6NDcuMjU4WiIsImVuYWJsZWQiOnRydWUsInJvbGUiOls0NiwxLDJdLCJyb2xlTmFtZXMiOlsiRWFybmluZ3MgTWFuYWdlciIsIkFkbWluIiwiVHJlZSBNYW5hZ2VyIl0sInBvbGljeSI6eyJwb2xpY2llcyI6W3sibmFtZSI6Imxpc3RfZWFybmluZ3MiLCJkZXNjcmlwdGlvbiI6IkNhbiB2aWV3IGVhcm5pbmdzIn0seyJuYW1lIjoibWFuYWdlX2Vhcm5pbmdzIiwiZGVzY3JpcHRpb24iOiJDYW4gbW9kaWZ5L2V4cG9ydCBlYXJuaW5ncyJ9LHsibmFtZSI6InN1cGVyX3Blcm1pc3Npb24iLCJkZXNjcmlwdGlvbiI6IkNhbiBkbyBhbnl0aGluZyJ9LHsibmFtZSI6Imxpc3RfdXNlciIsImRlc2NyaXB0aW9uIjoiQ2FuIHZpZXcgYWRtaW4gdXNlcnMifSx7Im5hbWUiOiJtYW5hZ2VyX3VzZXIiLCJkZXNjcmlwdGlvbiI6IkNhbiBjcmVhdGUvbW9kaWZ5IGFkbWluIHVzZXIifSx7Im5hbWUiOiJsaXN0X3RyZWUiLCJkZXNjcmlwdGlvbiI6IkNhbiB2aWV3IHRyZWVzIn0seyJuYW1lIjoiYXBwcm92ZV90cmVlIiwiZGVzY3JpcHRpb24iOiJDYW4gYXBwcm92ZS9yZWplY3QgdHJlZXMifV19LCJpYXQiOjE2NDI4NzIyNjV9.JYWYUo7B6y2jNyoaaw9uA0jTU4AYIZLXg0oGwF3XoVE'; describe('Earnings API tests.', () => { describe('Earnings PATCH', () => { @@ -471,7 +471,7 @@ describe('Earnings API tests.', () => { let earnings_updated = false; for (const earning of res.body.earnings) { expect(earning).to.have.keys([ - 'id', + 'id', 'grower', 'funder', 'phone', @@ -559,7 +559,7 @@ describe('Earnings API tests.', () => { request(server) .patch(`/earnings/batch`) .set('Accept', 'multipart/form-data') - .set('authorization', jwtToken) + .set('authorization', jwtToken) .attach( 'csv', './__tests__/api-tests/earningsFailedTestInvalidHeader2.csv', @@ -575,7 +575,7 @@ describe('Earnings API tests.', () => { request(server) .patch(`/earnings/batch`) .set('Accept', 'multipart/form-data') - .set('authorization', jwtToken) + .set('authorization', jwtToken) .attach( 'csv', './__tests__/api-tests/earningsFailedTestInvalidHeader3.csv', @@ -590,7 +590,7 @@ describe('Earnings API tests.', () => { it(`Should raise validation error with error code 422 -- invalid headers; amount does not exist `, function (done) { request(server) .patch(`/earnings/batch`) - .set('authorization', jwtToken) + .set('authorization', jwtToken) .set('Accept', 'multipart/form-data') .attach( 'csv', @@ -607,7 +607,7 @@ describe('Earnings API tests.', () => { request(server) .patch(`/earnings/batch`) .set('Accept', 'multipart/form-data') - .set('authorization', jwtToken) + .set('authorization', jwtToken) .attach( 'csv', './__tests__/api-tests/earningsFailedTestInvalidHeader5.csv', @@ -623,7 +623,7 @@ describe('Earnings API tests.', () => { request(server) .patch(`/earnings/batch`) .set('Accept', 'multipart/form-data') - .set('authorization', jwtToken) + .set('authorization', jwtToken) .attach( 'csv', './__tests__/api-tests/earningsFailedTestInvalidHeader6.csv', @@ -639,7 +639,7 @@ describe('Earnings API tests.', () => { request(server) .patch(`/earnings/batch`) .set('Accept', 'multipart/form-data') - .set('authorization', jwtToken) + .set('authorization', jwtToken) .attach( 'csv', './__tests__/api-tests/earningsFailedTestRowWithNotCalculatedStatus.csv', @@ -655,7 +655,7 @@ describe('Earnings API tests.', () => { request(server) .patch(`/earnings/batch`) .set('Accept', 'multipart/form-data') - .set('authorization', jwtToken) + .set('authorization', jwtToken) .attach('csv', './__tests__/api-tests/earningsSuccessfulTest.csv') .expect(200) .end(function (err, res) { diff --git a/commitlint.config.js b/commitlint.config.js index 3347cb9..422b194 100644 --- a/commitlint.config.js +++ b/commitlint.config.js @@ -1 +1 @@ -module.exports = {extends: ['@commitlint/config-conventional']}; +module.exports = { extends: ['@commitlint/config-conventional'] }; diff --git a/database/migrations/20211010192212-createBatch.js b/database/migrations/20211010192212-createBatch.js index 7ee361b..a2cefc6 100644 --- a/database/migrations/20211010192212-createBatch.js +++ b/database/migrations/20211010192212-createBatch.js @@ -1,5 +1,3 @@ - - let dbm; let type; let seed; @@ -9,46 +7,52 @@ const path = require('path'); let Promise; /** - * We receive the dbmigrate dependency from dbmigrate initially. - * This enables us to not have to rely on NODE_PATH. - */ -exports.setup = function(options, seedLink) { + * We receive the dbmigrate dependency from dbmigrate initially. + * This enables us to not have to rely on NODE_PATH. + */ +exports.setup = function (options, seedLink) { dbm = options.dbmigrate; type = dbm.dataType; seed = seedLink; Promise = options.Promise; }; -exports.up = function(db) { - const filePath = path.join(__dirname, 'sqls', '20211010192212-createBatch-up.sql'); - return new Promise( function( resolve, reject ) { - fs.readFile(filePath, {encoding: 'utf-8'}, function(err,data){ +exports.up = function (db) { + const filePath = path.join( + __dirname, + 'sqls', + '20211010192212-createBatch-up.sql', + ); + return new Promise(function (resolve, reject) { + fs.readFile(filePath, { encoding: 'utf-8' }, function (err, data) { if (err) return reject(err); - console.log(`received data: ${ data}`); + console.log(`received data: ${data}`); resolve(data); }); - }) - .then(function(data) { + }).then(function (data) { return db.runSql(data); }); }; -exports.down = function(db) { - const filePath = path.join(__dirname, 'sqls', '20211010192212-createBatch-down.sql'); - return new Promise( function( resolve, reject ) { - fs.readFile(filePath, {encoding: 'utf-8'}, function(err,data){ +exports.down = function (db) { + const filePath = path.join( + __dirname, + 'sqls', + '20211010192212-createBatch-down.sql', + ); + return new Promise(function (resolve, reject) { + fs.readFile(filePath, { encoding: 'utf-8' }, function (err, data) { if (err) return reject(err); - console.log(`received data: ${ data}`); + console.log(`received data: ${data}`); resolve(data); }); - }) - .then(function(data) { + }).then(function (data) { return db.runSql(data); }); }; exports._meta = { - "version": 1 + version: 1, }; diff --git a/database/migrations/20211018163418-createEarning.js b/database/migrations/20211018163418-createEarning.js index 809011a..eb4770d 100644 --- a/database/migrations/20211018163418-createEarning.js +++ b/database/migrations/20211018163418-createEarning.js @@ -1,5 +1,3 @@ - - let dbm; let type; let seed; @@ -9,46 +7,52 @@ const path = require('path'); let Promise; /** - * We receive the dbmigrate dependency from dbmigrate initially. - * This enables us to not have to rely on NODE_PATH. - */ -exports.setup = function(options, seedLink) { + * We receive the dbmigrate dependency from dbmigrate initially. + * This enables us to not have to rely on NODE_PATH. + */ +exports.setup = function (options, seedLink) { dbm = options.dbmigrate; type = dbm.dataType; seed = seedLink; Promise = options.Promise; }; -exports.up = function(db) { - const filePath = path.join(__dirname, 'sqls', '20211018163418-createEarning-up.sql'); - return new Promise( function( resolve, reject ) { - fs.readFile(filePath, {encoding: 'utf-8'}, function(err,data){ +exports.up = function (db) { + const filePath = path.join( + __dirname, + 'sqls', + '20211018163418-createEarning-up.sql', + ); + return new Promise(function (resolve, reject) { + fs.readFile(filePath, { encoding: 'utf-8' }, function (err, data) { if (err) return reject(err); - console.log(`received data: ${ data}`); + console.log(`received data: ${data}`); resolve(data); }); - }) - .then(function(data) { + }).then(function (data) { return db.runSql(data); }); }; -exports.down = function(db) { - const filePath = path.join(__dirname, 'sqls', '20211018163418-createEarning-down.sql'); - return new Promise( function( resolve, reject ) { - fs.readFile(filePath, {encoding: 'utf-8'}, function(err,data){ +exports.down = function (db) { + const filePath = path.join( + __dirname, + 'sqls', + '20211018163418-createEarning-down.sql', + ); + return new Promise(function (resolve, reject) { + fs.readFile(filePath, { encoding: 'utf-8' }, function (err, data) { if (err) return reject(err); - console.log(`received data: ${ data}`); + console.log(`received data: ${data}`); resolve(data); }); - }) - .then(function(data) { + }).then(function (data) { return db.runSql(data); }); }; exports._meta = { - "version": 1 + version: 1, }; diff --git a/database/migrations/20211027043106-alterTableName.js b/database/migrations/20211027043106-alterTableName.js index 6340073..ee2112a 100644 --- a/database/migrations/20211027043106-alterTableName.js +++ b/database/migrations/20211027043106-alterTableName.js @@ -1,5 +1,3 @@ - - let dbm; let type; let seed; @@ -9,46 +7,52 @@ const path = require('path'); let Promise; /** - * We receive the dbmigrate dependency from dbmigrate initially. - * This enables us to not have to rely on NODE_PATH. - */ -exports.setup = function(options, seedLink) { + * We receive the dbmigrate dependency from dbmigrate initially. + * This enables us to not have to rely on NODE_PATH. + */ +exports.setup = function (options, seedLink) { dbm = options.dbmigrate; type = dbm.dataType; seed = seedLink; Promise = options.Promise; }; -exports.up = function(db) { - const filePath = path.join(__dirname, 'sqls', '20211027043106-alterTableName-up.sql'); - return new Promise( function( resolve, reject ) { - fs.readFile(filePath, {encoding: 'utf-8'}, function(err,data){ +exports.up = function (db) { + const filePath = path.join( + __dirname, + 'sqls', + '20211027043106-alterTableName-up.sql', + ); + return new Promise(function (resolve, reject) { + fs.readFile(filePath, { encoding: 'utf-8' }, function (err, data) { if (err) return reject(err); - console.log(`received data: ${ data}`); + console.log(`received data: ${data}`); resolve(data); }); - }) - .then(function(data) { + }).then(function (data) { return db.runSql(data); }); }; -exports.down = function(db) { - const filePath = path.join(__dirname, 'sqls', '20211027043106-alterTableName-down.sql'); - return new Promise( function( resolve, reject ) { - fs.readFile(filePath, {encoding: 'utf-8'}, function(err,data){ +exports.down = function (db) { + const filePath = path.join( + __dirname, + 'sqls', + '20211027043106-alterTableName-down.sql', + ); + return new Promise(function (resolve, reject) { + fs.readFile(filePath, { encoding: 'utf-8' }, function (err, data) { if (err) return reject(err); - console.log(`received data: ${ data}`); + console.log(`received data: ${data}`); resolve(data); }); - }) - .then(function(data) { + }).then(function (data) { return db.runSql(data); }); }; exports._meta = { - "version": 1 + version: 1, }; diff --git a/database/migrations/20211111020731-AddLeoneCurrency.js b/database/migrations/20211111020731-AddLeoneCurrency.js index c489e0a..e4df874 100644 --- a/database/migrations/20211111020731-AddLeoneCurrency.js +++ b/database/migrations/20211111020731-AddLeoneCurrency.js @@ -1,5 +1,3 @@ - - let dbm; let type; let seed; @@ -9,47 +7,53 @@ const path = require('path'); let Promise; /** - * We receive the dbmigrate dependency from dbmigrate initially. - * This enables us to not have to rely on NODE_PATH. - */ -exports.setup = function(options, seedLink) { + * We receive the dbmigrate dependency from dbmigrate initially. + * This enables us to not have to rely on NODE_PATH. + */ +exports.setup = function (options, seedLink) { dbm = options.dbmigrate; type = dbm.dataType; seed = seedLink; Promise = options.Promise; }; -exports.up = function(db) { - const filePath = path.join(__dirname, 'sqls', '20211111020731-AddLeoneCurrency-up.sql'); - return new Promise( function( resolve, reject ) { - fs.readFile(filePath, {encoding: 'utf-8'}, function(err,data){ +exports.up = function (db) { + const filePath = path.join( + __dirname, + 'sqls', + '20211111020731-AddLeoneCurrency-up.sql', + ); + return new Promise(function (resolve, reject) { + fs.readFile(filePath, { encoding: 'utf-8' }, function (err, data) { if (err) return reject(err); - console.log(`received data: ${ data}`); + console.log(`received data: ${data}`); resolve(data); }); - }) - .then(function(data) { + }).then(function (data) { db.endMigration(); // get outside of transaction, necessary for adding enum value return db.runSql(data); }); }; -exports.down = function(db) { - const filePath = path.join(__dirname, 'sqls', '20211111020731-AddLeoneCurrency-down.sql'); - return new Promise( function( resolve, reject ) { - fs.readFile(filePath, {encoding: 'utf-8'}, function(err,data){ +exports.down = function (db) { + const filePath = path.join( + __dirname, + 'sqls', + '20211111020731-AddLeoneCurrency-down.sql', + ); + return new Promise(function (resolve, reject) { + fs.readFile(filePath, { encoding: 'utf-8' }, function (err, data) { if (err) return reject(err); - console.log(`received data: ${ data}`); + console.log(`received data: ${data}`); resolve(data); }); - }) - .then(function(data) { + }).then(function (data) { return db.runSql(data); }); }; exports._meta = { - "version": 1 + version: 1, }; diff --git a/database/migrations/20220203165550-change-payment-confirmed-by-data-type.js b/database/migrations/20220203165550-change-payment-confirmed-by-data-type.js index 66f3a15..86c3eeb 100644 --- a/database/migrations/20220203165550-change-payment-confirmed-by-data-type.js +++ b/database/migrations/20220203165550-change-payment-confirmed-by-data-type.js @@ -1,5 +1,3 @@ - - let dbm; let type; let seed; @@ -9,46 +7,52 @@ const path = require('path'); let Promise; /** - * We receive the dbmigrate dependency from dbmigrate initially. - * This enables us to not have to rely on NODE_PATH. - */ -exports.setup = function(options, seedLink) { + * We receive the dbmigrate dependency from dbmigrate initially. + * This enables us to not have to rely on NODE_PATH. + */ +exports.setup = function (options, seedLink) { dbm = options.dbmigrate; type = dbm.dataType; seed = seedLink; Promise = options.Promise; }; -exports.up = function(db) { - const filePath = path.join(__dirname, 'sqls', '20220203165550-change-payment-confirmed-by-data-type-up.sql'); - return new Promise( function( resolve, reject ) { - fs.readFile(filePath, {encoding: 'utf-8'}, function(err,data){ +exports.up = function (db) { + const filePath = path.join( + __dirname, + 'sqls', + '20220203165550-change-payment-confirmed-by-data-type-up.sql', + ); + return new Promise(function (resolve, reject) { + fs.readFile(filePath, { encoding: 'utf-8' }, function (err, data) { if (err) return reject(err); console.log('received data: ', data); resolve(data); }); - }) - .then(function(data) { + }).then(function (data) { return db.runSql(data); }); }; -exports.down = function(db) { - const filePath = path.join(__dirname, 'sqls', '20220203165550-change-payment-confirmed-by-data-type-down.sql'); - return new Promise( function( resolve, reject ) { - fs.readFile(filePath, {encoding: 'utf-8'}, function(err,data){ +exports.down = function (db) { + const filePath = path.join( + __dirname, + 'sqls', + '20220203165550-change-payment-confirmed-by-data-type-down.sql', + ); + return new Promise(function (resolve, reject) { + fs.readFile(filePath, { encoding: 'utf-8' }, function (err, data) { if (err) return reject(err); console.log('received data: ', data); resolve(data); }); - }) - .then(function(data) { + }).then(function (data) { return db.runSql(data); }); }; exports._meta = { - "version": 1 + version: 1, }; diff --git a/deployment/base/database-connection-sealed-secret.yaml b/deployment/base/database-connection-sealed-secret.yaml index dc827fe..5113a4f 100644 --- a/deployment/base/database-connection-sealed-secret.yaml +++ b/deployment/base/database-connection-sealed-secret.yaml @@ -5,9 +5,8 @@ metadata: namespace: earnings-api spec: encryptedData: - db: xxx + db: xxx template: metadata: name: database-connection namespace: earnings-api - diff --git a/deployment/base/deployment.yaml b/deployment/base/deployment.yaml index e73a9f0..29302e4 100644 --- a/deployment/base/deployment.yaml +++ b/deployment/base/deployment.yaml @@ -18,21 +18,21 @@ spec: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - - matchExpressions: - - key: doks.digitalocean.com/node-pool - operator: In - values: - - microservices-node-pool + - matchExpressions: + - key: doks.digitalocean.com/node-pool + operator: In + values: + - microservices-node-pool containers: - - name: treetracker-earnings-api - image: greenstand/treetracker-earnings:TAG - ports: - - containerPort: 80 - env: - - name: DATABASE_URL - valueFrom: - secretKeyRef: - name: database-connection - key: db - - name: DATABASE_SCHEMA - value: earnings + - name: treetracker-earnings-api + image: greenstand/treetracker-earnings:TAG + ports: + - containerPort: 80 + env: + - name: DATABASE_URL + valueFrom: + secretKeyRef: + name: database-connection + key: db + - name: DATABASE_SCHEMA + value: earnings diff --git a/deployment/base/kustomization.yaml b/deployment/base/kustomization.yaml index df85089..d566e0b 100644 --- a/deployment/base/kustomization.yaml +++ b/deployment/base/kustomization.yaml @@ -1,5 +1,5 @@ resources: -- deployment.yaml -- mapping.yaml -- service.yaml -- database-connection-sealed-secret.yaml + - deployment.yaml + - mapping.yaml + - service.yaml + - database-connection-sealed-secret.yaml diff --git a/deployment/overlays/development/database-connection-sealed-secret.yaml b/deployment/overlays/development/database-connection-sealed-secret.yaml index e81a7c4..1d42dc5 100644 --- a/deployment/overlays/development/database-connection-sealed-secret.yaml +++ b/deployment/overlays/development/database-connection-sealed-secret.yaml @@ -12,4 +12,3 @@ spec: creationTimestamp: null name: database-connection namespace: earnings-api - diff --git a/deployment/overlays/development/mapping.yaml b/deployment/overlays/development/mapping.yaml index 89bd924..0b36a2a 100644 --- a/deployment/overlays/development/mapping.yaml +++ b/deployment/overlays/development/mapping.yaml @@ -7,9 +7,9 @@ spec: cors: cors: origins: - - '*' + - '*' headers: - - Content-Type - - Authorization + - Content-Type + - Authorization methods: - - 'GET, POST, PATCH, PUT, OPTIONS, DELETE' + - 'GET, POST, PATCH, PUT, OPTIONS, DELETE' diff --git a/deployment/overlays/production/database-connection-sealed-secret.yaml b/deployment/overlays/production/database-connection-sealed-secret.yaml index b56a32d..9e6bdf7 100644 --- a/deployment/overlays/production/database-connection-sealed-secret.yaml +++ b/deployment/overlays/production/database-connection-sealed-secret.yaml @@ -12,4 +12,3 @@ spec: creationTimestamp: null name: database-connection namespace: earnings-api - diff --git a/deployment/overlays/test/database-connection-sealed-secret.yaml b/deployment/overlays/test/database-connection-sealed-secret.yaml index 153c25f..d3facfa 100644 --- a/deployment/overlays/test/database-connection-sealed-secret.yaml +++ b/deployment/overlays/test/database-connection-sealed-secret.yaml @@ -12,4 +12,3 @@ spec: creationTimestamp: null name: database-connection namespace: earnings-api - diff --git a/package.json b/package.json index cacc2bd..c3e435c 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "main": "server/server.js", "scripts": { "lint": "npm run eslint", - "lint:fix": "npm run eslint:fix && npm run prettier:fix", + "lint:fix": "npm run eslint:fix && npm run prettier-fix", "eslint": "eslint --report-unused-disable-directives .", "eslint:fix": "npm run eslint -- --fix", "test-unit-ci": "NODE_ENV=test mocha -r dotenv/config dotenv_config_path=.env.test --exit ./server/models/**/*.spec.js ./server/routes/**/*.spec.js", diff --git a/server/app.spec.js b/server/app.spec.js index e06248a..b6b67a1 100644 --- a/server/app.spec.js +++ b/server/app.spec.js @@ -1,20 +1,18 @@ const request = require('supertest'); const { expect } = require('chai'); -const server = require("./app"); +const server = require('./app'); - -describe("", () => { - - it("Test header: content-type: application/json", async () => { - const res = await request(server) - .get('/'); +describe('', () => { + it('Test header: content-type: application/json', async () => { + const res = await request(server).get('/'); expect(res.statusCode).eq(200); }); - it("Test header: content-type: application/json", async () => { - const res = await request(server) - .post('/'); + it('Test header: content-type: application/json', async () => { + const res = await request(server).post('/'); expect(res.statusCode).eq(415); - expect(res.body).property("message").match(/application.json/); + expect(res.body) + .property('message') + .match(/application.json/); }); }); diff --git a/server/database/knex.js b/server/database/knex.js index 017ebbf..6546ca5 100644 --- a/server/database/knex.js +++ b/server/database/knex.js @@ -2,7 +2,8 @@ const expect = require('expect-runtime'); const log = require('loglevel'); const connection = require('../../config/config').connectionString; -console.log('connection', connection) + +console.log('connection', connection); expect(connection).to.match(/^postgresql:\//); diff --git a/server/handlers/earningsHandler.js b/server/handlers/earningsHandler.js index 96d88a9..22d72c2 100644 --- a/server/handlers/earningsHandler.js +++ b/server/handlers/earningsHandler.js @@ -37,7 +37,7 @@ const earningsGetQuerySchema = Joi.object({ 'payment_system', 'effective_payment_date', 'status', - 'paid_at' + 'paid_at', ), order: Joi.string().valid('asc', 'desc'), }).unknown(false); @@ -73,20 +73,20 @@ const earningsPatch = async (req, res, next) => { const earningsRepo = new EarningsRepository(session); const authorizationHeader = req.get('authorization'); - const adminPanelUser = authorizationHeader ? jwt_decode(authorizationHeader) : null; + const adminPanelUser = authorizationHeader + ? jwt_decode(authorizationHeader) + : null; const isAdmin = adminPanelUser?.roleNames.includes('Admin'); - if (!authorizationHeader || !isAdmin) throw new HttpError( - 401, - 'Unauthorized!', - ); + if (!authorizationHeader || !isAdmin) + throw new HttpError(401, 'Unauthorized!'); try { await session.beginTransaction(); const result = await updateEarnings(earningsRepo, { payment_confirmation_method: 'single', payment_confirmed_by: adminPanelUser?.id, - ...req.body + ...req.body, }); await session.commitTransaction(); res.status(200).send(result); @@ -146,13 +146,13 @@ const earningsBatchPatch = async (req, res, next) => { const batchUpdateEarnings = (batch_id) => { let count = 0; const authorizationHeader = req.get('authorization'); - const adminPanelUser = authorizationHeader ? jwt_decode(authorizationHeader) : null; + const adminPanelUser = authorizationHeader + ? jwt_decode(authorizationHeader) + : null; const isAdmin = adminPanelUser?.roleNames.includes('Admin'); - if (!authorizationHeader || !isAdmin) throw new HttpError( - 401, - 'Unauthorized!', - ); + if (!authorizationHeader || !isAdmin) + throw new HttpError(401, 'Unauthorized!'); return new Promise((resolve, reject) => { csv() diff --git a/server/models/Earnings.js b/server/models/Earnings.js index 9840824..498827d 100644 --- a/server/models/Earnings.js +++ b/server/models/Earnings.js @@ -4,289 +4,291 @@ const axios = require('axios').default; const stakeholderUrl = `${process.env.TREETRACKER_STAKEHOLDER_API_URL}/stakeholder`; const Earning = async ({ - id, - worker_id, - funder_id, - amount, - currency, - calculated_at, - consolidation_rule_id, - consolidation_period_start, - consolidation_period_end, - payment_confirmation_id, - payment_system, - payment_confirmed_by, - payment_confirmation_method, - payment_confirmed_at, - paid_at, - status, - batch_id, - }) => { - const consolidation_rule = `CONSOLIDATION_RULE_${consolidation_rule_id}`; - const growerResponse = await axios.get( - `${stakeholderUrl}?id=${worker_id}`, - ); - const funderResponse = await axios.get( - `${stakeholderUrl}?id=${funder_id}`, - ); - - return Object.freeze({ - id, - worker_id, - grower: `${growerResponse.data.stakeholders[0]?.first_name} ${growerResponse.data.stakeholders[0]?.last_name}`, - funder_id, - funder: funderResponse.data.stakeholders[0]?.name, - phone: growerResponse.data.stakeholders[0]?.phone, - amount, - currency, - calculated_at, - consolidation_rule, - consolidation_period_start, - consolidation_period_end, - payment_confirmation_id, - payment_system, - payment_confirmed_by, - payment_confirmation_method, - paid_at, - payment_confirmed_at, - status, - batch_id, - }); + id, + worker_id, + funder_id, + amount, + currency, + calculated_at, + consolidation_rule_id, + consolidation_period_start, + consolidation_period_end, + payment_confirmation_id, + payment_system, + payment_confirmed_by, + payment_confirmation_method, + payment_confirmed_at, + paid_at, + status, + batch_id, +}) => { + const consolidation_rule = `CONSOLIDATION_RULE_${consolidation_rule_id}`; + const growerResponse = await axios.get(`${stakeholderUrl}?id=${worker_id}`); + const funderResponse = await axios.get(`${stakeholderUrl}?id=${funder_id}`); + + return Object.freeze({ + id, + worker_id, + grower: `${growerResponse.data.stakeholders[0]?.first_name} ${growerResponse.data.stakeholders[0]?.last_name}`, + funder_id, + funder: funderResponse.data.stakeholders[0]?.name, + phone: growerResponse.data.stakeholders[0]?.phone, + amount, + currency, + calculated_at, + consolidation_rule, + consolidation_period_start, + consolidation_period_end, + payment_confirmation_id, + payment_system, + payment_confirmed_by, + payment_confirmation_method, + paid_at, + payment_confirmed_at, + status, + batch_id, + }); }; -const BatchEarning = async ({id, worker_id, amount, currency, status}) => { - // Get the phone value from the entities API - const response = await axios.get( - `${stakeholderUrl}?stakeholder_uuid=${worker_id}`, - ); - - return Object.freeze({ - earnings_id: id, - worker_id, - phone: response.data.stakeholders[0]?.phone, - currency, - amount, - status, - }); +const BatchEarning = async ({ id, worker_id, amount, currency, status }) => { + // Get the phone value from the entities API + const response = await axios.get( + `${stakeholderUrl}?stakeholder_uuid=${worker_id}`, + ); + + return Object.freeze({ + earnings_id: id, + worker_id, + phone: response.data.stakeholders[0]?.phone, + currency, + amount, + status, + }); }; const FilterCriteria = ({ - earnings_status = undefined, - funder_id = undefined, - worker_id = undefined, - contract_id = undefined, - start_date = undefined, - end_date = undefined, - sort_by = undefined, - order = 'asc', - }) => { - let orderBy = ''; - - switch (sort_by) { - case 'id': - orderBy = 'id'; - break; - case 'amount': - orderBy = 'amount'; - break; - case 'payment_system': - orderBy = 'payment_system'; - break; - case 'effective_payment_date': - orderBy = 'calculated_at'; - break; - default: - orderBy = undefined; - break; - } - return Object.entries({ - status: earnings_status, - worker_id, - funder_id, - contract_id, - calculated_at_end: end_date ? new Date(end_date) : end_date, - calculated_at_start: start_date ? new Date(start_date) : start_date, - orderBy, - order, - }) - .filter((entry) => entry[1] !== undefined) - .reduce((result, item) => { - result[item[0]] = item[1]; - return result; - }, {}); + earnings_status = undefined, + funder_id = undefined, + worker_id = undefined, + contract_id = undefined, + start_date = undefined, + end_date = undefined, + sort_by = undefined, + order = 'asc', +}) => { + let orderBy = ''; + + switch (sort_by) { + case 'id': + orderBy = 'id'; + break; + case 'amount': + orderBy = 'amount'; + break; + case 'payment_system': + orderBy = 'payment_system'; + break; + case 'effective_payment_date': + orderBy = 'calculated_at'; + break; + default: + orderBy = undefined; + break; + } + return Object.entries({ + status: earnings_status, + worker_id, + funder_id, + contract_id, + calculated_at_end: end_date ? new Date(end_date) : end_date, + calculated_at_start: start_date ? new Date(start_date) : start_date, + orderBy, + order, + }) + .filter((entry) => entry[1] !== undefined) + .reduce((result, item) => { + result[item[0]] = item[1]; + return result; + }, {}); }; -const QueryOptions = ({limit = undefined, offset = undefined}) => { - return Object.entries({limit, offset}) - .filter((entry) => entry[1] !== undefined) - .reduce((result, item) => { - result[item[0]] = item[1]; - return result; - }, {}); +const QueryOptions = ({ limit = undefined, offset = undefined }) => { + return Object.entries({ limit, offset }) + .filter((entry) => entry[1] !== undefined) + .reduce((result, item) => { + result[item[0]] = item[1]; + return result; + }, {}); }; const getEarnings = - (earningsRepo) => - async (filterCriteria = undefined, url) => { - let filter = {}; - let options = {limit: 100, offset: 0}; - filter = FilterCriteria({ - ...filterCriteria, - }); - options = {...options, ...QueryOptions({...filterCriteria})}; - - const queryFilterObjects = {...filterCriteria}; - queryFilterObjects.limit = options.limit; - - // remove offset property, as it is calculated later - delete queryFilterObjects.offset; - - const query = Object.keys(queryFilterObjects) - .map((key) => `${key}=${encodeURIComponent(queryFilterObjects[key])}`) - .join('&'); - - const urlWithLimitAndOffset = `${url}?${query}&offset=`; - - const next = `${urlWithLimitAndOffset}${+options.offset + +options.limit}`; - let prev = null; - if (options.offset - +options.limit >= 0) { - prev = `${urlWithLimitAndOffset}${+options.offset - +options.limit}`; - } - - const {earnings, count} = await earningsRepo.getEarnings(filter, options); - - const formattedEarnings = await Promise.all( - earnings.map((row) => { - return Earning({...row}); - }), - ); - - const {sort_by, order = 'asc'} = filterCriteria; - - if (sort_by === 'grower') { - formattedEarnings.sort((a, b) => { - const nameA = a.grower?.toUpperCase(); // ignore upper and lowercase - const nameB = b.grower?.toUpperCase(); // ignore upper and lowercase - - if (nameA < nameB) { - return order === 'asc' ? -1 : 1; - } - if (nameA > nameB) { - return order === 'asc' ? 1 : -1; - } - return 0; - }); - } - if (filterCriteria.sort_by === 'funder') { - formattedEarnings.sort((a, b) => { - const nameA = a.funder?.toUpperCase(); // ignore upper and lowercase - const nameB = b.funder?.toUpperCase(); // ignore upper and lowercase - if (nameA < nameB) { - return order === 'asc' ? -1 : 1; - } - if (nameA > nameB) { - return order === 'asc' ? 1 : -1; - } - return 0; - }); - } - - if (filterCriteria?.grower || filterCriteria?.phone) { - const filterFormattedEarnings = formattedEarnings - .filter(({grower}) => filterCriteria?.grower ? - grower.toLowerCase().includes(filterCriteria?.grower.toLowerCase()): true) - .filter(({phone}) => filterCriteria?.phone ? - phone.toLowerCase().includes(filterCriteria?.phone.toLowerCase()) : true) - - return { - earnings: filterFormattedEarnings, - totalCount: filterFormattedEarnings.length, - links: { - prev, - next, - }, - }; - - } - - - return { - earnings: formattedEarnings, - totalCount: count, - links: { - prev, - next, - }, - }; - }; + (earningsRepo) => + async (filterCriteria = undefined, url) => { + let filter = {}; + let options = { limit: 100, offset: 0 }; + filter = FilterCriteria({ + ...filterCriteria, + }); + options = { ...options, ...QueryOptions({ ...filterCriteria }) }; -const updateEarnings = async (earningsRepo, requestBody) => { - const body = {...requestBody}; - const {worker_id, currency, amount} = body; + const queryFilterObjects = { ...filterCriteria }; + queryFilterObjects.limit = options.limit; - // If data is coming from csv file - if (body.earnings_id) { - body.id = body.earnings_id; - delete body.earnings_id; - } - delete body.phone; + // remove offset property, as it is calculated later + delete queryFilterObjects.offset; - const earnings = await earningsRepo.getById(body.id); + const query = Object.keys(queryFilterObjects) + .map((key) => `${key}=${encodeURIComponent(queryFilterObjects[key])}`) + .join('&'); - if (earnings.status !== 'calculated') - throw new HttpError(409, 'Earnings have either been paid or cancelled'); + const urlWithLimitAndOffset = `${url}?${query}&offset=`; - if (earnings.payment_confirmation_id) - throw new HttpError(409, 'Earnings already have a payment_confirmation_id'); + const next = `${urlWithLimitAndOffset}${+options.offset + +options.limit}`; + let prev = null; + if (options.offset - +options.limit >= 0) { + prev = `${urlWithLimitAndOffset}${+options.offset - +options.limit}`; + } - if (earnings.worker_id !== worker_id) - throw new HttpError( - 409, - 'The worker id specified does not match that of the earning', - ); + const { earnings, count } = await earningsRepo.getEarnings(filter, options); - if (earnings.currency !== currency) - throw new HttpError( - 409, - 'The currency specified does not match that of the earning', - ); + const formattedEarnings = await Promise.all( + earnings.map((row) => { + return Earning({ ...row }); + }), + ); + + const { sort_by, order = 'asc' } = filterCriteria; + + if (sort_by === 'grower') { + formattedEarnings.sort((a, b) => { + const nameA = a.grower?.toUpperCase(); // ignore upper and lowercase + const nameB = b.grower?.toUpperCase(); // ignore upper and lowercase + + if (nameA < nameB) { + return order === 'asc' ? -1 : 1; + } + if (nameA > nameB) { + return order === 'asc' ? 1 : -1; + } + return 0; + }); + } + if (filterCriteria.sort_by === 'funder') { + formattedEarnings.sort((a, b) => { + const nameA = a.funder?.toUpperCase(); // ignore upper and lowercase + const nameB = b.funder?.toUpperCase(); // ignore upper and lowercase + if (nameA < nameB) { + return order === 'asc' ? -1 : 1; + } + if (nameA > nameB) { + return order === 'asc' ? 1 : -1; + } + return 0; + }); + } - if (+earnings.amount !== +amount) - throw new HttpError( - 409, - 'The amount specified does not match that of the earning', + if (filterCriteria?.grower || filterCriteria?.phone) { + const filterFormattedEarnings = formattedEarnings + .filter(({ grower }) => + filterCriteria?.grower + ? grower + .toLowerCase() + .includes(filterCriteria?.grower.toLowerCase()) + : true, + ) + .filter(({ phone }) => + filterCriteria?.phone + ? phone.toLowerCase().includes(filterCriteria?.phone.toLowerCase()) + : true, ); - await earningsRepo.update({ - ...body, - status: 'paid', - payment_confirmed_at: new Date(), - }); + return { + earnings: filterFormattedEarnings, + totalCount: filterFormattedEarnings.length, + links: { + prev, + next, + }, + }; + } return { - status: 'completed', - count: 1, + earnings: formattedEarnings, + totalCount: count, + links: { + prev, + next, + }, }; + }; + +const updateEarnings = async (earningsRepo, requestBody) => { + const body = { ...requestBody }; + const { worker_id, currency, amount } = body; + + // If data is coming from csv file + if (body.earnings_id) { + body.id = body.earnings_id; + delete body.earnings_id; + } + delete body.phone; + + const earnings = await earningsRepo.getById(body.id); + + if (earnings.status !== 'calculated') + throw new HttpError(409, 'Earnings have either been paid or cancelled'); + + if (earnings.payment_confirmation_id) + throw new HttpError(409, 'Earnings already have a payment_confirmation_id'); + + if (earnings.worker_id !== worker_id) + throw new HttpError( + 409, + 'The worker id specified does not match that of the earning', + ); + + if (earnings.currency !== currency) + throw new HttpError( + 409, + 'The currency specified does not match that of the earning', + ); + + if (+earnings.amount !== +amount) + throw new HttpError( + 409, + 'The amount specified does not match that of the earning', + ); + + await earningsRepo.update({ + ...body, + status: 'paid', + payment_confirmed_at: new Date(), + }); + + return { + status: 'completed', + count: 1, + }; }; const getBatchEarnings = - (earningsRepo) => - async (filterCriteria = undefined) => { - let filter = {}; - filter = FilterCriteria({ - ...filterCriteria, - }); + (earningsRepo) => + async (filterCriteria = undefined) => { + let filter = {}; + filter = FilterCriteria({ + ...filterCriteria, + }); - const earningsStream = await earningsRepo.getEarnings(filter, { - stream: true, - }); + const earningsStream = await earningsRepo.getEarnings(filter, { + stream: true, + }); - return {earningsStream}; - }; + return { earningsStream }; + }; module.exports = { - getEarnings, - updateEarnings, - getBatchEarnings, - BatchEarning, + getEarnings, + updateEarnings, + getBatchEarnings, + BatchEarning, }; diff --git a/server/setup.js b/server/setup.js index 76f67b5..3216bb8 100644 --- a/server/setup.js +++ b/server/setup.js @@ -1,11 +1,10 @@ /* * A file to setup some global setting, like log level */ -const log = require("loglevel"); +const log = require('loglevel'); -if(process.env.NODE_LOG_LEVEL){ +if (process.env.NODE_LOG_LEVEL) { log.setDefaultLevel(process.env.NODE_LOG_LEVEL); -}else{ - log.setDefaultLevel("info"); +} else { + log.setDefaultLevel('info'); } - diff --git a/server/utils/HttpError.js b/server/utils/HttpError.js index 908f303..5e9b842 100644 --- a/server/utils/HttpError.js +++ b/server/utils/HttpError.js @@ -1,12 +1,12 @@ /* - * To define a extended error for API, can pass through the error message, http - * code, to bring some convenient for the internal class to throw out the error - * and the outside of the layer can catch the error and convert to a http + * To define a extended error for API, can pass through the error message, http + * code, to bring some convenient for the internal class to throw out the error + * and the outside of the layer can catch the error and convert to a http * response to client */ class HttpError extends Error { - constructor(code, message, toRollback){ + constructor(code, message, toRollback) { super(message); this.code = code; // set rollback flag, so the transaction of db would rollback when catch this error @@ -14,7 +14,7 @@ class HttpError extends Error { this._toRollback = toRollback || true; } - shouldRollback(){ + shouldRollback() { return this._toRollback; } }