From b47bebfa2ba270f6d218c7b4617e4cb54f17d235 Mon Sep 17 00:00:00 2001 From: Alexandre Rezende Date: Wed, 25 Jun 2025 12:12:30 -0300 Subject: [PATCH 1/5] gh*55 --- README.md | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index b952191..b4a3a56 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,5 @@ -

Material referente as entregas dos desafios no curso de Back-End na Coderhouse Brasil

- +

Descrição:


- -

Aulas com entrega:

- - -Acesse as aulas pelas branches +
+

06/05/2025 Desafio ( Documentar API ) da Aula 39 ( Documentação da API REST ) do curso de Back-end na CoderHouse; Turma #63515 de 2024/25.

+
\ No newline at end of file From 866d87947435b53add2a6fbefba108b3f3ce0c55 Mon Sep 17 00:00:00 2001 From: Alexandre Rezende Date: Wed, 25 Jun 2025 12:37:10 -0300 Subject: [PATCH 2/5] aula39 --- .gitignore | 33 ++ package.json | 7 + pnpm-lock.yaml | 827 +++++++++++++++++++++++++++++++++++ src/routes/cart.routes.js | 0 src/routes/product.routes.js | 0 swagger.js | 0 6 files changed, 867 insertions(+) create mode 100644 .gitignore create mode 100644 package.json create mode 100644 pnpm-lock.yaml create mode 100644 src/routes/cart.routes.js create mode 100644 src/routes/product.routes.js create mode 100644 swagger.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..95c5cc4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local +.env.example +.env +.env.local +.env.development.local +/.env.example \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..ff436c8 --- /dev/null +++ b/package.json @@ -0,0 +1,7 @@ +{ + "dependencies": { + "express": "^5.1.0", + "swagger-ui-express": "^5.0.1", + "swagger-jsdoc": "^6.2.8" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..edadaa5 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,827 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + express: + specifier: ^5.1.0 + version: 5.1.0 + swagger-jsdoc: + specifier: ^6.2.8 + version: 6.2.8(openapi-types@12.1.3) + swagger-ui-express: + specifier: ^5.0.1 + version: 5.0.1(express@5.1.0) + +packages: + + '@apidevtools/json-schema-ref-parser@9.1.2': + resolution: {integrity: sha512-r1w81DpR+KyRWd3f+rk6TNqMgedmAxZP5v5KWlXQWlgMUUtyEJch0DKEci1SorPMiSeM8XPl7MZ3miJ60JIpQg==} + + '@apidevtools/openapi-schemas@2.1.0': + resolution: {integrity: sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==} + engines: {node: '>=10'} + + '@apidevtools/swagger-methods@3.0.2': + resolution: {integrity: sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==} + + '@apidevtools/swagger-parser@10.0.3': + resolution: {integrity: sha512-sNiLY51vZOmSPFZA5TF35KZ2HbgYklQnTSDnkghamzLb3EkNtcQnrBQEj5AOCxHpTtXpqMCRM1CrmV2rG6nw4g==} + peerDependencies: + openapi-types: '>=7' + + '@jsdevtools/ono@7.1.3': + resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} + + '@scarf/scarf@1.4.0': + resolution: {integrity: sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + body-parser@2.2.0: + resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==} + engines: {node: '>=18'} + + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + call-me-maybe@1.0.2: + resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==} + + commander@6.2.0: + resolution: {integrity: sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==} + engines: {node: '>= 6'} + + commander@9.5.0: + resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} + engines: {node: ^12.20.0 || >=14} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + content-disposition@1.0.0: + resolution: {integrity: sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==} + engines: {node: '>= 0.6'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + cookie-signature@1.2.2: + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + + debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + express@5.1.0: + resolution: {integrity: sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==} + engines: {node: '>= 18'} + + finalhandler@2.1.0: + resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==} + engines: {node: '>= 0.8'} + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + glob@7.1.6: + resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==} + deprecated: Glob versions prior to v9 are no longer supported + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + lodash.get@4.4.2: + resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} + deprecated: This package is deprecated. Use the optional chaining (?.) operator instead. + + lodash.isequal@4.5.0: + resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} + deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead. + + lodash.mergewith@4.6.2: + resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + media-typer@1.1.0: + resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} + engines: {node: '>= 0.8'} + + merge-descriptors@2.0.0: + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} + + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + + mime-types@3.0.1: + resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} + engines: {node: '>= 0.6'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + openapi-types@12.1.3: + resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-to-regexp@8.2.0: + resolution: {integrity: sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==} + engines: {node: '>=16'} + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + + qs@6.14.0: + resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} + engines: {node: '>=0.6'} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + raw-body@3.0.0: + resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==} + engines: {node: '>= 0.8'} + + router@2.2.0: + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + send@1.2.0: + resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==} + engines: {node: '>= 18'} + + serve-static@2.2.0: + resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==} + engines: {node: '>= 18'} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + + swagger-jsdoc@6.2.8: + resolution: {integrity: sha512-VPvil1+JRpmJ55CgAtn8DIcpBs0bL5L3q5bVQvF4tAW/k/9JYSj7dCpaYCAv5rufe0vcCbBRQXGvzpkWjvLklQ==} + engines: {node: '>=12.0.0'} + hasBin: true + + swagger-parser@10.0.3: + resolution: {integrity: sha512-nF7oMeL4KypldrQhac8RyHerJeGPD1p2xDh900GPvc+Nk7nWP6jX2FcC7WmkinMoAmoO774+AFXcWsW8gMWEIg==} + engines: {node: '>=10'} + + swagger-ui-dist@5.25.2: + resolution: {integrity: sha512-V4JyoygUe5nCbn7bAD0fVKSC0yNcL3ROIQtGC7M0NATKuyosCSmMU6T0yDZIIuGpSxjsjZh/D2Ejb8lnF2jjxw==} + + swagger-ui-express@5.0.1: + resolution: {integrity: sha512-SrNU3RiBGTLLmFU8GIJdOdanJTl4TOmT27tt3bWWHppqYmAZ6IDuEuBvMU6nZq0zLEe6b/1rACXCgLZqO6ZfrA==} + engines: {node: '>= v0.10.32'} + peerDependencies: + express: '>=4.0.0 || >=5.0.0-beta' + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + type-is@2.0.1: + resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} + engines: {node: '>= 0.6'} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + validator@13.15.15: + resolution: {integrity: sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==} + engines: {node: '>= 0.10'} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + yaml@2.0.0-1: + resolution: {integrity: sha512-W7h5dEhywMKenDJh2iX/LABkbFnBxasD27oyXWDS/feDsxiw0dD5ncXdYXgkvAsXIY2MpW/ZKkr9IU30DBdMNQ==} + engines: {node: '>= 6'} + + z-schema@5.0.5: + resolution: {integrity: sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==} + engines: {node: '>=8.0.0'} + hasBin: true + +snapshots: + + '@apidevtools/json-schema-ref-parser@9.1.2': + dependencies: + '@jsdevtools/ono': 7.1.3 + '@types/json-schema': 7.0.15 + call-me-maybe: 1.0.2 + js-yaml: 4.1.0 + + '@apidevtools/openapi-schemas@2.1.0': {} + + '@apidevtools/swagger-methods@3.0.2': {} + + '@apidevtools/swagger-parser@10.0.3(openapi-types@12.1.3)': + dependencies: + '@apidevtools/json-schema-ref-parser': 9.1.2 + '@apidevtools/openapi-schemas': 2.1.0 + '@apidevtools/swagger-methods': 3.0.2 + '@jsdevtools/ono': 7.1.3 + call-me-maybe: 1.0.2 + openapi-types: 12.1.3 + z-schema: 5.0.5 + + '@jsdevtools/ono@7.1.3': {} + + '@scarf/scarf@1.4.0': {} + + '@types/json-schema@7.0.15': {} + + accepts@2.0.0: + dependencies: + mime-types: 3.0.1 + negotiator: 1.0.0 + + argparse@2.0.1: {} + + balanced-match@1.0.2: {} + + body-parser@2.2.0: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 4.4.1 + http-errors: 2.0.0 + iconv-lite: 0.6.3 + on-finished: 2.4.1 + qs: 6.14.0 + raw-body: 3.0.0 + type-is: 2.0.1 + transitivePeerDependencies: + - supports-color + + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + bytes@3.1.2: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + call-me-maybe@1.0.2: {} + + commander@6.2.0: {} + + commander@9.5.0: + optional: true + + concat-map@0.0.1: {} + + content-disposition@1.0.0: + dependencies: + safe-buffer: 5.2.1 + + content-type@1.0.5: {} + + cookie-signature@1.2.2: {} + + cookie@0.7.2: {} + + debug@4.4.1: + dependencies: + ms: 2.1.3 + + depd@2.0.0: {} + + doctrine@3.0.0: + dependencies: + esutils: 2.0.3 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + ee-first@1.1.1: {} + + encodeurl@2.0.0: {} + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + escape-html@1.0.3: {} + + esutils@2.0.3: {} + + etag@1.8.1: {} + + express@5.1.0: + dependencies: + accepts: 2.0.0 + body-parser: 2.2.0 + content-disposition: 1.0.0 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.2.2 + debug: 4.4.1 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.1.0 + fresh: 2.0.0 + http-errors: 2.0.0 + merge-descriptors: 2.0.0 + mime-types: 3.0.1 + on-finished: 2.4.1 + once: 1.4.0 + parseurl: 1.3.3 + proxy-addr: 2.0.7 + qs: 6.14.0 + range-parser: 1.2.1 + router: 2.2.0 + send: 1.2.0 + serve-static: 2.2.0 + statuses: 2.0.2 + type-is: 2.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + finalhandler@2.1.0: + dependencies: + debug: 4.4.1 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + forwarded@0.2.0: {} + + fresh@2.0.0: {} + + fs.realpath@1.0.0: {} + + function-bind@1.1.2: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + glob@7.1.6: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + + gopd@1.2.0: {} + + has-symbols@1.1.0: {} + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + http-errors@2.0.0: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + ipaddr.js@1.9.1: {} + + is-promise@4.0.0: {} + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + lodash.get@4.4.2: {} + + lodash.isequal@4.5.0: {} + + lodash.mergewith@4.6.2: {} + + math-intrinsics@1.1.0: {} + + media-typer@1.1.0: {} + + merge-descriptors@2.0.0: {} + + mime-db@1.54.0: {} + + mime-types@3.0.1: + dependencies: + mime-db: 1.54.0 + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.12 + + ms@2.1.3: {} + + negotiator@1.0.0: {} + + object-inspect@1.13.4: {} + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + openapi-types@12.1.3: {} + + parseurl@1.3.3: {} + + path-is-absolute@1.0.1: {} + + path-to-regexp@8.2.0: {} + + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + + qs@6.14.0: + dependencies: + side-channel: 1.1.0 + + range-parser@1.2.1: {} + + raw-body@3.0.0: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.6.3 + unpipe: 1.0.0 + + router@2.2.0: + dependencies: + debug: 4.4.1 + depd: 2.0.0 + is-promise: 4.0.0 + parseurl: 1.3.3 + path-to-regexp: 8.2.0 + transitivePeerDependencies: + - supports-color + + safe-buffer@5.2.1: {} + + safer-buffer@2.1.2: {} + + send@1.2.0: + dependencies: + debug: 4.4.1 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.0 + mime-types: 3.0.1 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + serve-static@2.2.0: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.0 + transitivePeerDependencies: + - supports-color + + setprototypeof@1.2.0: {} + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + statuses@2.0.1: {} + + statuses@2.0.2: {} + + swagger-jsdoc@6.2.8(openapi-types@12.1.3): + dependencies: + commander: 6.2.0 + doctrine: 3.0.0 + glob: 7.1.6 + lodash.mergewith: 4.6.2 + swagger-parser: 10.0.3(openapi-types@12.1.3) + yaml: 2.0.0-1 + transitivePeerDependencies: + - openapi-types + + swagger-parser@10.0.3(openapi-types@12.1.3): + dependencies: + '@apidevtools/swagger-parser': 10.0.3(openapi-types@12.1.3) + transitivePeerDependencies: + - openapi-types + + swagger-ui-dist@5.25.2: + dependencies: + '@scarf/scarf': 1.4.0 + + swagger-ui-express@5.0.1(express@5.1.0): + dependencies: + express: 5.1.0 + swagger-ui-dist: 5.25.2 + + toidentifier@1.0.1: {} + + type-is@2.0.1: + dependencies: + content-type: 1.0.5 + media-typer: 1.1.0 + mime-types: 3.0.1 + + unpipe@1.0.0: {} + + validator@13.15.15: {} + + vary@1.1.2: {} + + wrappy@1.0.2: {} + + yaml@2.0.0-1: {} + + z-schema@5.0.5: + dependencies: + lodash.get: 4.4.2 + lodash.isequal: 4.5.0 + validator: 13.15.15 + optionalDependencies: + commander: 9.5.0 diff --git a/src/routes/cart.routes.js b/src/routes/cart.routes.js new file mode 100644 index 0000000..e69de29 diff --git a/src/routes/product.routes.js b/src/routes/product.routes.js new file mode 100644 index 0000000..e69de29 diff --git a/swagger.js b/swagger.js new file mode 100644 index 0000000..e69de29 From c843763366bfb0e2bc517524fa4327e6251edc31 Mon Sep 17 00:00:00 2001 From: Alexandre Rezende Date: Wed, 25 Jun 2025 12:39:52 -0300 Subject: [PATCH 3/5] swagger config; (#001) --- src/app.js | 0 swagger.js | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 src/app.js diff --git a/src/app.js b/src/app.js new file mode 100644 index 0000000..e69de29 diff --git a/swagger.js b/swagger.js index e69de29..04f5523 100644 --- a/swagger.js +++ b/swagger.js @@ -0,0 +1,37 @@ +const swaggerJsdoc = require('swagger-jsdoc'); + +const options = { + definition: { + openapi: '3.0.0', + info: { + title: 'API de E-commerce', + version: '1.0.0', + description: 'API para gerenciamento de produtos e carrinhos de compras de uma loja virtual. Não documenta sessões.', + }, + servers: [ + { + url: 'http://localhost:3000/api/v1', + description: 'Servidor de Desenvolvimento', + }, + ], + // components: { + // securitySchemes: { + // bearerAuth: { + // type: 'http', + // scheme: 'bearer', + // bearerFormat: 'JWT', + // } + // } + // }, + // security: [ + // { + // bearerAuth: [] + // } + // ] + }, + apis: ['./src/routes/*.js'], +}; + +const swaggerSpec = swaggerJsdoc(options); + +module.exports = swaggerSpec; \ No newline at end of file From 2020dce9bdccce876914fb5ef396cda37c0c0865 Mon Sep 17 00:00:00 2001 From: Alexandre Rezende Date: Wed, 25 Jun 2025 12:41:19 -0300 Subject: [PATCH 4/5] components; (#002) --- src/routes/cart.routes.js | 281 +++++++++++++++++++++++++++++++++++ src/routes/product.routes.js | 231 ++++++++++++++++++++++++++++ 2 files changed, 512 insertions(+) diff --git a/src/routes/cart.routes.js b/src/routes/cart.routes.js index e69de29..64063b9 100644 --- a/src/routes/cart.routes.js +++ b/src/routes/cart.routes.js @@ -0,0 +1,281 @@ +const express = require('express'); +const router = express.Router(); + +// Simulação de banco de dados de carrinhos +let carts = {}; // { userId: { userId: '...', items: [{ productId, name, quantity, price }], totalPrice: ... } } + +// Helper para encontrar produto (simulação) +const findProductById = (id) => { + // Em um cenário real, você buscaria isso em um serviço de produtos ou DB + const products = [ + { id: 'prod_001', name: 'Smartphone X', price: 2500.00 }, + { id: 'prod_002', name: 'Fone de Ouvido Bluetooth', price: 350.00 }, + { id: 'prod_003', name: 'Tablet Pro', price: 1800.00 } + ]; + return products.find(p => p.id === id); +}; + +/** + * @swagger + * /cart/{userId}: + * get: + * summary: Retorna o carrinho de compras de um usuário. + * description: Permite buscar o carrinho de compras associado a um ID de usuário específico. Se o carrinho não existir, pode ser criado um novo (se a lógica da API permitir) ou retornada uma resposta de "não encontrado". + * tags: + * - Carrinhos + * parameters: + * - in: path + * name: userId + * required: true + * schema: + * type: string + * description: O ID único do usuário. + * example: user_abc + * responses: + * 200: + * description: Carrinho retornado com sucesso. + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Cart' + * 404: + * description: Carrinho para o usuário não encontrado. + * content: + * application/json: + * schema: + * type: object + * properties: + * error: + * type: string + * example: Carrinho para o usuário 'user_xyz' não encontrado. + */ +router.get('/cart/:userId', (req, res) => { + const { userId } = req.params; + const cart = carts[userId]; + if (cart) { + res.json(cart); + } else { + // Em um cenário real, você pode criar um carrinho vazio aqui ou retornar 404 + res.status(404).json({ error: `Carrinho para o usuário '${userId}' não encontrado.` }); + } +}); + +/** + * @swagger + * /cart/{userId}/items: + * post: + * summary: Adiciona um item ao carrinho de um usuário. + * description: Adiciona uma determinada quantidade de um produto ao carrinho de compras do usuário. Se o produto já estiver no carrinho, a quantidade será atualizada. + * tags: + * - Carrinhos + * parameters: + * - in: path + * name: userId + * required: true + * schema: + * type: string + * description: O ID único do usuário. + * example: user_abc + * requestBody: + * required: true + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/CartItemInput' + * example: + * productId: prod_003 + * quantity: 1 + * responses: + * 200: + * description: Item adicionado/atualizado no carrinho com sucesso. + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Cart' + * 400: + * description: Dados de entrada inválidos ou quantidade inválida. + * content: + * application/json: + * schema: + * type: object + * properties: + * error: + * type: string + * example: Quantidade inválida para o produto 'prod_003'. + * 404: + * description: Produto não encontrado ou usuário não existe para criar carrinho. + * content: + * application/json: + * schema: + * type: object + * properties: + * error: + * type: string + * example: Produto com ID 'prod_999' não encontrado. + */ +router.post('/cart/:userId/items', (req, res) => { + const { userId } = req.params; + const { productId, quantity } = req.body; + + if (!productId || quantity === undefined || quantity <= 0) { + return res.status(400).json({ error: 'productId e quantity (maior que zero) são obrigatórios.' }); + } + + const product = findProductById(productId); + if (!product) { + return res.status(404).json({ error: `Produto com ID '${productId}' não encontrado.` }); + } + + if (!carts[userId]) { + carts[userId] = { userId: userId, items: [], totalPrice: 0 }; + } + + const cart = carts[userId]; + const existingItemIndex = cart.items.findIndex(item => item.productId === productId); + + if (existingItemIndex > -1) { + cart.items[existingItemIndex].quantity += quantity; + } else { + cart.items.push({ + productId: product.id, + name: product.name, + quantity: quantity, + price: product.price + }); + } + + cart.totalPrice = cart.items.reduce((sum, item) => sum + (item.quantity * item.price), 0); + res.status(200).json(cart); +}); + +/** + * @swagger + * /cart/{userId}/items/{productId}: + * delete: + * summary: Remove um item específico do carrinho de um usuário. + * description: Remove um produto completamente do carrinho de compras do usuário. + * tags: + * - Carrinhos + * parameters: + * - in: path + * name: userId + * required: true + * schema: + * type: string + * description: O ID único do usuário. + * example: user_abc + * - in: path + * name: productId + * required: true + * schema: + * type: string + * description: O ID único do produto a ser removido. + * example: prod_002 + * responses: + * 200: + * description: Item removido do carrinho com sucesso. + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * example: Produto 'prod_002' removido do carrinho do usuário 'user_abc'. + * 404: + * description: Carrinho ou item não encontrado. + * content: + * application/json: + * schema: + * type: object + * properties: + * error: + * type: string + * example: Produto 'prod_999' não encontrado no carrinho do usuário 'user_abc'. + */ +router.delete('/cart/:userId/items/:productId', (req, res) => { + const { userId, productId } = req.params; + const cart = carts[userId]; + + if (!cart) { + return res.status(404).json({ error: `Carrinho para o usuário '${userId}' não encontrado.` }); + } + + const initialLength = cart.items.length; + cart.items = cart.items.filter(item => item.productId !== productId); + + if (cart.items.length === initialLength) { + return res.status(404).json({ error: `Produto '${productId}' não encontrado no carrinho do usuário '${userId}'.` }); + } + + cart.totalPrice = cart.items.reduce((sum, item) => sum + (item.quantity * item.price), 0); + res.status(200).json({ message: `Produto '${productId}' removido do carrinho do usuário '${userId}'.` }); +}); + +// Definições de Schemas (Modelos de Dados) para reutilização na documentação +/** + * @swagger + * components: + * schemas: + * CartItem: + * type: object + * required: + * - productId + * - name + * - quantity + * - price + * properties: + * productId: + * type: string + * description: O ID único do produto. + * example: prod_001 + * name: + * type: string + * description: O nome do produto. + * example: Smartphone X + * quantity: + * type: integer + * description: A quantidade do produto no carrinho. + * example: 1 + * price: + * type: number + * format: float + * description: O preço unitário do produto no momento da adição. + * example: 2500.00 + * Cart: + * type: object + * required: + * - userId + * - items + * - totalPrice + * properties: + * userId: + * type: string + * description: O ID único do usuário. + * example: user_abc + * items: + * type: array + * items: + * $ref: '#/components/schemas/CartItem' + * description: Lista de itens no carrinho. + * totalPrice: + * type: number + * format: float + * description: O preço total do carrinho. + * example: 3200.00 + * CartItemInput: + * type: object + * required: + * - productId + * - quantity + * properties: + * productId: + * type: string + * description: O ID do produto a ser adicionado ou atualizado. + * example: prod_003 + * quantity: + * type: integer + * description: A quantidade do produto a ser adicionada. + * example: 1 + */ +module.exports = router; \ No newline at end of file diff --git a/src/routes/product.routes.js b/src/routes/product.routes.js index e69de29..e216dc0 100644 --- a/src/routes/product.routes.js +++ b/src/routes/product.routes.js @@ -0,0 +1,231 @@ +const express = require('express'); +const router = express.Router(); + +// Simulação de banco de dados de produtos +let products = [ + { id: 'prod_001', name: 'Smartphone X', description: 'Última geração de smartphone com câmera de alta resolução.', price: 2500.00, category: 'electronics', stock: 50 }, + { id: 'prod_002', name: 'Fone de Ouvido Bluetooth', description: 'Fone sem fio com cancelamento de ruído.', price: 350.00, category: 'electronics', stock: 120 } +]; + +/** + * @swagger + * /products: + * get: + * summary: Retorna uma lista de todos os produtos. + * description: Este endpoint permite obter uma lista completa de todos os produtos disponíveis no catálogo da loja, com opção de filtragem por categoria e paginação. + * tags: + * - Produtos + * parameters: + * - in: query + * name: category + * schema: + * type: string + * description: Filtra produtos por categoria. + * example: electronics + * - in: query + * name: limit + * schema: + * type: integer + * description: Define o número máximo de produtos a serem retornados. + * example: 10 + * responses: + * 200: + * description: Lista de produtos retornada com sucesso. + * content: + * application/json: + * schema: + * type: array + * items: + * $ref: '#/components/schemas/Product' + * 500: + * description: Erro interno no servidor. + * content: + * application/json: + * schema: + * type: object + * properties: + * error: + * type: string + * example: Ocorreu um erro inesperado. + */ +router.get('/products', (req, res) => { + const { category, limit } = req.query; + let filteredProducts = products; + + if (category) { + filteredProducts = filteredProducts.filter(p => p.category === category); + } + if (limit) { + filteredProducts = filteredProducts.slice(0, parseInt(limit)); + } + res.json(filteredProducts); +}); + +/** + * @swagger + * /products/{id}: + * get: + * summary: Retorna os detalhes de um produto específico. + * description: Permite buscar os detalhes de um produto usando seu ID único. + * tags: + * - Produtos + * parameters: + * - in: path + * name: id + * required: true + * schema: + * type: string + * description: O ID único do produto. + * example: prod_001 + * responses: + * 200: + * description: Detalhes do produto retornados com sucesso. + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Product' + * 404: + * description: Produto não encontrado. + * content: + * application/json: + * schema: + * type: object + * properties: + * error: + * type: string + * example: Produto com o ID 'prod_999' não encontrado. + */ +router.get('/products/:id', (req, res) => { + const { id } = req.params; + const product = products.find(p => p.id === id); + if (product) { + res.json(product); + } else { + res.status(404).json({ error: `Produto com o ID '${id}' não encontrado.` }); + } +}); + +/** + * @swagger + * /products: + * post: + * summary: Adiciona um novo produto ao catálogo. + * description: Cria um novo registro de produto com as informações fornecidas. + * tags: + * - Produtos + * requestBody: + * required: true + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/ProductInput' + * example: + * name: Tablet Pro + * description: Tablet de alta performance com tela de 12 polegadas. + * price: 1800.00 + * category: electronics + * stock: 30 + * responses: + * 201: + * description: Produto criado com sucesso. + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Product' + * 400: + * description: Dados de entrada inválidos. + * content: + * application/json: + * schema: + * type: object + * properties: + * error: + * type: string + * example: Nome do produto é obrigatório. + */ +router.post('/products', (req, res) => { + const { name, description, price, category, stock } = req.body; + if (!name || !price || !category || stock === undefined) { + return res.status(400).json({ error: 'Nome, preço, categoria e estoque são obrigatórios.' }); + } + const newProduct = { + id: `prod_${(products.length + 1).toString().padStart(3, '0')}`, + name, + description, + price, + category, + stock + }; + products.push(newProduct); + res.status(201).json(newProduct); +}); + +/** + * @swagger + * components: + * schemas: + * Product: + * type: object + * required: + * - id + * - name + * - price + * - category + * - stock + * properties: + * id: + * type: string + * description: O ID único do produto. + * example: prod_001 + * name: + * type: string + * description: O nome do produto. + * example: Smartphone X + * description: + * type: string + * description: Descrição detalhada do produto. + * example: Última geração de smartphone com câmera de alta resolução. + * price: + * type: number + * format: float + * description: O preço do produto. + * example: 2500.00 + * category: + * type: string + * description: A categoria do produto. + * example: electronics + * stock: + * type: integer + * description: A quantidade em estoque do produto. + * example: 50 + * ProductInput: + * type: object + * required: + * - name + * - price + * - category + * - stock + * properties: + * name: + * type: string + * description: O nome do produto. + * example: Tablet Pro + * description: + * type: string + * description: Descrição detalhada do produto. + * example: Tablet de alta performance com tela de 12 polegadas. + * price: + * type: number + * format: float + * description: O preço do produto. + * example: 1800.00 + * category: + * type: string + * description: A categoria do produto. + * example: electronics + * stock: + * type: integer + * description: A quantidade em estoque do produto. + * example: 30 + */ +module.exports = router; \ No newline at end of file From 38da04adb02a1c3300d9437bbccfe00d536e56a0 Mon Sep 17 00:00:00 2001 From: Alexandre Rezende Date: Wed, 25 Jun 2025 12:42:32 -0300 Subject: [PATCH 5/5] MAIN@AleRzendee --- src/app.js | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/app.js b/src/app.js index e69de29..c00f923 100644 --- a/src/app.js +++ b/src/app.js @@ -0,0 +1,27 @@ +const express = require('express'); +const productRoutes = require('./routes/product.routes'); +const cartRoutes = require('./routes/cart.routes'); +const swaggerUi = require('swagger-ui-express'); +const swaggerSpec = require('../swagger'); + +const app = express(); +const PORT = process.env.PORT || 3000; + +app.use(express.json()); + +//! Rotas da API +app.use('/api/v1', productRoutes); +app.use('/api/v1', cartRoutes); + +//! Configuração do Swagger UI +app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec)); // A documentação estará disponível em http://localhost:3000/api-docs + +//! Rota de teste simples +app.get('/', (req, res) => { + res.send('API de E-commerce está funcionando! Acesse /api-docs para a documentação.'); +}); + +app.listen(PORT, () => { + console.log(`Servidor rodando na porta ${PORT}`); + console.log(`Documentação Swagger disponível em http://localhost:${PORT}/api-docs`); +}); \ No newline at end of file