Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

web-sprint-challenge-build-a-web-api #68

Open
wants to merge 27 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 18 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,20 +45,23 @@ A _"test"_ script already exists you can use to run tests against your code.
#### Build an API

- [ ] Inside `api/actions/actions-router.js` build endpoints for performing CRUD operations on _actions_:

- `[GET] /api/actions` returns an array of actions (or an empty array) as the body of the _response_.
- `[GET] /api/actions/:id` returns an action with the given `id` as the body of the _response_.
- `[POST] /api/actions` returns the newly created action as the body of the _response_.
- `[PUT] /api/actions/:id` returns the updated action as the body of the _response_.
- `[DELETE] /api/actions/:id` returns no _response_ body.

- [ ] Inside `api/projects/projects-router.js` build endpoints for performing CRUD operations on _projects_:

- `[GET] /api/projects` returns an array of projects (or an empty array) as the body of the response.
- `[GET] /api/projects/:id` returns a project with the given `id` as the body of the _response_.
- `[POST] /api/projects` returns the newly created project as the body of the _response_.
- `[PUT] /api/projects/:id` returns the updated project as the body of the _response_.
- `[DELETE] /api/projects/:id` returns no _response_ body.

- [ ] Inside `api/projects/projects-router.js` add an endpoint for retrieving the list of actions for a project:

- `[GET] /api/projects/:id/actions` sends an array of actions (or an empty array) as the body of the response.

- Both Projects and Actions have an optional `completed` property (see Database Schemas below). In both cases it's a boolean stored in the database as a 1 or a 0. Make sure to transform the raw `completed` values obtained from the db to `true` or `false`, before sending them back to the client.
Expand All @@ -85,13 +88,13 @@ The description of the structure and extra information about each _resource_ sto

#### Actions

| Field | Data Type | Metadata |
| ----------- | --------- | ------------------------------------------------------------------------------------------------ |
| id | number | no need to provide it when creating posts, the database will automatically generate it |
| project_id | number | required, must be the id of an existing project |
| description | string | up to 128 characters long, required |
| notes | string | no size limit, required. Used to record additional notes or requirements to complete the action |
| completed | boolean | used to indicate if the action has been completed, not required |
| Field | Data Type | Metadata |
| ----------- | --------- | ----------------------------------------------------------------------------------------------- |
| id | number | no need to provide it when creating posts, the database will automatically generate it |
| project_id | number | required, must be the id of an existing project |
| description | string | up to 128 characters long, required |
| notes | string | no size limit, required. Used to record additional notes or requirements to complete the action |
| completed | boolean | used to indicate if the action has been completed, not required |

### Database Persistence Helpers

Expand Down Expand Up @@ -140,7 +143,11 @@ After finishing your required elements, you can push your work further. These go
Be prepared to demonstrate your understanding of this week's concepts by answering questions on the following topics. You might prepare by writing down your own answers before hand.

1. The core features of Node.js and Express and why they are useful.
1. Understand and explain the use of Middleware.
1. The basic principles of the REST architectural style.
1. Understand and explain the use of Express Routers.
1. Describe tooling used to manually test the correctness of an API.

2. Understand and explain the use of Middleware.

3. The basic principles of the REST architectural style.

4. Understand and explain the use of Express Routers.

5. Describe tooling used to manually test the correctness of an API.
18 changes: 9 additions & 9 deletions api/actions/actions-model.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const db = require('../../data/dbConfig.js');
const mappers = require('../../data/helpers/mappers');
const db = require("../../data/dbConfig.js");
const mappers = require("../../data/helpers/mappers");

module.exports = {
get,
Expand All @@ -9,11 +9,11 @@ module.exports = {
};

function get(id) {
let query = db('actions');
let query = db("actions");

if (id) {
return query
.where('id', id)
.where("id", id)
.first()
.then((action) => {
if (action) {
Expand All @@ -30,18 +30,18 @@ function get(id) {
}

function insert(action) {
return db('actions')
.insert(action, 'id')
return db("actions")
.insert(action, "id")
.then(([id]) => get(id));
}

function update(id, changes) {
return db('actions')
.where('id', id)
return db("actions")
.where("id", id)
.update(changes)
.then((count) => (count > 0 ? get(id) : null));
}

function remove(id) {
return db('actions').where('id', id).del();
return db("actions").where("id", id).del();
}
55 changes: 55 additions & 0 deletions api/actions/actions-router.js
Original file line number Diff line number Diff line change
@@ -1 +1,56 @@
// Write your "actions" router here!
const express = require("express");
const {
validateActionId,
validateAction,
} = require("../middleware/actions-middleware");
const Action = require("./actions-model");

const router = express.Router();

router.get("/", (req, res, next) => {
Action.get()
.then((actions) => {
res.json(actions);
})
.catch(next);
});

router.get("/:id", validateActionId, (req, res) => {
res.json(req.action);
});

router.post("/", validateAction, (req, res, next) => {
Action.insert(req.body)
.then((action) => {
res.status(201).json(action);
})
.catch(next);
});

router.put("/:id", validateActionId, validateAction, (req, res, next) => {
Action.update(req.params.id, req.body)
.then((action) => {
res.status(200).json(action);
})
.catch(next);
});

router.delete("/:id", validateActionId, (req, res, next) => {
Action.remove(req.params.id)
.then((action) => {
res.status(200).json(action);
})
.catch(next);
});

// eslint-disable-next-line no-unused-vars
router.use((err, req, res, next) => {
res.status(500).json({
message: err.message,
stack: err.stack,
custom: "something went wrong in the actions router",
});
});

module.exports = router;
34 changes: 34 additions & 0 deletions api/middleware/actions-middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const Action = require("../actions/actions-model");

const validateActionId = async (req, res, next) => {
try {
const action = await Action.get(req.params.id);
if (!action) {
res.status(404).json({ message: "action not found" });
} else {
req.action = action;
next();
}
} catch (err) {
next(err);
}
};

const validateAction = (req, res, next) => {
if (!req.body) {
res.status(400).json({ message: "missing action data" });
} else if (!req.body.description || !req.body.description.trim()) {
res.status(400).json({ message: "missing required description field" });
} else if (!req.body.notes || !req.body.notes.trim()) {
res.status(400).json({ message: "missing required notes field" });
} else if (!req.body.project_id) {
res.status(400).json({ message: "missing required project_id field" });
} else {
next();
}
};

module.exports = {
validateActionId,
validateAction,
};
32 changes: 32 additions & 0 deletions api/middleware/projects-middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const Project = require("../projects/projects-model");

const validateProjectId = async (req, res, next) => {
try {
const project = await Project.get(req.params.id);
if (!project) {
res.status(404).json({ message: "project not found" });
} else {
req.project = project;
next();
}
} catch (err) {
next(err);
}
};

const validateProject = (req, res, next) => {
if (!req.body) {
res.status(400).json({ message: "missing project data" });
} else if (!req.body.description || !req.body.description.trim()) {
res.status(400).json({ message: "missing required description field" });
} else if (!req.body.name || !req.body.name.trim()) {
res.status(400).json({ message: "missing required name field" });
} else {
next();
}
};

module.exports = {
validateProjectId,
validateProject,
};
16 changes: 7 additions & 9 deletions api/projects/projects-model.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const db = require("../../data/dbConfig.js");
const mappers = require('../../data/helpers/mappers');
const mappers = require("../../data/helpers/mappers");

module.exports = {
get,
Expand All @@ -17,7 +17,7 @@ function get(id) {

const promises = [query, getProjectActions(id)]; // [ projects, actions ]

return Promise.all(promises).then(function(results) {
return Promise.all(promises).then(function (results) {
let [project, actions] = results;

if (project) {
Expand All @@ -29,8 +29,8 @@ function get(id) {
}
});
} else {
return query.then(projects => {
return projects.map(project => mappers.projectToBody(project));
return query.then((projects) => {
return projects.map((project) => mappers.projectToBody(project));
});
}
}
Expand All @@ -45,17 +45,15 @@ function update(id, changes) {
return db("projects")
.where("id", id)
.update(changes)
.then(count => (count > 0 ? get(id) : null));
.then((count) => (count > 0 ? get(id) : null));
}

function remove(id) {
return db("projects")
.where("id", id)
.del();
return db("projects").where("id", id).del();
}

function getProjectActions(projectId) {
return db("actions")
.where("project_id", projectId)
.then(actions => actions.map(action => mappers.actionToBody(action)));
.then((actions) => actions.map((action) => mappers.actionToBody(action)));
}
63 changes: 63 additions & 0 deletions api/projects/projects-router.js
Original file line number Diff line number Diff line change
@@ -1 +1,64 @@
// Write your "projects" router here!
const express = require("express");
const {
validateProjectId,
validateProject,
} = require("../middleware/projects-middleware");
const Project = require("./projects-model");

const router = express.Router();

router.get("/", (req, res, next) => {
Project.get()
.then((actions) => {
res.json(actions);
})
.catch(next);
});

router.get("/:id", validateProjectId, (req, res) => {
res.json(req.project);
});

router.post("/", validateProject, (req, res, next) => {
Project.insert(req.body)
.then((project) => {
res.status(201).json(project);
})
.catch(next);
});

router.put("/:id", validateProjectId, validateProject, (req, res, next) => {
Project.update(req.params.id, req.body)
.then((project) => {
res.status(200).json(project);
})
.catch(next);
});

router.delete("/:id", validateProjectId, (req, res, next) => {
Project.remove(req.params.id)
.then((project) => {
res.status(200).json(project);
})
.catch(next);
});

router.get("/:id/actions", validateProjectId, (req, res, next) => {
Project.getProjectActions(req.params.id)
.then((actions) => {
res.json(actions);
})
.catch(next);
});

// eslint-disable-next-line no-unused-vars
router.use((err, req, res, next) => {
res.status(500).json({
message: err.message,
stack: err.stack,
custom: "something went wrong in the projects router",
});
});

module.exports = router;
8 changes: 7 additions & 1 deletion api/server.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
const express = require('express');
const express = require("express");
const actionsRouter = require("./actions/actions-router");
const projectsRouter = require("./projects/projects-router");
const server = express();

// Complete your server here!
// Do NOT `server.listen()` inside this file!
server.use(express.json());

server.use("/api/actions", actionsRouter);
server.use("/api/projects", projectsRouter);

module.exports = server;
Binary file modified data/lambda.db3
Binary file not shown.
13 changes: 13 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,16 @@ I need this code, but don't know where, perhaps should make some middleware, don

Go code!
*/
require("dotenv").config();
const server = require("./api/server.js");

if (process.env.NODE_ENV === "development") {
const cors = require("cors");
server.use(cors());
}

const PORT = process.env.PORT || 4000;

server.listen(PORT, () => {
console.log(`\n* Server Running on http://localhost:${PORT} *\n`);
});
Loading