diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..8dc6807 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,21 @@ +{ + "rules": { + "no-console": "off", + "indent": [ "error", 2 ], + "quotes": [ "error", "single" ], + "semi": ["error", "always"], + "linebreak-style": [ "error", "unix" ] + }, + "env": { + "es6": true, + "node": true, + "mocha": true, + "jasmine": true + }, + "ecmaFeatures": { + "modules": true, + "experimentalObjectRestSpread": true, + "impliedStrict": true + }, + "extends": "eslint:recommended" +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..345130c --- /dev/null +++ b/.gitignore @@ -0,0 +1,136 @@ +# Created by https://www.gitignore.io/api/osx,vim,node,macos,windows + +### macOS ### +*.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + + +### OSX ### + +# Icon must end with two \r + +# Thumbnails + +# Files that might appear in the root of a volume + +# Directories potentially created on remote AFP share + +### Vim ### +# swap +[._]*.s[a-v][a-z] +[._]*.sw[a-p] +[._]s[a-v][a-z] +[._]sw[a-p] +# session +Session.vim +# temporary +.netrwhist +*~ +# auto-generated tag files +tags + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.gitignore.io/api/osx,vim,node,macos,windows diff --git a/README.md b/README.md index feb511f..0852e4e 100644 --- a/README.md +++ b/README.md @@ -1,53 +1,38 @@ -![cf](https://i.imgur.com/7v5ASc8.png) 11: Single Resource Express API -====== - -## Submission Instructions - * fork this repository & create a new branch for your work - * write all of your code in a directory named `lab-` + `` **e.g.** `lab-susan` - * push to your repository - * submit a pull request to this repository - * submit a link to your PR in canvas - * write a question and observation on canvas - -## Learning Objectives -* students will be able to create a single resource API using the express framework -* students will be able to leverage 3rd party helper modules for debugging, logging, and handling errors - -## Requirements - -#### Configuration -* `package.json` -* `.eslintrc` -* `.gitignore` -* `README.md` - * your `README.md` should include detailed instructions on how to use your API - -#### Feature Tasks -* create an HTTP server using `express` -* create a object constructor that creates a _simple resource_ with at least 3 properties - * it can **not** have the same properties as the in-class sample code (other than the `id`) - * a unique `id` property should be included *(node-uuid)* - * include two additional properties of your choice -* use the JSON parser included with the `body-parser` module as a middleware component to parse the request body on `POST` and `PUT` routes -* use the npm `debug` module to log the methods in your application -* create an `npm` script to automate the `debug` process and start the server -* persist your API data using the storage module and file system persistence - -#### Server Endpoints -* **`/api/simple-resource-name`** -* `POST` request - * pass data as stringifed JSON in the body of a **POST** request to create a new resource -* `GET` request - * pass `?id=` as a query string parameter to retrieve a specific resource (as JSON) -* `DELETE` request - * pass `?id=` in the query string to **DELETE** a specific resource - * this should return a 204 status code with no content in the body - -#### Tests -* write a test to ensure that your api returns a status code of 404 for routes that have not been registered -* write tests to ensure the `/api/simple-resource-name` endpoint responds as described for each condition below: - * `GET`: test 404, it should respond with 'not found' for valid requests made with an id that was not found - * `GET`: test 400, it should respond with 'bad request' if no id was provided in the request - * `GET`: test 200, it should contain a response body for a request made with a valid id - * `POST`: test 400, it should respond with 'bad request' if no request body was provided or the body was invalid - * `POST`: test 200, it should respond with the body content for a post request with a valid body +#Overview + +* This RESTful API provides the back-end infrastructure and funtionality to create, read, update, and delete data relative to superheroes. + +#Architecture + +

This API is structured on a Model View Controller(MVC) architectue pattern. The basic technologies are: node.js server, node.http module, our own "express like" middleware.

+ +

Middleware:

+ +* We wrote a vanilla version of our own router that handles the base routing. + +* We wrote a vanilla version of a body-parser module that parses JSON data. + +#API endpoints + +##POST /api/superhero + +

Required Data:

+ +* Provide superhero name, the comic universe where they are from as JSON. + +

This route will create a new superhero by providing a superhero name and a comic book universe from which they reside in the body of the request.

+ +##GET /api/superhero + +

Required Data:

+ +* Provide a specific unique superhero id. + +

This route will require a unique superhero id and grab the specific JSON object associated with that id.

+ +#Testing + +* Testing Framework mocha test runner +* chai(expect) +* bluebird promise library +* eslint diff --git a/data/superhero/04d9f4f9-3eda-4a4d-9d12-7e53e1296030.json b/data/superhero/04d9f4f9-3eda-4a4d-9d12-7e53e1296030.json new file mode 100644 index 0000000..8541e75 --- /dev/null +++ b/data/superhero/04d9f4f9-3eda-4a4d-9d12-7e53e1296030.json @@ -0,0 +1 @@ +{"id":"04d9f4f9-3eda-4a4d-9d12-7e53e1296030","name":"name","comicUni":"comicUni"} \ No newline at end of file diff --git a/data/superhero/0c63b92a-4e9e-460b-869e-60a6db4f4376.json b/data/superhero/0c63b92a-4e9e-460b-869e-60a6db4f4376.json new file mode 100644 index 0000000..1430503 --- /dev/null +++ b/data/superhero/0c63b92a-4e9e-460b-869e-60a6db4f4376.json @@ -0,0 +1 @@ +{"id":"0c63b92a-4e9e-460b-869e-60a6db4f4376","name":"name","comicUni":"comicUni"} \ No newline at end of file diff --git a/data/superhero/0ff87c8a-1b91-4d6c-8cd9-9519bf8ed854.json b/data/superhero/0ff87c8a-1b91-4d6c-8cd9-9519bf8ed854.json new file mode 100644 index 0000000..d6ef5ca --- /dev/null +++ b/data/superhero/0ff87c8a-1b91-4d6c-8cd9-9519bf8ed854.json @@ -0,0 +1 @@ +{"id":"0ff87c8a-1b91-4d6c-8cd9-9519bf8ed854","name":"name","comicUni":"comicUni"} \ No newline at end of file diff --git a/data/superhero/29f74f26-995f-41ee-876d-185d12c6a0a5.json b/data/superhero/29f74f26-995f-41ee-876d-185d12c6a0a5.json new file mode 100644 index 0000000..933a12f --- /dev/null +++ b/data/superhero/29f74f26-995f-41ee-876d-185d12c6a0a5.json @@ -0,0 +1 @@ +{"id":"29f74f26-995f-41ee-876d-185d12c6a0a5","name":"name","comicUni":"comicUni"} \ No newline at end of file diff --git a/data/superhero/2fad148c-6270-4f9c-91c3-98298816516e.json b/data/superhero/2fad148c-6270-4f9c-91c3-98298816516e.json new file mode 100644 index 0000000..b9ea41d --- /dev/null +++ b/data/superhero/2fad148c-6270-4f9c-91c3-98298816516e.json @@ -0,0 +1 @@ +{"id":"2fad148c-6270-4f9c-91c3-98298816516e","name":"name","comicUni":"comicUni"} \ No newline at end of file diff --git a/data/superhero/30ae2331-481d-48c5-a969-ceece81f92b6.json b/data/superhero/30ae2331-481d-48c5-a969-ceece81f92b6.json new file mode 100644 index 0000000..8f33fe2 --- /dev/null +++ b/data/superhero/30ae2331-481d-48c5-a969-ceece81f92b6.json @@ -0,0 +1 @@ +{"id":"30ae2331-481d-48c5-a969-ceece81f92b6","name":"name","comicUni":"comicUni"} \ No newline at end of file diff --git a/data/superhero/3997b53b-30e4-415e-b5e5-411a80890b57.json b/data/superhero/3997b53b-30e4-415e-b5e5-411a80890b57.json new file mode 100644 index 0000000..86dbbc0 --- /dev/null +++ b/data/superhero/3997b53b-30e4-415e-b5e5-411a80890b57.json @@ -0,0 +1 @@ +{"id":"3997b53b-30e4-415e-b5e5-411a80890b57","name":"name","comicUni":"comicUni"} \ No newline at end of file diff --git a/data/superhero/41053efe-70bb-4b45-91a1-2568dcd7c275.json b/data/superhero/41053efe-70bb-4b45-91a1-2568dcd7c275.json new file mode 100644 index 0000000..5ecf279 --- /dev/null +++ b/data/superhero/41053efe-70bb-4b45-91a1-2568dcd7c275.json @@ -0,0 +1 @@ +{"id":"41053efe-70bb-4b45-91a1-2568dcd7c275","name":"name","comicUni":"comicUni"} \ No newline at end of file diff --git a/data/superhero/64e8a01b-c71a-4a61-9ad1-2e814f65f48d.json b/data/superhero/64e8a01b-c71a-4a61-9ad1-2e814f65f48d.json new file mode 100644 index 0000000..7d90988 --- /dev/null +++ b/data/superhero/64e8a01b-c71a-4a61-9ad1-2e814f65f48d.json @@ -0,0 +1 @@ +{"id":"64e8a01b-c71a-4a61-9ad1-2e814f65f48d","name":"name","comicUni":"comicUni"} \ No newline at end of file diff --git a/data/superhero/650f9531-8614-4d95-9a7f-7aed5488f060.json b/data/superhero/650f9531-8614-4d95-9a7f-7aed5488f060.json new file mode 100644 index 0000000..65267b5 --- /dev/null +++ b/data/superhero/650f9531-8614-4d95-9a7f-7aed5488f060.json @@ -0,0 +1 @@ +{"id":"650f9531-8614-4d95-9a7f-7aed5488f060","name":"name","comicUni":"comicUni"} \ No newline at end of file diff --git a/data/superhero/71cb0b3d-dd06-4785-a143-9d749d740184.json b/data/superhero/71cb0b3d-dd06-4785-a143-9d749d740184.json new file mode 100644 index 0000000..0d72015 --- /dev/null +++ b/data/superhero/71cb0b3d-dd06-4785-a143-9d749d740184.json @@ -0,0 +1 @@ +{"id":"71cb0b3d-dd06-4785-a143-9d749d740184","name":"name","comicUni":"comicUni"} \ No newline at end of file diff --git a/data/superhero/772d3f92-9b38-4b2c-8f77-8d35eea7a7ab.json b/data/superhero/772d3f92-9b38-4b2c-8f77-8d35eea7a7ab.json new file mode 100644 index 0000000..4d644c8 --- /dev/null +++ b/data/superhero/772d3f92-9b38-4b2c-8f77-8d35eea7a7ab.json @@ -0,0 +1 @@ +{"id":"772d3f92-9b38-4b2c-8f77-8d35eea7a7ab","name":"name","comicUni":"comicUni"} \ No newline at end of file diff --git a/data/superhero/7d2ce11c-d782-4b07-893e-e53d4b5b1e11.json b/data/superhero/7d2ce11c-d782-4b07-893e-e53d4b5b1e11.json new file mode 100644 index 0000000..ab58c0c --- /dev/null +++ b/data/superhero/7d2ce11c-d782-4b07-893e-e53d4b5b1e11.json @@ -0,0 +1 @@ +{"id":"7d2ce11c-d782-4b07-893e-e53d4b5b1e11","name":"name","comicUni":"comicUni"} \ No newline at end of file diff --git a/data/superhero/83f9c5ff-414f-43fc-bd72-0933461a78cf.json b/data/superhero/83f9c5ff-414f-43fc-bd72-0933461a78cf.json new file mode 100644 index 0000000..204ca44 --- /dev/null +++ b/data/superhero/83f9c5ff-414f-43fc-bd72-0933461a78cf.json @@ -0,0 +1 @@ +{"id":"83f9c5ff-414f-43fc-bd72-0933461a78cf","name":"name","comicUni":"comicUni"} \ No newline at end of file diff --git a/data/superhero/8989cf0e-7039-45c6-818f-9ded0e6128d0.json b/data/superhero/8989cf0e-7039-45c6-818f-9ded0e6128d0.json new file mode 100644 index 0000000..18e7264 --- /dev/null +++ b/data/superhero/8989cf0e-7039-45c6-818f-9ded0e6128d0.json @@ -0,0 +1 @@ +{"id":"8989cf0e-7039-45c6-818f-9ded0e6128d0","name":"name","comicUni":"comicUni"} \ No newline at end of file diff --git a/data/superhero/9816731e-a4e5-4802-97f6-ae57d265620a.json b/data/superhero/9816731e-a4e5-4802-97f6-ae57d265620a.json new file mode 100644 index 0000000..fc4df2a --- /dev/null +++ b/data/superhero/9816731e-a4e5-4802-97f6-ae57d265620a.json @@ -0,0 +1 @@ +{"id":"9816731e-a4e5-4802-97f6-ae57d265620a","name":"name","comicUni":"comicUni"} \ No newline at end of file diff --git a/data/superhero/9c0837ef-f9ba-4a25-8778-93b8e0d4859b.json b/data/superhero/9c0837ef-f9ba-4a25-8778-93b8e0d4859b.json new file mode 100644 index 0000000..b79411d --- /dev/null +++ b/data/superhero/9c0837ef-f9ba-4a25-8778-93b8e0d4859b.json @@ -0,0 +1 @@ +{"id":"9c0837ef-f9ba-4a25-8778-93b8e0d4859b","name":"name","comicUni":"comicUni"} \ No newline at end of file diff --git a/data/superhero/a1fc937b-559f-45d6-be36-7fbb61c00735.json b/data/superhero/a1fc937b-559f-45d6-be36-7fbb61c00735.json new file mode 100644 index 0000000..33b0765 --- /dev/null +++ b/data/superhero/a1fc937b-559f-45d6-be36-7fbb61c00735.json @@ -0,0 +1 @@ +{"id":"a1fc937b-559f-45d6-be36-7fbb61c00735","name":"name","comicUni":"comicUni"} \ No newline at end of file diff --git a/data/superhero/a73f04c2-5d4b-44dd-a390-b675daa7e314.json b/data/superhero/a73f04c2-5d4b-44dd-a390-b675daa7e314.json new file mode 100644 index 0000000..9ec8c20 --- /dev/null +++ b/data/superhero/a73f04c2-5d4b-44dd-a390-b675daa7e314.json @@ -0,0 +1 @@ +{"id":"a73f04c2-5d4b-44dd-a390-b675daa7e314","name":"name","comicUni":"comicUni"} \ No newline at end of file diff --git a/data/superhero/b1e296d2-d8a2-4530-979d-99c6328a7f02.json b/data/superhero/b1e296d2-d8a2-4530-979d-99c6328a7f02.json new file mode 100644 index 0000000..5823bc6 --- /dev/null +++ b/data/superhero/b1e296d2-d8a2-4530-979d-99c6328a7f02.json @@ -0,0 +1 @@ +{"id":"b1e296d2-d8a2-4530-979d-99c6328a7f02","name":"name","comicUni":"comicUni"} \ No newline at end of file diff --git a/data/superhero/b523b4fb-a225-4fd2-8d91-e3ba3d4752f9.json b/data/superhero/b523b4fb-a225-4fd2-8d91-e3ba3d4752f9.json new file mode 100644 index 0000000..f511510 --- /dev/null +++ b/data/superhero/b523b4fb-a225-4fd2-8d91-e3ba3d4752f9.json @@ -0,0 +1 @@ +{"id":"b523b4fb-a225-4fd2-8d91-e3ba3d4752f9","name":"name","comicUni":"comicUni"} \ No newline at end of file diff --git a/data/superhero/baa30cb6-83a0-4d0b-a41e-d6800f7ce34d.json b/data/superhero/baa30cb6-83a0-4d0b-a41e-d6800f7ce34d.json new file mode 100644 index 0000000..4085c42 --- /dev/null +++ b/data/superhero/baa30cb6-83a0-4d0b-a41e-d6800f7ce34d.json @@ -0,0 +1 @@ +{"id":"baa30cb6-83a0-4d0b-a41e-d6800f7ce34d","name":"name","comicUni":"comicUni"} \ No newline at end of file diff --git a/data/superhero/c31d8a7e-4d79-4486-a202-2f0532c90339.json b/data/superhero/c31d8a7e-4d79-4486-a202-2f0532c90339.json new file mode 100644 index 0000000..931fdcd --- /dev/null +++ b/data/superhero/c31d8a7e-4d79-4486-a202-2f0532c90339.json @@ -0,0 +1 @@ +{"id":"c31d8a7e-4d79-4486-a202-2f0532c90339","name":"name","comicUni":"comicUni"} \ No newline at end of file diff --git a/data/superhero/d875e38c-8b3a-4f25-a1f7-26db117fa2af.json b/data/superhero/d875e38c-8b3a-4f25-a1f7-26db117fa2af.json new file mode 100644 index 0000000..1d0e26f --- /dev/null +++ b/data/superhero/d875e38c-8b3a-4f25-a1f7-26db117fa2af.json @@ -0,0 +1 @@ +{"id":"d875e38c-8b3a-4f25-a1f7-26db117fa2af","name":"name","comicUni":"comicUni"} \ No newline at end of file diff --git a/data/superhero/d8da8660-677e-442c-a330-3e4b132aebb0.json b/data/superhero/d8da8660-677e-442c-a330-3e4b132aebb0.json new file mode 100644 index 0000000..10223f7 --- /dev/null +++ b/data/superhero/d8da8660-677e-442c-a330-3e4b132aebb0.json @@ -0,0 +1 @@ +{"id":"d8da8660-677e-442c-a330-3e4b132aebb0","name":"name","comicUni":"comicUni"} \ No newline at end of file diff --git a/data/superhero/dadd0c64-7022-41f3-be4c-f2ce83ec2f96.json b/data/superhero/dadd0c64-7022-41f3-be4c-f2ce83ec2f96.json new file mode 100644 index 0000000..dfb2c10 --- /dev/null +++ b/data/superhero/dadd0c64-7022-41f3-be4c-f2ce83ec2f96.json @@ -0,0 +1 @@ +{"id":"dadd0c64-7022-41f3-be4c-f2ce83ec2f96","name":"name","comicUni":"comicUni"} \ No newline at end of file diff --git a/data/superhero/db4026c4-603e-493c-a538-279060297dd1.json b/data/superhero/db4026c4-603e-493c-a538-279060297dd1.json new file mode 100644 index 0000000..7349c42 --- /dev/null +++ b/data/superhero/db4026c4-603e-493c-a538-279060297dd1.json @@ -0,0 +1 @@ +{"id":"db4026c4-603e-493c-a538-279060297dd1","name":"name","comicUni":"comicUni"} \ No newline at end of file diff --git a/data/superhero/e2a105fb-758c-4e28-8588-17ad9e32033a.json b/data/superhero/e2a105fb-758c-4e28-8588-17ad9e32033a.json new file mode 100644 index 0000000..966b3b3 --- /dev/null +++ b/data/superhero/e2a105fb-758c-4e28-8588-17ad9e32033a.json @@ -0,0 +1 @@ +{"id":"e2a105fb-758c-4e28-8588-17ad9e32033a","name":"name","comicUni":"comicUni"} \ No newline at end of file diff --git a/data/superhero/e98e4ca9-d402-4921-9097-c6c50dab666f.json b/data/superhero/e98e4ca9-d402-4921-9097-c6c50dab666f.json new file mode 100644 index 0000000..3cfa415 --- /dev/null +++ b/data/superhero/e98e4ca9-d402-4921-9097-c6c50dab666f.json @@ -0,0 +1 @@ +{"id":"e98e4ca9-d402-4921-9097-c6c50dab666f","name":"name","comicUni":"comicUni"} \ No newline at end of file diff --git a/data/superhero/eb9d8df1-4f8e-4de5-8fc2-17b4031c45cf.json b/data/superhero/eb9d8df1-4f8e-4de5-8fc2-17b4031c45cf.json new file mode 100644 index 0000000..86fb03d --- /dev/null +++ b/data/superhero/eb9d8df1-4f8e-4de5-8fc2-17b4031c45cf.json @@ -0,0 +1 @@ +{"id":"eb9d8df1-4f8e-4de5-8fc2-17b4031c45cf","name":"name","comicUni":"comicUni"} \ No newline at end of file diff --git a/data/superhero/f798dcfd-4171-49f6-a082-fe256dfce193.json b/data/superhero/f798dcfd-4171-49f6-a082-fe256dfce193.json new file mode 100644 index 0000000..f311494 --- /dev/null +++ b/data/superhero/f798dcfd-4171-49f6-a082-fe256dfce193.json @@ -0,0 +1 @@ +{"id":"f798dcfd-4171-49f6-a082-fe256dfce193","name":"name","comicUni":"comicUni"} \ No newline at end of file diff --git a/data/superhero/undefined.json b/data/superhero/undefined.json new file mode 100644 index 0000000..88bf035 --- /dev/null +++ b/data/superhero/undefined.json @@ -0,0 +1 @@ +{"message":"bad request"} \ No newline at end of file diff --git a/lib/storage.js b/lib/storage.js new file mode 100644 index 0000000..c2dab65 --- /dev/null +++ b/lib/storage.js @@ -0,0 +1,48 @@ +'use strict'; + +const Promise = require('bluebird'); +const fs = Promise.promisifyAll(require('fs'), {suffix: 'Prom'}); +const createError = require('http-errors'); +const debug = require('debug')('superhero:storage'); + +module.exports = exports = {}; + +exports.createItem = function(schemaName, item) { + debug('createItem'); + + if (!schemaName) return Promise.reject(createError(400, 'bad request')); + if (!item) return Promise.reject(createError(400, 'bad request')); + + let json = JSON.stringify(item); + return fs.writeFileProm(`${__dirname}/../data/${schemaName}/${item.id}.json`, json) + .then( () => item) + .catch( err => Promise.reject(err)); +}; + +exports.fetchItem = function(schemaName, id) { + debug('fetchItem'); + + if (!schemaName) return Promise.reject(createError(400, 'bad request')); + if (!id) return Promise.reject(createError(400, 'bad request')); + + return fs.readFileProm(`${__dirname}/../data/${schemaName}/${id}.json`) + .then( data => { + debug('then block'); + try { + let item = JSON.parse(data.toString()); + return item; + } catch (err) { + return Promise.reject(err); + } + }) + .catch( err => Promise.reject(createError(404, `${schemaName} not found`))); +}; + +exports.deleteItem = function(schemaName, id) { + debug('deleteItem'); + + if (!schemaName) return Promise.reject(new Error('expected schema name')); + if (!id) return Promise.reject(new Error('expected id')); + + return fs.unlinkProm(`${__dirname}/../data/${schemaName}/${id}.json`); +}; diff --git a/model/superhero.js b/model/superhero.js new file mode 100644 index 0000000..8798d7b --- /dev/null +++ b/model/superhero.js @@ -0,0 +1,37 @@ +'use strict'; + +const uuidv4 = require('uuid/v4'); +const createError = require('http-errors'); +const debug = require('debug')('superhero:superhero'); +const storage = require('../lib/storage.js'); + +const Superhero = module.exports = function(name, comicUni) { + debug('superhero constructor'); + + if (!name) throw createError(400, 'bad request'); + if (!comicUni) throw createError(400, 'bad request'); + this.id = uuidv4(); + this.name = name; + this.comicUni = comicUni; +}; + +Superhero.createSuperhero = function(_superhero) { + debug('createSuperhero'); + + try { + let superhero = new Superhero(_superhero.name, _superhero.comicUni); + return storage.createItem('superhero', superhero); + } catch (err) { + return Promise.reject(err); + } +}; + +Superhero.fetchSuperhero = function(id) { + debug('fetchSuperhero'); + return storage.fetchItem('superhero', id); +}; + +Superhero.deleteSuperhero = function(id) { + debug('deleteSuperhero'); + return storage.deleteItem('superhero', id); +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..e0bc8fb --- /dev/null +++ b/package.json @@ -0,0 +1,35 @@ +{ + "name": "11-express-api", + "version": "1.0.0", + "description": "![cf](https://i.imgur.com/7v5ASc8.png) 11: Single Resource Express API ======", + "main": "server.js", + "scripts": { + "test": "DEBUG='superhero*' mocha", + "start": "DEBUG='superhero*' node server.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/bretladenburg/11-express-api.git" + }, + "keywords": [], + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/bretladenburg/11-express-api/issues" + }, + "homepage": "https://github.com/bretladenburg/11-express-api#readme", + "dependencies": { + "bluebird": "^3.5.0", + "body-parser": "^1.17.2", + "debug": "^2.6.8", + "express": "^4.15.3", + "http-errors": "^1.6.1", + "morgan": "^1.8.2", + "uuid": "^3.1.0" + }, + "devDependencies": { + "chai": "^4.1.0", + "mocha": "^3.5.0", + "superagent": "^3.5.2" + } +} diff --git a/server.js b/server.js new file mode 100644 index 0000000..dafe129 --- /dev/null +++ b/server.js @@ -0,0 +1,64 @@ +'use strict'; + +const express = require('express'); +const morgan = require('morgan'); +const createError = require('http-errors'); +const jsonParser = require('body-parser').json(); +const debug = require('debug')('superhero:server'); +const Superhero = require('./model/superhero.js'); + +const PORT = process.env.PORT || 3000; +const app = express(); + +app.use(morgan('dev')); + +app.get('/test', function(req, res) { + debug('GET: /test'); + res.json({msg: 'hello from /test land'}); +}); + +app.post('/api/superhero', jsonParser, function(req, res, next) { + debug('POST: /api/superhero'); + + Superhero.createSuperhero(req.body) + .then( superhero => { + res.json(superhero); + }) + + .catch( err => { + next(err); + }); +}); + +app.get('/api/superhero', function(req, res, next) { + debug('GET: /api/superhero'); + + Superhero.fetchSuperhero(req.query.id) + .then( superhero => res.json(superhero)) + .catch( err => next(err)); +}); + +app.delete('/api/superhero', function(req, res, next) { + debug('DELETE: /api/superhero'); + + Superhero.deleteSuperhero(req.query.id) + .then( superhero => res.json(superhero)) + .catch( err => next(err)); +}); + +app.use(function(err, req, res, next) { + debug('error middleware'); + + + if (err.status) { + res.status(err.status).send(err.message); + return; + } + + err = createError(500, err.message); + res.status(err.status).send(err.name); +}); + +app.listen(PORT, () => { + debug('server up:', PORT); +}); diff --git a/test/superhero-route-test.js b/test/superhero-route-test.js new file mode 100644 index 0000000..1198faf --- /dev/null +++ b/test/superhero-route-test.js @@ -0,0 +1,66 @@ + +const request = require('superagent'); +const expect = require('chai').expect; +const PORT = process.env.PORT || 3000; + +require('../server.js'); + +describe('Superhero Routes', function() { + var superhero = null; + + describe('POST: /api/superhero', function() { + it('should return a superhero', function(done) { + request.post(`localhost:${PORT}/api/superhero`) + .send({name: 'name', comicUni: 'comicUni'}) + .end((err, res) => { + if (err) return done(err); + expect(res.status).to.equal(200); + expect(res.body.name).to.equal('name'); + expect(res.body.comicUni).to.equal('comicUni'); + superhero = res.body; + done(); + }); + }); + + it('should return a bad request', function(done) { + request.post(`localhost:${PORT}/api/superhero`) + .send({viewers: '10000'}) + .end((err, res) => { + expect(res.status).to.equal(400); + expect(res.text).to.equal('bad request'); + done(); + }); + }); + }); + + describe('GET: /api/superhero', function() { + it('should return a superhero', function(done) { + request.get(`localhost:${PORT}/api/superhero?id=${superhero.id}`) + .end((err, res) => { + if (err) return done(err); + expect(res.status).to.equal(200); + expect(res.body.name).to.equal('name'); + expect(res.body.comicUni).to.equal('comicUni'); + done(); + }); + }); + + it('should return a bad request', function(done) { + request.get(`localhost:${PORT}/api/superhero`) + .end((err, res) => { + expect(res.status).to.equal(400); + expect(res.text).to.equal('bad request'); + done(); + }); + }); + }); + + it('should return with superhero not found', function(done) { + request.get(`localhost:${PORT}/api/superhero?id=12345`) + .end((err, res) => { + expect(res.status).to.equal(404); + expect(res.text).to.equal('superhero not found'); + done(); + }); + }); +});