From aef079b59b65b1a1a9518da5fa5585645d86586c Mon Sep 17 00:00:00 2001 From: galta95 <48960890+galta95@users.noreply.github.com> Date: Sun, 29 Nov 2020 17:18:01 +0200 Subject: [PATCH 1/3] feat(/api/0.6/changeset/create): create changeset (#7) * feat(/api/0.6/changeset/create): new rout * test(/api/0.6/changeset/create): unit test * feat(eslint): added semicolon role * style(lint): missing semicolons and change file name * build(npm): added nock * style(lint): added semicolon * refactor(/api/0.6/changeset/create): refactor requested changes * fix(/api/0.6/changeset/create): move out the res parsing from try & catch * refactor(all): use generics * refactor(/api/0.6/changeset/create): error handler * ci(tests): added unit tests and lint workflows (#11) * ci(tests): added unit tets * fix(github- workflows): split to two different .yml files for each job category * refactor(.github/workflows): added linting to tests directories --- .eslintrc.js | 20 - .github/workflows/{main.yml => lint.yml} | 14 +- .github/workflows/tests.yml | 23 + .gitignore | 2 + index.ts | 0 package-lock.json | 858 ++++++++++++++++++++++- package.json | 25 +- src/api/v6/index.ts | 35 + src/index.ts | 3 + src/lib/endpoints.ts | 1 + src/lib/error-handler.ts | 29 + tests/unit/apiv6.test.ts | 46 ++ tests/unit/config/tests-config.ts | 5 + tests/unit/lib/osm-xml.ts | 18 + tsconfig.json | 73 ++ 15 files changed, 1116 insertions(+), 36 deletions(-) delete mode 100644 .eslintrc.js rename .github/workflows/{main.yml => lint.yml} (56%) create mode 100644 .github/workflows/tests.yml delete mode 100644 index.ts create mode 100644 src/api/v6/index.ts create mode 100755 src/index.ts create mode 100644 src/lib/endpoints.ts create mode 100644 src/lib/error-handler.ts create mode 100644 tests/unit/apiv6.test.ts create mode 100644 tests/unit/config/tests-config.ts create mode 100644 tests/unit/lib/osm-xml.ts create mode 100644 tsconfig.json diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 8c91b85..0000000 --- a/.eslintrc.js +++ /dev/null @@ -1,20 +0,0 @@ -module.exports = { - "env": { - "browser": true, - "es2020": true - }, - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended" - ], - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaVersion": 11, - "sourceType": "module" - }, - "plugins": [ - "@typescript-eslint" - ], - "rules": { - } -}; diff --git a/.github/workflows/main.yml b/.github/workflows/lint.yml similarity index 56% rename from .github/workflows/main.yml rename to .github/workflows/lint.yml index 39acaf9..e45340b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/lint.yml @@ -4,11 +4,11 @@ on: [pull_request] jobs: eslint: - name: Run eslint + name: Run TS Project eslint runs-on: ubuntu-latest steps: - - name: Check out Git repository + - name: Check out TS Project Git repository uses: actions/checkout@v2 - name: Set up Node.js @@ -16,15 +16,13 @@ jobs: with: node-version: 12 - # ESLint and Prettier must be in `package.json` - - name: Install Node.js dependencies + - name: Install TS Project dependencies run: npm install - - name: Run linters - uses: samuelmeuli/lint-action@v1 + - name: Run TS Project linters + uses: wearerequired/lint-action@v1 with: github_token: ${{ secrets.github_token }} # Enable linters eslint: true - eslint_extensions: ts - # prettier: true \ No newline at end of file + eslint_extensions: ts \ No newline at end of file diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..3f0610b --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,23 @@ +name: Tests + +on: [pull_request] + +jobs: + tests: + name: Run TS Project Tests + runs-on: ubuntu-latest + + steps: + - name: Check out TS Project Git repository + uses: actions/checkout@v2 + + - name: Set up Node.js + uses: actions/setup-node@v1 + with: + node-version: 12 + + - name: Install Node.js dependencies + run: npm ci + + - name: Run tests + run: npm run test \ No newline at end of file diff --git a/.gitignore b/.gitignore index 76efb07..b41629d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ node_modules .vscode +build +*.config.ts \ No newline at end of file diff --git a/index.ts b/index.ts deleted file mode 100644 index e69de29..0000000 diff --git a/package-lock.json b/package-lock.json index 8d6e396..8f506a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -230,6 +230,99 @@ "strip-json-comments": "^3.1.1" } }, + "@map-colonies/eslint-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@map-colonies/eslint-config/-/eslint-config-1.1.0.tgz", + "integrity": "sha512-CKKcgyB0lcwzgrV5Q4/OeUiqp/1ouLWoF40pNQ+6C0IqQQUZ4/VWiQd0jU19WhrwDPWHHqcxzcSBoibUNWy8OA==", + "dev": true, + "requires": { + "@typescript-eslint/eslint-plugin": "3.6.1", + "@typescript-eslint/parser": "3.6.1", + "eslint-config-prettier": "^6.11.0", + "eslint-config-react-app": "^5.2.1", + "eslint-plugin-import": "^2.22.0", + "eslint-plugin-jest": "^23.20.0" + }, + "dependencies": { + "@typescript-eslint/eslint-plugin": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.6.1.tgz", + "integrity": "sha512-06lfjo76naNeOMDl+mWG9Fh/a0UHKLGhin+mGaIw72FUMbMGBkdi/FEJmgEDzh4eE73KIYzHWvOCYJ0ak7nrJQ==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "3.6.1", + "debug": "^4.1.1", + "functional-red-black-tree": "^1.0.1", + "regexpp": "^3.0.0", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + } + }, + "@typescript-eslint/experimental-utils": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.6.1.tgz", + "integrity": "sha512-oS+hihzQE5M84ewXrTlVx7eTgc52eu+sVmG7ayLfOhyZmJ8Unvf3osyFQNADHP26yoThFfbxcibbO0d2FjnYhg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/types": "3.6.1", + "@typescript-eslint/typescript-estree": "3.6.1", + "eslint-scope": "^5.0.0", + "eslint-utils": "^2.0.0" + } + }, + "@typescript-eslint/parser": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-3.6.1.tgz", + "integrity": "sha512-SLihQU8RMe77YJ/jGTqOt0lMq7k3hlPVfp7v/cxMnXA9T0bQYoMDfTsNgHXpwSJM1Iq2aAJ8WqekxUwGv5F67Q==", + "dev": true, + "requires": { + "@types/eslint-visitor-keys": "^1.0.0", + "@typescript-eslint/experimental-utils": "3.6.1", + "@typescript-eslint/types": "3.6.1", + "@typescript-eslint/typescript-estree": "3.6.1", + "eslint-visitor-keys": "^1.1.0" + } + }, + "@typescript-eslint/types": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-3.6.1.tgz", + "integrity": "sha512-NPxd5yXG63gx57WDTW1rp0cF3XlNuuFFB5G+Kc48zZ+51ZnQn9yjDEsjTPQ+aWM+V+Z0I4kuTFKjKvgcT1F7xQ==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.6.1.tgz", + "integrity": "sha512-G4XRe/ZbCZkL1fy09DPN3U0mR6SayIv1zSeBNquRFRk7CnVLgkC2ZPj8llEMJg5Y8dJ3T76SvTGtceytniaztQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "3.6.1", + "@typescript-eslint/visitor-keys": "3.6.1", + "debug": "^4.1.1", + "glob": "^7.1.6", + "is-glob": "^4.0.1", + "lodash": "^4.17.15", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-3.6.1.tgz", + "integrity": "sha512-qC8Olwz5ZyMTZrh4Wl3K4U6tfms0R/mzU4/5W3XeUZptVraGVmbptJbn6h2Ey6Rb3hOs3zWoAUebZk8t47KGiQ==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, "@nodelib/fs.scandir": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", @@ -256,18 +349,56 @@ "fastq": "^1.6.0" } }, + "@types/chai": { + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.14.tgz", + "integrity": "sha512-G+ITQPXkwTrslfG5L/BksmbLUA0M1iybEsmCWPqzSxsRRhJZimBKJkoMi8fr/CPygPTj4zO5pJH7I2/cm9M7SQ==", + "dev": true + }, + "@types/eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==", + "dev": true + }, "@types/json-schema": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz", "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==", "dev": true }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true + }, "@types/minimist": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.1.tgz", "integrity": "sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==", "dev": true }, + "@types/mocha": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.0.4.tgz", + "integrity": "sha512-M4BwiTJjHmLq6kjON7ZoI2JMlBvpY3BYSdiP6s/qCT3jb1s9/DeJF0JELpAxiVSIxXDzfNKe+r7yedMIoLbknQ==", + "dev": true + }, + "@types/nock": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/@types/nock/-/nock-11.1.0.tgz", + "integrity": "sha512-jI/ewavBQ7X5178262JQR0ewicPAcJhXS/iFaNJl0VHLfyosZ/kwSrsa6VNQNSO8i9d8SqdRgOtZSOKJ/+iNMw==", + "dev": true, + "requires": { + "nock": "*" + } + }, + "@types/node": { + "version": "14.14.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.9.tgz", + "integrity": "sha512-JsoLXFppG62tWTklIoO4knA+oDTYsmqWxHRvd4lpmfQRNhX6osheUOWETP2jMoV/2bEHuMra8Pp3Dmo/stBFcw==" + }, "@types/normalize-package-data": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", @@ -280,6 +411,15 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, + "@types/xml": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/xml/-/xml-1.0.5.tgz", + "integrity": "sha512-h3PVM7waRi2UeoaY2BhpLGvettU/3vfCbsjXMV/9Ex5WjvIy82J8Qfp1xiPxM4kTSOLdFFpjRwQ7YY7XJeKBvg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@typescript-eslint/eslint-plugin": { "version": "4.8.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.8.1.tgz", @@ -434,6 +574,12 @@ "picomatch": "^2.0.4" } }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -449,12 +595,36 @@ "integrity": "sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4=", "dev": true }, + "array-includes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.2.tgz", + "integrity": "sha512-w2GspexNQpx+PutG3QpT437/BenZBj0M/MZGn5mzv/MofYqo0xmRHzn4lFsoDlWJ+THYsGJmFlW68WlDFx7VRw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1", + "get-intrinsic": "^1.0.1", + "is-string": "^1.0.5" + } + }, "array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, + "array.prototype.flat": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz", + "integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1" + } + }, "arrify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", @@ -524,6 +694,22 @@ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "call-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.0.tgz", + "integrity": "sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.0" + } + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -877,6 +1063,18 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, + "confusing-browser-globals": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.10.tgz", + "integrity": "sha512-gNld/3lySHwuhaVluJUKLePYirM3QNCKzVxqAdhJII9/WXKVX5PURzMVJspS1jTslSqjeuG4KMVTSouit5YPHA==", + "dev": true + }, + "contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", + "dev": true + }, "conventional-changelog-angular": { "version": "5.0.12", "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.12.tgz", @@ -1003,6 +1201,15 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, "diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -1019,12 +1226,13 @@ } }, "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", "dev": true, "requires": { - "esutils": "^2.0.2" + "esutils": "^2.0.2", + "isarray": "^1.0.0" } }, "dot-prop": { @@ -1060,6 +1268,37 @@ "is-arrayish": "^0.2.1" } }, + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -1109,6 +1348,340 @@ "table": "^5.2.3", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + } + } + }, + "eslint-config-prettier": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.15.0.tgz", + "integrity": "sha512-a1+kOYLR8wMGustcgAjdydMsQ2A/2ipRPwRKUmfYaSxc9ZPcrku080Ctl6zrZzZNs/U82MjSv+qKREkoq3bJaw==", + "dev": true, + "requires": { + "get-stdin": "^6.0.0" + }, + "dependencies": { + "get-stdin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", + "dev": true + } + } + }, + "eslint-config-react-app": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-5.2.1.tgz", + "integrity": "sha512-pGIZ8t0mFLcV+6ZirRgYK6RVqUIKRIi9MmgzUEmrIknsn3AdO0I32asO86dJgloHq+9ZPl8UIg8mYrvgP5u2wQ==", + "dev": true, + "requires": { + "confusing-browser-globals": "^1.0.9" + } + }, + "eslint-import-resolver-node": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", + "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "resolve": "^1.13.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "eslint-module-utils": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz", + "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "pkg-dir": "^2.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + } + } + }, + "eslint-plugin-import": { + "version": "2.22.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz", + "integrity": "sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw==", + "dev": true, + "requires": { + "array-includes": "^3.1.1", + "array.prototype.flat": "^1.2.3", + "contains-path": "^0.1.0", + "debug": "^2.6.9", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "^0.3.4", + "eslint-module-utils": "^2.6.0", + "has": "^1.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.1", + "read-pkg-up": "^2.0.0", + "resolve": "^1.17.0", + "tsconfig-paths": "^3.9.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "^2.0.0" + } + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "eslint-plugin-jest": { + "version": "23.20.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-23.20.0.tgz", + "integrity": "sha512-+6BGQt85OREevBDWCvhqj1yYA4+BFK4XnRZSGJionuEYmcglMZYLNNBBemwzbqUAckURaHdJSBcjHPyrtypZOw==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "^2.5.0" + }, + "dependencies": { + "@typescript-eslint/experimental-utils": { + "version": "2.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.34.0.tgz", + "integrity": "sha512-eS6FTkq+wuMJ+sgtuNTtcqavWXqsflWcfBnlYhg/nS4aZ1leewkXGbvBhaapn1q6qf4M71bsR1tez5JTRMuqwA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/typescript-estree": "2.34.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^2.0.0" + } + }, + "@typescript-eslint/typescript-estree": { + "version": "2.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.34.0.tgz", + "integrity": "sha512-OMAr+nJWKdlVM9LOqCqh3pQQPwxHAN7Du8DR6dmwCrAmxtiXQnhHJ6tBNtf+cggqfo51SG/FCwnKhXCIM7hnVg==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "eslint-visitor-keys": "^1.1.0", + "glob": "^7.1.6", + "is-glob": "^4.0.1", + "lodash": "^4.17.15", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + } + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } } }, "eslint-scope": { @@ -1370,6 +1943,17 @@ "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", "dev": true }, + "get-intrinsic": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.1.tgz", + "integrity": "sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, "get-stdin": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", @@ -1485,6 +2069,12 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, "he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -1500,6 +2090,11 @@ "lru-cache": "^6.0.0" } }, + "http-status-codes": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.1.4.tgz", + "integrity": "sha512-MZVIsLKGVOVE1KEnldppe6Ij+vmemMuApDfjhVSLzyYP+td0bREEYyAoIw9yFePoBXManCuBqmiNP5FqJS5Xkg==" + }, "husky": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/husky/-/husky-4.3.0.tgz", @@ -1583,6 +2178,12 @@ "binary-extensions": "^2.0.0" } }, + "is-callable": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", + "dev": true + }, "is-core-module": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.1.0.tgz", @@ -1592,6 +2193,12 @@ "has": "^1.0.3" } }, + "is-date-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", + "dev": true + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1613,6 +2220,12 @@ "is-extglob": "^2.1.1" } }, + "is-negative-zero": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", + "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=", + "dev": true + }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -1631,6 +2244,30 @@ "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true }, + "is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, + "is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", + "dev": true + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, "is-text-path": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", @@ -1686,6 +2323,21 @@ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", "dev": true }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, "jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", @@ -1732,6 +2384,29 @@ "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", "dev": true }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + } + } + }, "locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -1753,6 +2428,12 @@ "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", "dev": true }, + "lodash.set": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", + "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=", + "dev": true + }, "lodash.template": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", @@ -1790,6 +2471,12 @@ "yallist": "^4.0.0" } }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, "map-obj": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.1.0.tgz", @@ -1977,6 +2664,18 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "nock": { + "version": "13.0.5", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.0.5.tgz", + "integrity": "sha512-1ILZl0zfFm2G4TIeJFW0iHknxr2NyA+aGCMTjDVUsBY4CkMRispF1pfIYkTRdAR/3Bg+UzdEuK0B6HczMQZcCg==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "lodash.set": "^4.3.2", + "propagate": "^2.0.0" + } + }, "normalize-package-data": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.0.tgz", @@ -1995,6 +2694,63 @@ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, + "object-inspect": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "object.values": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", + "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "has": "^1.0.3" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", + "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + } + } + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -2111,6 +2867,12 @@ "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", "dev": true }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, "pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", @@ -2186,6 +2948,12 @@ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, + "propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", + "dev": true + }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -2490,6 +3258,22 @@ "is-fullwidth-code-point": "^2.0.0" } }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "spdx-correct": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", @@ -2577,6 +3361,26 @@ } } }, + "string.prototype.trimend": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz", + "integrity": "sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + } + }, + "string.prototype.trimstart": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz", + "integrity": "sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + } + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -2603,6 +3407,12 @@ "ansi-regex": "^5.0.0" } }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, "strip-indent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", @@ -2700,6 +3510,31 @@ "integrity": "sha1-n5up2e+odkw4dpi8v+sshI8RrbM=", "dev": true }, + "ts-node": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.0.0.tgz", + "integrity": "sha512-/TqB4SnererCDR/vb4S/QvSZvzQMJN8daAslg7MeaiHvD8rDZsSfXmNeNumyZZzMned72Xoq/isQljYSt8Ynfg==", + "dev": true, + "requires": { + "arg": "^4.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + } + }, + "tsconfig-paths": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", + "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.0", + "strip-bom": "^3.0.0" + } + }, "tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", @@ -2739,7 +3574,8 @@ "typescript": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.2.tgz", - "integrity": "sha512-thGloWsGH3SOxv1SoY7QojKi0tc+8FnOmiarEGMbd/lar7QOEd3hvlx3Fp5y6FlDUGl9L+pd4n2e+oToGMmhRQ==" + "integrity": "sha512-thGloWsGH3SOxv1SoY7QojKi0tc+8FnOmiarEGMbd/lar7QOEd3hvlx3Fp5y6FlDUGl9L+pd4n2e+oToGMmhRQ==", + "dev": true }, "universalify": { "version": "1.0.0", @@ -2890,6 +3726,12 @@ "mkdirp": "^0.5.1" } }, + "xml": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", + "integrity": "sha1-eLpyAgApxbyHuKgaPPzXS0ovweU=", + "dev": true + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -3012,6 +3854,12 @@ "dev": true } } + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true } } } diff --git a/package.json b/package.json index 31799a5..601e435 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,8 @@ "description": "easy communication with osm api", "main": "index.js", "scripts": { - "test": "mocha" + "test": "mocha -r ts-node/register 'tests/**/*.test.ts'", + "tsc": "tsc" }, "repository": { "type": "git", @@ -15,6 +16,14 @@ "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" } }, + "eslintConfig": { + "extends": [ + "@map-colonies/eslint-config/ts-base" + ], + "parserOptions": { + "project": "./tsconfig.json" + } + }, "author": "galta95", "license": "ISC", "bugs": { @@ -23,16 +32,26 @@ "homepage": "https://github.com/MapColonies/node-osm-api#readme", "dependencies": { "axios": "^0.21.0", - "typescript": "^4.1.2" + "http-status-codes": "^2.1.4" }, "devDependencies": { + "@types/node": "^14.14.9", "@commitlint/config-conventional": "^11.0.0", + "@map-colonies/eslint-config": "^1.1.0", + "@types/chai": "^4.2.14", + "@types/mocha": "^8.0.4", + "@types/nock": "^11.1.0", "@typescript-eslint/eslint-plugin": "^4.8.1", "@typescript-eslint/parser": "^4.8.1", + "@types/xml": "^1.0.5", + "typescript": "^4.1.2", "chai": "^4.2.0", "commitlint": "^11.0.0", "eslint": "^7.14.0", "husky": "^4.3.0", - "mocha": "^8.2.1" + "mocha": "^8.2.1", + "nock": "^13.0.5", + "ts-node": "^9.0.0", + "xml": "^1.0.1" } } diff --git a/src/api/v6/index.ts b/src/api/v6/index.ts new file mode 100644 index 0000000..836ae22 --- /dev/null +++ b/src/api/v6/index.ts @@ -0,0 +1,35 @@ +import axios, { AxiosError, AxiosInstance, AxiosResponse } from 'axios'; +import StatusCodes from 'http-status-codes'; +import { createChangesetEndPoint } from '../../lib/endpoints'; +import { UnauthorizedError, BadXmlError, InternalServerError } from '../../lib/error-handler'; +class Apiv6 { + private readonly httpClient: AxiosInstance; + + public constructor(private readonly baseUrl: string, username: string, password: string) { + this.httpClient = axios.create({ baseURL: baseUrl, auth: { username, password } }); + } + + public async createChangeset(data: string): Promise { + let res: AxiosResponse; + try { + res = await this.httpClient.put(createChangesetEndPoint, data); + } + catch (e) { + const axiosError = e as AxiosError; + + if (axiosError.response?.status === StatusCodes.BAD_REQUEST) { + throw new BadXmlError(axiosError); + } else if (axiosError.response?.status === StatusCodes.UNAUTHORIZED) { + throw new UnauthorizedError(axiosError); + } else if (axiosError.response?.status === StatusCodes.INTERNAL_SERVER_ERROR) { + throw new InternalServerError(axiosError); + } else { + throw new Error(e); + } + } + const { data: changeSetId } = res; + return changeSetId; + } +} + +export default Apiv6; \ No newline at end of file diff --git a/src/index.ts b/src/index.ts new file mode 100755 index 0000000..1ccb111 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,3 @@ +import Apiv6 from './api/v6'; + +export default Apiv6; \ No newline at end of file diff --git a/src/lib/endpoints.ts b/src/lib/endpoints.ts new file mode 100644 index 0000000..d1a8f26 --- /dev/null +++ b/src/lib/endpoints.ts @@ -0,0 +1 @@ +export const createChangesetEndPoint = '/api/0.6/changeset/create'; \ No newline at end of file diff --git a/src/lib/error-handler.ts b/src/lib/error-handler.ts new file mode 100644 index 0000000..3722169 --- /dev/null +++ b/src/lib/error-handler.ts @@ -0,0 +1,29 @@ +import { AxiosError } from "axios"; + +class HttpErrorHandler extends Error { + public constructor(error: AxiosError) { + super(error.response?.data); + Object.setPrototypeOf(this, HttpErrorHandler.prototype); + } +} + +export class UnauthorizedError extends HttpErrorHandler { + public constructor(error: AxiosError) { + super(error); + Object.setPrototypeOf(this, UnauthorizedError.prototype); + } +} + +export class BadXmlError extends HttpErrorHandler { + public constructor(error: AxiosError) { + super(error); + Object.setPrototypeOf(this, BadXmlError.prototype); + } +} + +export class InternalServerError extends HttpErrorHandler { + public constructor(error: AxiosError) { + super(error); + Object.setPrototypeOf(this, InternalServerError.prototype); + } +} \ No newline at end of file diff --git a/tests/unit/apiv6.test.ts b/tests/unit/apiv6.test.ts new file mode 100644 index 0000000..e3208ac --- /dev/null +++ b/tests/unit/apiv6.test.ts @@ -0,0 +1,46 @@ +import { expect } from 'chai'; +import nock = require('nock'); + +import Apiv6 from '../../src/index'; +import { createChangesetEndPoint } from '../../src/lib/endpoints'; +import { testConf } from './config/tests-config'; +import { createChangesetXml } from './lib/osm-xml'; + +const { baseUrl, username, password } = testConf; + +describe('apiv6', function () { + describe('happy flow', function () { + describe('/changeset/create', function () { + describe('with register user', function () { + it('should return 200 and changset number', async function () { + const apiv6 = new Apiv6(baseUrl, username, password); + + nock(baseUrl).put(createChangesetEndPoint).reply(200, '12'); + + const xmlData = createChangesetXml("test-generator", "test-user"); + const res = await apiv6.createChangeset(xmlData); + + expect(res).to.be.equal(12); + }); + }); + describe('with unregisterd user', function () { + it('should return error', async function () { + const apiv6 = new Apiv6(baseUrl, 'not-registerd', '123456'); + + nock(baseUrl).put(createChangesetEndPoint).reply(401, 'Couldn\'t authenticate you'); + + const xmlData = createChangesetXml("test-generator", "test-user"); + + try { + await apiv6.createChangeset(xmlData); + } catch (e) { + console.log(e); + expect(e).to.be.a('Error') + .with.property('message') + .and.to.be.equal('Couldn\'t authenticate you'); + } + }); + }); + }); + }); +}); \ No newline at end of file diff --git a/tests/unit/config/tests-config.ts b/tests/unit/config/tests-config.ts new file mode 100644 index 0000000..a93952e --- /dev/null +++ b/tests/unit/config/tests-config.ts @@ -0,0 +1,5 @@ +export const testConf = { + baseUrl: 'http://test.com:8080', + username: 'USERNAME', + password: 'PASSWORD' +}; \ No newline at end of file diff --git a/tests/unit/lib/osm-xml.ts b/tests/unit/lib/osm-xml.ts new file mode 100644 index 0000000..ffc7e3f --- /dev/null +++ b/tests/unit/lib/osm-xml.ts @@ -0,0 +1,18 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import xml from 'xml'; + +export function createChangesetXml(generator: string, createdBy: string): string { + const data = [{ + osm: [ + { changeset: [ + { _atter: {version: 0.6, generator: generator}}, + { tag: {_attr: {k: "created_by", v: createdBy}}} + ]}] + }]; + return createtXml(data); +} + +function createtXml(file: any): string { + const osmXml = xml(file); + return osmXml; +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..08a9ed8 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,73 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "es5", /* 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": [ + "ES2015", + "DOM" + ], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + // "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + "outDir": "./build", /* Redirect output structure to the directory. */ + "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ + + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "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. */ + + /* Advanced Options */ + "skipLibCheck": true, /* Skip type checking of declaration files. */ + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + } +} From a291b71c38b80a495f797a3c293ccec8764bdade Mon Sep 17 00:00:00 2001 From: galta95 <48960890+galta95@users.noreply.github.com> Date: Thu, 3 Dec 2020 10:33:24 +0200 Subject: [PATCH 2/3] feat(apiv6): implemented closeChangeset (#12) * feat(apiv06): new route /api/0.6/changeset/#id/close * style(prettier): format all documents * refactor(apiv06): export api res messages to constants * style(prettier): added print width to 150 * style(rename-file): error-handler to errors * style(constants): rename constants --- .github/workflows/lint.yml | 3 +- .github/workflows/tests.yml | 2 +- .prettierrc.json | 7 ++ README.md | 1 + commitlint.config.js | 2 +- package-lock.json | 24 ++--- package.json | 7 +- src/api/v6/index.ts | 83 +++++++++++----- src/index.ts | 2 +- src/lib/constants.ts | 1 + src/lib/endpoints.ts | 3 +- src/lib/error-handler.ts | 29 ------ src/lib/errors.ts | 50 ++++++++++ tests/unit/apiv6.test.ts | 159 +++++++++++++++++++++++------- tests/unit/config/tests-config.ts | 16 ++- tests/unit/lib/osm-xml.ts | 18 ---- tsconfig.json | 23 ++--- 17 files changed, 278 insertions(+), 152 deletions(-) create mode 100644 .prettierrc.json create mode 100644 src/lib/constants.ts delete mode 100644 src/lib/error-handler.ts create mode 100644 src/lib/errors.ts delete mode 100644 tests/unit/lib/osm-xml.ts diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index e45340b..9db2409 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -25,4 +25,5 @@ jobs: github_token: ${{ secrets.github_token }} # Enable linters eslint: true - eslint_extensions: ts \ No newline at end of file + prettier: true + eslint_extensions: ts diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3f0610b..c078ac4 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -20,4 +20,4 @@ jobs: run: npm ci - name: Run tests - run: npm run test \ No newline at end of file + run: npm run test diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..8e83df9 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,7 @@ +{ + "trailingComma": "es5", + "tabWidth": 2, + "printWidth": 150, + "semi": true, + "singleQuote": true +} diff --git a/README.md b/README.md index f09077f..24509da 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,3 @@ # node-osm-api + easy communication with osm api diff --git a/commitlint.config.js b/commitlint.config.js index 3d88028..422b194 100644 --- a/commitlint.config.js +++ b/commitlint.config.js @@ -1 +1 @@ -module.exports = { extends: ['@commitlint/config-conventional'] }; \ No newline at end of file +module.exports = { extends: ['@commitlint/config-conventional'] }; diff --git a/package-lock.json b/package-lock.json index 8f506a4..ecd50b5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -397,7 +397,8 @@ "@types/node": { "version": "14.14.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.9.tgz", - "integrity": "sha512-JsoLXFppG62tWTklIoO4knA+oDTYsmqWxHRvd4lpmfQRNhX6osheUOWETP2jMoV/2bEHuMra8Pp3Dmo/stBFcw==" + "integrity": "sha512-JsoLXFppG62tWTklIoO4knA+oDTYsmqWxHRvd4lpmfQRNhX6osheUOWETP2jMoV/2bEHuMra8Pp3Dmo/stBFcw==", + "dev": true }, "@types/normalize-package-data": { "version": "2.4.0", @@ -411,15 +412,6 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, - "@types/xml": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/xml/-/xml-1.0.5.tgz", - "integrity": "sha512-h3PVM7waRi2UeoaY2BhpLGvettU/3vfCbsjXMV/9Ex5WjvIy82J8Qfp1xiPxM4kTSOLdFFpjRwQ7YY7XJeKBvg==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, "@typescript-eslint/eslint-plugin": { "version": "4.8.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.8.1.tgz", @@ -2936,6 +2928,12 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, + "prettier": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", + "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", + "dev": true + }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -3726,12 +3724,6 @@ "mkdirp": "^0.5.1" } }, - "xml": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", - "integrity": "sha1-eLpyAgApxbyHuKgaPPzXS0ovweU=", - "dev": true - }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/package.json b/package.json index 601e435..40411fd 100644 --- a/package.json +++ b/package.json @@ -35,16 +35,14 @@ "http-status-codes": "^2.1.4" }, "devDependencies": { - "@types/node": "^14.14.9", "@commitlint/config-conventional": "^11.0.0", "@map-colonies/eslint-config": "^1.1.0", "@types/chai": "^4.2.14", "@types/mocha": "^8.0.4", "@types/nock": "^11.1.0", + "@types/node": "^14.14.9", "@typescript-eslint/eslint-plugin": "^4.8.1", "@typescript-eslint/parser": "^4.8.1", - "@types/xml": "^1.0.5", - "typescript": "^4.1.2", "chai": "^4.2.0", "commitlint": "^11.0.0", "eslint": "^7.14.0", @@ -52,6 +50,7 @@ "mocha": "^8.2.1", "nock": "^13.0.5", "ts-node": "^9.0.0", - "xml": "^1.0.1" + "typescript": "^4.1.2", + "prettier": "^2.2.1" } } diff --git a/src/api/v6/index.ts b/src/api/v6/index.ts index 836ae22..6ff3962 100644 --- a/src/api/v6/index.ts +++ b/src/api/v6/index.ts @@ -1,35 +1,66 @@ import axios, { AxiosError, AxiosInstance, AxiosResponse } from 'axios'; import StatusCodes from 'http-status-codes'; -import { createChangesetEndPoint } from '../../lib/endpoints'; -import { UnauthorizedError, BadXmlError, InternalServerError } from '../../lib/error-handler'; +import { createChangesetEndPoint, closeChangesetEndPoint } from '../../lib/endpoints'; +import { + UnauthorizedError, + BadXmlError, + ChangesetNotFoundError, + OwnerMismatchError, + NotAllowedError, + ChangesetAlreadyClosedError, +} from '../../lib/errors'; +import { OWNER_MISMATCH } from '../../lib/constants'; class Apiv6 { - private readonly httpClient: AxiosInstance; - - public constructor(private readonly baseUrl: string, username: string, password: string) { - this.httpClient = axios.create({ baseURL: baseUrl, auth: { username, password } }); + private readonly httpClient: AxiosInstance; + + public constructor(private readonly baseUrl: string, username: string, password: string) { + this.httpClient = axios.create({ + baseURL: baseUrl, + auth: { username, password }, + }); + } + + public async createChangeset(data: string): Promise { + let res: AxiosResponse; + try { + res = await this.httpClient.put(createChangesetEndPoint, data); + } catch (e) { + const axiosError = e as AxiosError; + + if (axiosError.response?.status === StatusCodes.BAD_REQUEST) { + throw new BadXmlError(axiosError); + } else if (axiosError.response?.status === StatusCodes.UNAUTHORIZED) { + throw new UnauthorizedError(axiosError); + } else { + throw new Error(e); + } } - - public async createChangeset(data: string): Promise { - let res: AxiosResponse; - try { - res = await this.httpClient.put(createChangesetEndPoint, data); - } - catch (e) { - const axiosError = e as AxiosError; + const { data: changeSetId } = res; + return changeSetId; + } + + public async closeChangeset(id: number): Promise { + try { + await this.httpClient.put(closeChangesetEndPoint(id)); + } catch (e) { + const axiosError = e as AxiosError; - if (axiosError.response?.status === StatusCodes.BAD_REQUEST) { - throw new BadXmlError(axiosError); - } else if (axiosError.response?.status === StatusCodes.UNAUTHORIZED) { - throw new UnauthorizedError(axiosError); - } else if (axiosError.response?.status === StatusCodes.INTERNAL_SERVER_ERROR) { - throw new InternalServerError(axiosError); - } else { - throw new Error(e); - } + if (axiosError.response?.status === StatusCodes.UNAUTHORIZED) { + throw new UnauthorizedError(axiosError); + } else if (axiosError.response?.status === StatusCodes.METHOD_NOT_ALLOWED) { + throw new NotAllowedError(axiosError); + } else if (axiosError.response?.status === StatusCodes.NOT_FOUND) { + throw new ChangesetNotFoundError(axiosError); + } else if (axiosError.response?.status === StatusCodes.CONFLICT) { + if (axiosError.response.data === OWNER_MISMATCH) { + throw new OwnerMismatchError(axiosError); } - const { data: changeSetId } = res; - return changeSetId; + throw new ChangesetAlreadyClosedError(axiosError); + } else { + throw new Error(e); + } } + } } -export default Apiv6; \ No newline at end of file +export default Apiv6; diff --git a/src/index.ts b/src/index.ts index 1ccb111..8c4bf7f 100755 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,3 @@ import Apiv6 from './api/v6'; -export default Apiv6; \ No newline at end of file +export default Apiv6; diff --git a/src/lib/constants.ts b/src/lib/constants.ts new file mode 100644 index 0000000..f1ee6c0 --- /dev/null +++ b/src/lib/constants.ts @@ -0,0 +1 @@ +export const OWNER_MISMATCH = "The user doesn't own that changeset"; diff --git a/src/lib/endpoints.ts b/src/lib/endpoints.ts index d1a8f26..7e50ff8 100644 --- a/src/lib/endpoints.ts +++ b/src/lib/endpoints.ts @@ -1 +1,2 @@ -export const createChangesetEndPoint = '/api/0.6/changeset/create'; \ No newline at end of file +export const createChangesetEndPoint = '/api/0.6/changeset/create'; +export const closeChangesetEndPoint = (id: number): string => `/api/0.6/changeset/${id}/close`; diff --git a/src/lib/error-handler.ts b/src/lib/error-handler.ts deleted file mode 100644 index 3722169..0000000 --- a/src/lib/error-handler.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { AxiosError } from "axios"; - -class HttpErrorHandler extends Error { - public constructor(error: AxiosError) { - super(error.response?.data); - Object.setPrototypeOf(this, HttpErrorHandler.prototype); - } -} - -export class UnauthorizedError extends HttpErrorHandler { - public constructor(error: AxiosError) { - super(error); - Object.setPrototypeOf(this, UnauthorizedError.prototype); - } -} - -export class BadXmlError extends HttpErrorHandler { - public constructor(error: AxiosError) { - super(error); - Object.setPrototypeOf(this, BadXmlError.prototype); - } -} - -export class InternalServerError extends HttpErrorHandler { - public constructor(error: AxiosError) { - super(error); - Object.setPrototypeOf(this, InternalServerError.prototype); - } -} \ No newline at end of file diff --git a/src/lib/errors.ts b/src/lib/errors.ts new file mode 100644 index 0000000..1fe981a --- /dev/null +++ b/src/lib/errors.ts @@ -0,0 +1,50 @@ +import { AxiosError } from 'axios'; + +class HttpErrorHandler extends Error { + public constructor(error: AxiosError) { + super(error.response?.data); + Object.setPrototypeOf(this, HttpErrorHandler.prototype); + } +} + +export class UnauthorizedError extends HttpErrorHandler { + public constructor(error: AxiosError) { + super(error); + Object.setPrototypeOf(this, UnauthorizedError.prototype); + } +} + +export class BadXmlError extends HttpErrorHandler { + public constructor(error: AxiosError) { + super(error); + Object.setPrototypeOf(this, BadXmlError.prototype); + } +} + +export class ChangesetNotFoundError extends HttpErrorHandler { + public constructor(error: AxiosError) { + super(error); + Object.setPrototypeOf(this, ChangesetNotFoundError.prototype); + } +} + +export class ChangesetAlreadyClosedError extends HttpErrorHandler { + public constructor(error: AxiosError) { + super(error); + Object.setPrototypeOf(this, ChangesetAlreadyClosedError.prototype); + } +} + +export class OwnerMismatchError extends HttpErrorHandler { + public constructor(error: AxiosError) { + super(error); + Object.setPrototypeOf(this, OwnerMismatchError.prototype); + } +} + +export class NotAllowedError extends HttpErrorHandler { + public constructor(error: AxiosError) { + super(error); + Object.setPrototypeOf(this, NotAllowedError.prototype); + } +} diff --git a/tests/unit/apiv6.test.ts b/tests/unit/apiv6.test.ts index e3208ac..fbbb2dc 100644 --- a/tests/unit/apiv6.test.ts +++ b/tests/unit/apiv6.test.ts @@ -2,45 +2,132 @@ import { expect } from 'chai'; import nock = require('nock'); import Apiv6 from '../../src/index'; -import { createChangesetEndPoint } from '../../src/lib/endpoints'; +import { createChangesetEndPoint, closeChangesetEndPoint } from '../../src/lib/endpoints'; +import { UnauthorizedError, BadXmlError, ChangesetNotFoundError, ChangesetAlreadyClosedError, OwnerMismatchError } from '../../src/lib/errors'; import { testConf } from './config/tests-config'; -import { createChangesetXml } from './lib/osm-xml'; - -const { baseUrl, username, password } = testConf; +const { baseUrl, username, password, changeSetNumber } = testConf; describe('apiv6', function () { + describe('/changeset/create', function () { describe('happy flow', function () { - describe('/changeset/create', function () { - describe('with register user', function () { - it('should return 200 and changset number', async function () { - const apiv6 = new Apiv6(baseUrl, username, password); - - nock(baseUrl).put(createChangesetEndPoint).reply(200, '12'); - - const xmlData = createChangesetXml("test-generator", "test-user"); - const res = await apiv6.createChangeset(xmlData); - - expect(res).to.be.equal(12); - }); - }); - describe('with unregisterd user', function () { - it('should return error', async function () { - const apiv6 = new Apiv6(baseUrl, 'not-registerd', '123456'); - - nock(baseUrl).put(createChangesetEndPoint).reply(401, 'Couldn\'t authenticate you'); - - const xmlData = createChangesetXml("test-generator", "test-user"); - - try { - await apiv6.createChangeset(xmlData); - } catch (e) { - console.log(e); - expect(e).to.be.a('Error') - .with.property('message') - .and.to.be.equal('Couldn\'t authenticate you'); - } - }); - }); + describe('with register user', function () { + it('should return changset number', async function () { + const apiv6 = new Apiv6(baseUrl, username, password); + + nock(baseUrl).put(createChangesetEndPoint).reply(200, '12'); + + const xmlData = ` + + + + `; + const res = await apiv6.createChangeset(xmlData); + + expect(res).to.be.equal(12); + }); + }); + }); + describe('sad flow', function () { + describe('with unregisterd user', function () { + it('should return UnauthorizedError', async function () { + const apiv6 = new Apiv6(baseUrl, 'not-registerd', '123456'); + + nock(baseUrl).put(createChangesetEndPoint).reply(401, "Couldn't authenticate you"); + + const xmlData = ` + + + + `; + try { + await apiv6.createChangeset(xmlData); + } catch (e) { + return expect(e).to.be.instanceOf(UnauthorizedError).with.property('message').and.to.be.equal("Couldn't authenticate you"); + } + throw new Error('should have thrown an error'); + }); + }); + describe('with bad xml', function () { + it('should return BadXmlError', async function () { + const apiv6 = new Apiv6(baseUrl, 'not-registerd', '123456'); + + nock(baseUrl).put(createChangesetEndPoint).reply(400, 'Cannot parse valid changeset from xml string'); + + const xmlData = ` + + + + + + `; + try { + await apiv6.createChangeset(xmlData); + } catch (e) { + return expect(e).to.be.instanceOf(BadXmlError).with.property('message').and.to.be.equal('Cannot parse valid changeset from xml string'); + } + throw new Error('should have thrown an error'); + }); + }); + }); + }); + describe('/changeset/{id}/close}', function () { + describe('happy flow', function () { + describe('with opened changeset', function () { + it('should close the changset', async function () { + const apiv6 = new Apiv6(baseUrl, username, password); + + nock(baseUrl).put(closeChangesetEndPoint(changeSetNumber)).reply(200); + const res = await apiv6.closeChangeset(changeSetNumber); + expect(res).to.be.equal(undefined); + }); + }); + }); + describe('sad flow', function () { + describe('with mismatch user', function () { + it('should return OwnerMismatchError', async function () { + const apiv6 = new Apiv6(baseUrl, username, password); + + nock(baseUrl).put(closeChangesetEndPoint(changeSetNumber)).reply(409, "The user doesn't own that changeset"); + + try { + await apiv6.closeChangeset(changeSetNumber); + } catch (e) { + return expect(e).to.be.instanceof(OwnerMismatchError).with.property('message').and.to.be.equal("The user doesn't own that changeset"); + } + throw new Error('should have thrown an error'); + }); + }); + describe('with already closed changeset', function () { + it('should return ChangesetAlreadyClosedError', async function () { + const apiv6 = new Apiv6(baseUrl, username, password); + + nock(baseUrl).put(closeChangesetEndPoint(changeSetNumber)).reply(409, `changeset ${changeSetNumber} was closed at`); + + try { + await apiv6.closeChangeset(changeSetNumber); + } catch (e) { + return expect(e) + .to.be.instanceof(ChangesetAlreadyClosedError) + .with.property('message') + .and.to.be.equal(`changeset ${changeSetNumber} was closed at`); + } + throw new Error('should have thrown an error'); + }); + }); + describe('with not exsits changeset', function () { + it('should return ChangesetNotFoundError', async function () { + const apiv6 = new Apiv6(baseUrl, username, password); + + nock(baseUrl).put(closeChangesetEndPoint(changeSetNumber)).reply(404); + + try { + await apiv6.closeChangeset(changeSetNumber); + } catch (e) { + return expect(e).to.be.instanceof(ChangesetNotFoundError); + } + throw new Error('should have thrown an error'); }); + }); }); -}); \ No newline at end of file + }); +}); diff --git a/tests/unit/config/tests-config.ts b/tests/unit/config/tests-config.ts index a93952e..1aab28d 100644 --- a/tests/unit/config/tests-config.ts +++ b/tests/unit/config/tests-config.ts @@ -1,5 +1,11 @@ -export const testConf = { - baseUrl: 'http://test.com:8080', - username: 'USERNAME', - password: 'PASSWORD' -}; \ No newline at end of file +export const testConf: { + baseUrl: string; + username: string; + password: string; + changeSetNumber: number; +} = { + baseUrl: 'http://test.com:8080', + username: 'USERNAME', + password: 'PASSWORD', + changeSetNumber: 12, +}; diff --git a/tests/unit/lib/osm-xml.ts b/tests/unit/lib/osm-xml.ts deleted file mode 100644 index ffc7e3f..0000000 --- a/tests/unit/lib/osm-xml.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -import xml from 'xml'; - -export function createChangesetXml(generator: string, createdBy: string): string { - const data = [{ - osm: [ - { changeset: [ - { _atter: {version: 0.6, generator: generator}}, - { tag: {_attr: {k: "created_by", v: createdBy}}} - ]}] - }]; - return createtXml(data); -} - -function createtXml(file: any): string { - const osmXml = xml(file); - return osmXml; -} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 08a9ed8..aefb5c7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,21 +4,18 @@ /* Basic Options */ // "incremental": true, /* Enable incremental compilation */ - "target": "es5", /* 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": [ - "ES2015", - "DOM" - ], /* Specify library files to be included in the compilation. */ + "target": "es5" /* 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": ["ES2015", "DOM"] /* Specify library files to be included in the compilation. */, // "allowJs": true, /* Allow javascript files to be compiled. */ // "checkJs": true, /* Report errors in .js files. */ // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ // "declaration": true, /* Generates corresponding '.d.ts' file. */ // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - "sourceMap": true, /* Generates corresponding '.map' file. */ + "sourceMap": true /* Generates corresponding '.map' file. */, // "outFile": "./", /* Concatenate and emit output to single file. */ - "outDir": "./build", /* Redirect output structure to the directory. */ - "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + "outDir": "./build" /* Redirect output structure to the directory. */, + "rootDir": "./" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */, // "composite": true, /* Enable project compilation */ // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ // "removeComments": true, /* Do not emit comments to output. */ @@ -28,7 +25,7 @@ // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ /* Strict Type-Checking Options */ - "strict": true, /* Enable all strict type-checking options. */ + "strict": true /* Enable all strict type-checking options. */, // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ // "strictNullChecks": true, /* Enable strict null checks. */ // "strictFunctionTypes": true, /* Enable strict checking of function types. */ @@ -52,7 +49,7 @@ // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ - "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ @@ -67,7 +64,7 @@ // "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. */ + "skipLibCheck": true /* Skip type checking of declaration files. */, + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ } } From 9b0147aca1990b209b39f6a7151dd4b85e7240c3 Mon Sep 17 00:00:00 2001 From: galta95 <48960890+galta95@users.noreply.github.com> Date: Thu, 3 Dec 2020 16:47:53 +0200 Subject: [PATCH 3/3] feat(apiv6): implemented upload Changeset (#14) * feat(apiv6): implemented upload Changeset * refactor(apiv06): switch case in upload route instead of if else --- src/api/v6/index.ts | 57 ++++++++- src/lib/constants.ts | 2 + src/lib/endpoints.ts | 1 + src/lib/errors.ts | 16 +++ tests/unit/apiv6.test.ts | 188 +++++++++++++++++++++++++++++- tests/unit/config/tests-config.ts | 2 - 6 files changed, 258 insertions(+), 8 deletions(-) diff --git a/src/api/v6/index.ts b/src/api/v6/index.ts index 6ff3962..4e369aa 100644 --- a/src/api/v6/index.ts +++ b/src/api/v6/index.ts @@ -1,6 +1,6 @@ import axios, { AxiosError, AxiosInstance, AxiosResponse } from 'axios'; import StatusCodes from 'http-status-codes'; -import { createChangesetEndPoint, closeChangesetEndPoint } from '../../lib/endpoints'; +import { createChangesetEndPoint, closeChangesetEndPoint, uploadChangesetEndPoint } from '../../lib/endpoints'; import { UnauthorizedError, BadXmlError, @@ -8,8 +8,12 @@ import { OwnerMismatchError, NotAllowedError, ChangesetAlreadyClosedError, + MismatchChangesetError, + ChangesetOrDiffElementsNotFoundError, + ConflictErrorType, } from '../../lib/errors'; -import { OWNER_MISMATCH } from '../../lib/constants'; +import { OWNER_MISMATCH, CHANGESET_MISMATCH, CHANGESET_ALREADY_CLOSED } from '../../lib/constants'; + class Apiv6 { private readonly httpClient: AxiosInstance; @@ -61,6 +65,55 @@ class Apiv6 { } } } + + public async uploadChangeset(id: number, data: string): Promise { + let res: AxiosResponse; + try { + res = await this.httpClient.post(uploadChangesetEndPoint(id), data); + } catch (e) { + const axiosError = e as AxiosError; + let error; + + switch (axiosError.response?.status) { + case StatusCodes.BAD_REQUEST: { + error = new BadXmlError(axiosError); + break; + } + case StatusCodes.NOT_FOUND: { + error = new ChangesetOrDiffElementsNotFoundError(axiosError); + break; + } + case StatusCodes.CONFLICT: { + const data = axiosError.response.data; + error = this.conflictErrorFactory(data, axiosError); + break; + } + case StatusCodes.UNAUTHORIZED: { + error = new UnauthorizedError(axiosError); + break; + } + default: { + error = new Error(e); + break; + } + } + throw error; + } + const { data: diffResult } = res; + return diffResult; + } + + private conflictErrorFactory(data: string, axiosError: AxiosError): ConflictErrorType { + if (data.includes(CHANGESET_ALREADY_CLOSED)) { + return new ChangesetAlreadyClosedError(axiosError); + } else if (data.includes(OWNER_MISMATCH)) { + return new OwnerMismatchError(axiosError); + } else if (data.includes(CHANGESET_MISMATCH)) { + return new MismatchChangesetError(axiosError); + } else { + return new Error((axiosError as unknown) as string); + } + } } export default Apiv6; diff --git a/src/lib/constants.ts b/src/lib/constants.ts index f1ee6c0..d6d9311 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -1 +1,3 @@ export const OWNER_MISMATCH = "The user doesn't own that changeset"; +export const CHANGESET_MISMATCH = 'Changeset mismatch'; +export const CHANGESET_ALREADY_CLOSED = 'was closed'; diff --git a/src/lib/endpoints.ts b/src/lib/endpoints.ts index 7e50ff8..9aa9975 100644 --- a/src/lib/endpoints.ts +++ b/src/lib/endpoints.ts @@ -1,2 +1,3 @@ export const createChangesetEndPoint = '/api/0.6/changeset/create'; export const closeChangesetEndPoint = (id: number): string => `/api/0.6/changeset/${id}/close`; +export const uploadChangesetEndPoint = (id: number): string => `/api/0.6/changeset/${id}/upload`; diff --git a/src/lib/errors.ts b/src/lib/errors.ts index 1fe981a..0ffc8d0 100644 --- a/src/lib/errors.ts +++ b/src/lib/errors.ts @@ -1,5 +1,7 @@ import { AxiosError } from 'axios'; +export type ConflictErrorType = ChangesetAlreadyClosedError | MismatchChangesetError | ChangesetAlreadyClosedError | Error; + class HttpErrorHandler extends Error { public constructor(error: AxiosError) { super(error.response?.data); @@ -28,6 +30,13 @@ export class ChangesetNotFoundError extends HttpErrorHandler { } } +export class ChangesetOrDiffElementsNotFoundError extends HttpErrorHandler { + public constructor(error: AxiosError) { + super(error); + Object.setPrototypeOf(this, ChangesetOrDiffElementsNotFoundError.prototype); + } +} + export class ChangesetAlreadyClosedError extends HttpErrorHandler { public constructor(error: AxiosError) { super(error); @@ -48,3 +57,10 @@ export class NotAllowedError extends HttpErrorHandler { Object.setPrototypeOf(this, NotAllowedError.prototype); } } + +export class MismatchChangesetError extends HttpErrorHandler { + public constructor(error: AxiosError) { + super(error); + Object.setPrototypeOf(this, MismatchChangesetError.prototype); + } +} diff --git a/tests/unit/apiv6.test.ts b/tests/unit/apiv6.test.ts index fbbb2dc..322876d 100644 --- a/tests/unit/apiv6.test.ts +++ b/tests/unit/apiv6.test.ts @@ -2,10 +2,18 @@ import { expect } from 'chai'; import nock = require('nock'); import Apiv6 from '../../src/index'; -import { createChangesetEndPoint, closeChangesetEndPoint } from '../../src/lib/endpoints'; -import { UnauthorizedError, BadXmlError, ChangesetNotFoundError, ChangesetAlreadyClosedError, OwnerMismatchError } from '../../src/lib/errors'; +import { createChangesetEndPoint, closeChangesetEndPoint, uploadChangesetEndPoint } from '../../src/lib/endpoints'; +import { + UnauthorizedError, + BadXmlError, + ChangesetNotFoundError, + ChangesetAlreadyClosedError, + OwnerMismatchError, + MismatchChangesetError, + ChangesetOrDiffElementsNotFoundError, +} from '../../src/lib/errors'; import { testConf } from './config/tests-config'; -const { baseUrl, username, password, changeSetNumber } = testConf; +const { baseUrl, username, password } = testConf; describe('apiv6', function () { describe('/changeset/create', function () { @@ -70,13 +78,16 @@ describe('apiv6', function () { }); }); }); - describe('/changeset/{id}/close}', function () { + + describe('/changeset/#id/close', function () { describe('happy flow', function () { describe('with opened changeset', function () { it('should close the changset', async function () { const apiv6 = new Apiv6(baseUrl, username, password); + const changeSetNumber = 12; nock(baseUrl).put(closeChangesetEndPoint(changeSetNumber)).reply(200); + const res = await apiv6.closeChangeset(changeSetNumber); expect(res).to.be.equal(undefined); }); @@ -86,6 +97,7 @@ describe('apiv6', function () { describe('with mismatch user', function () { it('should return OwnerMismatchError', async function () { const apiv6 = new Apiv6(baseUrl, username, password); + const changeSetNumber = 12; nock(baseUrl).put(closeChangesetEndPoint(changeSetNumber)).reply(409, "The user doesn't own that changeset"); @@ -100,6 +112,7 @@ describe('apiv6', function () { describe('with already closed changeset', function () { it('should return ChangesetAlreadyClosedError', async function () { const apiv6 = new Apiv6(baseUrl, username, password); + const changeSetNumber = 12; nock(baseUrl).put(closeChangesetEndPoint(changeSetNumber)).reply(409, `changeset ${changeSetNumber} was closed at`); @@ -117,6 +130,7 @@ describe('apiv6', function () { describe('with not exsits changeset', function () { it('should return ChangesetNotFoundError', async function () { const apiv6 = new Apiv6(baseUrl, username, password); + const changeSetNumber = 12; nock(baseUrl).put(closeChangesetEndPoint(changeSetNumber)).reply(404); @@ -130,4 +144,170 @@ describe('apiv6', function () { }); }); }); + + describe('/changeset/#id/upload', function () { + describe('happy flow', function () { + describe('with valid osm change', function () { + it('should return 200 and diff result', async function () { + const apiv6 = new Apiv6(baseUrl, username, password); + const changeSetNumber = 12; + + const mockRes = ` + + + `; + nock(baseUrl).post(uploadChangesetEndPoint(changeSetNumber)).reply(200, mockRes); + + const xmlData = ` + + + + + + `; + + const res = await apiv6.uploadChangeset(changeSetNumber, xmlData); + + expect(res).to.be.equal(mockRes); + }); + }); + }); + describe('sad flow', function () { + describe('with bad xml', function () { + it('should return BadXmlError', async function () { + const apiv6 = new Apiv6(baseUrl, username, password); + const changeSetNumber = 12; + + nock(baseUrl).post(uploadChangesetEndPoint(changeSetNumber)).reply(400, "Document element should be 'osmChange'"); + + const xmlData = ` + + + + + + `; + try { + await apiv6.uploadChangeset(changeSetNumber, xmlData); + } catch (e) { + return expect(e).to.be.instanceof(BadXmlError); + } + throw new Error('should have thrown an error'); + }); + }); + describe('with not exsits changeset', function () { + it('should return ChangesetOrDiffElementsNotFoundError', async function () { + const apiv6 = new Apiv6(baseUrl, username, password); + const changeSetNumber = 7000; + + nock(baseUrl).post(uploadChangesetEndPoint(changeSetNumber)).reply(404); + + const xmlData = ` + + + + + + `; + try { + await apiv6.uploadChangeset(changeSetNumber, xmlData); + } catch (e) { + return expect(e).to.be.instanceof(ChangesetOrDiffElementsNotFoundError); + } + throw new Error('should have thrown an error'); + }); + }); + describe('with already closed changeset', function () { + it('should return ChangesetAlreadyClosedError', async function () { + const apiv6 = new Apiv6(baseUrl, username, password); + const changeSetNumber = 12; + + nock(baseUrl).post(uploadChangesetEndPoint(changeSetNumber)).reply(409, `changeset ${changeSetNumber} was closed at`); + + const xmlData = ` + + + + + + `; + try { + await apiv6.uploadChangeset(changeSetNumber, xmlData); + } catch (e) { + return expect(e).to.be.instanceof(ChangesetAlreadyClosedError); + } + throw new Error('should have thrown an error'); + }); + }); + describe('with mismatch user', function () { + it('should return OwnerMismatchError', async function () { + const apiv6 = new Apiv6(baseUrl, username, password); + const changeSetNumber = 12; + + nock(baseUrl).post(uploadChangesetEndPoint(changeSetNumber)).reply(409, "The user doesn't own that changeset"); + + const xmlData = ` + + + + + + `; + try { + await apiv6.uploadChangeset(changeSetNumber, xmlData); + } catch (e) { + return expect(e).to.be.instanceof(OwnerMismatchError); + } + throw new Error('should have thrown an error'); + }); + }); + describe('with mismatch changeset', function () { + it('should return MismatchChangesetError', async function () { + const apiv6 = new Apiv6(baseUrl, username, password); + const changeSetNumber = 12; + const mismatchChangesetNumber = 13; + + nock(baseUrl) + .post(uploadChangesetEndPoint(changeSetNumber)) + .reply(409, `Changeset mismatch: Provided ${mismatchChangesetNumber} but only ${changeSetNumber} is allowed`); + + const xmlData = ` + + + + + + `; + try { + await apiv6.uploadChangeset(changeSetNumber, xmlData); + } catch (e) { + return expect(e).to.be.instanceof(MismatchChangesetError); + } + throw new Error('should have thrown an error'); + }); + }); + describe('with unregisterd user', function () { + it('should return UnauthorizedError', async function () { + const apiv6 = new Apiv6(baseUrl, username, password); + const changeSetNumber = 12; + + nock(baseUrl).post(uploadChangesetEndPoint(changeSetNumber)).reply(401, "Couldn't authenticate you"); + + const xmlData = ` + + + + + + `; + try { + await apiv6.uploadChangeset(changeSetNumber, xmlData); + } catch (e) { + return expect(e).to.be.instanceof(UnauthorizedError); + } + throw new Error('should have thrown an error'); + }); + }); + }); + }); }); diff --git a/tests/unit/config/tests-config.ts b/tests/unit/config/tests-config.ts index 1aab28d..8115f35 100644 --- a/tests/unit/config/tests-config.ts +++ b/tests/unit/config/tests-config.ts @@ -2,10 +2,8 @@ export const testConf: { baseUrl: string; username: string; password: string; - changeSetNumber: number; } = { baseUrl: 'http://test.com:8080', username: 'USERNAME', password: 'PASSWORD', - changeSetNumber: 12, };