From cb2f6440b5209d8b7c156dd9f8a14333757ff0b1 Mon Sep 17 00:00:00 2001 From: GPortas Date: Mon, 15 May 2023 13:18:59 +0100 Subject: [PATCH 01/30] Stash: GetDatasetSummaryFieldName use case added, pending repository --- package-lock.json | 628 ++++++++---------- .../repositories/IDatasetsRepository.ts | 3 + .../useCases/GetDatasetSummaryFieldNames.ts | 14 + .../GetDatasetSummaryFieldNames.test.ts | 35 + 4 files changed, 326 insertions(+), 354 deletions(-) create mode 100644 src/datasets/domain/repositories/IDatasetsRepository.ts create mode 100644 src/datasets/domain/useCases/GetDatasetSummaryFieldNames.ts create mode 100644 test/unit/datasets/GetDatasetSummaryFieldNames.test.ts diff --git a/package-lock.json b/package-lock.json index 8836e639..05a2c1dc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,12 +29,12 @@ } }, "node_modules/@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", "dev": true, "dependencies": { - "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { @@ -54,30 +54,30 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.4.tgz", - "integrity": "sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g==", + "version": "7.21.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.7.tgz", + "integrity": "sha512-KYMqFYTaenzMK4yUtf4EW9wc4N9ef80FsbMtkwool5zpwl4YrT1SdWYSTRcT94KO4hannogdS+LxY7L+arP3gA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.4.tgz", - "integrity": "sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA==", + "version": "7.21.8", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.8.tgz", + "integrity": "sha512-YeM22Sondbo523Sz0+CirSPnbj9bG3P0CdHcBZdqUuaeOaYEFbOLoGU7lebvGP6P5J/WE9wOn7u7C4J9HvS1xQ==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.21.4", - "@babel/generator": "^7.21.4", - "@babel/helper-compilation-targets": "^7.21.4", - "@babel/helper-module-transforms": "^7.21.2", - "@babel/helpers": "^7.21.0", - "@babel/parser": "^7.21.4", + "@babel/generator": "^7.21.5", + "@babel/helper-compilation-targets": "^7.21.5", + "@babel/helper-module-transforms": "^7.21.5", + "@babel/helpers": "^7.21.5", + "@babel/parser": "^7.21.8", "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.4", - "@babel/types": "^7.21.4", + "@babel/traverse": "^7.21.5", + "@babel/types": "^7.21.5", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -108,12 +108,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.4.tgz", - "integrity": "sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA==", + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.5.tgz", + "integrity": "sha512-SrKK/sRv8GesIW1bDagf9cCG38IOMYZusoe1dfg0D8aiUe3Amvoj1QtjTPAWcfrZFvIwlleLb0gxzQidL9w14w==", "dev": true, "dependencies": { - "@babel/types": "^7.21.4", + "@babel/types": "^7.21.5", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -122,27 +122,13 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz", - "integrity": "sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg==", + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.5.tgz", + "integrity": "sha512-1RkbFGUKex4lvsB9yhIfWltJM5cZKUftB2eNajaDv3dCMEp49iBG0K14uH8NnX9IPux2+mK7JGEOB0jn48/J6w==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.21.4", + "@babel/compat-data": "^7.21.5", "@babel/helper-validator-option": "^7.21.0", "browserslist": "^4.21.3", "lru-cache": "^5.1.1", @@ -180,9 +166,9 @@ "dev": true }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.21.5.tgz", + "integrity": "sha512-IYl4gZ3ETsWocUWgsFZLM5i1BYx9SoemminVEXadgLBa9TdeorzgLKm8wWLA6J1N/kT3Kch8XIk1laNzYoHKvQ==", "dev": true, "engines": { "node": ">=6.9.0" @@ -226,40 +212,40 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", - "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.5.tgz", + "integrity": "sha512-bI2Z9zBGY2q5yMHoBvJ2a9iX3ZOAzJPm7Q8Yz6YeoUjU/Cvhmi2G4QyTNyPBqqXSgTjUxRg3L0xV45HvkNWWBw==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.20.2", + "@babel/helper-environment-visitor": "^7.21.5", + "@babel/helper-module-imports": "^7.21.4", + "@babel/helper-simple-access": "^7.21.5", "@babel/helper-split-export-declaration": "^7.18.6", "@babel/helper-validator-identifier": "^7.19.1", "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.2", - "@babel/types": "^7.21.2" + "@babel/traverse": "^7.21.5", + "@babel/types": "^7.21.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", - "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.21.5.tgz", + "integrity": "sha512-0WDaIlXKOX/3KfBK/dwP1oQGiPh6rjMkT7HIRv7i5RR2VUMwrx5ZL0dwBkKx7+SW1zwNdgjHd34IMk5ZjTeHVg==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-simple-access": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", - "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.21.5.tgz", + "integrity": "sha512-ENPDAMC1wAjR0uaCUwliBdiSl1KBJAVnMTzXqi64c2MG8MPR6ii4qf7bSXDqSFbr4W6W028/rf5ivoHop5/mkg==", "dev": true, "dependencies": { - "@babel/types": "^7.20.2" + "@babel/types": "^7.21.5" }, "engines": { "node": ">=6.9.0" @@ -278,9 +264,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.21.5.tgz", + "integrity": "sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w==", "dev": true, "engines": { "node": ">=6.9.0" @@ -305,14 +291,14 @@ } }, "node_modules/@babel/helpers": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", - "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.5.tgz", + "integrity": "sha512-BSY+JSlHxOmGsPTydUkPf1MdMQ3M81x5xGCOVgWM3G8XH77sJ292Y2oqcp0CbbgxhqBuI46iUz1tT7hqP7EfgA==", "dev": true, "dependencies": { "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.0", - "@babel/types": "^7.21.0" + "@babel/traverse": "^7.21.5", + "@babel/types": "^7.21.5" }, "engines": { "node": ">=6.9.0" @@ -395,9 +381,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz", - "integrity": "sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==", + "version": "7.21.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.8.tgz", + "integrity": "sha512-6zavDGdzG3gUqAdWvlLFfk+36RilI+Pwyuuh7HItyeScCWP3k6i8vKclAQ0bM/0y/Kz/xiwvxhMv9MgTJP5gmA==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -598,19 +584,19 @@ } }, "node_modules/@babel/traverse": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.4.tgz", - "integrity": "sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q==", + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.5.tgz", + "integrity": "sha512-AhQoI3YjWi6u/y/ntv7k48mcrCXmus0t79J9qPNlk/lAsFlCiJ047RmbfMOawySTHtywXhbXgpx/8nXMYd+oFw==", "dev": true, "dependencies": { "@babel/code-frame": "^7.21.4", - "@babel/generator": "^7.21.4", - "@babel/helper-environment-visitor": "^7.18.9", + "@babel/generator": "^7.21.5", + "@babel/helper-environment-visitor": "^7.21.5", "@babel/helper-function-name": "^7.21.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.21.4", - "@babel/types": "^7.21.4", + "@babel/parser": "^7.21.5", + "@babel/types": "^7.21.5", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -628,12 +614,12 @@ } }, "node_modules/@babel/types": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.4.tgz", - "integrity": "sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA==", + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.5.tgz", + "integrity": "sha512-m4AfNvVF2mVC/F7fDEdH2El3HzUg9It/XsCxZiOTTA3m3qYfcSVSbTfM6Q9xG+hYDniZssYhlXKKUMD5m8tF4Q==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-string-parser": "^7.21.5", "@babel/helper-validator-identifier": "^7.19.1", "to-fast-properties": "^2.0.0" }, @@ -647,15 +633,39 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", + "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, "node_modules/@eslint/eslintrc": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.2.tgz", - "integrity": "sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", + "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.5.1", + "espree": "^9.5.2", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -671,9 +681,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.35.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.35.0.tgz", - "integrity": "sha512-JXdzbRiWclLVoD8sNUjR443VVlYqiYmDVT6rGUEIEHU5YJW0gaVZwV2xgM7D4arkvASqD0IlLUVjHiFuxaftRw==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.40.0.tgz", + "integrity": "sha512-ElyB54bJIhXQYVKjDSvCkPO1iU1tSAeVQJbllWJq1XQSmmA4dgFk8CbiBGpiOPxleE48vDogxCtmMYku4HSVLA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1099,13 +1109,14 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { "node": ">=6.0.0" @@ -1130,21 +1141,27 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "3.1.0", "@jridgewell/sourcemap-codec": "1.4.14" } }, + "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1187,21 +1204,21 @@ "dev": true }, "node_modules/@sinonjs/commons": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", - "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", "dev": true, "dependencies": { "type-detect": "4.0.8" } }, "node_modules/@sinonjs/fake-timers": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz", - "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.1.0.tgz", + "integrity": "sha512-w1qd368vtrwttm1PRJWPW1QHlbmHrVDGs1eBH/jZvRPUFS4MNXV9Q33EQdjOdeAxZ7O8+3wM7zxztm2nfUSyKw==", "dev": true, "dependencies": { - "@sinonjs/commons": "^2.0.0" + "@sinonjs/commons": "^3.0.0" } }, "node_modules/@sinonjs/samsam": { @@ -1215,6 +1232,15 @@ "type-detect": "^4.0.8" } }, + "node_modules/@sinonjs/samsam/node_modules/@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, "node_modules/@sinonjs/text-encoding": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", @@ -1254,18 +1280,18 @@ } }, "node_modules/@types/babel__traverse": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", - "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", + "version": "7.18.5", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.5.tgz", + "integrity": "sha512-enCvTL8m/EHS/zIvJno9nE+ndYPh1/oNFzRYRmtUqJICG2VnCSBzMLW5VN2KCQU91f23tsNKR8v7VJJQMatl7Q==", "dev": true, "dependencies": { "@babel/types": "^7.3.0" } }, "node_modules/@types/chai": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.4.tgz", - "integrity": "sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.5.tgz", + "integrity": "sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==", "dev": true }, "node_modules/@types/graceful-fs": { @@ -1302,9 +1328,9 @@ } }, "node_modules/@types/jest": { - "version": "29.4.0", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.4.0.tgz", - "integrity": "sha512-VaywcGQ9tPorCX/Jkkni7RWGFfI11whqzs8dvxF41P17Z+z872thvEvlIbznjPJ02kl1HMX3LmLOonsj2n7HeQ==", + "version": "29.5.1", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.1.tgz", + "integrity": "sha512-tEuVcHrpaixS36w7hpsfLBLpjtMRJUE09/MHXn923LOVojDwyC14cWcfc0rDs0VEfUyYmt/+iX1kxxp+gZMcaQ==", "dev": true, "dependencies": { "expect": "^29.0.0", @@ -1329,15 +1355,15 @@ "dev": true }, "node_modules/@types/semver": { - "version": "7.3.13", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", - "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", "dev": true }, "node_modules/@types/sinon": { - "version": "10.0.13", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.13.tgz", - "integrity": "sha512-UVjDqJblVNQYvVNUsj0PuYYw0ELRmgt1Nt5Vk0pT5f16ROGfcKJY8o1HVuMOJOpD727RrGB9EGvoaTQE5tgxZQ==", + "version": "10.0.15", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.15.tgz", + "integrity": "sha512-3lrFNQG0Kr2LDzvjyjB6AMJk4ge+8iYhQfdnSwIwlG88FUOV43kPcQqDZkDa/h3WSZy6i8Fr0BSjfQtB1B3xuQ==", "dev": true, "dependencies": { "@types/sinonjs__fake-timers": "*" @@ -1371,19 +1397,19 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.54.0.tgz", - "integrity": "sha512-+hSN9BdSr629RF02d7mMtXhAJvDTyCbprNYJKrXETlul/Aml6YZwd90XioVbjejQeHbb3R8Dg0CkRgoJDxo8aw==", + "version": "5.59.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.5.tgz", + "integrity": "sha512-feA9xbVRWJZor+AnLNAr7A8JRWeZqHUf4T9tlP+TN04b05pFVhO5eN7/O93Y/1OUlLMHKbnJisgDURs/qvtqdg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.54.0", - "@typescript-eslint/type-utils": "5.54.0", - "@typescript-eslint/utils": "5.54.0", + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.59.5", + "@typescript-eslint/type-utils": "5.59.5", + "@typescript-eslint/utils": "5.59.5", "debug": "^4.3.4", "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", "natural-compare-lite": "^1.4.0", - "regexpp": "^3.2.0", "semver": "^7.3.7", "tsutils": "^3.21.0" }, @@ -1405,14 +1431,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.52.0.tgz", - "integrity": "sha512-e2KiLQOZRo4Y0D/b+3y08i3jsekoSkOYStROYmPUnGMEoA0h+k2qOH5H6tcjIc68WDvGwH+PaOrP1XRzLJ6QlA==", + "version": "5.59.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.5.tgz", + "integrity": "sha512-NJXQC4MRnF9N9yWqQE2/KLRSOLvrrlZb48NGVfBa+RuPMN6B7ZcK5jZOvhuygv4D64fRKnZI4L4p8+M+rfeQuw==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.52.0", - "@typescript-eslint/types": "5.52.0", - "@typescript-eslint/typescript-estree": "5.52.0", + "@typescript-eslint/scope-manager": "5.59.5", + "@typescript-eslint/types": "5.59.5", + "@typescript-eslint/typescript-estree": "5.59.5", "debug": "^4.3.4" }, "engines": { @@ -1431,88 +1457,14 @@ } } }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { - "version": "5.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.52.0.tgz", - "integrity": "sha512-AR7sxxfBKiNV0FWBSARxM8DmNxrwgnYMPwmpkC1Pl1n+eT8/I2NAUPuwDy/FmDcC6F8pBfmOcaxcxRHspgOBMw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.52.0", - "@typescript-eslint/visitor-keys": "5.52.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { - "version": "5.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.52.0.tgz", - "integrity": "sha512-oV7XU4CHYfBhk78fS7tkum+/Dpgsfi91IIDy7fjCyq2k6KB63M6gMC0YIvy+iABzmXThCRI6xpCEyVObBdWSDQ==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { - "version": "5.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.52.0.tgz", - "integrity": "sha512-WeWnjanyEwt6+fVrSR0MYgEpUAuROxuAH516WPjUblIrClzYJj0kBbjdnbQXLpgAN8qbEuGywiQsXUVDiAoEuQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.52.0", - "@typescript-eslint/visitor-keys": "5.52.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { - "version": "5.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.52.0.tgz", - "integrity": "sha512-qMwpw6SU5VHCPr99y274xhbm+PRViK/NATY6qzt+Et7+mThGuFSl/ompj2/hrBlRP/kq+BFdgagnOSgw9TB0eA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.52.0", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.54.0.tgz", - "integrity": "sha512-VTPYNZ7vaWtYna9M4oD42zENOBrb+ZYyCNdFs949GcN8Miwn37b8b7eMj+EZaq7VK9fx0Jd+JhmkhjFhvnovhg==", + "version": "5.59.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.5.tgz", + "integrity": "sha512-jVecWwnkX6ZgutF+DovbBJirZcAxgxC0EOHYt/niMROf8p4PwxxG32Qdhj/iIQQIuOflLjNkxoXyArkcIP7C3A==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.54.0", - "@typescript-eslint/visitor-keys": "5.54.0" + "@typescript-eslint/types": "5.59.5", + "@typescript-eslint/visitor-keys": "5.59.5" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1523,13 +1475,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.54.0.tgz", - "integrity": "sha512-WI+WMJ8+oS+LyflqsD4nlXMsVdzTMYTxl16myXPaCXnSgc7LWwMsjxQFZCK/rVmTZ3FN71Ct78ehO9bRC7erYQ==", + "version": "5.59.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.5.tgz", + "integrity": "sha512-4eyhS7oGym67/pSxA2mmNq7X164oqDYNnZCUayBwJZIRVvKpBCMBzFnFxjeoDeShjtO6RQBHBuwybuX3POnDqg==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.54.0", - "@typescript-eslint/utils": "5.54.0", + "@typescript-eslint/typescript-estree": "5.59.5", + "@typescript-eslint/utils": "5.59.5", "debug": "^4.3.4", "tsutils": "^3.21.0" }, @@ -1550,9 +1502,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.54.0.tgz", - "integrity": "sha512-nExy+fDCBEgqblasfeE3aQ3NuafBUxZxgxXcYfzYRZFHdVvk5q60KhCSkG0noHgHRo/xQ/BOzURLZAafFpTkmQ==", + "version": "5.59.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.5.tgz", + "integrity": "sha512-xkfRPHbqSH4Ggx4eHRIO/eGL8XL4Ysb4woL8c87YuAo8Md7AUjyWKa9YMwTL519SyDPrfEgKdewjkxNCVeJW7w==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1563,13 +1515,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.54.0.tgz", - "integrity": "sha512-X2rJG97Wj/VRo5YxJ8Qx26Zqf0RRKsVHd4sav8NElhbZzhpBI8jU54i6hfo9eheumj4oO4dcRN1B/zIVEqR/MQ==", + "version": "5.59.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.5.tgz", + "integrity": "sha512-+XXdLN2CZLZcD/mO7mQtJMvCkzRfmODbeSKuMY/yXbGkzvA9rJyDY5qDYNoiz2kP/dmyAxXquL2BvLQLJFPQIg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.54.0", - "@typescript-eslint/visitor-keys": "5.54.0", + "@typescript-eslint/types": "5.59.5", + "@typescript-eslint/visitor-keys": "5.59.5", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1590,18 +1542,18 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.54.0.tgz", - "integrity": "sha512-cuwm8D/Z/7AuyAeJ+T0r4WZmlnlxQ8wt7C7fLpFlKMR+dY6QO79Cq1WpJhvZbMA4ZeZGHiRWnht7ZJ8qkdAunw==", + "version": "5.59.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.5.tgz", + "integrity": "sha512-sCEHOiw+RbyTii9c3/qN74hYDPNORb8yWCoPLmB7BIflhplJ65u2PBpdRla12e3SSTJ2erRkPjz7ngLHhUegxA==", "dev": true, "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.54.0", - "@typescript-eslint/types": "5.54.0", - "@typescript-eslint/typescript-estree": "5.54.0", + "@typescript-eslint/scope-manager": "5.59.5", + "@typescript-eslint/types": "5.59.5", + "@typescript-eslint/typescript-estree": "5.59.5", "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0", "semver": "^7.3.7" }, "engines": { @@ -1616,12 +1568,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.54.0.tgz", - "integrity": "sha512-xu4wT7aRCakGINTLGeyGqDn+78BwFlggwBjnHa1ar/KaGagnmwLYmlrXIrgAaQ3AE1Vd6nLfKASm7LrFHNbKGA==", + "version": "5.59.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.5.tgz", + "integrity": "sha512-qL+Oz+dbeBRTeyJTIy0eniD3uvqU7x+y1QceBismZ41hd4aBSRh8UAw4pZP0+XzLuPZmx4raNMq/I+59W2lXKA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.54.0", + "@typescript-eslint/types": "5.59.5", "eslint-visitor-keys": "^3.3.0" }, "engines": { @@ -1965,9 +1917,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001473", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001473.tgz", - "integrity": "sha512-ewDad7+D2vlyy+E4UJuVfiBsU69IL+8oVmTuZnH5Q6CIUbxNfI50uVpRHbUPDD6SUaN2o0Lh4DhTrvLG/Tn1yg==", + "version": "1.0.30001487", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001487.tgz", + "integrity": "sha512-83564Z3yWGqXsh2vaH/mhXfEM0wX+NlBCm1jYHOb97TrTWJEmPTccZgeLTPBUUb0PNVo+oomb7wkimZBIERClA==", "dev": true, "funding": [ { @@ -2209,6 +2161,15 @@ "node": ">=8" } }, + "node_modules/diff": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", + "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/diff-sequences": { "version": "29.4.3", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", @@ -2243,9 +2204,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.348", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.348.tgz", - "integrity": "sha512-gM7TdwuG3amns/1rlgxMbeeyNoBFPa+4Uu0c7FeROWh4qWmvSOnvcslKmWy51ggLKZ2n/F/4i2HJ+PVNxH9uCQ==", + "version": "1.4.394", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.394.tgz", + "integrity": "sha512-0IbC2cfr8w5LxTz+nmn2cJTGafsK9iauV2r5A5scfzyovqLrxuLoxOHE5OBobP3oVIggJT+0JfKnw9sm87c8Hw==", "dev": true }, "node_modules/emittery": { @@ -2294,13 +2255,15 @@ } }, "node_modules/eslint": { - "version": "8.35.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.35.0.tgz", - "integrity": "sha512-BxAf1fVL7w+JLRQhWl2pzGeSiGqbWumV4WNvc9Rhp6tiCtm4oHnyPBSEtMGZwrQgudFQ+otqzWoPB7x+hxoWsw==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.40.0.tgz", + "integrity": "sha512-bvR+TsP9EHL3TqNtj9sCNJVAFK3fBN8Q7g5waghxyRsPLIMwL73XSKnZFK0hk/O2ANC+iAoq6PWMQ+IfBAJIiQ==", "dev": true, "dependencies": { - "@eslint/eslintrc": "^2.0.0", - "@eslint/js": "8.35.0", + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.0.3", + "@eslint/js": "8.40.0", "@humanwhocodes/config-array": "^0.11.8", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -2310,10 +2273,9 @@ "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", + "eslint-scope": "^7.2.0", + "eslint-visitor-keys": "^3.4.1", + "espree": "^9.5.2", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -2335,7 +2297,6 @@ "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.1", - "regexpp": "^3.2.0", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" @@ -2387,37 +2348,10 @@ "node": ">=8.0.0" } }, - "node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=5" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/eslint-visitor-keys": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz", - "integrity": "sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -2439,9 +2373,9 @@ } }, "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", @@ -2449,6 +2383,9 @@ }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint/node_modules/estraverse": { @@ -2473,14 +2410,14 @@ } }, "node_modules/espree": { - "version": "9.5.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.1.tgz", - "integrity": "sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg==", + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", + "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", "dev": true, "dependencies": { "acorn": "^8.8.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.0" + "eslint-visitor-keys": "^3.4.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -3018,9 +2955,9 @@ "dev": true }, "node_modules/is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.0.tgz", + "integrity": "sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==", "dev": true, "dependencies": { "has": "^1.0.3" @@ -3186,15 +3123,15 @@ } }, "node_modules/jest": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.4.3.tgz", - "integrity": "sha512-XvK65feuEFGZT8OO0fB/QAQS+LGHvQpaadkH5p47/j3Ocqq3xf2pK9R+G0GzgfuhXVxEv76qCOOcMb5efLk6PA==", + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.5.0.tgz", + "integrity": "sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ==", "dev": true, "dependencies": { - "@jest/core": "^29.4.3", - "@jest/types": "^29.4.3", + "@jest/core": "^29.5.0", + "@jest/types": "^29.5.0", "import-local": "^3.0.2", - "jest-cli": "^29.4.3" + "jest-cli": "^29.5.0" }, "bin": { "jest": "bin/jest.js" @@ -4048,6 +3985,15 @@ "path-to-regexp": "^1.7.0" } }, + "node_modules/nise/node_modules/@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -4352,9 +4298,9 @@ } }, "node_modules/prettier": { - "version": "2.8.4", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz", - "integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==", + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true, "bin": { "prettier": "bin-prettier.js" @@ -4420,9 +4366,9 @@ } }, "node_modules/pure-rand": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.1.tgz", - "integrity": "sha512-t+x1zEHDjBwkDGY5v5ApnZ/utcd4XYDiJsaQQoptTXgUXX95sDg1elCdJghzicm7n2mbCBJ3uYWr6M22SO19rg==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.2.tgz", + "integrity": "sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ==", "dev": true, "funding": [ { @@ -4461,18 +4407,6 @@ "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "dev": true }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -4483,12 +4417,12 @@ } }, "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", + "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", "dev": true, "dependencies": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.11.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -4587,9 +4521,9 @@ } }, "node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -4629,9 +4563,9 @@ "dev": true }, "node_modules/sinon": { - "version": "15.0.3", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.0.3.tgz", - "integrity": "sha512-si3geiRkeovP7Iel2O+qGL4NrO9vbMf3KsrJEi0ghP1l5aBkB5UxARea5j0FUsSqH3HLBh0dQPAyQ8fObRUqHw==", + "version": "15.0.4", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.0.4.tgz", + "integrity": "sha512-uzmfN6zx3GQaria1kwgWGeKiXSSbShBbue6Dcj0SI8fiCNFbiUDqKl57WFlY5lyhxZVUKmXvzgG2pilRQCBwWg==", "dev": true, "dependencies": { "@sinonjs/commons": "^3.0.0", @@ -4646,24 +4580,6 @@ "url": "https://opencollective.com/sinon" } }, - "node_modules/sinon/node_modules/@sinonjs/commons": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", - "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", - "dev": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/sinon/node_modules/diff": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", - "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -4866,9 +4782,9 @@ } }, "node_modules/ts-jest": { - "version": "29.0.5", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.0.5.tgz", - "integrity": "sha512-PL3UciSgIpQ7f6XjVOmbi96vmDHUqAyqDr8YxzopDqX3kfgYtX1cuNeBjP+L9sFXi6nzsGGA6R3fP3DDDJyrxA==", + "version": "29.1.0", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.0.tgz", + "integrity": "sha512-ZhNr7Z4PcYa+JjMl62ir+zPiNJfXJN6E8hSLnaUKhOgqcn8vb3e537cpkd0FuAfRK3sR1LSqM1MOhliXNgOFPA==", "dev": true, "dependencies": { "bs-logger": "0.x", @@ -4891,7 +4807,7 @@ "@jest/types": "^29.0.0", "babel-jest": "^29.0.0", "jest": "^29.0.0", - "typescript": ">=4.3" + "typescript": ">=4.3 <6" }, "peerDependenciesMeta": { "@babel/core": { @@ -4975,9 +4891,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", + "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", "dev": true, "funding": [ { @@ -4987,6 +4903,10 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { @@ -4994,7 +4914,7 @@ "picocolors": "^1.0.0" }, "bin": { - "browserslist-lint": "cli.js" + "update-browserslist-db": "cli.js" }, "peerDependencies": { "browserslist": ">= 4.21.0" @@ -5114,9 +5034,9 @@ "dev": true }, "node_modules/yargs": { - "version": "17.7.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", - "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "dependencies": { "cliui": "^8.0.1", diff --git a/src/datasets/domain/repositories/IDatasetsRepository.ts b/src/datasets/domain/repositories/IDatasetsRepository.ts new file mode 100644 index 00000000..f6c45288 --- /dev/null +++ b/src/datasets/domain/repositories/IDatasetsRepository.ts @@ -0,0 +1,3 @@ +export interface IDatasetsRepository { + getDatasetSummaryFieldNames(): Promise; +} diff --git a/src/datasets/domain/useCases/GetDatasetSummaryFieldNames.ts b/src/datasets/domain/useCases/GetDatasetSummaryFieldNames.ts new file mode 100644 index 00000000..4041e408 --- /dev/null +++ b/src/datasets/domain/useCases/GetDatasetSummaryFieldNames.ts @@ -0,0 +1,14 @@ +import { UseCase } from '../../../core/domain/useCases/UseCase'; +import { IDatasetsRepository } from '../repositories/IDatasetsRepository'; + +export class GetDatasetSummaryFieldNames implements UseCase { + private datasetsRepository: IDatasetsRepository; + + constructor(datasetsRepository: IDatasetsRepository) { + this.datasetsRepository = datasetsRepository; + } + + async execute(): Promise { + return await this.datasetsRepository.getDatasetSummaryFieldNames(); + } +} diff --git a/test/unit/datasets/GetDatasetSummaryFieldNames.test.ts b/test/unit/datasets/GetDatasetSummaryFieldNames.test.ts new file mode 100644 index 00000000..87ab181f --- /dev/null +++ b/test/unit/datasets/GetDatasetSummaryFieldNames.test.ts @@ -0,0 +1,35 @@ +import { GetDatasetSummaryFieldNames } from '../../../src/datasets/domain/useCases/GetDatasetSummaryFieldNames'; +import { IDatasetsRepository } from '../../../src/datasets/domain/repositories/IDatasetsRepository'; +import { ReadError } from '../../../src/core/domain/repositories/ReadError'; +import { assert, createSandbox, SinonSandbox } from 'sinon'; + +describe('execute', () => { + const sandbox: SinonSandbox = createSandbox(); + + afterEach(() => { + sandbox.restore(); + }); + + test('should return successful result with field names on repository success', async () => { + const testFieldNames = ['test1','test2']; + const datasetsRepositoryStub = {}; + datasetsRepositoryStub.getDatasetSummaryFieldNames = sandbox.stub().returns(testFieldNames); + const sut = new GetDatasetSummaryFieldNames(datasetsRepositoryStub); + + const actual = await sut.execute(); + + assert.match(actual, testFieldNames); + }); + + test('should return error result on repository error', async () => { + const datasetsRepositoryStub = {}; + const testReadError = new ReadError(); + datasetsRepositoryStub.getDatasetSummaryFieldNames = sandbox.stub().throwsException(testReadError); + const sut = new GetDatasetSummaryFieldNames(datasetsRepositoryStub); + + let actualError: ReadError = undefined; + await sut.execute().catch((e) => (actualError = e)); + + assert.match(actualError, testReadError); + }); +}); From f25c7b05d86c801eb23fa60595508fe1b6e56792 Mon Sep 17 00:00:00 2001 From: GPortas Date: Mon, 15 May 2023 13:46:09 +0100 Subject: [PATCH 02/30] Added: DatasetsRepository with getDatasetSummaryFieldNames support --- .../infra/repositories/DatasetsRepository.ts | 12 +++++ .../datasets/DatasetsRepository.test.ts | 13 +++++ test/unit/datasets/DatasetsRepository.test.ts | 50 +++++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 src/datasets/infra/repositories/DatasetsRepository.ts create mode 100644 test/integration/datasets/DatasetsRepository.test.ts create mode 100644 test/unit/datasets/DatasetsRepository.test.ts diff --git a/src/datasets/infra/repositories/DatasetsRepository.ts b/src/datasets/infra/repositories/DatasetsRepository.ts new file mode 100644 index 00000000..468773ca --- /dev/null +++ b/src/datasets/infra/repositories/DatasetsRepository.ts @@ -0,0 +1,12 @@ +import { ApiRepository } from '../../../core/infra/repositories/ApiRepository'; +import { IDatasetsRepository } from '../../domain/repositories/IDatasetsRepository'; + +export class DatasetsRepository extends ApiRepository implements IDatasetsRepository { + public async getDatasetSummaryFieldNames(): Promise { + return this.doGet('/datasets/summaryFieldNames') + .then((response) => response.data.data) + .catch((error) => { + throw error; + }); + } +} diff --git a/test/integration/datasets/DatasetsRepository.test.ts b/test/integration/datasets/DatasetsRepository.test.ts new file mode 100644 index 00000000..6c3aaba5 --- /dev/null +++ b/test/integration/datasets/DatasetsRepository.test.ts @@ -0,0 +1,13 @@ +import { DatasetsRepository } from '../../../src/datasets/infra/repositories/DatasetsRepository'; +import { ApiConfig } from '../../../src/core/infra/repositories/ApiConfig'; + +describe('getDatasetSummaryFieldNames', () => { + // TODO: Change API URL to another of an integration test oriented Dataverse instance + const sut: DatasetsRepository = new DatasetsRepository(); + + ApiConfig.init('https://demo.dataverse.org/api/v1'); + + test('should return dataset field names', async () => { + // TODO + }); +}); diff --git a/test/unit/datasets/DatasetsRepository.test.ts b/test/unit/datasets/DatasetsRepository.test.ts new file mode 100644 index 00000000..a67157c5 --- /dev/null +++ b/test/unit/datasets/DatasetsRepository.test.ts @@ -0,0 +1,50 @@ +import { DatasetsRepository } from '../../../src/datasets/infra/repositories/DatasetsRepository'; +import { assert, createSandbox, SinonSandbox } from 'sinon'; +import axios from 'axios'; +import { expect } from 'chai'; +import { ReadError } from '../../../src/core/domain/repositories/ReadError'; +import { ApiConfig } from '../../../src/core/infra/repositories/ApiConfig'; + +describe('getDatasetSummaryFieldNames', () => { + const sandbox: SinonSandbox = createSandbox(); + const sut: DatasetsRepository = new DatasetsRepository(); + const testApiUrl = 'https://test.dataverse.org/api/v1'; + + ApiConfig.init(testApiUrl); + + afterEach(() => { + sandbox.restore(); + }); + + test('should return fields on successful response', async () => { + const testFieldNames = ['test1', 'test2']; + const testSuccessfulResponse = { + data: { + status: 'OK', + data: testFieldNames, + }, + }; + const axiosGetStub = sandbox.stub(axios, 'get').resolves(testSuccessfulResponse); + + const actual = await sut.getDatasetSummaryFieldNames(); + + assert.calledWithExactly(axiosGetStub, `${testApiUrl}/datasets/summaryFieldNames`, { withCredentials: false }); + assert.match(actual, testFieldNames); + }); + + test('should return error result on error response', async () => { + const testErrorResponse = { + response: { + status: 'ERROR', + message: 'test', + }, + }; + const axiosGetStub = sandbox.stub(axios, 'get').rejects(testErrorResponse); + + let error: ReadError = undefined; + await sut.getDatasetSummaryFieldNames().catch((e) => (error = e)); + + assert.calledWithExactly(axiosGetStub, `${testApiUrl}/datasets/summaryFieldNames`, { withCredentials: false }); + expect(error).to.be.instanceOf(Error); + }); +}); From 26a1762b978ef8d0ea6cff9adef8e0df98bf1a28 Mon Sep 17 00:00:00 2001 From: GPortas Date: Mon, 15 May 2023 15:28:15 +0100 Subject: [PATCH 03/30] Added: GetDatasetSummaryFieldNames use case export --- src/datasets/index.ts | 6 ++++++ src/index.ts | 1 + test/integration/datasets/DatasetsRepository.test.ts | 3 --- 3 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 src/datasets/index.ts diff --git a/src/datasets/index.ts b/src/datasets/index.ts new file mode 100644 index 00000000..9645e949 --- /dev/null +++ b/src/datasets/index.ts @@ -0,0 +1,6 @@ +import { DatasetsRepository } from './infra/repositories/DatasetsRepository'; +import { GetDatasetSummaryFieldNames } from './domain/useCases/GetDatasetSummaryFieldNames'; + +const getDatasetSummaryFieldNames = new GetDatasetSummaryFieldNames(new DatasetsRepository()); + +export { getDatasetSummaryFieldNames }; diff --git a/src/index.ts b/src/index.ts index b04f0a42..ada6375d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,3 +2,4 @@ export * from './core'; export * from './info'; export * from './users'; export * from './auth'; +export * from './datasets'; diff --git a/test/integration/datasets/DatasetsRepository.test.ts b/test/integration/datasets/DatasetsRepository.test.ts index 6c3aaba5..ddea54c7 100644 --- a/test/integration/datasets/DatasetsRepository.test.ts +++ b/test/integration/datasets/DatasetsRepository.test.ts @@ -1,10 +1,7 @@ -import { DatasetsRepository } from '../../../src/datasets/infra/repositories/DatasetsRepository'; import { ApiConfig } from '../../../src/core/infra/repositories/ApiConfig'; describe('getDatasetSummaryFieldNames', () => { // TODO: Change API URL to another of an integration test oriented Dataverse instance - const sut: DatasetsRepository = new DatasetsRepository(); - ApiConfig.init('https://demo.dataverse.org/api/v1'); test('should return dataset field names', async () => { From c3dbce0e0aa2b3da557ae0c0bb70df92b336ba81 Mon Sep 17 00:00:00 2001 From: GPortas Date: Tue, 16 May 2023 16:53:29 +0100 Subject: [PATCH 04/30] Added: dataset models and GetDataset use case (pending repository logic) --- src/datasets/domain/models/Dataset.ts | 40 +++++++++++++++++++ .../repositories/IDatasetsRepository.ts | 3 ++ src/datasets/domain/useCases/GetDataset.ts | 15 +++++++ .../infra/repositories/DatasetsRepository.ts | 10 +++++ test/testHelpers/datasets/datasetHelper.ts | 39 ++++++++++++++++++ test/unit/datasets/GetDataset.test.ts | 36 +++++++++++++++++ 6 files changed, 143 insertions(+) create mode 100644 src/datasets/domain/models/Dataset.ts create mode 100644 src/datasets/domain/useCases/GetDataset.ts create mode 100644 test/testHelpers/datasets/datasetHelper.ts create mode 100644 test/unit/datasets/GetDataset.test.ts diff --git a/src/datasets/domain/models/Dataset.ts b/src/datasets/domain/models/Dataset.ts new file mode 100644 index 00000000..35ffca6c --- /dev/null +++ b/src/datasets/domain/models/Dataset.ts @@ -0,0 +1,40 @@ +export interface Dataset { + id: number; + persistentId: string; + versionId: number; + versionInfo: DatasetVersionInfo; + license: DatasetLicense; + metadataBlocks: DatasetMetadataBlock[]; +} + +export interface DatasetVersionInfo { + majorNumber: number; + minorNumber: number; + state: DatasetVersionState; + createTime: Date; + lastUpdateTime: Date; + releaseTime?: Date; +} + +export enum DatasetVersionState { + DRAFT, + RELEASED, + ARCHIVED, + DEACCESSIONED, +} + +export interface DatasetLicense { + name: string; + uri: string; +} + +export interface DatasetMetadataBlock { + name: string; + fields: DatasetMetadataFields; +} + +export type DatasetMetadataFields = Record; + +export type DatasetMetadataFieldValue = string | DatasetMetadataSubField[]; + +export type DatasetMetadataSubField = Record; diff --git a/src/datasets/domain/repositories/IDatasetsRepository.ts b/src/datasets/domain/repositories/IDatasetsRepository.ts index f6c45288..62cdcdd2 100644 --- a/src/datasets/domain/repositories/IDatasetsRepository.ts +++ b/src/datasets/domain/repositories/IDatasetsRepository.ts @@ -1,3 +1,6 @@ +import { Dataset } from '../models/Dataset'; + export interface IDatasetsRepository { getDatasetSummaryFieldNames(): Promise; + getDataset(datasetId?: string, datasetPersistentId?: string, datasetVersionId?: string): Promise; } diff --git a/src/datasets/domain/useCases/GetDataset.ts b/src/datasets/domain/useCases/GetDataset.ts new file mode 100644 index 00000000..0fbcb5a1 --- /dev/null +++ b/src/datasets/domain/useCases/GetDataset.ts @@ -0,0 +1,15 @@ +import { UseCase } from '../../../core/domain/useCases/UseCase'; +import { IDatasetsRepository } from '../repositories/IDatasetsRepository'; +import { Dataset } from '../models/Dataset'; + +export class GetDataset implements UseCase { + private datasetsRepository: IDatasetsRepository; + + constructor(datasetsRepository: IDatasetsRepository) { + this.datasetsRepository = datasetsRepository; + } + + async execute(datasetId?: string, datasetPersistentId?: string, datasetVersionId?: string): Promise { + return await this.datasetsRepository.getDataset(datasetId, datasetPersistentId, datasetVersionId); + } +} diff --git a/src/datasets/infra/repositories/DatasetsRepository.ts b/src/datasets/infra/repositories/DatasetsRepository.ts index 468773ca..465ab3e3 100644 --- a/src/datasets/infra/repositories/DatasetsRepository.ts +++ b/src/datasets/infra/repositories/DatasetsRepository.ts @@ -1,5 +1,6 @@ import { ApiRepository } from '../../../core/infra/repositories/ApiRepository'; import { IDatasetsRepository } from '../../domain/repositories/IDatasetsRepository'; +import { Dataset } from '../../domain/models/Dataset'; export class DatasetsRepository extends ApiRepository implements IDatasetsRepository { public async getDatasetSummaryFieldNames(): Promise { @@ -9,4 +10,13 @@ export class DatasetsRepository extends ApiRepository implements IDatasetsReposi throw error; }); } + + public async getDataset( + datasetId?: string, + datasetPersistentId?: string, + datasetVersionId?: string, + ): Promise { + console.log(datasetId + datasetPersistentId + datasetVersionId); + throw new Error('Method not implemented.'); + } } diff --git a/test/testHelpers/datasets/datasetHelper.ts b/test/testHelpers/datasets/datasetHelper.ts new file mode 100644 index 00000000..2dec7199 --- /dev/null +++ b/test/testHelpers/datasets/datasetHelper.ts @@ -0,0 +1,39 @@ +import { Dataset, DatasetVersionState } from '../../../src/datasets/domain/models/Dataset'; + +export const createDataset = (): Dataset => { + return { + id: 1, + persistentId: 'doi:10.5072/FK2/HC6KTB', + versionId: 19, + versionInfo: { + majorNumber: 1, + minorNumber: 0, + state: DatasetVersionState.RELEASED, + createTime: new Date('2020-01-01'), + lastUpdateTime: new Date('2020-01-05'), + releaseTime: new Date('2020-01-03'), + }, + license: { + name: 'CC0 1.0', + uri: 'https://creativecommons.org/publicdomain/zero/1.0/', + }, + metadataBlocks: [ + { + name: 'citation', + fields: { + title: 'test', + author: [ + { + authorName: 'Admin, Dataverse', + authorAffiliation: 'Dataverse.org', + }, + { + authorName: 'Owner, Dataverse', + authorAffiliation: 'Dataverse.org', + }, + ], + }, + }, + ], + }; +}; diff --git a/test/unit/datasets/GetDataset.test.ts b/test/unit/datasets/GetDataset.test.ts new file mode 100644 index 00000000..c75cdf4b --- /dev/null +++ b/test/unit/datasets/GetDataset.test.ts @@ -0,0 +1,36 @@ +import { GetDataset } from '../../../src/datasets/domain/useCases/GetDataset'; +import { IDatasetsRepository } from '../../../src/datasets/domain/repositories/IDatasetsRepository'; +import { assert, createSandbox, SinonSandbox } from 'sinon'; +import { createDataset } from '../../testHelpers/datasets/datasetHelper'; +import { ReadError } from '../../../src/core/domain/repositories/ReadError'; + +describe('execute', () => { + const sandbox: SinonSandbox = createSandbox(); + + afterEach(() => { + sandbox.restore(); + }); + + test('should return dataset on repository success', async () => { + const testDataset = createDataset(); + const datasetsRepositoryStub = {}; + datasetsRepositoryStub.getDataset = sandbox.stub().returns(testDataset); + const sut = new GetDataset(datasetsRepositoryStub); + + const actual = await sut.execute(); + + assert.match(actual, testDataset); + }); + + test('should return error result on repository error', async () => { + const datasetsRepositoryStub = {}; + const testReadError = new ReadError(); + datasetsRepositoryStub.getDataset = sandbox.stub().throwsException(testReadError); + const sut = new GetDataset(datasetsRepositoryStub); + + let actualError: ReadError = undefined; + await sut.execute().catch((e) => (actualError = e)); + + assert.match(actualError, testReadError); + }); +}); From f2106d241e7bb028a39453e9c3b4a3e4ebd6ee06 Mon Sep 17 00:00:00 2001 From: GPortas Date: Tue, 16 May 2023 17:06:05 +0100 Subject: [PATCH 05/30] Added: type tweaks and improved GetDataset use case testing --- src/datasets/domain/repositories/IDatasetsRepository.ts | 2 +- src/datasets/domain/useCases/GetDataset.ts | 2 +- src/datasets/infra/repositories/DatasetsRepository.ts | 2 +- test/unit/datasets/GetDataset.test.ts | 6 ++++-- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/datasets/domain/repositories/IDatasetsRepository.ts b/src/datasets/domain/repositories/IDatasetsRepository.ts index 62cdcdd2..cbc843cd 100644 --- a/src/datasets/domain/repositories/IDatasetsRepository.ts +++ b/src/datasets/domain/repositories/IDatasetsRepository.ts @@ -2,5 +2,5 @@ import { Dataset } from '../models/Dataset'; export interface IDatasetsRepository { getDatasetSummaryFieldNames(): Promise; - getDataset(datasetId?: string, datasetPersistentId?: string, datasetVersionId?: string): Promise; + getDataset(datasetId?: string, datasetPersistentId?: string, datasetVersionId?: number): Promise; } diff --git a/src/datasets/domain/useCases/GetDataset.ts b/src/datasets/domain/useCases/GetDataset.ts index 0fbcb5a1..b09b55f1 100644 --- a/src/datasets/domain/useCases/GetDataset.ts +++ b/src/datasets/domain/useCases/GetDataset.ts @@ -9,7 +9,7 @@ export class GetDataset implements UseCase { this.datasetsRepository = datasetsRepository; } - async execute(datasetId?: string, datasetPersistentId?: string, datasetVersionId?: string): Promise { + async execute(datasetId?: string, datasetPersistentId?: string, datasetVersionId?: number): Promise { return await this.datasetsRepository.getDataset(datasetId, datasetPersistentId, datasetVersionId); } } diff --git a/src/datasets/infra/repositories/DatasetsRepository.ts b/src/datasets/infra/repositories/DatasetsRepository.ts index 465ab3e3..d452702d 100644 --- a/src/datasets/infra/repositories/DatasetsRepository.ts +++ b/src/datasets/infra/repositories/DatasetsRepository.ts @@ -14,7 +14,7 @@ export class DatasetsRepository extends ApiRepository implements IDatasetsReposi public async getDataset( datasetId?: string, datasetPersistentId?: string, - datasetVersionId?: string, + datasetVersionId?: number, ): Promise { console.log(datasetId + datasetPersistentId + datasetVersionId); throw new Error('Method not implemented.'); diff --git a/test/unit/datasets/GetDataset.test.ts b/test/unit/datasets/GetDataset.test.ts index c75cdf4b..eaa2afaa 100644 --- a/test/unit/datasets/GetDataset.test.ts +++ b/test/unit/datasets/GetDataset.test.ts @@ -14,12 +14,14 @@ describe('execute', () => { test('should return dataset on repository success', async () => { const testDataset = createDataset(); const datasetsRepositoryStub = {}; - datasetsRepositoryStub.getDataset = sandbox.stub().returns(testDataset); + const getDatasetStub = sandbox.stub().returns(testDataset); + datasetsRepositoryStub.getDataset = getDatasetStub; const sut = new GetDataset(datasetsRepositoryStub); - const actual = await sut.execute(); + const actual = await sut.execute(null, '1', null); assert.match(actual, testDataset); + assert.calledWithExactly(getDatasetStub, null, '1', null); }); test('should return error result on repository error', async () => { From 546bd9c3672ac274a7025c38f3641477c32b84b1 Mon Sep 17 00:00:00 2001 From: GPortas Date: Thu, 18 May 2023 10:23:09 +0100 Subject: [PATCH 06/30] Added: getDataset repository logic --- .../repositories/MissingParameterError.ts | 7 ++ src/datasets/domain/models/Dataset.ts | 10 +- .../repositories/IDatasetsRepository.ts | 2 +- src/datasets/domain/useCases/GetDataset.ts | 2 +- .../infra/repositories/DatasetsRepository.ts | 50 +++++++- .../transformers/datasetTransformers.ts | 87 +++++++++++++ .../datasets/DatasetsRepository.test.ts | 2 + test/testHelpers/datasets/datasetHelper.ts | 83 +++++++++++- test/unit/datasets/DatasetsRepository.test.ts | 119 ++++++++++++++---- test/unit/datasets/GetDataset.test.ts | 4 +- .../GetDatasetSummaryFieldNames.test.ts | 2 +- 11 files changed, 328 insertions(+), 40 deletions(-) create mode 100644 src/core/domain/repositories/MissingParameterError.ts create mode 100644 src/datasets/infra/repositories/transformers/datasetTransformers.ts diff --git a/src/core/domain/repositories/MissingParameterError.ts b/src/core/domain/repositories/MissingParameterError.ts new file mode 100644 index 00000000..8f8ce957 --- /dev/null +++ b/src/core/domain/repositories/MissingParameterError.ts @@ -0,0 +1,7 @@ +import { RepositoryError } from './RepositoryError'; + +export class MissingParameterError extends RepositoryError { + constructor(reason?: string) { + super('Missing parameter when performing repository operation', reason); + } +} diff --git a/src/datasets/domain/models/Dataset.ts b/src/datasets/domain/models/Dataset.ts index 35ffca6c..364751f1 100644 --- a/src/datasets/domain/models/Dataset.ts +++ b/src/datasets/domain/models/Dataset.ts @@ -17,10 +17,10 @@ export interface DatasetVersionInfo { } export enum DatasetVersionState { - DRAFT, - RELEASED, - ARCHIVED, - DEACCESSIONED, + DRAFT = 'DRAFT', + RELEASED = 'RELEASED', + ARCHIVED = 'ARCHIVED', + DEACCESSIONED = 'DEACCESSIONED', } export interface DatasetLicense { @@ -35,6 +35,6 @@ export interface DatasetMetadataBlock { export type DatasetMetadataFields = Record; -export type DatasetMetadataFieldValue = string | DatasetMetadataSubField[]; +export type DatasetMetadataFieldValue = string | string[] | DatasetMetadataSubField | DatasetMetadataSubField[]; export type DatasetMetadataSubField = Record; diff --git a/src/datasets/domain/repositories/IDatasetsRepository.ts b/src/datasets/domain/repositories/IDatasetsRepository.ts index cbc843cd..0e6c856a 100644 --- a/src/datasets/domain/repositories/IDatasetsRepository.ts +++ b/src/datasets/domain/repositories/IDatasetsRepository.ts @@ -2,5 +2,5 @@ import { Dataset } from '../models/Dataset'; export interface IDatasetsRepository { getDatasetSummaryFieldNames(): Promise; - getDataset(datasetId?: string, datasetPersistentId?: string, datasetVersionId?: number): Promise; + getDataset(datasetId?: number, datasetPersistentId?: string, datasetVersionId?: number): Promise; } diff --git a/src/datasets/domain/useCases/GetDataset.ts b/src/datasets/domain/useCases/GetDataset.ts index b09b55f1..8a22b5a9 100644 --- a/src/datasets/domain/useCases/GetDataset.ts +++ b/src/datasets/domain/useCases/GetDataset.ts @@ -9,7 +9,7 @@ export class GetDataset implements UseCase { this.datasetsRepository = datasetsRepository; } - async execute(datasetId?: string, datasetPersistentId?: string, datasetVersionId?: number): Promise { + async execute(datasetId?: number, datasetPersistentId?: string, datasetVersionId?: number): Promise { return await this.datasetsRepository.getDataset(datasetId, datasetPersistentId, datasetVersionId); } } diff --git a/src/datasets/infra/repositories/DatasetsRepository.ts b/src/datasets/infra/repositories/DatasetsRepository.ts index d452702d..33ed8011 100644 --- a/src/datasets/infra/repositories/DatasetsRepository.ts +++ b/src/datasets/infra/repositories/DatasetsRepository.ts @@ -1,6 +1,11 @@ import { ApiRepository } from '../../../core/infra/repositories/ApiRepository'; import { IDatasetsRepository } from '../../domain/repositories/IDatasetsRepository'; import { Dataset } from '../../domain/models/Dataset'; +import { MissingParameterError } from '../../../core/domain/repositories/MissingParameterError'; +import { + transformVersionResponseToDataset, + transformLatestVersionResponseToDataset, +} from './transformers/datasetTransformers'; export class DatasetsRepository extends ApiRepository implements IDatasetsRepository { public async getDatasetSummaryFieldNames(): Promise { @@ -12,11 +17,50 @@ export class DatasetsRepository extends ApiRepository implements IDatasetsReposi } public async getDataset( - datasetId?: string, + datasetId?: number, datasetPersistentId?: string, datasetVersionId?: number, ): Promise { - console.log(datasetId + datasetPersistentId + datasetVersionId); - throw new Error('Method not implemented.'); + if (datasetId != null) { + return this.getDatasetById(datasetId, datasetVersionId); + } else if (datasetPersistentId != null) { + return this.getDatasetByPersistentId(datasetPersistentId, datasetVersionId); + } else { + throw new MissingParameterError('Dataset ID or Persistent ID should be provided to getDataset operation'); + } + } + + private async getDatasetById(datasetId: number, datasetVersionId?: number): Promise { + if (datasetVersionId == null) { + return this.getDatasetLatestVersion(`/datasets/${datasetId}`); + } else { + return this.getDatasetParticularVersion(`/datasets/${datasetId}/versions/${datasetVersionId}`); + } + } + + private async getDatasetByPersistentId(datasetPersistentId: string, datasetVersionId?: number): Promise { + if (datasetVersionId == null) { + return this.getDatasetLatestVersion(`/datasets/:persistentId?persistentId=${datasetPersistentId}`); + } else { + return this.getDatasetParticularVersion( + `/datasets/:persistentId/versions/${datasetVersionId}?persistentId=${datasetPersistentId}`, + ); + } + } + + private async getDatasetLatestVersion(endpoint: string): Promise { + return this.doGet(endpoint, true) + .then((response) => transformLatestVersionResponseToDataset(response)) + .catch((error) => { + throw error; + }); + } + + private async getDatasetParticularVersion(endpoint: string): Promise { + return this.doGet(endpoint, true) + .then((response) => transformVersionResponseToDataset(response)) + .catch((error) => { + throw error; + }); } } diff --git a/src/datasets/infra/repositories/transformers/datasetTransformers.ts b/src/datasets/infra/repositories/transformers/datasetTransformers.ts new file mode 100644 index 00000000..b4a4a3b9 --- /dev/null +++ b/src/datasets/infra/repositories/transformers/datasetTransformers.ts @@ -0,0 +1,87 @@ +import { + Dataset, + DatasetVersionState, + DatasetMetadataFields, + DatasetMetadataBlock, + DatasetMetadataSubField, + DatasetMetadataFieldValue, +} from '../../../domain/models/Dataset'; +import { AxiosResponse } from 'axios'; + +export const transformVersionResponseToDataset = (response: AxiosResponse): Dataset => { + const versionPayload = response.data; + return transformVersionPayloadToDataset(versionPayload); +}; + +export const transformLatestVersionResponseToDataset = (response: AxiosResponse): Dataset => { + const versionPayload = response.data.latestVersion; + return transformVersionPayloadToDataset(versionPayload); +}; + +const transformVersionPayloadToDataset = (versionPayload: any): Dataset => { + return { + id: versionPayload.datasetId, + persistentId: versionPayload.datasetPersistentId, + versionId: versionPayload.id, + versionInfo: { + majorNumber: versionPayload.versionNumber, + minorNumber: versionPayload.versionMinorNumber, + state: versionPayload.versionState as DatasetVersionState, + createTime: new Date(versionPayload.createTime), + lastUpdateTime: new Date(versionPayload.lastUpdateTime), + releaseTime: new Date(versionPayload.releaseTime), + }, + license: { + name: versionPayload.license.name, + uri: versionPayload.license.uri, + }, + metadataBlocks: transformPayloadToDatasetMetadataBlocks(versionPayload.metadataBlocks), + }; +}; + +const transformPayloadToDatasetMetadataBlocks = (metadataBlocksPayload: any): DatasetMetadataBlock[] => { + const metadataBlockKeys = Object.keys(metadataBlocksPayload); + const datasetMetadataBlocks: DatasetMetadataBlock[] = []; + for (let metadataBlockKey of metadataBlockKeys) { + datasetMetadataBlocks.push({ + name: metadataBlockKey, + fields: transformPayloadToDatasetMetadataFields(metadataBlocksPayload[metadataBlockKey].fields), + }); + } + return datasetMetadataBlocks; +}; + +const transformPayloadToDatasetMetadataFields = (metadataFieldsPayload: any): DatasetMetadataFields => { + const metadataFieldKeys = Object.keys(metadataFieldsPayload); + const metadataFields: DatasetMetadataFields = {}; + for (let metadataFieldKey of metadataFieldKeys) { + const metadataField = metadataFieldsPayload[metadataFieldKey]; + const metadataFieldTypeName = metadataField.typeName; + metadataFields[metadataFieldTypeName] = transformPayloadToDatasetMetadataFieldValue(metadataField.value); + } + return metadataFields; +}; + +const transformPayloadToDatasetMetadataFieldValue = (metadataFieldValuePayload: any): DatasetMetadataFieldValue => { + let metadataFieldValue: DatasetMetadataFieldValue; + if (Array.isArray(metadataFieldValuePayload)) { + const isArrayOfObjects = typeof metadataFieldValuePayload[0] === 'object'; + if (!isArrayOfObjects) { + metadataFieldValue = metadataFieldValuePayload; + } else { + const datasetMetadataSubfields: DatasetMetadataSubField[] = []; + metadataFieldValuePayload.forEach(function (metadataSubFieldValuePayload) { + const subFieldKeys = Object.keys(metadataSubFieldValuePayload); + const record: DatasetMetadataSubField = {}; + for (let subFieldKey of subFieldKeys) { + record[subFieldKey] = metadataSubFieldValuePayload[subFieldKey].value; + } + datasetMetadataSubfields.push(record); + }); + metadataFieldValue = datasetMetadataSubfields; + } + } else { + metadataFieldValue = metadataFieldValuePayload; + } + return metadataFieldValue; +}; diff --git a/test/integration/datasets/DatasetsRepository.test.ts b/test/integration/datasets/DatasetsRepository.test.ts index ddea54c7..66c8601e 100644 --- a/test/integration/datasets/DatasetsRepository.test.ts +++ b/test/integration/datasets/DatasetsRepository.test.ts @@ -8,3 +8,5 @@ describe('getDatasetSummaryFieldNames', () => { // TODO }); }); + +// TODO: getDataset tests diff --git a/test/testHelpers/datasets/datasetHelper.ts b/test/testHelpers/datasets/datasetHelper.ts index 2dec7199..76f151b5 100644 --- a/test/testHelpers/datasets/datasetHelper.ts +++ b/test/testHelpers/datasets/datasetHelper.ts @@ -1,6 +1,10 @@ import { Dataset, DatasetVersionState } from '../../../src/datasets/domain/models/Dataset'; -export const createDataset = (): Dataset => { +const DATASET_CREATE_TIME_STR = '2023-05-15T08:21:01Z'; +const DATASET_UPDATE_TIME_STR = '2023-05-15T08:21:03Z'; +const DATASET_RELEASE_TIME_STR = '2023-05-15T08:21:03Z'; + +export const createDatasetModel = (): Dataset => { return { id: 1, persistentId: 'doi:10.5072/FK2/HC6KTB', @@ -9,9 +13,9 @@ export const createDataset = (): Dataset => { majorNumber: 1, minorNumber: 0, state: DatasetVersionState.RELEASED, - createTime: new Date('2020-01-01'), - lastUpdateTime: new Date('2020-01-05'), - releaseTime: new Date('2020-01-03'), + createTime: new Date(DATASET_CREATE_TIME_STR), + lastUpdateTime: new Date(DATASET_UPDATE_TIME_STR), + releaseTime: new Date(DATASET_RELEASE_TIME_STR), }, license: { name: 'CC0 1.0', @@ -32,8 +36,79 @@ export const createDataset = (): Dataset => { authorAffiliation: 'Dataverse.org', }, ], + subject: ['Subject1', 'Subject2'], }, }, ], }; }; + +export const createDatasetVersionPayload = (): any => { + return { + id: 19, + datasetId: 1, + datasetPersistentId: 'doi:10.5072/FK2/HC6KTB', + versionNumber: 1, + versionMinorNumber: 0, + versionState: 'RELEASED', + lastUpdateTime: DATASET_UPDATE_TIME_STR, + releaseTime: DATASET_RELEASE_TIME_STR, + createTime: DATASET_CREATE_TIME_STR, + license: { name: 'CC0 1.0', uri: 'https://creativecommons.org/publicdomain/zero/1.0/' }, + metadataBlocks: { + citation: { + name: 'citation', + fields: [ + { + typeName: 'title', + multiple: false, + typeClass: 'primitive', + value: 'test', + }, + { + typeName: 'author', + multiple: true, + typeClass: 'compound', + value: [ + { + authorName: { + typeName: 'authorName', + multiple: false, + typeClass: 'primitive', + value: 'Admin, Dataverse', + }, + authorAffiliation: { + typeName: 'authorAffiliation', + multiple: false, + typeClass: 'primitive', + value: 'Dataverse.org', + }, + }, + { + authorName: { + typeName: 'authorName', + multiple: false, + typeClass: 'primitive', + value: 'Owner, Dataverse', + }, + authorAffiliation: { + typeName: 'authorAffiliation', + multiple: false, + typeClass: 'primitive', + value: 'Dataverse.org', + }, + }, + ], + }, + { + typeName: 'subject', + multiple: true, + typeClass: 'controlledVocabulary', + value: ['Subject1', 'Subject2'], + }, + ], + }, + }, + files: [], + }; +}; diff --git a/test/unit/datasets/DatasetsRepository.test.ts b/test/unit/datasets/DatasetsRepository.test.ts index a67157c5..40f2829d 100644 --- a/test/unit/datasets/DatasetsRepository.test.ts +++ b/test/unit/datasets/DatasetsRepository.test.ts @@ -4,8 +4,9 @@ import axios from 'axios'; import { expect } from 'chai'; import { ReadError } from '../../../src/core/domain/repositories/ReadError'; import { ApiConfig } from '../../../src/core/infra/repositories/ApiConfig'; +import { createDatasetModel, createDatasetVersionPayload } from '../../testHelpers/datasets/datasetHelper'; -describe('getDatasetSummaryFieldNames', () => { +describe('DatasetsRepository', () => { const sandbox: SinonSandbox = createSandbox(); const sut: DatasetsRepository = new DatasetsRepository(); const testApiUrl = 'https://test.dataverse.org/api/v1'; @@ -16,35 +17,107 @@ describe('getDatasetSummaryFieldNames', () => { sandbox.restore(); }); - test('should return fields on successful response', async () => { - const testFieldNames = ['test1', 'test2']; - const testSuccessfulResponse = { - data: { - status: 'OK', - data: testFieldNames, - }, - }; - const axiosGetStub = sandbox.stub(axios, 'get').resolves(testSuccessfulResponse); + describe('getDatasetSummaryFieldNames', () => { + test('should return fields on successful response', async () => { + const testFieldNames = ['test1', 'test2']; + const testSuccessfulResponse = { + data: { + status: 'OK', + data: testFieldNames, + }, + }; + const axiosGetStub = sandbox.stub(axios, 'get').resolves(testSuccessfulResponse); + + const actual = await sut.getDatasetSummaryFieldNames(); + + assert.calledWithExactly(axiosGetStub, `${testApiUrl}/datasets/summaryFieldNames`, { withCredentials: false }); + assert.match(actual, testFieldNames); + }); - const actual = await sut.getDatasetSummaryFieldNames(); + test('should return error result on error response', async () => { + const testErrorResponse = { + response: { + status: 'ERROR', + message: 'test', + }, + }; + const axiosGetStub = sandbox.stub(axios, 'get').rejects(testErrorResponse); - assert.calledWithExactly(axiosGetStub, `${testApiUrl}/datasets/summaryFieldNames`, { withCredentials: false }); - assert.match(actual, testFieldNames); + let error: ReadError = undefined; + await sut.getDatasetSummaryFieldNames().catch((e) => (error = e)); + + assert.calledWithExactly(axiosGetStub, `${testApiUrl}/datasets/summaryFieldNames`, { withCredentials: false }); + expect(error).to.be.instanceOf(Error); + }); }); - test('should return error result on error response', async () => { - const testErrorResponse = { - response: { - status: 'ERROR', - message: 'test', + describe('getDataset', () => { + const testLatestVersionSuccessfulResponse = { + status: 'OK', + data: { + latestVersion: createDatasetVersionPayload(), }, }; - const axiosGetStub = sandbox.stub(axios, 'get').rejects(testErrorResponse); + const testVersionSuccessfulResponse = { + status: 'OK', + data: createDatasetVersionPayload(), + }; + const testDatasetModel = createDatasetModel(); + + test('should return Dataset when providing id, no version, and response is successful', async () => { + const axiosGetStub = sandbox.stub(axios, 'get').resolves(testLatestVersionSuccessfulResponse); + + const actual = await sut.getDataset(testDatasetModel.id, null, null); + + assert.calledWithExactly(axiosGetStub, `${testApiUrl}/datasets/${testDatasetModel.id}`, { + withCredentials: true, + }); + assert.match(actual, testDatasetModel); + }); + + test('should return Dataset when providing id, version, and response is successful', async () => { + const axiosGetStub = sandbox.stub(axios, 'get').resolves(testVersionSuccessfulResponse); + + const actual = await sut.getDataset(testDatasetModel.id, null, testDatasetModel.versionId); + + assert.calledWithExactly( + axiosGetStub, + `${testApiUrl}/datasets/${testDatasetModel.id}/versions/${testDatasetModel.versionId}`, + { + withCredentials: true, + }, + ); + assert.match(actual, testDatasetModel); + }); + + test('should return Dataset when providing persistent id, no version, and response is successful', async () => { + const axiosGetStub = sandbox.stub(axios, 'get').resolves(testLatestVersionSuccessfulResponse); + + const actual = await sut.getDataset(null, testDatasetModel.persistentId, null); + + assert.calledWithExactly( + axiosGetStub, + `${testApiUrl}/datasets/:persistentId?persistentId=${testDatasetModel.persistentId}`, + { + withCredentials: true, + }, + ); + assert.match(actual, testDatasetModel); + }); + + test('should return Dataset when providing id, version, and response is successful', async () => { + const axiosGetStub = sandbox.stub(axios, 'get').resolves(testVersionSuccessfulResponse); - let error: ReadError = undefined; - await sut.getDatasetSummaryFieldNames().catch((e) => (error = e)); + const actual = await sut.getDataset(null, testDatasetModel.persistentId, testDatasetModel.versionId); - assert.calledWithExactly(axiosGetStub, `${testApiUrl}/datasets/summaryFieldNames`, { withCredentials: false }); - expect(error).to.be.instanceOf(Error); + assert.calledWithExactly( + axiosGetStub, + `${testApiUrl}/datasets/:persistentId/versions/${testDatasetModel.versionId}?persistentId=${testDatasetModel.persistentId}`, + { + withCredentials: true, + }, + ); + assert.match(actual, testDatasetModel); + }); }); }); diff --git a/test/unit/datasets/GetDataset.test.ts b/test/unit/datasets/GetDataset.test.ts index eaa2afaa..3d78b816 100644 --- a/test/unit/datasets/GetDataset.test.ts +++ b/test/unit/datasets/GetDataset.test.ts @@ -1,7 +1,7 @@ import { GetDataset } from '../../../src/datasets/domain/useCases/GetDataset'; import { IDatasetsRepository } from '../../../src/datasets/domain/repositories/IDatasetsRepository'; import { assert, createSandbox, SinonSandbox } from 'sinon'; -import { createDataset } from '../../testHelpers/datasets/datasetHelper'; +import { createDatasetModel } from '../../testHelpers/datasets/datasetHelper'; import { ReadError } from '../../../src/core/domain/repositories/ReadError'; describe('execute', () => { @@ -12,7 +12,7 @@ describe('execute', () => { }); test('should return dataset on repository success', async () => { - const testDataset = createDataset(); + const testDataset = createDatasetModel(); const datasetsRepositoryStub = {}; const getDatasetStub = sandbox.stub().returns(testDataset); datasetsRepositoryStub.getDataset = getDatasetStub; diff --git a/test/unit/datasets/GetDatasetSummaryFieldNames.test.ts b/test/unit/datasets/GetDatasetSummaryFieldNames.test.ts index 87ab181f..c661cc29 100644 --- a/test/unit/datasets/GetDatasetSummaryFieldNames.test.ts +++ b/test/unit/datasets/GetDatasetSummaryFieldNames.test.ts @@ -11,7 +11,7 @@ describe('execute', () => { }); test('should return successful result with field names on repository success', async () => { - const testFieldNames = ['test1','test2']; + const testFieldNames = ['test1', 'test2']; const datasetsRepositoryStub = {}; datasetsRepositoryStub.getDatasetSummaryFieldNames = sandbox.stub().returns(testFieldNames); const sut = new GetDatasetSummaryFieldNames(datasetsRepositoryStub); From 056e34f12e5f2ed490a007f5724224df3413b638 Mon Sep 17 00:00:00 2001 From: GPortas Date: Thu, 18 May 2023 15:54:46 +0100 Subject: [PATCH 07/30] Added: dataset models export --- src/datasets/index.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/datasets/index.ts b/src/datasets/index.ts index 9645e949..47c3c044 100644 --- a/src/datasets/index.ts +++ b/src/datasets/index.ts @@ -4,3 +4,13 @@ import { GetDatasetSummaryFieldNames } from './domain/useCases/GetDatasetSummary const getDatasetSummaryFieldNames = new GetDatasetSummaryFieldNames(new DatasetsRepository()); export { getDatasetSummaryFieldNames }; +export { + Dataset, + DatasetVersionInfo, + DatasetVersionState, + DatasetLicense, + DatasetMetadataBlock, + DatasetMetadataFields, + DatasetMetadataFieldValue, + DatasetMetadataSubField, +} from './domain/models/Dataset'; From 8f0a2de87c9c7d9e83e3d66d1f1bbaf5d484d899 Mon Sep 17 00:00:00 2001 From: GPortas Date: Thu, 18 May 2023 15:56:23 +0100 Subject: [PATCH 08/30] Added: getDataset use case export --- src/datasets/index.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/datasets/index.ts b/src/datasets/index.ts index 47c3c044..1e147517 100644 --- a/src/datasets/index.ts +++ b/src/datasets/index.ts @@ -1,9 +1,13 @@ import { DatasetsRepository } from './infra/repositories/DatasetsRepository'; import { GetDatasetSummaryFieldNames } from './domain/useCases/GetDatasetSummaryFieldNames'; +import { GetDataset } from './domain/useCases/GetDataset'; -const getDatasetSummaryFieldNames = new GetDatasetSummaryFieldNames(new DatasetsRepository()); +const datasetsRepository = new DatasetsRepository(); -export { getDatasetSummaryFieldNames }; +const getDatasetSummaryFieldNames = new GetDatasetSummaryFieldNames(datasetsRepository); +const getDataset = new GetDataset(datasetsRepository); + +export { getDatasetSummaryFieldNames, getDataset }; export { Dataset, DatasetVersionInfo, From 180101666c7ef4ad8249e15f9f9610a48f25e38c Mon Sep 17 00:00:00 2001 From: GPortas Date: Thu, 18 May 2023 16:00:36 +0100 Subject: [PATCH 09/30] Added: DatasetRepository unit test missing error cases --- src/core/index.ts | 1 + test/unit/datasets/DatasetsRepository.test.ts | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/core/index.ts b/src/core/index.ts index 66d49fb9..5be8c3e6 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -1,3 +1,4 @@ export { ReadError } from './domain/repositories/ReadError'; export { WriteError } from './domain/repositories/WriteError'; +export { MissingParameterError } from './domain/repositories/MissingParameterError'; export { ApiConfig } from './infra/repositories/ApiConfig'; diff --git a/test/unit/datasets/DatasetsRepository.test.ts b/test/unit/datasets/DatasetsRepository.test.ts index 40f2829d..cf48c40e 100644 --- a/test/unit/datasets/DatasetsRepository.test.ts +++ b/test/unit/datasets/DatasetsRepository.test.ts @@ -5,6 +5,7 @@ import { expect } from 'chai'; import { ReadError } from '../../../src/core/domain/repositories/ReadError'; import { ApiConfig } from '../../../src/core/infra/repositories/ApiConfig'; import { createDatasetModel, createDatasetVersionPayload } from '../../testHelpers/datasets/datasetHelper'; +import { MissingParameterError } from '../../../src/core/domain/repositories/MissingParameterError'; describe('DatasetsRepository', () => { const sandbox: SinonSandbox = createSandbox(); @@ -119,5 +120,29 @@ describe('DatasetsRepository', () => { ); assert.match(actual, testDatasetModel); }); + + test('should return error on repository read error', async () => { + const testErrorResponse = { + response: { + status: 'ERROR', + message: 'test', + }, + }; + const axiosGetStub = sandbox.stub(axios, 'get').rejects(testErrorResponse); + + let error: ReadError = undefined; + await sut.getDataset(testDatasetModel.id, null, null).catch((e) => (error = e)); + + assert.calledWithExactly(axiosGetStub, `${testApiUrl}/datasets/${testDatasetModel.id}`, { + withCredentials: true, + }); + expect(error).to.be.instanceOf(Error); + }); + + test('should return error when parameters are missing', async () => { + let error: MissingParameterError = undefined; + await sut.getDataset(null, null, null).catch((e) => (error = e)); + expect(error).to.be.instanceOf(Error); + }); }); }); From 3a80aac82c40892060d9e061582d16730ff284d9 Mon Sep 17 00:00:00 2001 From: GPortas Date: Fri, 19 May 2023 10:50:08 +0100 Subject: [PATCH 10/30] Added: html to markdown translations --- package-lock.json | 133 ++++++++++++++++++ package.json | 3 +- .../transformers/datasetTransformers.ts | 15 +- test/testHelpers/datasets/datasetHelper.ts | 24 ++++ 4 files changed, 171 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 05a2c1dc..65cde6d4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "@types/node": "^18.15.11", "axios": "^1.3.4", + "node-html-markdown": "^1.3.0", "typescript": "^4.9.5" }, "devDependencies": { @@ -1821,6 +1822,11 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -2094,6 +2100,32 @@ "node": ">= 8" } }, + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -2203,6 +2235,57 @@ "node": ">=6.0.0" } }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, "node_modules/electron-to-chromium": { "version": "1.4.394", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.394.tgz", @@ -2227,6 +2310,17 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -2864,6 +2958,14 @@ "node": ">=8" } }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "bin": { + "he": "bin/he" + } + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -3994,6 +4096,26 @@ "type-detect": "4.0.8" } }, + "node_modules/node-html-markdown": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/node-html-markdown/-/node-html-markdown-1.3.0.tgz", + "integrity": "sha512-OeFi3QwC/cPjvVKZ114tzzu+YoR+v9UXW5RwSXGUqGb0qCl0DvP406tzdL7SFn8pZrMyzXoisfG2zcuF9+zw4g==", + "dependencies": { + "node-html-parser": "^6.1.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/node-html-parser": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-6.1.5.tgz", + "integrity": "sha512-fAaM511feX++/Chnhe475a0NHD8M7AxDInsqQpz6x63GRF7xYNdS8Vo5dKsIVPgsOvG7eioRRTZQnWBrhDHBSg==", + "dependencies": { + "css-select": "^5.1.0", + "he": "1.2.0" + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -4027,6 +4149,17 @@ "node": ">=8" } }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", diff --git a/package.json b/package.json index cdd257d6..0367a99a 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "dependencies": { "@types/node": "^18.15.11", "axios": "^1.3.4", - "typescript": "^4.9.5" + "typescript": "^4.9.5", + "node-html-markdown": "^1.3.0" } } diff --git a/src/datasets/infra/repositories/transformers/datasetTransformers.ts b/src/datasets/infra/repositories/transformers/datasetTransformers.ts index b4a4a3b9..1812c600 100644 --- a/src/datasets/infra/repositories/transformers/datasetTransformers.ts +++ b/src/datasets/infra/repositories/transformers/datasetTransformers.ts @@ -7,6 +7,7 @@ import { DatasetMetadataFieldValue, } from '../../../domain/models/Dataset'; import { AxiosResponse } from 'axios'; +import { NodeHtmlMarkdown } from 'node-html-markdown'; export const transformVersionResponseToDataset = (response: AxiosResponse): Dataset => { const versionPayload = response.data; @@ -67,21 +68,29 @@ const transformPayloadToDatasetMetadataFieldValue = (metadataFieldValuePayload: if (Array.isArray(metadataFieldValuePayload)) { const isArrayOfObjects = typeof metadataFieldValuePayload[0] === 'object'; if (!isArrayOfObjects) { - metadataFieldValue = metadataFieldValuePayload; + const datasetMetadataSubfields: string[] = []; + metadataFieldValuePayload.forEach(function (metadataValuePayload) { + datasetMetadataSubfields.push(transformHtmlToMarkdown(metadataValuePayload)); + }); + metadataFieldValue = datasetMetadataSubfields; } else { const datasetMetadataSubfields: DatasetMetadataSubField[] = []; metadataFieldValuePayload.forEach(function (metadataSubFieldValuePayload) { const subFieldKeys = Object.keys(metadataSubFieldValuePayload); const record: DatasetMetadataSubField = {}; for (let subFieldKey of subFieldKeys) { - record[subFieldKey] = metadataSubFieldValuePayload[subFieldKey].value; + record[subFieldKey] = transformHtmlToMarkdown(metadataSubFieldValuePayload[subFieldKey].value); } datasetMetadataSubfields.push(record); }); metadataFieldValue = datasetMetadataSubfields; } } else { - metadataFieldValue = metadataFieldValuePayload; + metadataFieldValue = transformHtmlToMarkdown(metadataFieldValuePayload); } return metadataFieldValue; }; + +const transformHtmlToMarkdown = (source: string): string => { + return NodeHtmlMarkdown.translate(source); +}; diff --git a/test/testHelpers/datasets/datasetHelper.ts b/test/testHelpers/datasets/datasetHelper.ts index 76f151b5..e1a3ca0f 100644 --- a/test/testHelpers/datasets/datasetHelper.ts +++ b/test/testHelpers/datasets/datasetHelper.ts @@ -1,9 +1,13 @@ import { Dataset, DatasetVersionState } from '../../../src/datasets/domain/models/Dataset'; +import { NodeHtmlMarkdown } from 'node-html-markdown'; const DATASET_CREATE_TIME_STR = '2023-05-15T08:21:01Z'; const DATASET_UPDATE_TIME_STR = '2023-05-15T08:21:03Z'; const DATASET_RELEASE_TIME_STR = '2023-05-15T08:21:03Z'; +const DATASET_HTML_DESCRIPTION = + '

Title 1

Test paragraph 1

Test paragraph 2

Hello world

Title 2

Title 3

'; + export const createDatasetModel = (): Dataset => { return { id: 1, @@ -37,6 +41,11 @@ export const createDatasetModel = (): Dataset => { }, ], subject: ['Subject1', 'Subject2'], + dsDescription: [ + { + dsDescriptionValue: NodeHtmlMarkdown.translate(DATASET_HTML_DESCRIPTION), + }, + ], }, }, ], @@ -106,6 +115,21 @@ export const createDatasetVersionPayload = (): any => { typeClass: 'controlledVocabulary', value: ['Subject1', 'Subject2'], }, + { + typeName: 'dsDescription', + multiple: true, + typeClass: 'compound', + value: [ + { + dsDescriptionValue: { + typeName: 'dsDescriptionValue', + multiple: false, + typeClass: 'primitive', + value: DATASET_HTML_DESCRIPTION, + }, + }, + ], + }, ], }, }, From 66775a0b08ce723f14bd5072ecf98f012c87cb62 Mon Sep 17 00:00:00 2001 From: GPortas Date: Mon, 22 May 2023 11:05:37 +0100 Subject: [PATCH 11/30] Added: getPrivateUrlDataset use case --- .../repositories/IDatasetsRepository.ts | 1 + .../domain/useCases/GetPrivateUrlDataset.ts | 15 +++++ src/datasets/index.ts | 4 +- .../infra/repositories/DatasetsRepository.ts | 12 ++++ .../datasets/DatasetsRepository.test.ts | 2 + test/unit/datasets/DatasetsRepository.test.ts | 59 +++++++++++++++++-- .../datasets/GetPrivateUrlDataset.test.ts | 39 ++++++++++++ 7 files changed, 126 insertions(+), 6 deletions(-) create mode 100644 src/datasets/domain/useCases/GetPrivateUrlDataset.ts create mode 100644 test/unit/datasets/GetPrivateUrlDataset.test.ts diff --git a/src/datasets/domain/repositories/IDatasetsRepository.ts b/src/datasets/domain/repositories/IDatasetsRepository.ts index 0e6c856a..edb4af14 100644 --- a/src/datasets/domain/repositories/IDatasetsRepository.ts +++ b/src/datasets/domain/repositories/IDatasetsRepository.ts @@ -3,4 +3,5 @@ import { Dataset } from '../models/Dataset'; export interface IDatasetsRepository { getDatasetSummaryFieldNames(): Promise; getDataset(datasetId?: number, datasetPersistentId?: string, datasetVersionId?: number): Promise; + getPrivateUrlDataset(token: string, anonymizedFieldValue?: string): Promise; } diff --git a/src/datasets/domain/useCases/GetPrivateUrlDataset.ts b/src/datasets/domain/useCases/GetPrivateUrlDataset.ts new file mode 100644 index 00000000..487e02ba --- /dev/null +++ b/src/datasets/domain/useCases/GetPrivateUrlDataset.ts @@ -0,0 +1,15 @@ +import { UseCase } from '../../../core/domain/useCases/UseCase'; +import { IDatasetsRepository } from '../repositories/IDatasetsRepository'; +import { Dataset } from '../models/Dataset'; + +export class GetPrivateUrlDataset implements UseCase { + private datasetsRepository: IDatasetsRepository; + + constructor(datasetsRepository: IDatasetsRepository) { + this.datasetsRepository = datasetsRepository; + } + + async execute(token: string, anonymizedFieldValue?: string): Promise { + return await this.datasetsRepository.getPrivateUrlDataset(token, anonymizedFieldValue); + } +} diff --git a/src/datasets/index.ts b/src/datasets/index.ts index 1e147517..98005937 100644 --- a/src/datasets/index.ts +++ b/src/datasets/index.ts @@ -1,13 +1,15 @@ import { DatasetsRepository } from './infra/repositories/DatasetsRepository'; import { GetDatasetSummaryFieldNames } from './domain/useCases/GetDatasetSummaryFieldNames'; import { GetDataset } from './domain/useCases/GetDataset'; +import { GetPrivateUrlDataset } from './domain/useCases/GetPrivateUrlDataset'; const datasetsRepository = new DatasetsRepository(); const getDatasetSummaryFieldNames = new GetDatasetSummaryFieldNames(datasetsRepository); const getDataset = new GetDataset(datasetsRepository); +const getPrivateUrlDataset = new GetPrivateUrlDataset(datasetsRepository); -export { getDatasetSummaryFieldNames, getDataset }; +export { getDatasetSummaryFieldNames, getDataset, getPrivateUrlDataset }; export { Dataset, DatasetVersionInfo, diff --git a/src/datasets/infra/repositories/DatasetsRepository.ts b/src/datasets/infra/repositories/DatasetsRepository.ts index 33ed8011..54e49e96 100644 --- a/src/datasets/infra/repositories/DatasetsRepository.ts +++ b/src/datasets/infra/repositories/DatasetsRepository.ts @@ -30,6 +30,18 @@ export class DatasetsRepository extends ApiRepository implements IDatasetsReposi } } + public async getPrivateUrlDataset(token: string, anonymizedFieldValue?: string): Promise { + let apiEndpoint = `/datasets/privateUrlDatasetVersion/${token}`; + if (anonymizedFieldValue != null) { + apiEndpoint = apiEndpoint + `?anonymizedFieldValue=${anonymizedFieldValue}`; + } + return this.doGet(apiEndpoint) + .then((response) => transformVersionResponseToDataset(response)) + .catch((error) => { + throw error; + }); + } + private async getDatasetById(datasetId: number, datasetVersionId?: number): Promise { if (datasetVersionId == null) { return this.getDatasetLatestVersion(`/datasets/${datasetId}`); diff --git a/test/integration/datasets/DatasetsRepository.test.ts b/test/integration/datasets/DatasetsRepository.test.ts index 66c8601e..7d799736 100644 --- a/test/integration/datasets/DatasetsRepository.test.ts +++ b/test/integration/datasets/DatasetsRepository.test.ts @@ -10,3 +10,5 @@ describe('getDatasetSummaryFieldNames', () => { }); // TODO: getDataset tests + +// TODO: getPrivateUrlDataset tests diff --git a/test/unit/datasets/DatasetsRepository.test.ts b/test/unit/datasets/DatasetsRepository.test.ts index cf48c40e..5a12f7c0 100644 --- a/test/unit/datasets/DatasetsRepository.test.ts +++ b/test/unit/datasets/DatasetsRepository.test.ts @@ -10,6 +10,11 @@ import { MissingParameterError } from '../../../src/core/domain/repositories/Mis describe('DatasetsRepository', () => { const sandbox: SinonSandbox = createSandbox(); const sut: DatasetsRepository = new DatasetsRepository(); + const testVersionSuccessfulResponse = { + status: 'OK', + data: createDatasetVersionPayload(), + }; + const testDatasetModel = createDatasetModel(); const testApiUrl = 'https://test.dataverse.org/api/v1'; ApiConfig.init(testApiUrl); @@ -59,11 +64,6 @@ describe('DatasetsRepository', () => { latestVersion: createDatasetVersionPayload(), }, }; - const testVersionSuccessfulResponse = { - status: 'OK', - data: createDatasetVersionPayload(), - }; - const testDatasetModel = createDatasetModel(); test('should return Dataset when providing id, no version, and response is successful', async () => { const axiosGetStub = sandbox.stub(axios, 'get').resolves(testLatestVersionSuccessfulResponse); @@ -145,4 +145,53 @@ describe('DatasetsRepository', () => { expect(error).to.be.instanceOf(Error); }); }); + + describe('getPrivateUrlDataset', () => { + const testToken = 'testToken'; + + test('should return Dataset when providing anonymized field value and response is successful', async () => { + const axiosGetStub = sandbox.stub(axios, 'get').resolves(testVersionSuccessfulResponse); + + const testAnonymizedFieldValue = 'testValue'; + const actual = await sut.getPrivateUrlDataset(testToken, 'testValue'); + + assert.calledWithExactly( + axiosGetStub, + `${testApiUrl}/datasets/privateUrlDatasetVersion/${testToken}?anonymizedFieldValue=${testAnonymizedFieldValue}`, + { + withCredentials: false, + }, + ); + assert.match(actual, testDatasetModel); + }); + + test('should return Dataset when not providing anonymized field value and response is successful', async () => { + const axiosGetStub = sandbox.stub(axios, 'get').resolves(testVersionSuccessfulResponse); + + const actual = await sut.getPrivateUrlDataset(testToken, null); + + assert.calledWithExactly(axiosGetStub, `${testApiUrl}/datasets/privateUrlDatasetVersion/${testToken}`, { + withCredentials: false, + }); + assert.match(actual, testDatasetModel); + }); + + test('should return error on repository read error', async () => { + const testErrorResponse = { + response: { + status: 'ERROR', + message: 'test', + }, + }; + const axiosGetStub = sandbox.stub(axios, 'get').rejects(testErrorResponse); + + let error: ReadError = undefined; + await sut.getPrivateUrlDataset(testToken, null).catch((e) => (error = e)); + + assert.calledWithExactly(axiosGetStub, `${testApiUrl}/datasets/privateUrlDatasetVersion/${testToken}`, { + withCredentials: false, + }); + expect(error).to.be.instanceOf(Error); + }); + }); }); diff --git a/test/unit/datasets/GetPrivateUrlDataset.test.ts b/test/unit/datasets/GetPrivateUrlDataset.test.ts new file mode 100644 index 00000000..c925006e --- /dev/null +++ b/test/unit/datasets/GetPrivateUrlDataset.test.ts @@ -0,0 +1,39 @@ +import { GetPrivateUrlDataset } from '../../../src/datasets/domain/useCases/GetPrivateUrlDataset'; +import { IDatasetsRepository } from '../../../src/datasets/domain/repositories/IDatasetsRepository'; +import { assert, createSandbox, SinonSandbox } from 'sinon'; +import { createDatasetModel } from '../../testHelpers/datasets/datasetHelper'; +import { ReadError } from '../../../src/core/domain/repositories/ReadError'; + +describe('execute', () => { + const sandbox: SinonSandbox = createSandbox(); + const testToken = 'token'; + + afterEach(() => { + sandbox.restore(); + }); + + test('should return dataset on repository success', async () => { + const testDataset = createDatasetModel(); + const datasetsRepositoryStub = {}; + const getDatasetStub = sandbox.stub().returns(testDataset); + datasetsRepositoryStub.getPrivateUrlDataset = getDatasetStub; + const sut = new GetPrivateUrlDataset(datasetsRepositoryStub); + + const actual = await sut.execute(testToken, null); + + assert.match(actual, testDataset); + assert.calledWithExactly(getDatasetStub, testToken, null); + }); + + test('should return error result on repository error', async () => { + const datasetsRepositoryStub = {}; + const testReadError = new ReadError(); + datasetsRepositoryStub.getPrivateUrlDataset = sandbox.stub().throwsException(testReadError); + const sut = new GetPrivateUrlDataset(datasetsRepositoryStub); + + let actualError: ReadError = undefined; + await sut.execute(testToken, null).catch((e) => (actualError = e)); + + assert.match(actualError, testReadError); + }); +}); From 53a5bb579ff74d9cdcf58ef6670ef5e975d3a14d Mon Sep 17 00:00:00 2001 From: GPortas Date: Tue, 23 May 2023 13:51:39 +0100 Subject: [PATCH 12/30] Added: github action for gpr package deployment on PRs --- .github/workflows/deploy_pr.yml | 51 +++++++++++++++++++++++++++++++++ package.json | 1 - 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/deploy_pr.yml diff --git a/.github/workflows/deploy_pr.yml b/.github/workflows/deploy_pr.yml new file mode 100644 index 00000000..849dde7d --- /dev/null +++ b/.github/workflows/deploy_pr.yml @@ -0,0 +1,51 @@ +name: deploy_pr + +on: + pull_request: + branches: + - develop + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 19 + + - name: Install npm dependencies + run: npm ci + + - name: Run unit tests + run: npm run test:unit + + publish-gpr: + needs: test + runs-on: ubuntu-latest + permissions: + packages: write + contents: read + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 19 + registry-url: https://npm.pkg.github.com/ + + - name: Install npm dependencies + run: npm ci + + - name: Build package + run: npm run build + + - name: Update package version + run: npm version '${{ github.event.number }}.${{ github.run_number }}.0' --no-git-tag-version + + - name: Publish package + run: | + echo "$(jq '.publishConfig.registry = "https://npm.pkg.github.com"' package.json)" > package.json + echo "$( jq '.name = "@gportas/dataverse-client-javascript"' package.json )" > package.json + npm publish --@gportas:registry=https://npm.pkg.github.com + env: + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/package.json b/package.json index cdd257d6..9e21ca7a 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,6 @@ ], "scripts": { "build": "tsc", - "publish": "tsc && npm publish", "test": "jest -c jest.config.js", "test:unit": "jest -c jest.config.unit.js", "test:integration": "jest -c jest.config.integration.js", From 3016ccf8ce662bb9517e49e38f9478592e0e4a9d Mon Sep 17 00:00:00 2001 From: GPortas Date: Tue, 23 May 2023 14:28:54 +0100 Subject: [PATCH 13/30] Fixed: GitHub username / org name --- .github/workflows/deploy_pr.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy_pr.yml b/.github/workflows/deploy_pr.yml index 849dde7d..43bb8d91 100644 --- a/.github/workflows/deploy_pr.yml +++ b/.github/workflows/deploy_pr.yml @@ -45,7 +45,7 @@ jobs: - name: Publish package run: | echo "$(jq '.publishConfig.registry = "https://npm.pkg.github.com"' package.json)" > package.json - echo "$( jq '.name = "@gportas/dataverse-client-javascript"' package.json )" > package.json - npm publish --@gportas:registry=https://npm.pkg.github.com + echo "$( jq '.name = "@IQSS/dataverse-client-javascript"' package.json )" > package.json + npm publish --@IQSS:registry=https://npm.pkg.github.com env: NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 8ac5f2595ea3af0c40c10b53ee3ae924b810f0ae Mon Sep 17 00:00:00 2001 From: GPortas Date: Tue, 23 May 2023 17:15:19 +0100 Subject: [PATCH 14/30] Added: enhanced versioning --- .github/workflows/deploy_pr.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy_pr.yml b/.github/workflows/deploy_pr.yml index 43bb8d91..35af45c1 100644 --- a/.github/workflows/deploy_pr.yml +++ b/.github/workflows/deploy_pr.yml @@ -38,9 +38,12 @@ jobs: - name: Build package run: npm run build - + - name: Update package version - run: npm version '${{ github.event.number }}.${{ github.run_number }}.0' --no-git-tag-version + run: | + SHORT_SHA=$(git rev-parse --short "${{ github.event.pull_request.head.sha }}") + CURRENT_PACKAGE_VERSION=$(cat package.json | jq -r '.version') + npm version "${CURRENT_PACKAGE_VERSION}-pr${{ github.event.number }}.${SHORT_SHA}" --no-git-tag-version - name: Publish package run: | From 2ae4602f6165d9fd7e6293297f36a626c2d0ae24 Mon Sep 17 00:00:00 2001 From: GPortas Date: Thu, 25 May 2023 12:22:24 +0100 Subject: [PATCH 15/30] Fixed: using undefined instead of null --- .../infra/repositories/DatasetsRepository.ts | 4 ++-- test/unit/datasets/DatasetsRepository.test.ts | 16 ++++++++-------- test/unit/datasets/GetDataset.test.ts | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/datasets/infra/repositories/DatasetsRepository.ts b/src/datasets/infra/repositories/DatasetsRepository.ts index 54e49e96..001558cf 100644 --- a/src/datasets/infra/repositories/DatasetsRepository.ts +++ b/src/datasets/infra/repositories/DatasetsRepository.ts @@ -21,9 +21,9 @@ export class DatasetsRepository extends ApiRepository implements IDatasetsReposi datasetPersistentId?: string, datasetVersionId?: number, ): Promise { - if (datasetId != null) { + if (datasetId != undefined) { return this.getDatasetById(datasetId, datasetVersionId); - } else if (datasetPersistentId != null) { + } else if (datasetPersistentId != undefined) { return this.getDatasetByPersistentId(datasetPersistentId, datasetVersionId); } else { throw new MissingParameterError('Dataset ID or Persistent ID should be provided to getDataset operation'); diff --git a/test/unit/datasets/DatasetsRepository.test.ts b/test/unit/datasets/DatasetsRepository.test.ts index 5a12f7c0..493ed6fd 100644 --- a/test/unit/datasets/DatasetsRepository.test.ts +++ b/test/unit/datasets/DatasetsRepository.test.ts @@ -68,7 +68,7 @@ describe('DatasetsRepository', () => { test('should return Dataset when providing id, no version, and response is successful', async () => { const axiosGetStub = sandbox.stub(axios, 'get').resolves(testLatestVersionSuccessfulResponse); - const actual = await sut.getDataset(testDatasetModel.id, null, null); + const actual = await sut.getDataset(testDatasetModel.id, undefined, undefined); assert.calledWithExactly(axiosGetStub, `${testApiUrl}/datasets/${testDatasetModel.id}`, { withCredentials: true, @@ -79,7 +79,7 @@ describe('DatasetsRepository', () => { test('should return Dataset when providing id, version, and response is successful', async () => { const axiosGetStub = sandbox.stub(axios, 'get').resolves(testVersionSuccessfulResponse); - const actual = await sut.getDataset(testDatasetModel.id, null, testDatasetModel.versionId); + const actual = await sut.getDataset(testDatasetModel.id, undefined, testDatasetModel.versionId); assert.calledWithExactly( axiosGetStub, @@ -94,7 +94,7 @@ describe('DatasetsRepository', () => { test('should return Dataset when providing persistent id, no version, and response is successful', async () => { const axiosGetStub = sandbox.stub(axios, 'get').resolves(testLatestVersionSuccessfulResponse); - const actual = await sut.getDataset(null, testDatasetModel.persistentId, null); + const actual = await sut.getDataset(undefined, testDatasetModel.persistentId, undefined); assert.calledWithExactly( axiosGetStub, @@ -109,7 +109,7 @@ describe('DatasetsRepository', () => { test('should return Dataset when providing id, version, and response is successful', async () => { const axiosGetStub = sandbox.stub(axios, 'get').resolves(testVersionSuccessfulResponse); - const actual = await sut.getDataset(null, testDatasetModel.persistentId, testDatasetModel.versionId); + const actual = await sut.getDataset(undefined, testDatasetModel.persistentId, testDatasetModel.versionId); assert.calledWithExactly( axiosGetStub, @@ -131,7 +131,7 @@ describe('DatasetsRepository', () => { const axiosGetStub = sandbox.stub(axios, 'get').rejects(testErrorResponse); let error: ReadError = undefined; - await sut.getDataset(testDatasetModel.id, null, null).catch((e) => (error = e)); + await sut.getDataset(testDatasetModel.id, undefined, undefined).catch((e) => (error = e)); assert.calledWithExactly(axiosGetStub, `${testApiUrl}/datasets/${testDatasetModel.id}`, { withCredentials: true, @@ -141,7 +141,7 @@ describe('DatasetsRepository', () => { test('should return error when parameters are missing', async () => { let error: MissingParameterError = undefined; - await sut.getDataset(null, null, null).catch((e) => (error = e)); + await sut.getDataset(undefined, undefined, undefined).catch((e) => (error = e)); expect(error).to.be.instanceOf(Error); }); }); @@ -168,7 +168,7 @@ describe('DatasetsRepository', () => { test('should return Dataset when not providing anonymized field value and response is successful', async () => { const axiosGetStub = sandbox.stub(axios, 'get').resolves(testVersionSuccessfulResponse); - const actual = await sut.getPrivateUrlDataset(testToken, null); + const actual = await sut.getPrivateUrlDataset(testToken, undefined); assert.calledWithExactly(axiosGetStub, `${testApiUrl}/datasets/privateUrlDatasetVersion/${testToken}`, { withCredentials: false, @@ -186,7 +186,7 @@ describe('DatasetsRepository', () => { const axiosGetStub = sandbox.stub(axios, 'get').rejects(testErrorResponse); let error: ReadError = undefined; - await sut.getPrivateUrlDataset(testToken, null).catch((e) => (error = e)); + await sut.getPrivateUrlDataset(testToken, undefined).catch((e) => (error = e)); assert.calledWithExactly(axiosGetStub, `${testApiUrl}/datasets/privateUrlDatasetVersion/${testToken}`, { withCredentials: false, diff --git a/test/unit/datasets/GetDataset.test.ts b/test/unit/datasets/GetDataset.test.ts index 3d78b816..6993aff6 100644 --- a/test/unit/datasets/GetDataset.test.ts +++ b/test/unit/datasets/GetDataset.test.ts @@ -18,10 +18,10 @@ describe('execute', () => { datasetsRepositoryStub.getDataset = getDatasetStub; const sut = new GetDataset(datasetsRepositoryStub); - const actual = await sut.execute(null, '1', null); + const actual = await sut.execute(undefined, '1', undefined); assert.match(actual, testDataset); - assert.calledWithExactly(getDatasetStub, null, '1', null); + assert.calledWithExactly(getDatasetStub, undefined, '1', undefined); }); test('should return error result on repository error', async () => { From e9b12611e0617fe49a6bfbeb6d10403a3ab26ce4 Mon Sep 17 00:00:00 2001 From: GPortas Date: Fri, 26 May 2023 09:44:21 +0100 Subject: [PATCH 16/30] Fixed: undefined param checks in repository and use case and repository refactor --- .../repositories/MissingParameterError.ts | 7 -- .../repositories/IDatasetsRepository.ts | 3 +- .../{GetDataset.ts => GetDatasetById.ts} | 6 +- .../useCases/GetDatasetByPersistentId.ts | 15 ++++ src/datasets/index.ts | 8 ++- .../infra/repositories/DatasetsRepository.ts | 25 ++----- test/unit/datasets/DatasetsRepository.test.ts | 68 +++++++++---------- ...Dataset.test.ts => GetDatasetById.test.ts} | 16 ++--- .../datasets/GetDatasetByPersistentId.test.ts | 38 +++++++++++ 9 files changed, 110 insertions(+), 76 deletions(-) delete mode 100644 src/core/domain/repositories/MissingParameterError.ts rename src/datasets/domain/useCases/{GetDataset.ts => GetDatasetById.ts} (56%) create mode 100644 src/datasets/domain/useCases/GetDatasetByPersistentId.ts rename test/unit/datasets/{GetDataset.test.ts => GetDatasetById.test.ts} (65%) create mode 100644 test/unit/datasets/GetDatasetByPersistentId.test.ts diff --git a/src/core/domain/repositories/MissingParameterError.ts b/src/core/domain/repositories/MissingParameterError.ts deleted file mode 100644 index 8f8ce957..00000000 --- a/src/core/domain/repositories/MissingParameterError.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { RepositoryError } from './RepositoryError'; - -export class MissingParameterError extends RepositoryError { - constructor(reason?: string) { - super('Missing parameter when performing repository operation', reason); - } -} diff --git a/src/datasets/domain/repositories/IDatasetsRepository.ts b/src/datasets/domain/repositories/IDatasetsRepository.ts index edb4af14..5c22e054 100644 --- a/src/datasets/domain/repositories/IDatasetsRepository.ts +++ b/src/datasets/domain/repositories/IDatasetsRepository.ts @@ -2,6 +2,7 @@ import { Dataset } from '../models/Dataset'; export interface IDatasetsRepository { getDatasetSummaryFieldNames(): Promise; - getDataset(datasetId?: number, datasetPersistentId?: string, datasetVersionId?: number): Promise; + getDatasetById(datasetId: number, datasetVersionId?: number): Promise; + getDatasetByPersistentId(datasetPersistentId: string, datasetVersionId?: number): Promise; getPrivateUrlDataset(token: string, anonymizedFieldValue?: string): Promise; } diff --git a/src/datasets/domain/useCases/GetDataset.ts b/src/datasets/domain/useCases/GetDatasetById.ts similarity index 56% rename from src/datasets/domain/useCases/GetDataset.ts rename to src/datasets/domain/useCases/GetDatasetById.ts index 8a22b5a9..0fe0c3c4 100644 --- a/src/datasets/domain/useCases/GetDataset.ts +++ b/src/datasets/domain/useCases/GetDatasetById.ts @@ -2,14 +2,14 @@ import { UseCase } from '../../../core/domain/useCases/UseCase'; import { IDatasetsRepository } from '../repositories/IDatasetsRepository'; import { Dataset } from '../models/Dataset'; -export class GetDataset implements UseCase { +export class GetDatasetById implements UseCase { private datasetsRepository: IDatasetsRepository; constructor(datasetsRepository: IDatasetsRepository) { this.datasetsRepository = datasetsRepository; } - async execute(datasetId?: number, datasetPersistentId?: string, datasetVersionId?: number): Promise { - return await this.datasetsRepository.getDataset(datasetId, datasetPersistentId, datasetVersionId); + async execute(datasetId: number, datasetVersionId?: number): Promise { + return await this.datasetsRepository.getDatasetById(datasetId, datasetVersionId); } } diff --git a/src/datasets/domain/useCases/GetDatasetByPersistentId.ts b/src/datasets/domain/useCases/GetDatasetByPersistentId.ts new file mode 100644 index 00000000..12796a1a --- /dev/null +++ b/src/datasets/domain/useCases/GetDatasetByPersistentId.ts @@ -0,0 +1,15 @@ +import { UseCase } from '../../../core/domain/useCases/UseCase'; +import { IDatasetsRepository } from '../repositories/IDatasetsRepository'; +import { Dataset } from '../models/Dataset'; + +export class GetDatasetByPersistentId implements UseCase { + private datasetsRepository: IDatasetsRepository; + + constructor(datasetsRepository: IDatasetsRepository) { + this.datasetsRepository = datasetsRepository; + } + + async execute(datasetPersistentId: string, datasetVersionId?: number): Promise { + return await this.datasetsRepository.getDatasetByPersistentId(datasetPersistentId, datasetVersionId); + } +} diff --git a/src/datasets/index.ts b/src/datasets/index.ts index 98005937..3cad854c 100644 --- a/src/datasets/index.ts +++ b/src/datasets/index.ts @@ -1,15 +1,17 @@ import { DatasetsRepository } from './infra/repositories/DatasetsRepository'; import { GetDatasetSummaryFieldNames } from './domain/useCases/GetDatasetSummaryFieldNames'; -import { GetDataset } from './domain/useCases/GetDataset'; +import { GetDatasetById } from './domain/useCases/GetDatasetById'; +import { GetDatasetByPersistentId } from './domain/useCases/GetDatasetByPersistentId'; import { GetPrivateUrlDataset } from './domain/useCases/GetPrivateUrlDataset'; const datasetsRepository = new DatasetsRepository(); const getDatasetSummaryFieldNames = new GetDatasetSummaryFieldNames(datasetsRepository); -const getDataset = new GetDataset(datasetsRepository); +const getDatasetById = new GetDatasetById(datasetsRepository); +const getDatasetByPersistentId = new GetDatasetByPersistentId(datasetsRepository); const getPrivateUrlDataset = new GetPrivateUrlDataset(datasetsRepository); -export { getDatasetSummaryFieldNames, getDataset, getPrivateUrlDataset }; +export { getDatasetSummaryFieldNames, getDatasetById, getDatasetByPersistentId, getPrivateUrlDataset }; export { Dataset, DatasetVersionInfo, diff --git a/src/datasets/infra/repositories/DatasetsRepository.ts b/src/datasets/infra/repositories/DatasetsRepository.ts index 001558cf..b9e3afe7 100644 --- a/src/datasets/infra/repositories/DatasetsRepository.ts +++ b/src/datasets/infra/repositories/DatasetsRepository.ts @@ -1,7 +1,6 @@ import { ApiRepository } from '../../../core/infra/repositories/ApiRepository'; import { IDatasetsRepository } from '../../domain/repositories/IDatasetsRepository'; import { Dataset } from '../../domain/models/Dataset'; -import { MissingParameterError } from '../../../core/domain/repositories/MissingParameterError'; import { transformVersionResponseToDataset, transformLatestVersionResponseToDataset, @@ -16,23 +15,9 @@ export class DatasetsRepository extends ApiRepository implements IDatasetsReposi }); } - public async getDataset( - datasetId?: number, - datasetPersistentId?: string, - datasetVersionId?: number, - ): Promise { - if (datasetId != undefined) { - return this.getDatasetById(datasetId, datasetVersionId); - } else if (datasetPersistentId != undefined) { - return this.getDatasetByPersistentId(datasetPersistentId, datasetVersionId); - } else { - throw new MissingParameterError('Dataset ID or Persistent ID should be provided to getDataset operation'); - } - } - public async getPrivateUrlDataset(token: string, anonymizedFieldValue?: string): Promise { let apiEndpoint = `/datasets/privateUrlDatasetVersion/${token}`; - if (anonymizedFieldValue != null) { + if (anonymizedFieldValue !== undefined) { apiEndpoint = apiEndpoint + `?anonymizedFieldValue=${anonymizedFieldValue}`; } return this.doGet(apiEndpoint) @@ -42,16 +27,16 @@ export class DatasetsRepository extends ApiRepository implements IDatasetsReposi }); } - private async getDatasetById(datasetId: number, datasetVersionId?: number): Promise { - if (datasetVersionId == null) { + public async getDatasetById(datasetId: number, datasetVersionId?: number): Promise { + if (datasetVersionId === undefined) { return this.getDatasetLatestVersion(`/datasets/${datasetId}`); } else { return this.getDatasetParticularVersion(`/datasets/${datasetId}/versions/${datasetVersionId}`); } } - private async getDatasetByPersistentId(datasetPersistentId: string, datasetVersionId?: number): Promise { - if (datasetVersionId == null) { + public async getDatasetByPersistentId(datasetPersistentId: string, datasetVersionId?: number): Promise { + if (datasetVersionId === undefined) { return this.getDatasetLatestVersion(`/datasets/:persistentId?persistentId=${datasetPersistentId}`); } else { return this.getDatasetParticularVersion( diff --git a/test/unit/datasets/DatasetsRepository.test.ts b/test/unit/datasets/DatasetsRepository.test.ts index 493ed6fd..8f875dd8 100644 --- a/test/unit/datasets/DatasetsRepository.test.ts +++ b/test/unit/datasets/DatasetsRepository.test.ts @@ -5,7 +5,6 @@ import { expect } from 'chai'; import { ReadError } from '../../../src/core/domain/repositories/ReadError'; import { ApiConfig } from '../../../src/core/infra/repositories/ApiConfig'; import { createDatasetModel, createDatasetVersionPayload } from '../../testHelpers/datasets/datasetHelper'; -import { MissingParameterError } from '../../../src/core/domain/repositories/MissingParameterError'; describe('DatasetsRepository', () => { const sandbox: SinonSandbox = createSandbox(); @@ -14,6 +13,18 @@ describe('DatasetsRepository', () => { status: 'OK', data: createDatasetVersionPayload(), }; + const testLatestVersionSuccessfulResponse = { + status: 'OK', + data: { + latestVersion: createDatasetVersionPayload(), + }, + }; + const testErrorResponse = { + response: { + status: 'ERROR', + message: 'test', + }, + }; const testDatasetModel = createDatasetModel(); const testApiUrl = 'https://test.dataverse.org/api/v1'; @@ -57,18 +68,11 @@ describe('DatasetsRepository', () => { }); }); - describe('getDataset', () => { - const testLatestVersionSuccessfulResponse = { - status: 'OK', - data: { - latestVersion: createDatasetVersionPayload(), - }, - }; - + describe('getDatasetById', () => { test('should return Dataset when providing id, no version, and response is successful', async () => { const axiosGetStub = sandbox.stub(axios, 'get').resolves(testLatestVersionSuccessfulResponse); - const actual = await sut.getDataset(testDatasetModel.id, undefined, undefined); + const actual = await sut.getDatasetById(testDatasetModel.id); assert.calledWithExactly(axiosGetStub, `${testApiUrl}/datasets/${testDatasetModel.id}`, { withCredentials: true, @@ -79,7 +83,7 @@ describe('DatasetsRepository', () => { test('should return Dataset when providing id, version, and response is successful', async () => { const axiosGetStub = sandbox.stub(axios, 'get').resolves(testVersionSuccessfulResponse); - const actual = await sut.getDataset(testDatasetModel.id, undefined, testDatasetModel.versionId); + const actual = await sut.getDatasetById(testDatasetModel.id, testDatasetModel.versionId); assert.calledWithExactly( axiosGetStub, @@ -91,10 +95,24 @@ describe('DatasetsRepository', () => { assert.match(actual, testDatasetModel); }); + test('should return error on repository read error', async () => { + const axiosGetStub = sandbox.stub(axios, 'get').rejects(testErrorResponse); + + let error: ReadError = undefined; + await sut.getDatasetById(testDatasetModel.id).catch((e) => (error = e)); + + assert.calledWithExactly(axiosGetStub, `${testApiUrl}/datasets/${testDatasetModel.id}`, { + withCredentials: true, + }); + expect(error).to.be.instanceOf(Error); + }); + }); + + describe('getDatasetByPersistentId', () => { test('should return Dataset when providing persistent id, no version, and response is successful', async () => { const axiosGetStub = sandbox.stub(axios, 'get').resolves(testLatestVersionSuccessfulResponse); - const actual = await sut.getDataset(undefined, testDatasetModel.persistentId, undefined); + const actual = await sut.getDatasetByPersistentId(testDatasetModel.persistentId); assert.calledWithExactly( axiosGetStub, @@ -106,10 +124,10 @@ describe('DatasetsRepository', () => { assert.match(actual, testDatasetModel); }); - test('should return Dataset when providing id, version, and response is successful', async () => { + test('should return Dataset when providing persistent id, version, and response is successful', async () => { const axiosGetStub = sandbox.stub(axios, 'get').resolves(testVersionSuccessfulResponse); - const actual = await sut.getDataset(undefined, testDatasetModel.persistentId, testDatasetModel.versionId); + const actual = await sut.getDatasetByPersistentId(testDatasetModel.persistentId, testDatasetModel.versionId); assert.calledWithExactly( axiosGetStub, @@ -122,28 +140,16 @@ describe('DatasetsRepository', () => { }); test('should return error on repository read error', async () => { - const testErrorResponse = { - response: { - status: 'ERROR', - message: 'test', - }, - }; const axiosGetStub = sandbox.stub(axios, 'get').rejects(testErrorResponse); let error: ReadError = undefined; - await sut.getDataset(testDatasetModel.id, undefined, undefined).catch((e) => (error = e)); + await sut.getDatasetByPersistentId(testDatasetModel.persistentId).catch((e) => (error = e)); - assert.calledWithExactly(axiosGetStub, `${testApiUrl}/datasets/${testDatasetModel.id}`, { + assert.calledWithExactly(axiosGetStub, `${testApiUrl}/datasets/:persistentId?persistentId=${testDatasetModel.persistentId}`, { withCredentials: true, }); expect(error).to.be.instanceOf(Error); }); - - test('should return error when parameters are missing', async () => { - let error: MissingParameterError = undefined; - await sut.getDataset(undefined, undefined, undefined).catch((e) => (error = e)); - expect(error).to.be.instanceOf(Error); - }); }); describe('getPrivateUrlDataset', () => { @@ -177,12 +183,6 @@ describe('DatasetsRepository', () => { }); test('should return error on repository read error', async () => { - const testErrorResponse = { - response: { - status: 'ERROR', - message: 'test', - }, - }; const axiosGetStub = sandbox.stub(axios, 'get').rejects(testErrorResponse); let error: ReadError = undefined; diff --git a/test/unit/datasets/GetDataset.test.ts b/test/unit/datasets/GetDatasetById.test.ts similarity index 65% rename from test/unit/datasets/GetDataset.test.ts rename to test/unit/datasets/GetDatasetById.test.ts index 6993aff6..07199b37 100644 --- a/test/unit/datasets/GetDataset.test.ts +++ b/test/unit/datasets/GetDatasetById.test.ts @@ -1,4 +1,4 @@ -import { GetDataset } from '../../../src/datasets/domain/useCases/GetDataset'; +import { GetDatasetById } from '../../../src/datasets/domain/useCases/GetDatasetById'; import { IDatasetsRepository } from '../../../src/datasets/domain/repositories/IDatasetsRepository'; import { assert, createSandbox, SinonSandbox } from 'sinon'; import { createDatasetModel } from '../../testHelpers/datasets/datasetHelper'; @@ -15,23 +15,23 @@ describe('execute', () => { const testDataset = createDatasetModel(); const datasetsRepositoryStub = {}; const getDatasetStub = sandbox.stub().returns(testDataset); - datasetsRepositoryStub.getDataset = getDatasetStub; - const sut = new GetDataset(datasetsRepositoryStub); + datasetsRepositoryStub.getDatasetById = getDatasetStub; + const sut = new GetDatasetById(datasetsRepositoryStub); - const actual = await sut.execute(undefined, '1', undefined); + const actual = await sut.execute(1); assert.match(actual, testDataset); - assert.calledWithExactly(getDatasetStub, undefined, '1', undefined); + assert.calledWithExactly(getDatasetStub, 1, undefined); }); test('should return error result on repository error', async () => { const datasetsRepositoryStub = {}; const testReadError = new ReadError(); - datasetsRepositoryStub.getDataset = sandbox.stub().throwsException(testReadError); - const sut = new GetDataset(datasetsRepositoryStub); + datasetsRepositoryStub.getDatasetById = sandbox.stub().throwsException(testReadError); + const sut = new GetDatasetById(datasetsRepositoryStub); let actualError: ReadError = undefined; - await sut.execute().catch((e) => (actualError = e)); + await sut.execute(1).catch((e) => (actualError = e)); assert.match(actualError, testReadError); }); diff --git a/test/unit/datasets/GetDatasetByPersistentId.test.ts b/test/unit/datasets/GetDatasetByPersistentId.test.ts new file mode 100644 index 00000000..b3ba5060 --- /dev/null +++ b/test/unit/datasets/GetDatasetByPersistentId.test.ts @@ -0,0 +1,38 @@ +import { GetDatasetByPersistentId } from '../../../src/datasets/domain/useCases/GetDatasetByPersistentId'; +import { IDatasetsRepository } from '../../../src/datasets/domain/repositories/IDatasetsRepository'; +import { assert, createSandbox, SinonSandbox } from 'sinon'; +import { createDatasetModel } from '../../testHelpers/datasets/datasetHelper'; +import { ReadError } from '../../../src/core/domain/repositories/ReadError'; + +describe('execute', () => { + const sandbox: SinonSandbox = createSandbox(); + + afterEach(() => { + sandbox.restore(); + }); + + test('should return dataset on repository success', async () => { + const testDataset = createDatasetModel(); + const datasetsRepositoryStub = {}; + const getDatasetStub = sandbox.stub().returns(testDataset); + datasetsRepositoryStub.getDatasetByPersistentId = getDatasetStub; + const sut = new GetDatasetByPersistentId(datasetsRepositoryStub); + + const actual = await sut.execute('1'); + + assert.match(actual, testDataset); + assert.calledWithExactly(getDatasetStub, '1', undefined); + }); + + test('should return error result on repository error', async () => { + const datasetsRepositoryStub = {}; + const testReadError = new ReadError(); + datasetsRepositoryStub.getDatasetByPersistentId = sandbox.stub().throwsException(testReadError); + const sut = new GetDatasetByPersistentId(datasetsRepositoryStub); + + let actualError: ReadError = undefined; + await sut.execute('1').catch((e) => (actualError = e)); + + assert.match(actualError, testReadError); + }); +}); From be914ac1cacef7b7ca9c7a70b9459a4b5fc400e3 Mon Sep 17 00:00:00 2001 From: GPortas Date: Fri, 26 May 2023 09:49:08 +0100 Subject: [PATCH 17/30] Fixed: removed deleted error model from exports --- src/core/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/index.ts b/src/core/index.ts index 5be8c3e6..66d49fb9 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -1,4 +1,3 @@ export { ReadError } from './domain/repositories/ReadError'; export { WriteError } from './domain/repositories/WriteError'; -export { MissingParameterError } from './domain/repositories/MissingParameterError'; export { ApiConfig } from './infra/repositories/ApiConfig'; From e4fc3a17777ccb1677ce17ee382853c5baa88e3b Mon Sep 17 00:00:00 2001 From: GPortas Date: Fri, 26 May 2023 10:29:22 +0100 Subject: [PATCH 18/30] Refactor: reduced code by using maps --- .../transformers/datasetTransformers.ts | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/datasets/infra/repositories/transformers/datasetTransformers.ts b/src/datasets/infra/repositories/transformers/datasetTransformers.ts index 1812c600..ceb6dd24 100644 --- a/src/datasets/infra/repositories/transformers/datasetTransformers.ts +++ b/src/datasets/infra/repositories/transformers/datasetTransformers.ts @@ -41,15 +41,12 @@ const transformVersionPayloadToDataset = (versionPayload: any): Dataset => { }; const transformPayloadToDatasetMetadataBlocks = (metadataBlocksPayload: any): DatasetMetadataBlock[] => { - const metadataBlockKeys = Object.keys(metadataBlocksPayload); - const datasetMetadataBlocks: DatasetMetadataBlock[] = []; - for (let metadataBlockKey of metadataBlockKeys) { - datasetMetadataBlocks.push({ + return Object.keys(metadataBlocksPayload).map((metadataBlockKey) => { + return { name: metadataBlockKey, fields: transformPayloadToDatasetMetadataFields(metadataBlocksPayload[metadataBlockKey].fields), - }); - } - return datasetMetadataBlocks; + }; + }); }; const transformPayloadToDatasetMetadataFields = (metadataFieldsPayload: any): DatasetMetadataFields => { @@ -68,11 +65,7 @@ const transformPayloadToDatasetMetadataFieldValue = (metadataFieldValuePayload: if (Array.isArray(metadataFieldValuePayload)) { const isArrayOfObjects = typeof metadataFieldValuePayload[0] === 'object'; if (!isArrayOfObjects) { - const datasetMetadataSubfields: string[] = []; - metadataFieldValuePayload.forEach(function (metadataValuePayload) { - datasetMetadataSubfields.push(transformHtmlToMarkdown(metadataValuePayload)); - }); - metadataFieldValue = datasetMetadataSubfields; + metadataFieldValue = metadataFieldValuePayload.map(transformHtmlToMarkdown); } else { const datasetMetadataSubfields: DatasetMetadataSubField[] = []; metadataFieldValuePayload.forEach(function (metadataSubFieldValuePayload) { From 7a539a1f812d1dac704da89cceb6deba026432fa Mon Sep 17 00:00:00 2001 From: GPortas Date: Fri, 26 May 2023 11:13:12 +0100 Subject: [PATCH 19/30] Added: optional license iconUri --- src/datasets/domain/models/Dataset.ts | 1 + .../infra/repositories/transformers/datasetTransformers.ts | 1 + test/testHelpers/datasets/datasetHelper.ts | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/datasets/domain/models/Dataset.ts b/src/datasets/domain/models/Dataset.ts index 364751f1..d840d393 100644 --- a/src/datasets/domain/models/Dataset.ts +++ b/src/datasets/domain/models/Dataset.ts @@ -26,6 +26,7 @@ export enum DatasetVersionState { export interface DatasetLicense { name: string; uri: string; + iconUri?: string; } export interface DatasetMetadataBlock { diff --git a/src/datasets/infra/repositories/transformers/datasetTransformers.ts b/src/datasets/infra/repositories/transformers/datasetTransformers.ts index ceb6dd24..fbd0e5df 100644 --- a/src/datasets/infra/repositories/transformers/datasetTransformers.ts +++ b/src/datasets/infra/repositories/transformers/datasetTransformers.ts @@ -35,6 +35,7 @@ const transformVersionPayloadToDataset = (versionPayload: any): Dataset => { license: { name: versionPayload.license.name, uri: versionPayload.license.uri, + iconUri: versionPayload.license.iconUri, }, metadataBlocks: transformPayloadToDatasetMetadataBlocks(versionPayload.metadataBlocks), }; diff --git a/test/testHelpers/datasets/datasetHelper.ts b/test/testHelpers/datasets/datasetHelper.ts index e1a3ca0f..60c0dbf0 100644 --- a/test/testHelpers/datasets/datasetHelper.ts +++ b/test/testHelpers/datasets/datasetHelper.ts @@ -24,6 +24,7 @@ export const createDatasetModel = (): Dataset => { license: { name: 'CC0 1.0', uri: 'https://creativecommons.org/publicdomain/zero/1.0/', + iconUri: 'https://licensebuttons.net/p/zero/1.0/88x31.png', }, metadataBlocks: [ { @@ -63,7 +64,7 @@ export const createDatasetVersionPayload = (): any => { lastUpdateTime: DATASET_UPDATE_TIME_STR, releaseTime: DATASET_RELEASE_TIME_STR, createTime: DATASET_CREATE_TIME_STR, - license: { name: 'CC0 1.0', uri: 'https://creativecommons.org/publicdomain/zero/1.0/' }, + license: { name: 'CC0 1.0', uri: 'https://creativecommons.org/publicdomain/zero/1.0/', iconUri: 'https://licensebuttons.net/p/zero/1.0/88x31.png' }, metadataBlocks: { citation: { name: 'citation', From 28fa6967b4f016a459a2ec49d1f88f2c8bea3ce3 Mon Sep 17 00:00:00 2001 From: GPortas Date: Fri, 26 May 2023 13:48:28 +0100 Subject: [PATCH 20/30] Fixed: dataset response transformer --- .../transformers/datasetTransformers.ts | 4 ++-- test/unit/datasets/DatasetsRepository.test.ts | 22 +++++++++++++------ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/datasets/infra/repositories/transformers/datasetTransformers.ts b/src/datasets/infra/repositories/transformers/datasetTransformers.ts index fbd0e5df..552805ef 100644 --- a/src/datasets/infra/repositories/transformers/datasetTransformers.ts +++ b/src/datasets/infra/repositories/transformers/datasetTransformers.ts @@ -10,12 +10,12 @@ import { AxiosResponse } from 'axios'; import { NodeHtmlMarkdown } from 'node-html-markdown'; export const transformVersionResponseToDataset = (response: AxiosResponse): Dataset => { - const versionPayload = response.data; + const versionPayload = response.data.data; return transformVersionPayloadToDataset(versionPayload); }; export const transformLatestVersionResponseToDataset = (response: AxiosResponse): Dataset => { - const versionPayload = response.data.latestVersion; + const versionPayload = response.data.data.latestVersion; return transformVersionPayloadToDataset(versionPayload); }; diff --git a/test/unit/datasets/DatasetsRepository.test.ts b/test/unit/datasets/DatasetsRepository.test.ts index 8f875dd8..553baa22 100644 --- a/test/unit/datasets/DatasetsRepository.test.ts +++ b/test/unit/datasets/DatasetsRepository.test.ts @@ -10,13 +10,17 @@ describe('DatasetsRepository', () => { const sandbox: SinonSandbox = createSandbox(); const sut: DatasetsRepository = new DatasetsRepository(); const testVersionSuccessfulResponse = { - status: 'OK', - data: createDatasetVersionPayload(), + data: { + status: 'OK', + data: createDatasetVersionPayload(), + }, }; const testLatestVersionSuccessfulResponse = { - status: 'OK', data: { - latestVersion: createDatasetVersionPayload(), + status: 'OK', + data: { + latestVersion: createDatasetVersionPayload(), + }, }, }; const testErrorResponse = { @@ -145,9 +149,13 @@ describe('DatasetsRepository', () => { let error: ReadError = undefined; await sut.getDatasetByPersistentId(testDatasetModel.persistentId).catch((e) => (error = e)); - assert.calledWithExactly(axiosGetStub, `${testApiUrl}/datasets/:persistentId?persistentId=${testDatasetModel.persistentId}`, { - withCredentials: true, - }); + assert.calledWithExactly( + axiosGetStub, + `${testApiUrl}/datasets/:persistentId?persistentId=${testDatasetModel.persistentId}`, + { + withCredentials: true, + }, + ); expect(error).to.be.instanceOf(Error); }); }); From 301cf3e5e7416ff9feeb3ea695936cc122f087d9 Mon Sep 17 00:00:00 2001 From: GPortas Date: Fri, 26 May 2023 15:21:32 +0100 Subject: [PATCH 21/30] Changed: markdown translator dependency due to issues when consuming the package --- package-lock.json | 151 +++--------------- package.json | 3 +- .../transformers/datasetTransformers.ts | 6 +- test/testHelpers/datasets/datasetHelper.ts | 12 +- 4 files changed, 34 insertions(+), 138 deletions(-) diff --git a/package-lock.json b/package-lock.json index 65cde6d4..ca858690 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,8 +10,9 @@ "license": "MIT", "dependencies": { "@types/node": "^18.15.11", + "@types/turndown": "5.0.1", "axios": "^1.3.4", - "node-html-markdown": "^1.3.0", + "turndown": "^7.1.2", "typescript": "^4.9.5" }, "devDependencies": { @@ -1382,6 +1383,11 @@ "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", "dev": true }, + "node_modules/@types/turndown": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@types/turndown/-/turndown-5.0.1.tgz", + "integrity": "sha512-N8Ad4e3oJxh9n9BiZx9cbe/0M3kqDpOTm2wzj13wdDUxDPjfjloWIJaquZzWE1cYTAHpjOH3rcTnXQdpEfS/SQ==" + }, "node_modules/@types/yargs": { "version": "17.0.24", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", @@ -1822,11 +1828,6 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" - }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -2100,32 +2101,6 @@ "node": ">= 8" } }, - "node_modules/css-select": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", - "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -2235,56 +2210,10 @@ "node": ">=6.0.0" } }, - "node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } + "node_modules/domino": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/domino/-/domino-2.1.6.tgz", + "integrity": "sha512-3VdM/SXBZX2omc9JF9nOPCtDaYQ67BGp5CoLpIQlO2KCAPETs8TcDHacF26jXadGbvUteZzRTeos2fhID5+ucQ==" }, "node_modules/electron-to-chromium": { "version": "1.4.394", @@ -2310,17 +2239,6 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -2958,14 +2876,6 @@ "node": ">=8" } }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "bin": { - "he": "bin/he" - } - }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -4096,26 +4006,6 @@ "type-detect": "4.0.8" } }, - "node_modules/node-html-markdown": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/node-html-markdown/-/node-html-markdown-1.3.0.tgz", - "integrity": "sha512-OeFi3QwC/cPjvVKZ114tzzu+YoR+v9UXW5RwSXGUqGb0qCl0DvP406tzdL7SFn8pZrMyzXoisfG2zcuF9+zw4g==", - "dependencies": { - "node-html-parser": "^6.1.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/node-html-parser": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-6.1.5.tgz", - "integrity": "sha512-fAaM511feX++/Chnhe475a0NHD8M7AxDInsqQpz6x63GRF7xYNdS8Vo5dKsIVPgsOvG7eioRRTZQnWBrhDHBSg==", - "dependencies": { - "css-select": "^5.1.0", - "he": "1.2.0" - } - }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -4149,17 +4039,6 @@ "node": ">=8" } }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -4978,6 +4857,14 @@ "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" } }, + "node_modules/turndown": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/turndown/-/turndown-7.1.2.tgz", + "integrity": "sha512-ntI9R7fcUKjqBP6QU8rBK2Ehyt8LAzt3UBT9JR9tgo6GtuKvyUzpayWmeMKJw1DPdXzktvtIT8m2mVXz+bL/Qg==", + "dependencies": { + "domino": "^2.1.6" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/package.json b/package.json index 8ee8f0ae..7e3445b1 100644 --- a/package.json +++ b/package.json @@ -46,8 +46,9 @@ }, "dependencies": { "@types/node": "^18.15.11", + "@types/turndown": "5.0.1", "axios": "^1.3.4", "typescript": "^4.9.5", - "node-html-markdown": "^1.3.0" + "turndown": "^7.1.2" } } diff --git a/src/datasets/infra/repositories/transformers/datasetTransformers.ts b/src/datasets/infra/repositories/transformers/datasetTransformers.ts index 552805ef..e7d5da8c 100644 --- a/src/datasets/infra/repositories/transformers/datasetTransformers.ts +++ b/src/datasets/infra/repositories/transformers/datasetTransformers.ts @@ -7,7 +7,9 @@ import { DatasetMetadataFieldValue, } from '../../../domain/models/Dataset'; import { AxiosResponse } from 'axios'; -import { NodeHtmlMarkdown } from 'node-html-markdown'; +import TurndownService from 'turndown'; + +const turndownService = new TurndownService(); export const transformVersionResponseToDataset = (response: AxiosResponse): Dataset => { const versionPayload = response.data.data; @@ -86,5 +88,5 @@ const transformPayloadToDatasetMetadataFieldValue = (metadataFieldValuePayload: }; const transformHtmlToMarkdown = (source: string): string => { - return NodeHtmlMarkdown.translate(source); + return turndownService.turndown(source); }; diff --git a/test/testHelpers/datasets/datasetHelper.ts b/test/testHelpers/datasets/datasetHelper.ts index 60c0dbf0..bfdab5fb 100644 --- a/test/testHelpers/datasets/datasetHelper.ts +++ b/test/testHelpers/datasets/datasetHelper.ts @@ -1,5 +1,7 @@ import { Dataset, DatasetVersionState } from '../../../src/datasets/domain/models/Dataset'; -import { NodeHtmlMarkdown } from 'node-html-markdown'; +import TurndownService from 'turndown'; + +const turndownService = new TurndownService(); const DATASET_CREATE_TIME_STR = '2023-05-15T08:21:01Z'; const DATASET_UPDATE_TIME_STR = '2023-05-15T08:21:03Z'; @@ -44,7 +46,7 @@ export const createDatasetModel = (): Dataset => { subject: ['Subject1', 'Subject2'], dsDescription: [ { - dsDescriptionValue: NodeHtmlMarkdown.translate(DATASET_HTML_DESCRIPTION), + dsDescriptionValue: turndownService.turndown(DATASET_HTML_DESCRIPTION), }, ], }, @@ -64,7 +66,11 @@ export const createDatasetVersionPayload = (): any => { lastUpdateTime: DATASET_UPDATE_TIME_STR, releaseTime: DATASET_RELEASE_TIME_STR, createTime: DATASET_CREATE_TIME_STR, - license: { name: 'CC0 1.0', uri: 'https://creativecommons.org/publicdomain/zero/1.0/', iconUri: 'https://licensebuttons.net/p/zero/1.0/88x31.png' }, + license: { + name: 'CC0 1.0', + uri: 'https://creativecommons.org/publicdomain/zero/1.0/', + iconUri: 'https://licensebuttons.net/p/zero/1.0/88x31.png', + }, metadataBlocks: { citation: { name: 'citation', From d4c69b012bb7e16160013b60dce2b662e1c52952 Mon Sep 17 00:00:00 2001 From: GPortas Date: Fri, 26 May 2023 16:26:37 +0100 Subject: [PATCH 22/30] Added: GetDatasetCitation use case --- package.json | 2 +- .../repositories/IDatasetsRepository.ts | 1 + .../domain/useCases/GetDatasetCitation.ts | 14 ++++++ src/datasets/index.ts | 10 ++++- .../infra/repositories/DatasetsRepository.ts | 16 +++++++ .../datasets/DatasetsRepository.test.ts | 2 + test/unit/datasets/DatasetsRepository.test.ts | 44 +++++++++++++++++++ test/unit/datasets/GetDatasetCitation.test.ts | 39 ++++++++++++++++ 8 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 src/datasets/domain/useCases/GetDatasetCitation.ts create mode 100644 test/unit/datasets/GetDatasetCitation.test.ts diff --git a/package.json b/package.json index 7e3445b1..8e28323a 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ }, "dependencies": { "@types/node": "^18.15.11", - "@types/turndown": "5.0.1", + "@types/turndown": "^5.0.1", "axios": "^1.3.4", "typescript": "^4.9.5", "turndown": "^7.1.2" diff --git a/src/datasets/domain/repositories/IDatasetsRepository.ts b/src/datasets/domain/repositories/IDatasetsRepository.ts index 5c22e054..54fb448b 100644 --- a/src/datasets/domain/repositories/IDatasetsRepository.ts +++ b/src/datasets/domain/repositories/IDatasetsRepository.ts @@ -5,4 +5,5 @@ export interface IDatasetsRepository { getDatasetById(datasetId: number, datasetVersionId?: number): Promise; getDatasetByPersistentId(datasetPersistentId: string, datasetVersionId?: number): Promise; getPrivateUrlDataset(token: string, anonymizedFieldValue?: string): Promise; + getDatasetCitation(datasetId: number, anonymizedAccess: boolean, datasetVersionId?: string): Promise; } diff --git a/src/datasets/domain/useCases/GetDatasetCitation.ts b/src/datasets/domain/useCases/GetDatasetCitation.ts new file mode 100644 index 00000000..b2a0b1bd --- /dev/null +++ b/src/datasets/domain/useCases/GetDatasetCitation.ts @@ -0,0 +1,14 @@ +import { UseCase } from '../../../core/domain/useCases/UseCase'; +import { IDatasetsRepository } from '../repositories/IDatasetsRepository'; + +export class GetDatasetCitation implements UseCase { + private datasetsRepository: IDatasetsRepository; + + constructor(datasetsRepository: IDatasetsRepository) { + this.datasetsRepository = datasetsRepository; + } + + async execute(datasetId: number, datasetVersionId?: string, anonymizedAccess: boolean = false): Promise { + return await this.datasetsRepository.getDatasetCitation(datasetId, anonymizedAccess, datasetVersionId); + } +} diff --git a/src/datasets/index.ts b/src/datasets/index.ts index 3cad854c..7726b262 100644 --- a/src/datasets/index.ts +++ b/src/datasets/index.ts @@ -3,6 +3,7 @@ import { GetDatasetSummaryFieldNames } from './domain/useCases/GetDatasetSummary import { GetDatasetById } from './domain/useCases/GetDatasetById'; import { GetDatasetByPersistentId } from './domain/useCases/GetDatasetByPersistentId'; import { GetPrivateUrlDataset } from './domain/useCases/GetPrivateUrlDataset'; +import { GetDatasetCitation } from './domain/useCases/GetDatasetCitation'; const datasetsRepository = new DatasetsRepository(); @@ -10,8 +11,15 @@ const getDatasetSummaryFieldNames = new GetDatasetSummaryFieldNames(datasetsRepo const getDatasetById = new GetDatasetById(datasetsRepository); const getDatasetByPersistentId = new GetDatasetByPersistentId(datasetsRepository); const getPrivateUrlDataset = new GetPrivateUrlDataset(datasetsRepository); +const getDatasetCitationByPersistentId = new GetDatasetCitation(datasetsRepository); -export { getDatasetSummaryFieldNames, getDatasetById, getDatasetByPersistentId, getPrivateUrlDataset }; +export { + getDatasetSummaryFieldNames, + getDatasetById, + getDatasetByPersistentId, + getPrivateUrlDataset, + getDatasetCitationByPersistentId, +}; export { Dataset, DatasetVersionInfo, diff --git a/src/datasets/infra/repositories/DatasetsRepository.ts b/src/datasets/infra/repositories/DatasetsRepository.ts index b9e3afe7..f0f32874 100644 --- a/src/datasets/infra/repositories/DatasetsRepository.ts +++ b/src/datasets/infra/repositories/DatasetsRepository.ts @@ -45,6 +45,22 @@ export class DatasetsRepository extends ApiRepository implements IDatasetsReposi } } + public async getDatasetCitation( + datasetId: number, + anonymizedAccess: boolean = false, + datasetVersionId?: string, + ): Promise { + let version = ':latest'; + if (datasetVersionId !== undefined) { + version = datasetVersionId; + } + return this.doGet(`/datasets/${datasetId}/versions/${version}/citation?anonymizedAccess=${anonymizedAccess}`, true) + .then((response) => response.data.data.message) + .catch((error) => { + throw error; + }); + } + private async getDatasetLatestVersion(endpoint: string): Promise { return this.doGet(endpoint, true) .then((response) => transformLatestVersionResponseToDataset(response)) diff --git a/test/integration/datasets/DatasetsRepository.test.ts b/test/integration/datasets/DatasetsRepository.test.ts index 7d799736..4d5e5c34 100644 --- a/test/integration/datasets/DatasetsRepository.test.ts +++ b/test/integration/datasets/DatasetsRepository.test.ts @@ -12,3 +12,5 @@ describe('getDatasetSummaryFieldNames', () => { // TODO: getDataset tests // TODO: getPrivateUrlDataset tests + +// TODO: getDatasetCitation tests diff --git a/test/unit/datasets/DatasetsRepository.test.ts b/test/unit/datasets/DatasetsRepository.test.ts index 553baa22..8286437d 100644 --- a/test/unit/datasets/DatasetsRepository.test.ts +++ b/test/unit/datasets/DatasetsRepository.test.ts @@ -202,4 +202,48 @@ describe('DatasetsRepository', () => { expect(error).to.be.instanceOf(Error); }); }); + + describe('getDatasetCitation', () => { + const testDatasetId = 1; + + test('should return citation when response is successful', async () => { + const testCitation = 'test citation'; + const testCitationSuccessfulResponse = { + data: { + status: 'OK', + data: { + message: testCitation, + }, + }, + }; + const axiosGetStub = sandbox.stub(axios, 'get').resolves(testCitationSuccessfulResponse); + + const actual = await sut.getDatasetCitation(testDatasetId, false, undefined); + + assert.calledWithExactly( + axiosGetStub, + `${testApiUrl}/datasets/${testDatasetId}/versions/:latest/citation?anonymizedAccess=false`, + { + withCredentials: true, + }, + ); + assert.match(actual, testCitation); + }); + + test('should return error on repository read error', async () => { + const axiosGetStub = sandbox.stub(axios, 'get').rejects(testErrorResponse); + + let error: ReadError = undefined; + await sut.getDatasetCitation(1, false, undefined).catch((e) => (error = e)); + + assert.calledWithExactly( + axiosGetStub, + `${testApiUrl}/datasets/${testDatasetId}/versions/:latest/citation?anonymizedAccess=false`, + { + withCredentials: true, + }, + ); + expect(error).to.be.instanceOf(Error); + }); + }); }); diff --git a/test/unit/datasets/GetDatasetCitation.test.ts b/test/unit/datasets/GetDatasetCitation.test.ts new file mode 100644 index 00000000..171d877b --- /dev/null +++ b/test/unit/datasets/GetDatasetCitation.test.ts @@ -0,0 +1,39 @@ +import { GetDatasetCitation } from '../../../src/datasets/domain/useCases/GetDatasetCitation'; +import { IDatasetsRepository } from '../../../src/datasets/domain/repositories/IDatasetsRepository'; +import { ReadError } from '../../../src/core/domain/repositories/ReadError'; +import { assert, createSandbox, SinonSandbox } from 'sinon'; + +describe('execute', () => { + const sandbox: SinonSandbox = createSandbox(); + const testId = 1; + + afterEach(() => { + sandbox.restore(); + }); + + test('should return successful result with citation on repository success', async () => { + const testCitation = 'test citation'; + const datasetsRepositoryStub = {}; + const getDatasetCitationByPersistentIdStub = sandbox.stub().returns(testCitation); + datasetsRepositoryStub.getDatasetCitation = getDatasetCitationByPersistentIdStub; + + const sut = new GetDatasetCitation(datasetsRepositoryStub); + + const actual = await sut.execute(testId); + + assert.match(actual, testCitation); + assert.calledWithExactly(getDatasetCitationByPersistentIdStub, testId, false, undefined); + }); + + test('should return error result on repository error', async () => { + const datasetsRepositoryStub = {}; + const testReadError = new ReadError(); + datasetsRepositoryStub.getDatasetCitation = sandbox.stub().throwsException(testReadError); + const sut = new GetDatasetCitation(datasetsRepositoryStub); + + let actualError: ReadError = undefined; + await sut.execute(testId).catch((e) => (actualError = e)); + + assert.match(actualError, testReadError); + }); +}); From df1b9b0142e40966a59e9d808f0f54460db356b0 Mon Sep 17 00:00:00 2001 From: GPortas Date: Fri, 26 May 2023 17:49:15 +0100 Subject: [PATCH 23/30] Refactor: using same datasets endpoint for latest and specific version (getVersion) --- .../repositories/IDatasetsRepository.ts | 4 +- .../domain/useCases/GetDatasetById.ts | 2 +- .../useCases/GetDatasetByPersistentId.ts | 2 +- .../infra/repositories/DatasetsRepository.ts | 45 ++++++++----------- .../transformers/datasetTransformers.ts | 5 --- test/unit/datasets/DatasetsRepository.test.ts | 34 ++++++-------- 6 files changed, 35 insertions(+), 57 deletions(-) diff --git a/src/datasets/domain/repositories/IDatasetsRepository.ts b/src/datasets/domain/repositories/IDatasetsRepository.ts index 54fb448b..f1bc1f4b 100644 --- a/src/datasets/domain/repositories/IDatasetsRepository.ts +++ b/src/datasets/domain/repositories/IDatasetsRepository.ts @@ -2,8 +2,8 @@ import { Dataset } from '../models/Dataset'; export interface IDatasetsRepository { getDatasetSummaryFieldNames(): Promise; - getDatasetById(datasetId: number, datasetVersionId?: number): Promise; - getDatasetByPersistentId(datasetPersistentId: string, datasetVersionId?: number): Promise; + getDatasetById(datasetId: number, datasetVersionId?: string): Promise; + getDatasetByPersistentId(datasetPersistentId: string, datasetVersionId?: string): Promise; getPrivateUrlDataset(token: string, anonymizedFieldValue?: string): Promise; getDatasetCitation(datasetId: number, anonymizedAccess: boolean, datasetVersionId?: string): Promise; } diff --git a/src/datasets/domain/useCases/GetDatasetById.ts b/src/datasets/domain/useCases/GetDatasetById.ts index 0fe0c3c4..15e14748 100644 --- a/src/datasets/domain/useCases/GetDatasetById.ts +++ b/src/datasets/domain/useCases/GetDatasetById.ts @@ -9,7 +9,7 @@ export class GetDatasetById implements UseCase { this.datasetsRepository = datasetsRepository; } - async execute(datasetId: number, datasetVersionId?: number): Promise { + async execute(datasetId: number, datasetVersionId?: string): Promise { return await this.datasetsRepository.getDatasetById(datasetId, datasetVersionId); } } diff --git a/src/datasets/domain/useCases/GetDatasetByPersistentId.ts b/src/datasets/domain/useCases/GetDatasetByPersistentId.ts index 12796a1a..66a39a3b 100644 --- a/src/datasets/domain/useCases/GetDatasetByPersistentId.ts +++ b/src/datasets/domain/useCases/GetDatasetByPersistentId.ts @@ -9,7 +9,7 @@ export class GetDatasetByPersistentId implements UseCase { this.datasetsRepository = datasetsRepository; } - async execute(datasetPersistentId: string, datasetVersionId?: number): Promise { + async execute(datasetPersistentId: string, datasetVersionId?: string): Promise { return await this.datasetsRepository.getDatasetByPersistentId(datasetPersistentId, datasetVersionId); } } diff --git a/src/datasets/infra/repositories/DatasetsRepository.ts b/src/datasets/infra/repositories/DatasetsRepository.ts index f0f32874..69dfc3ea 100644 --- a/src/datasets/infra/repositories/DatasetsRepository.ts +++ b/src/datasets/infra/repositories/DatasetsRepository.ts @@ -1,12 +1,11 @@ import { ApiRepository } from '../../../core/infra/repositories/ApiRepository'; import { IDatasetsRepository } from '../../domain/repositories/IDatasetsRepository'; import { Dataset } from '../../domain/models/Dataset'; -import { - transformVersionResponseToDataset, - transformLatestVersionResponseToDataset, -} from './transformers/datasetTransformers'; +import { transformVersionResponseToDataset } from './transformers/datasetTransformers'; export class DatasetsRepository extends ApiRepository implements IDatasetsRepository { + DATASET_VERSION_LATEST = ':latest'; + public async getDatasetSummaryFieldNames(): Promise { return this.doGet('/datasets/summaryFieldNames') .then((response) => response.data.data) @@ -27,22 +26,20 @@ export class DatasetsRepository extends ApiRepository implements IDatasetsReposi }); } - public async getDatasetById(datasetId: number, datasetVersionId?: number): Promise { + public async getDatasetById(datasetId: number, datasetVersionId?: string): Promise { if (datasetVersionId === undefined) { - return this.getDatasetLatestVersion(`/datasets/${datasetId}`); - } else { - return this.getDatasetParticularVersion(`/datasets/${datasetId}/versions/${datasetVersionId}`); + datasetVersionId = this.DATASET_VERSION_LATEST; } + return this.getDatasetVersion(`/datasets/${datasetId}/versions/${datasetVersionId}`); } - public async getDatasetByPersistentId(datasetPersistentId: string, datasetVersionId?: number): Promise { + public async getDatasetByPersistentId(datasetPersistentId: string, datasetVersionId?: string): Promise { if (datasetVersionId === undefined) { - return this.getDatasetLatestVersion(`/datasets/:persistentId?persistentId=${datasetPersistentId}`); - } else { - return this.getDatasetParticularVersion( - `/datasets/:persistentId/versions/${datasetVersionId}?persistentId=${datasetPersistentId}`, - ); + datasetVersionId = this.DATASET_VERSION_LATEST; } + return this.getDatasetVersion( + `/datasets/:persistentId/versions/${datasetVersionId}?persistentId=${datasetPersistentId}`, + ); } public async getDatasetCitation( @@ -50,26 +47,20 @@ export class DatasetsRepository extends ApiRepository implements IDatasetsReposi anonymizedAccess: boolean = false, datasetVersionId?: string, ): Promise { - let version = ':latest'; - if (datasetVersionId !== undefined) { - version = datasetVersionId; + if (datasetVersionId === undefined) { + datasetVersionId = this.DATASET_VERSION_LATEST; } - return this.doGet(`/datasets/${datasetId}/versions/${version}/citation?anonymizedAccess=${anonymizedAccess}`, true) + return this.doGet( + `/datasets/${datasetId}/versions/${datasetVersionId}/citation?anonymizedAccess=${anonymizedAccess}`, + true, + ) .then((response) => response.data.data.message) .catch((error) => { throw error; }); } - private async getDatasetLatestVersion(endpoint: string): Promise { - return this.doGet(endpoint, true) - .then((response) => transformLatestVersionResponseToDataset(response)) - .catch((error) => { - throw error; - }); - } - - private async getDatasetParticularVersion(endpoint: string): Promise { + private async getDatasetVersion(endpoint: string): Promise { return this.doGet(endpoint, true) .then((response) => transformVersionResponseToDataset(response)) .catch((error) => { diff --git a/src/datasets/infra/repositories/transformers/datasetTransformers.ts b/src/datasets/infra/repositories/transformers/datasetTransformers.ts index e7d5da8c..895efc86 100644 --- a/src/datasets/infra/repositories/transformers/datasetTransformers.ts +++ b/src/datasets/infra/repositories/transformers/datasetTransformers.ts @@ -16,11 +16,6 @@ export const transformVersionResponseToDataset = (response: AxiosResponse): Data return transformVersionPayloadToDataset(versionPayload); }; -export const transformLatestVersionResponseToDataset = (response: AxiosResponse): Dataset => { - const versionPayload = response.data.data.latestVersion; - return transformVersionPayloadToDataset(versionPayload); -}; - const transformVersionPayloadToDataset = (versionPayload: any): Dataset => { return { id: versionPayload.datasetId, diff --git a/test/unit/datasets/DatasetsRepository.test.ts b/test/unit/datasets/DatasetsRepository.test.ts index 8286437d..cfc7561c 100644 --- a/test/unit/datasets/DatasetsRepository.test.ts +++ b/test/unit/datasets/DatasetsRepository.test.ts @@ -9,20 +9,12 @@ import { createDatasetModel, createDatasetVersionPayload } from '../../testHelpe describe('DatasetsRepository', () => { const sandbox: SinonSandbox = createSandbox(); const sut: DatasetsRepository = new DatasetsRepository(); - const testVersionSuccessfulResponse = { + const testDatasetVersionSuccessfulResponse = { data: { status: 'OK', data: createDatasetVersionPayload(), }, }; - const testLatestVersionSuccessfulResponse = { - data: { - status: 'OK', - data: { - latestVersion: createDatasetVersionPayload(), - }, - }, - }; const testErrorResponse = { response: { status: 'ERROR', @@ -74,20 +66,20 @@ describe('DatasetsRepository', () => { describe('getDatasetById', () => { test('should return Dataset when providing id, no version, and response is successful', async () => { - const axiosGetStub = sandbox.stub(axios, 'get').resolves(testLatestVersionSuccessfulResponse); + const axiosGetStub = sandbox.stub(axios, 'get').resolves(testDatasetVersionSuccessfulResponse); const actual = await sut.getDatasetById(testDatasetModel.id); - assert.calledWithExactly(axiosGetStub, `${testApiUrl}/datasets/${testDatasetModel.id}`, { + assert.calledWithExactly(axiosGetStub, `${testApiUrl}/datasets/${testDatasetModel.id}/versions/:latest`, { withCredentials: true, }); assert.match(actual, testDatasetModel); }); test('should return Dataset when providing id, version, and response is successful', async () => { - const axiosGetStub = sandbox.stub(axios, 'get').resolves(testVersionSuccessfulResponse); + const axiosGetStub = sandbox.stub(axios, 'get').resolves(testDatasetVersionSuccessfulResponse); - const actual = await sut.getDatasetById(testDatasetModel.id, testDatasetModel.versionId); + const actual = await sut.getDatasetById(testDatasetModel.id, String(testDatasetModel.versionId)); assert.calledWithExactly( axiosGetStub, @@ -105,7 +97,7 @@ describe('DatasetsRepository', () => { let error: ReadError = undefined; await sut.getDatasetById(testDatasetModel.id).catch((e) => (error = e)); - assert.calledWithExactly(axiosGetStub, `${testApiUrl}/datasets/${testDatasetModel.id}`, { + assert.calledWithExactly(axiosGetStub, `${testApiUrl}/datasets/${testDatasetModel.id}/versions/:latest`, { withCredentials: true, }); expect(error).to.be.instanceOf(Error); @@ -114,13 +106,13 @@ describe('DatasetsRepository', () => { describe('getDatasetByPersistentId', () => { test('should return Dataset when providing persistent id, no version, and response is successful', async () => { - const axiosGetStub = sandbox.stub(axios, 'get').resolves(testLatestVersionSuccessfulResponse); + const axiosGetStub = sandbox.stub(axios, 'get').resolves(testDatasetVersionSuccessfulResponse); const actual = await sut.getDatasetByPersistentId(testDatasetModel.persistentId); assert.calledWithExactly( axiosGetStub, - `${testApiUrl}/datasets/:persistentId?persistentId=${testDatasetModel.persistentId}`, + `${testApiUrl}/datasets/:persistentId/versions/:latest?persistentId=${testDatasetModel.persistentId}`, { withCredentials: true, }, @@ -129,9 +121,9 @@ describe('DatasetsRepository', () => { }); test('should return Dataset when providing persistent id, version, and response is successful', async () => { - const axiosGetStub = sandbox.stub(axios, 'get').resolves(testVersionSuccessfulResponse); + const axiosGetStub = sandbox.stub(axios, 'get').resolves(testDatasetVersionSuccessfulResponse); - const actual = await sut.getDatasetByPersistentId(testDatasetModel.persistentId, testDatasetModel.versionId); + const actual = await sut.getDatasetByPersistentId(testDatasetModel.persistentId, String(testDatasetModel.versionId)); assert.calledWithExactly( axiosGetStub, @@ -151,7 +143,7 @@ describe('DatasetsRepository', () => { assert.calledWithExactly( axiosGetStub, - `${testApiUrl}/datasets/:persistentId?persistentId=${testDatasetModel.persistentId}`, + `${testApiUrl}/datasets/:persistentId/versions/:latest?persistentId=${testDatasetModel.persistentId}`, { withCredentials: true, }, @@ -164,7 +156,7 @@ describe('DatasetsRepository', () => { const testToken = 'testToken'; test('should return Dataset when providing anonymized field value and response is successful', async () => { - const axiosGetStub = sandbox.stub(axios, 'get').resolves(testVersionSuccessfulResponse); + const axiosGetStub = sandbox.stub(axios, 'get').resolves(testDatasetVersionSuccessfulResponse); const testAnonymizedFieldValue = 'testValue'; const actual = await sut.getPrivateUrlDataset(testToken, 'testValue'); @@ -180,7 +172,7 @@ describe('DatasetsRepository', () => { }); test('should return Dataset when not providing anonymized field value and response is successful', async () => { - const axiosGetStub = sandbox.stub(axios, 'get').resolves(testVersionSuccessfulResponse); + const axiosGetStub = sandbox.stub(axios, 'get').resolves(testDatasetVersionSuccessfulResponse); const actual = await sut.getPrivateUrlDataset(testToken, undefined); From 17203460c1e1d7a8c03555eb4bd0073e7bb52520 Mon Sep 17 00:00:00 2001 From: GPortas Date: Fri, 26 May 2023 17:56:53 +0100 Subject: [PATCH 24/30] Fixed: use case export and variables names --- src/datasets/index.ts | 4 ++-- test/unit/datasets/GetDatasetCitation.test.ts | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/datasets/index.ts b/src/datasets/index.ts index 7726b262..ae002ec9 100644 --- a/src/datasets/index.ts +++ b/src/datasets/index.ts @@ -11,14 +11,14 @@ const getDatasetSummaryFieldNames = new GetDatasetSummaryFieldNames(datasetsRepo const getDatasetById = new GetDatasetById(datasetsRepository); const getDatasetByPersistentId = new GetDatasetByPersistentId(datasetsRepository); const getPrivateUrlDataset = new GetPrivateUrlDataset(datasetsRepository); -const getDatasetCitationByPersistentId = new GetDatasetCitation(datasetsRepository); +const getDatasetCitation = new GetDatasetCitation(datasetsRepository); export { getDatasetSummaryFieldNames, getDatasetById, getDatasetByPersistentId, getPrivateUrlDataset, - getDatasetCitationByPersistentId, + getDatasetCitation, }; export { Dataset, diff --git a/test/unit/datasets/GetDatasetCitation.test.ts b/test/unit/datasets/GetDatasetCitation.test.ts index 171d877b..0e1e9a10 100644 --- a/test/unit/datasets/GetDatasetCitation.test.ts +++ b/test/unit/datasets/GetDatasetCitation.test.ts @@ -14,15 +14,15 @@ describe('execute', () => { test('should return successful result with citation on repository success', async () => { const testCitation = 'test citation'; const datasetsRepositoryStub = {}; - const getDatasetCitationByPersistentIdStub = sandbox.stub().returns(testCitation); - datasetsRepositoryStub.getDatasetCitation = getDatasetCitationByPersistentIdStub; + const getDatasetCitationStub = sandbox.stub().returns(testCitation); + datasetsRepositoryStub.getDatasetCitation = getDatasetCitationStub; const sut = new GetDatasetCitation(datasetsRepositoryStub); const actual = await sut.execute(testId); assert.match(actual, testCitation); - assert.calledWithExactly(getDatasetCitationByPersistentIdStub, testId, false, undefined); + assert.calledWithExactly(getDatasetCitationStub, testId, false, undefined); }); test('should return error result on repository error', async () => { From 00ccd35423dbbf9846935d60dc78817d3d256212 Mon Sep 17 00:00:00 2001 From: GPortas Date: Sat, 27 May 2023 08:02:33 +0100 Subject: [PATCH 25/30] Removed: custom anonymized field value from getPrivateUrlDataset use case --- .../repositories/IDatasetsRepository.ts | 2 +- .../domain/useCases/GetPrivateUrlDataset.ts | 4 ++-- .../infra/repositories/DatasetsRepository.ts | 8 ++------ test/unit/datasets/DatasetsRepository.test.ts | 20 ++++--------------- .../datasets/GetPrivateUrlDataset.test.ts | 6 +++--- 5 files changed, 12 insertions(+), 28 deletions(-) diff --git a/src/datasets/domain/repositories/IDatasetsRepository.ts b/src/datasets/domain/repositories/IDatasetsRepository.ts index f1bc1f4b..637a5f3b 100644 --- a/src/datasets/domain/repositories/IDatasetsRepository.ts +++ b/src/datasets/domain/repositories/IDatasetsRepository.ts @@ -4,6 +4,6 @@ export interface IDatasetsRepository { getDatasetSummaryFieldNames(): Promise; getDatasetById(datasetId: number, datasetVersionId?: string): Promise; getDatasetByPersistentId(datasetPersistentId: string, datasetVersionId?: string): Promise; - getPrivateUrlDataset(token: string, anonymizedFieldValue?: string): Promise; + getPrivateUrlDataset(token: string): Promise; getDatasetCitation(datasetId: number, anonymizedAccess: boolean, datasetVersionId?: string): Promise; } diff --git a/src/datasets/domain/useCases/GetPrivateUrlDataset.ts b/src/datasets/domain/useCases/GetPrivateUrlDataset.ts index 487e02ba..3580632c 100644 --- a/src/datasets/domain/useCases/GetPrivateUrlDataset.ts +++ b/src/datasets/domain/useCases/GetPrivateUrlDataset.ts @@ -9,7 +9,7 @@ export class GetPrivateUrlDataset implements UseCase { this.datasetsRepository = datasetsRepository; } - async execute(token: string, anonymizedFieldValue?: string): Promise { - return await this.datasetsRepository.getPrivateUrlDataset(token, anonymizedFieldValue); + async execute(token: string): Promise { + return await this.datasetsRepository.getPrivateUrlDataset(token); } } diff --git a/src/datasets/infra/repositories/DatasetsRepository.ts b/src/datasets/infra/repositories/DatasetsRepository.ts index 69dfc3ea..05eb6f50 100644 --- a/src/datasets/infra/repositories/DatasetsRepository.ts +++ b/src/datasets/infra/repositories/DatasetsRepository.ts @@ -14,12 +14,8 @@ export class DatasetsRepository extends ApiRepository implements IDatasetsReposi }); } - public async getPrivateUrlDataset(token: string, anonymizedFieldValue?: string): Promise { - let apiEndpoint = `/datasets/privateUrlDatasetVersion/${token}`; - if (anonymizedFieldValue !== undefined) { - apiEndpoint = apiEndpoint + `?anonymizedFieldValue=${anonymizedFieldValue}`; - } - return this.doGet(apiEndpoint) + public async getPrivateUrlDataset(token: string): Promise { + return this.doGet(`/datasets/privateUrlDatasetVersion/${token}`) .then((response) => transformVersionResponseToDataset(response)) .catch((error) => { throw error; diff --git a/test/unit/datasets/DatasetsRepository.test.ts b/test/unit/datasets/DatasetsRepository.test.ts index cfc7561c..113470e7 100644 --- a/test/unit/datasets/DatasetsRepository.test.ts +++ b/test/unit/datasets/DatasetsRepository.test.ts @@ -155,15 +155,14 @@ describe('DatasetsRepository', () => { describe('getPrivateUrlDataset', () => { const testToken = 'testToken'; - test('should return Dataset when providing anonymized field value and response is successful', async () => { + test('should return Dataset when response is successful', async () => { const axiosGetStub = sandbox.stub(axios, 'get').resolves(testDatasetVersionSuccessfulResponse); - const testAnonymizedFieldValue = 'testValue'; - const actual = await sut.getPrivateUrlDataset(testToken, 'testValue'); + const actual = await sut.getPrivateUrlDataset(testToken); assert.calledWithExactly( axiosGetStub, - `${testApiUrl}/datasets/privateUrlDatasetVersion/${testToken}?anonymizedFieldValue=${testAnonymizedFieldValue}`, + `${testApiUrl}/datasets/privateUrlDatasetVersion/${testToken}`, { withCredentials: false, }, @@ -171,22 +170,11 @@ describe('DatasetsRepository', () => { assert.match(actual, testDatasetModel); }); - test('should return Dataset when not providing anonymized field value and response is successful', async () => { - const axiosGetStub = sandbox.stub(axios, 'get').resolves(testDatasetVersionSuccessfulResponse); - - const actual = await sut.getPrivateUrlDataset(testToken, undefined); - - assert.calledWithExactly(axiosGetStub, `${testApiUrl}/datasets/privateUrlDatasetVersion/${testToken}`, { - withCredentials: false, - }); - assert.match(actual, testDatasetModel); - }); - test('should return error on repository read error', async () => { const axiosGetStub = sandbox.stub(axios, 'get').rejects(testErrorResponse); let error: ReadError = undefined; - await sut.getPrivateUrlDataset(testToken, undefined).catch((e) => (error = e)); + await sut.getPrivateUrlDataset(testToken).catch((e) => (error = e)); assert.calledWithExactly(axiosGetStub, `${testApiUrl}/datasets/privateUrlDatasetVersion/${testToken}`, { withCredentials: false, diff --git a/test/unit/datasets/GetPrivateUrlDataset.test.ts b/test/unit/datasets/GetPrivateUrlDataset.test.ts index c925006e..14d0c47c 100644 --- a/test/unit/datasets/GetPrivateUrlDataset.test.ts +++ b/test/unit/datasets/GetPrivateUrlDataset.test.ts @@ -19,10 +19,10 @@ describe('execute', () => { datasetsRepositoryStub.getPrivateUrlDataset = getDatasetStub; const sut = new GetPrivateUrlDataset(datasetsRepositoryStub); - const actual = await sut.execute(testToken, null); + const actual = await sut.execute(testToken); assert.match(actual, testDataset); - assert.calledWithExactly(getDatasetStub, testToken, null); + assert.calledWithExactly(getDatasetStub, testToken); }); test('should return error result on repository error', async () => { @@ -32,7 +32,7 @@ describe('execute', () => { const sut = new GetPrivateUrlDataset(datasetsRepositoryStub); let actualError: ReadError = undefined; - await sut.execute(testToken, null).catch((e) => (actualError = e)); + await sut.execute(testToken).catch((e) => (actualError = e)); assert.match(actualError, testReadError); }); From d8d329c82f59e6c45782b0bd1ef3c9089714ff87 Mon Sep 17 00:00:00 2001 From: GPortas Date: Sat, 27 May 2023 08:45:01 +0100 Subject: [PATCH 26/30] Added: GetPrivateUrlDatasetCitation use case --- .../repositories/IDatasetsRepository.ts | 1 + .../useCases/GetPrivateUrlDatasetCitation.ts | 14 ++++ src/datasets/index.ts | 3 + .../infra/repositories/DatasetsRepository.ts | 8 ++ test/unit/datasets/DatasetsRepository.test.ts | 83 +++++++++++++------ .../datasets/GetPrivateUrlDataset.test.ts | 8 +- .../GetPrivateUrlDatasetCitation.test.ts | 39 +++++++++ 7 files changed, 125 insertions(+), 31 deletions(-) create mode 100644 src/datasets/domain/useCases/GetPrivateUrlDatasetCitation.ts create mode 100644 test/unit/datasets/GetPrivateUrlDatasetCitation.test.ts diff --git a/src/datasets/domain/repositories/IDatasetsRepository.ts b/src/datasets/domain/repositories/IDatasetsRepository.ts index 637a5f3b..92fe7c5d 100644 --- a/src/datasets/domain/repositories/IDatasetsRepository.ts +++ b/src/datasets/domain/repositories/IDatasetsRepository.ts @@ -6,4 +6,5 @@ export interface IDatasetsRepository { getDatasetByPersistentId(datasetPersistentId: string, datasetVersionId?: string): Promise; getPrivateUrlDataset(token: string): Promise; getDatasetCitation(datasetId: number, anonymizedAccess: boolean, datasetVersionId?: string): Promise; + getPrivateUrlDatasetCitation(token: string): Promise; } diff --git a/src/datasets/domain/useCases/GetPrivateUrlDatasetCitation.ts b/src/datasets/domain/useCases/GetPrivateUrlDatasetCitation.ts new file mode 100644 index 00000000..7ff06f47 --- /dev/null +++ b/src/datasets/domain/useCases/GetPrivateUrlDatasetCitation.ts @@ -0,0 +1,14 @@ +import { UseCase } from '../../../core/domain/useCases/UseCase'; +import { IDatasetsRepository } from '../repositories/IDatasetsRepository'; + +export class GetPrivateUrlDatasetCitation implements UseCase { + private datasetsRepository: IDatasetsRepository; + + constructor(datasetsRepository: IDatasetsRepository) { + this.datasetsRepository = datasetsRepository; + } + + async execute(token: string): Promise { + return await this.datasetsRepository.getPrivateUrlDatasetCitation(token); + } +} diff --git a/src/datasets/index.ts b/src/datasets/index.ts index ae002ec9..64c39963 100644 --- a/src/datasets/index.ts +++ b/src/datasets/index.ts @@ -4,6 +4,7 @@ import { GetDatasetById } from './domain/useCases/GetDatasetById'; import { GetDatasetByPersistentId } from './domain/useCases/GetDatasetByPersistentId'; import { GetPrivateUrlDataset } from './domain/useCases/GetPrivateUrlDataset'; import { GetDatasetCitation } from './domain/useCases/GetDatasetCitation'; +import { GetPrivateUrlDatasetCitation } from './domain/useCases/GetPrivateUrlDatasetCitation'; const datasetsRepository = new DatasetsRepository(); @@ -12,6 +13,7 @@ const getDatasetById = new GetDatasetById(datasetsRepository); const getDatasetByPersistentId = new GetDatasetByPersistentId(datasetsRepository); const getPrivateUrlDataset = new GetPrivateUrlDataset(datasetsRepository); const getDatasetCitation = new GetDatasetCitation(datasetsRepository); +const getPrivateUrlDatasetCitation = new GetPrivateUrlDatasetCitation(datasetsRepository); export { getDatasetSummaryFieldNames, @@ -19,6 +21,7 @@ export { getDatasetByPersistentId, getPrivateUrlDataset, getDatasetCitation, + getPrivateUrlDatasetCitation, }; export { Dataset, diff --git a/src/datasets/infra/repositories/DatasetsRepository.ts b/src/datasets/infra/repositories/DatasetsRepository.ts index 05eb6f50..cc731967 100644 --- a/src/datasets/infra/repositories/DatasetsRepository.ts +++ b/src/datasets/infra/repositories/DatasetsRepository.ts @@ -63,4 +63,12 @@ export class DatasetsRepository extends ApiRepository implements IDatasetsReposi throw error; }); } + + public async getPrivateUrlDatasetCitation(token: string): Promise { + return this.doGet(`/datasets/privateUrlDatasetVersion/${token}/citation`) + .then((response) => response.data.data.message) + .catch((error) => { + throw error; + }); + } } diff --git a/test/unit/datasets/DatasetsRepository.test.ts b/test/unit/datasets/DatasetsRepository.test.ts index 113470e7..dbe917de 100644 --- a/test/unit/datasets/DatasetsRepository.test.ts +++ b/test/unit/datasets/DatasetsRepository.test.ts @@ -15,12 +15,22 @@ describe('DatasetsRepository', () => { data: createDatasetVersionPayload(), }, }; + const testCitation = 'test citation'; + const testCitationSuccessfulResponse = { + data: { + status: 'OK', + data: { + message: testCitation, + }, + }, + }; const testErrorResponse = { response: { status: 'ERROR', message: 'test', }, }; + const testPrivateUrlToken = 'testToken'; const testDatasetModel = createDatasetModel(); const testApiUrl = 'https://test.dataverse.org/api/v1'; @@ -123,7 +133,10 @@ describe('DatasetsRepository', () => { test('should return Dataset when providing persistent id, version, and response is successful', async () => { const axiosGetStub = sandbox.stub(axios, 'get').resolves(testDatasetVersionSuccessfulResponse); - const actual = await sut.getDatasetByPersistentId(testDatasetModel.persistentId, String(testDatasetModel.versionId)); + const actual = await sut.getDatasetByPersistentId( + testDatasetModel.persistentId, + String(testDatasetModel.versionId), + ); assert.calledWithExactly( axiosGetStub, @@ -153,20 +166,14 @@ describe('DatasetsRepository', () => { }); describe('getPrivateUrlDataset', () => { - const testToken = 'testToken'; - test('should return Dataset when response is successful', async () => { const axiosGetStub = sandbox.stub(axios, 'get').resolves(testDatasetVersionSuccessfulResponse); - const actual = await sut.getPrivateUrlDataset(testToken); + const actual = await sut.getPrivateUrlDataset(testPrivateUrlToken); - assert.calledWithExactly( - axiosGetStub, - `${testApiUrl}/datasets/privateUrlDatasetVersion/${testToken}`, - { - withCredentials: false, - }, - ); + assert.calledWithExactly(axiosGetStub, `${testApiUrl}/datasets/privateUrlDatasetVersion/${testPrivateUrlToken}`, { + withCredentials: false, + }); assert.match(actual, testDatasetModel); }); @@ -174,9 +181,9 @@ describe('DatasetsRepository', () => { const axiosGetStub = sandbox.stub(axios, 'get').rejects(testErrorResponse); let error: ReadError = undefined; - await sut.getPrivateUrlDataset(testToken).catch((e) => (error = e)); + await sut.getPrivateUrlDataset(testPrivateUrlToken).catch((e) => (error = e)); - assert.calledWithExactly(axiosGetStub, `${testApiUrl}/datasets/privateUrlDatasetVersion/${testToken}`, { + assert.calledWithExactly(axiosGetStub, `${testApiUrl}/datasets/privateUrlDatasetVersion/${testPrivateUrlToken}`, { withCredentials: false, }); expect(error).to.be.instanceOf(Error); @@ -184,25 +191,14 @@ describe('DatasetsRepository', () => { }); describe('getDatasetCitation', () => { - const testDatasetId = 1; - test('should return citation when response is successful', async () => { - const testCitation = 'test citation'; - const testCitationSuccessfulResponse = { - data: { - status: 'OK', - data: { - message: testCitation, - }, - }, - }; const axiosGetStub = sandbox.stub(axios, 'get').resolves(testCitationSuccessfulResponse); - const actual = await sut.getDatasetCitation(testDatasetId, false, undefined); + const actual = await sut.getDatasetCitation(testDatasetModel.id, false, undefined); assert.calledWithExactly( axiosGetStub, - `${testApiUrl}/datasets/${testDatasetId}/versions/:latest/citation?anonymizedAccess=false`, + `${testApiUrl}/datasets/${testDatasetModel.id}/versions/:latest/citation?anonymizedAccess=false`, { withCredentials: true, }, @@ -218,7 +214,7 @@ describe('DatasetsRepository', () => { assert.calledWithExactly( axiosGetStub, - `${testApiUrl}/datasets/${testDatasetId}/versions/:latest/citation?anonymizedAccess=false`, + `${testApiUrl}/datasets/${testDatasetModel.id}/versions/:latest/citation?anonymizedAccess=false`, { withCredentials: true, }, @@ -226,4 +222,37 @@ describe('DatasetsRepository', () => { expect(error).to.be.instanceOf(Error); }); }); + + describe('getPrivateUrlDatasetCitation', () => { + test('should return citation when response is successful', async () => { + const axiosGetStub = sandbox.stub(axios, 'get').resolves(testCitationSuccessfulResponse); + + const actual = await sut.getPrivateUrlDatasetCitation(testPrivateUrlToken); + + assert.calledWithExactly( + axiosGetStub, + `${testApiUrl}/datasets/privateUrlDatasetVersion/${testPrivateUrlToken}/citation`, + { + withCredentials: false, + }, + ); + assert.match(actual, testCitation); + }); + + test('should return error on repository read error', async () => { + const axiosGetStub = sandbox.stub(axios, 'get').rejects(testErrorResponse); + + let error: ReadError = undefined; + await sut.getPrivateUrlDatasetCitation(testPrivateUrlToken).catch((e) => (error = e)); + + assert.calledWithExactly( + axiosGetStub, + `${testApiUrl}/datasets/privateUrlDatasetVersion/${testPrivateUrlToken}/citation`, + { + withCredentials: false, + }, + ); + expect(error).to.be.instanceOf(Error); + }); + }); }); diff --git a/test/unit/datasets/GetPrivateUrlDataset.test.ts b/test/unit/datasets/GetPrivateUrlDataset.test.ts index 14d0c47c..7f34232f 100644 --- a/test/unit/datasets/GetPrivateUrlDataset.test.ts +++ b/test/unit/datasets/GetPrivateUrlDataset.test.ts @@ -6,7 +6,7 @@ import { ReadError } from '../../../src/core/domain/repositories/ReadError'; describe('execute', () => { const sandbox: SinonSandbox = createSandbox(); - const testToken = 'token'; + const testPrivateUrlToken = 'token'; afterEach(() => { sandbox.restore(); @@ -19,10 +19,10 @@ describe('execute', () => { datasetsRepositoryStub.getPrivateUrlDataset = getDatasetStub; const sut = new GetPrivateUrlDataset(datasetsRepositoryStub); - const actual = await sut.execute(testToken); + const actual = await sut.execute(testPrivateUrlToken); assert.match(actual, testDataset); - assert.calledWithExactly(getDatasetStub, testToken); + assert.calledWithExactly(getDatasetStub, testPrivateUrlToken); }); test('should return error result on repository error', async () => { @@ -32,7 +32,7 @@ describe('execute', () => { const sut = new GetPrivateUrlDataset(datasetsRepositoryStub); let actualError: ReadError = undefined; - await sut.execute(testToken).catch((e) => (actualError = e)); + await sut.execute(testPrivateUrlToken).catch((e) => (actualError = e)); assert.match(actualError, testReadError); }); diff --git a/test/unit/datasets/GetPrivateUrlDatasetCitation.test.ts b/test/unit/datasets/GetPrivateUrlDatasetCitation.test.ts new file mode 100644 index 00000000..8aaac6a2 --- /dev/null +++ b/test/unit/datasets/GetPrivateUrlDatasetCitation.test.ts @@ -0,0 +1,39 @@ +import { GetPrivateUrlDatasetCitation } from '../../../src/datasets/domain/useCases/GetPrivateUrlDatasetCitation'; +import { IDatasetsRepository } from '../../../src/datasets/domain/repositories/IDatasetsRepository'; +import { ReadError } from '../../../src/core/domain/repositories/ReadError'; +import { assert, createSandbox, SinonSandbox } from 'sinon'; + +describe('execute', () => { + const sandbox: SinonSandbox = createSandbox(); + const testPrivateUrlToken = 'token'; + + afterEach(() => { + sandbox.restore(); + }); + + test('should return successful result with citation on repository success', async () => { + const testCitation = 'test citation'; + const datasetsRepositoryStub = {}; + const getPrivateUrlDatasetCitationStub = sandbox.stub().returns(testCitation); + datasetsRepositoryStub.getPrivateUrlDatasetCitation = getPrivateUrlDatasetCitationStub; + + const sut = new GetPrivateUrlDatasetCitation(datasetsRepositoryStub); + + const actual = await sut.execute(testPrivateUrlToken); + + assert.match(actual, testCitation); + assert.calledWithExactly(getPrivateUrlDatasetCitationStub, testPrivateUrlToken); + }); + + test('should return error result on repository error', async () => { + const datasetsRepositoryStub = {}; + const testReadError = new ReadError(); + datasetsRepositoryStub.getPrivateUrlDatasetCitation = sandbox.stub().throwsException(testReadError); + const sut = new GetPrivateUrlDatasetCitation(datasetsRepositoryStub); + + let actualError: ReadError = undefined; + await sut.execute(testPrivateUrlToken).catch((e) => (actualError = e)); + + assert.match(actualError, testReadError); + }); +}); From d6e4287314323e5e30377f72654bcc9107f09548 Mon Sep 17 00:00:00 2001 From: GPortas Date: Sat, 27 May 2023 08:49:06 +0100 Subject: [PATCH 27/30] Removed: anonymizedAccess optional param for getDatasetCitation use case --- .../domain/repositories/IDatasetsRepository.ts | 2 +- src/datasets/domain/useCases/GetDatasetCitation.ts | 4 ++-- src/datasets/infra/repositories/DatasetsRepository.ts | 11 ++--------- test/unit/datasets/DatasetsRepository.test.ts | 8 ++++---- test/unit/datasets/GetDatasetCitation.test.ts | 2 +- 5 files changed, 10 insertions(+), 17 deletions(-) diff --git a/src/datasets/domain/repositories/IDatasetsRepository.ts b/src/datasets/domain/repositories/IDatasetsRepository.ts index 92fe7c5d..59aae717 100644 --- a/src/datasets/domain/repositories/IDatasetsRepository.ts +++ b/src/datasets/domain/repositories/IDatasetsRepository.ts @@ -5,6 +5,6 @@ export interface IDatasetsRepository { getDatasetById(datasetId: number, datasetVersionId?: string): Promise; getDatasetByPersistentId(datasetPersistentId: string, datasetVersionId?: string): Promise; getPrivateUrlDataset(token: string): Promise; - getDatasetCitation(datasetId: number, anonymizedAccess: boolean, datasetVersionId?: string): Promise; + getDatasetCitation(datasetId: number, datasetVersionId?: string): Promise; getPrivateUrlDatasetCitation(token: string): Promise; } diff --git a/src/datasets/domain/useCases/GetDatasetCitation.ts b/src/datasets/domain/useCases/GetDatasetCitation.ts index b2a0b1bd..ac418df3 100644 --- a/src/datasets/domain/useCases/GetDatasetCitation.ts +++ b/src/datasets/domain/useCases/GetDatasetCitation.ts @@ -8,7 +8,7 @@ export class GetDatasetCitation implements UseCase { this.datasetsRepository = datasetsRepository; } - async execute(datasetId: number, datasetVersionId?: string, anonymizedAccess: boolean = false): Promise { - return await this.datasetsRepository.getDatasetCitation(datasetId, anonymizedAccess, datasetVersionId); + async execute(datasetId: number, datasetVersionId?: string): Promise { + return await this.datasetsRepository.getDatasetCitation(datasetId, datasetVersionId); } } diff --git a/src/datasets/infra/repositories/DatasetsRepository.ts b/src/datasets/infra/repositories/DatasetsRepository.ts index cc731967..56328fcb 100644 --- a/src/datasets/infra/repositories/DatasetsRepository.ts +++ b/src/datasets/infra/repositories/DatasetsRepository.ts @@ -38,18 +38,11 @@ export class DatasetsRepository extends ApiRepository implements IDatasetsReposi ); } - public async getDatasetCitation( - datasetId: number, - anonymizedAccess: boolean = false, - datasetVersionId?: string, - ): Promise { + public async getDatasetCitation(datasetId: number, datasetVersionId?: string): Promise { if (datasetVersionId === undefined) { datasetVersionId = this.DATASET_VERSION_LATEST; } - return this.doGet( - `/datasets/${datasetId}/versions/${datasetVersionId}/citation?anonymizedAccess=${anonymizedAccess}`, - true, - ) + return this.doGet(`/datasets/${datasetId}/versions/${datasetVersionId}/citation`, true) .then((response) => response.data.data.message) .catch((error) => { throw error; diff --git a/test/unit/datasets/DatasetsRepository.test.ts b/test/unit/datasets/DatasetsRepository.test.ts index dbe917de..e3de50fc 100644 --- a/test/unit/datasets/DatasetsRepository.test.ts +++ b/test/unit/datasets/DatasetsRepository.test.ts @@ -194,11 +194,11 @@ describe('DatasetsRepository', () => { test('should return citation when response is successful', async () => { const axiosGetStub = sandbox.stub(axios, 'get').resolves(testCitationSuccessfulResponse); - const actual = await sut.getDatasetCitation(testDatasetModel.id, false, undefined); + const actual = await sut.getDatasetCitation(testDatasetModel.id, undefined); assert.calledWithExactly( axiosGetStub, - `${testApiUrl}/datasets/${testDatasetModel.id}/versions/:latest/citation?anonymizedAccess=false`, + `${testApiUrl}/datasets/${testDatasetModel.id}/versions/:latest/citation`, { withCredentials: true, }, @@ -210,11 +210,11 @@ describe('DatasetsRepository', () => { const axiosGetStub = sandbox.stub(axios, 'get').rejects(testErrorResponse); let error: ReadError = undefined; - await sut.getDatasetCitation(1, false, undefined).catch((e) => (error = e)); + await sut.getDatasetCitation(1, undefined).catch((e) => (error = e)); assert.calledWithExactly( axiosGetStub, - `${testApiUrl}/datasets/${testDatasetModel.id}/versions/:latest/citation?anonymizedAccess=false`, + `${testApiUrl}/datasets/${testDatasetModel.id}/versions/:latest/citation`, { withCredentials: true, }, diff --git a/test/unit/datasets/GetDatasetCitation.test.ts b/test/unit/datasets/GetDatasetCitation.test.ts index 0e1e9a10..141fb3af 100644 --- a/test/unit/datasets/GetDatasetCitation.test.ts +++ b/test/unit/datasets/GetDatasetCitation.test.ts @@ -22,7 +22,7 @@ describe('execute', () => { const actual = await sut.execute(testId); assert.match(actual, testCitation); - assert.calledWithExactly(getDatasetCitationStub, testId, false, undefined); + assert.calledWithExactly(getDatasetCitationStub, testId, undefined); }); test('should return error result on repository error', async () => { From bb367b62b9e85e29743bbf8b070644ab14d542d9 Mon Sep 17 00:00:00 2001 From: GPortas Date: Mon, 29 May 2023 19:17:54 +0100 Subject: [PATCH 28/30] Changed: managing optional license dataset property --- src/datasets/domain/models/Dataset.ts | 2 +- .../transformers/datasetTransformers.ts | 23 ++++++--- test/testHelpers/datasets/datasetHelper.ts | 34 +++++++++---- test/unit/datasets/DatasetsRepository.test.ts | 50 ++++++++++++++++++- 4 files changed, 91 insertions(+), 18 deletions(-) diff --git a/src/datasets/domain/models/Dataset.ts b/src/datasets/domain/models/Dataset.ts index d840d393..046f4022 100644 --- a/src/datasets/domain/models/Dataset.ts +++ b/src/datasets/domain/models/Dataset.ts @@ -3,7 +3,7 @@ export interface Dataset { persistentId: string; versionId: number; versionInfo: DatasetVersionInfo; - license: DatasetLicense; + license?: DatasetLicense; metadataBlocks: DatasetMetadataBlock[]; } diff --git a/src/datasets/infra/repositories/transformers/datasetTransformers.ts b/src/datasets/infra/repositories/transformers/datasetTransformers.ts index 895efc86..a678ca70 100644 --- a/src/datasets/infra/repositories/transformers/datasetTransformers.ts +++ b/src/datasets/infra/repositories/transformers/datasetTransformers.ts @@ -5,6 +5,7 @@ import { DatasetMetadataBlock, DatasetMetadataSubField, DatasetMetadataFieldValue, + DatasetLicense, } from '../../../domain/models/Dataset'; import { AxiosResponse } from 'axios'; import TurndownService from 'turndown'; @@ -17,7 +18,7 @@ export const transformVersionResponseToDataset = (response: AxiosResponse): Data }; const transformVersionPayloadToDataset = (versionPayload: any): Dataset => { - return { + let datasetModel: Dataset = { id: versionPayload.datasetId, persistentId: versionPayload.datasetPersistentId, versionId: versionPayload.id, @@ -29,13 +30,23 @@ const transformVersionPayloadToDataset = (versionPayload: any): Dataset => { lastUpdateTime: new Date(versionPayload.lastUpdateTime), releaseTime: new Date(versionPayload.releaseTime), }, - license: { - name: versionPayload.license.name, - uri: versionPayload.license.uri, - iconUri: versionPayload.license.iconUri, - }, metadataBlocks: transformPayloadToDatasetMetadataBlocks(versionPayload.metadataBlocks), }; + if (versionPayload.hasOwnProperty('license')) { + datasetModel.license = transformPayloadToDatasetLicense(versionPayload.license); + } + return datasetModel; +}; + +const transformPayloadToDatasetLicense = (licensePayload: any): DatasetLicense => { + let datasetLicense: DatasetLicense = { + name: licensePayload.name, + uri: licensePayload.uri, + }; + if (licensePayload.hasOwnProperty('iconUri')) { + datasetLicense.iconUri = licensePayload.iconUri; + } + return datasetLicense; }; const transformPayloadToDatasetMetadataBlocks = (metadataBlocksPayload: any): DatasetMetadataBlock[] => { diff --git a/test/testHelpers/datasets/datasetHelper.ts b/test/testHelpers/datasets/datasetHelper.ts index bfdab5fb..c5116f04 100644 --- a/test/testHelpers/datasets/datasetHelper.ts +++ b/test/testHelpers/datasets/datasetHelper.ts @@ -1,4 +1,4 @@ -import { Dataset, DatasetVersionState } from '../../../src/datasets/domain/models/Dataset'; +import { Dataset, DatasetVersionState, DatasetLicense } from '../../../src/datasets/domain/models/Dataset'; import TurndownService from 'turndown'; const turndownService = new TurndownService(); @@ -10,8 +10,8 @@ const DATASET_RELEASE_TIME_STR = '2023-05-15T08:21:03Z'; const DATASET_HTML_DESCRIPTION = '

Title 1

Test paragraph 1

Test paragraph 2

Hello world

Title 2

Title 3

'; -export const createDatasetModel = (): Dataset => { - return { +export const createDatasetModel = (license?: DatasetLicense): Dataset => { + let datasetModel: Dataset = { id: 1, persistentId: 'doi:10.5072/FK2/HC6KTB', versionId: 19, @@ -23,11 +23,6 @@ export const createDatasetModel = (): Dataset => { lastUpdateTime: new Date(DATASET_UPDATE_TIME_STR), releaseTime: new Date(DATASET_RELEASE_TIME_STR), }, - license: { - name: 'CC0 1.0', - uri: 'https://creativecommons.org/publicdomain/zero/1.0/', - iconUri: 'https://licensebuttons.net/p/zero/1.0/88x31.png', - }, metadataBlocks: [ { name: 'citation', @@ -53,10 +48,14 @@ export const createDatasetModel = (): Dataset => { }, ], }; + if (license !== undefined) { + datasetModel.license = license; + } + return datasetModel; }; -export const createDatasetVersionPayload = (): any => { - return { +export const createDatasetVersionPayload = (license?: DatasetLicense): any => { + let datasetPayload: any = { id: 19, datasetId: 1, datasetPersistentId: 'doi:10.5072/FK2/HC6KTB', @@ -142,4 +141,19 @@ export const createDatasetVersionPayload = (): any => { }, files: [], }; + if (license !== undefined) { + datasetPayload.license = license; + } + return datasetPayload; +}; + +export const createDatasetLicenseModel = (withIconUri: boolean = true): DatasetLicense => { + let datasetLicense: DatasetLicense = { + name: 'CC0 1.0', + uri: 'https://creativecommons.org/publicdomain/zero/1.0/', + }; + if (withIconUri) { + datasetLicense.iconUri = 'https://licensebuttons.net/p/zero/1.0/88x31.png'; + } + return datasetLicense; }; diff --git a/test/unit/datasets/DatasetsRepository.test.ts b/test/unit/datasets/DatasetsRepository.test.ts index e3de50fc..8100bd66 100644 --- a/test/unit/datasets/DatasetsRepository.test.ts +++ b/test/unit/datasets/DatasetsRepository.test.ts @@ -4,7 +4,11 @@ import axios from 'axios'; import { expect } from 'chai'; import { ReadError } from '../../../src/core/domain/repositories/ReadError'; import { ApiConfig } from '../../../src/core/infra/repositories/ApiConfig'; -import { createDatasetModel, createDatasetVersionPayload } from '../../testHelpers/datasets/datasetHelper'; +import { + createDatasetModel, + createDatasetVersionPayload, + createDatasetLicenseModel, +} from '../../testHelpers/datasets/datasetHelper'; describe('DatasetsRepository', () => { const sandbox: SinonSandbox = createSandbox(); @@ -101,6 +105,50 @@ describe('DatasetsRepository', () => { assert.match(actual, testDatasetModel); }); + test('should return Dataset when providing id, version, and response with license is successful', async () => { + const testDatasetLicense = createDatasetLicenseModel(); + const testDatasetVersionWithLicenseSuccessfulResponse = { + data: { + status: 'OK', + data: createDatasetVersionPayload(testDatasetLicense), + }, + }; + const axiosGetStub = sandbox.stub(axios, 'get').resolves(testDatasetVersionWithLicenseSuccessfulResponse); + + const actual = await sut.getDatasetById(testDatasetModel.id, String(testDatasetModel.versionId)); + + assert.calledWithExactly( + axiosGetStub, + `${testApiUrl}/datasets/${testDatasetModel.id}/versions/${testDatasetModel.versionId}`, + { + withCredentials: true, + }, + ); + assert.match(actual, createDatasetModel(testDatasetLicense)); + }); + + test('should return Dataset when providing id, version, and response with license without icon URI is successful', async () => { + const testDatasetLicenseWithoutIconUri = createDatasetLicenseModel(false); + const testDatasetVersionWithLicenseSuccessfulResponse = { + data: { + status: 'OK', + data: createDatasetVersionPayload(testDatasetLicenseWithoutIconUri), + }, + }; + const axiosGetStub = sandbox.stub(axios, 'get').resolves(testDatasetVersionWithLicenseSuccessfulResponse); + + const actual = await sut.getDatasetById(testDatasetModel.id, String(testDatasetModel.versionId)); + + assert.calledWithExactly( + axiosGetStub, + `${testApiUrl}/datasets/${testDatasetModel.id}/versions/${testDatasetModel.versionId}`, + { + withCredentials: true, + }, + ); + assert.match(actual, createDatasetModel(testDatasetLicenseWithoutIconUri)); + }); + test('should return error on repository read error', async () => { const axiosGetStub = sandbox.stub(axios, 'get').rejects(testErrorResponse); From 95b20abcc34aba4574c6cd857160079099ee4d0e Mon Sep 17 00:00:00 2001 From: GPortas Date: Tue, 30 May 2023 17:28:10 +0100 Subject: [PATCH 29/30] Fixed: test:coverage by ignoring node_modules --- jest.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jest.config.js b/jest.config.js index 95fe9340..cbd89f75 100644 --- a/jest.config.js +++ b/jest.config.js @@ -5,5 +5,5 @@ module.exports = { }, testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.ts$', moduleFileExtensions: ['ts', 'js', 'json', 'node'], - coveragePathIgnorePatterns: ['testHelpers'], + coveragePathIgnorePatterns: ['node_modules', 'testHelpers'], }; From 28cea648fa7967eedac6f802b426ec7c806146c8 Mon Sep 17 00:00:00 2001 From: GPortas Date: Tue, 30 May 2023 17:30:01 +0100 Subject: [PATCH 30/30] Added: github registry mentioned in the README --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f5e007ff..0ade550a 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,9 @@ A JavaScript/TypeScript API wrapper for [Dataverse](http://guides.dataverse.org/ ## NPM -Module available as `js-dataverse` at https://www.npmjs.com/package/js-dataverse +A stable 1.x version of this package is available as `js-dataverse` at https://www.npmjs.com/package/js-dataverse + +An unstable 2.x version of this package with breaking changes is under development. Until a 2.0 version is officially released, it can be installed from https://github.com/IQSS/dataverse-client-javascript/pkgs/npm/dataverse-client-javascript ## Getting Started