Skip to content

Jeremiah candelaria #90

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

Open
wants to merge 3 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
30 changes: 17 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## Stretch Goal Completed:

This app is deployed on [Heroku](https://build-web-api.herokuapp.com/).

# Sprint Challenge Instructions

**Read these instructions carefully. Understand exactly what is expected _before_ starting this Sprint Challenge.**
Expand Down Expand Up @@ -92,22 +96,22 @@ The description of the structure and extra information about each _resource_ sto

#### Projects

| Field | Data Type | Metadata |
| ----------- | --------- | --------------------------------------------------------------------------- |
| id | number | do not provide it when creating projects, the database will generate it |
| name | string | required |
| description | string | required |
| completed | boolean | not required, defaults to false when creating projects |
| Field | Data Type | Metadata |
| ----------- | --------- | ----------------------------------------------------------------------- |
| id | number | do not provide it when creating projects, the database will generate it |
| name | string | required |
| description | string | required |
| completed | boolean | not required, defaults to false when creating projects |

#### Actions

| Field | Data Type | Metadata |
| ----------- | --------- | ------------------------------------------------------------------------------------------------ |
| id | number | do not provide it when creating actions, the database will generate it |
| project_id | number | required, must be the id of an existing project |
| description | string | required, up to 128 characters long |
| notes | string | required, no size limit. Used to record additional notes or requirements to complete the action |
| completed | boolean | not required, defaults to false when creating actions |
| Field | Data Type | Metadata |
| ----------- | --------- | ----------------------------------------------------------------------------------------------- |
| id | number | do not provide it when creating actions, the database will generate it |
| project_id | number | required, must be the id of an existing project |
| description | string | required, up to 128 characters long |
| notes | string | required, no size limit. Used to record additional notes or requirements to complete the action |
| completed | boolean | not required, defaults to false when creating actions |

### Database Persistence Helpers

Expand Down
44 changes: 44 additions & 0 deletions api/actions/actions-middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
const Actions = require('./actions-model');
const yup = require('yup');

function logger(req, res, next) {
console.log(`
${req.method} request to ${req.baseUrl} endpoint
`);
next();
}

async function validateActionId(req, res, next) {
const action = await Actions.get(req.params.id);
if (!action) {
res.status(404).json({
message: "no action found",
});
} else {
req.action = action;
next();
}
}

const actionSchema = yup.object({
project_id: yup.number().required(),
description: yup.string().trim().max(128).required(),
notes: yup.string().trim().required(),
completed: yup.bool(),
});

async function validateAction(req, res, next) {
try {
const validatedAction = await actionSchema.validate(req.body, {
stripUnknown: true,
});
req.body = validatedAction;
next();
} catch (err) {
res.status(400).json({
message: "project id, description, and notes fields required"
});
}
}

module.exports = { logger, validateActionId, validateAction };
63 changes: 62 additions & 1 deletion api/actions/actions-router.js
Original file line number Diff line number Diff line change
@@ -1 +1,62 @@
// Write your "actions" router here!
const express = require('express');
const Actions = require('./actions-model');
const { logger, validateActionId, validateAction } = require('./actions-middleware');

const router = express.Router();

// [GET] /api/actions
// returns array of actions as res.body
router.get('/', logger, (req, res, next) => {
Actions.get()
.then(actions => {
res.status(200).json(actions);
})
.catch(next);
})

// [GET] /api/actions/:id
// returns action with given id as res.body
// responds with a 404 if no action with given id
router.get('/:id', logger, validateActionId, (req, res) => {
res.status(200).json(req.action);
});

// [POST] /api/actions
// returns newly created action as res.body
// responds with a 400 if req.body missing required fields
// confirms project_id belongs to existing project
router.post('/', logger, validateAction, (req, res, next) => {
Actions.insert(req.body)
.then(action => {
res.status(201).json(action)
})
.catch(next);
});

// [PUT] /api/actions/:id
// returns updated action as res.body
// responds with a 404 if no action with given id
// responds with a 400 if req.body missing required fields
router.put('/:id', logger, validateActionId, validateAction, (req, res, next) => {
Actions.update(req.params.id, req.body)
.then(action => {
res.status(200).json(action);
})
.catch(next);
});

// [DELETE] /api/actions/:id
// returns no res.body
// responds with a 404 if no action with given id
router.delete('/:id', logger, validateActionId, async (req, res, next) => {
try {
await Actions.remove(req.params.id);
res.status(200).json({
message: "action deleted"
})
} catch (err) {
next(err);
}
});

module.exports = router;
43 changes: 43 additions & 0 deletions api/projects/projects-middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const Projects = require('./projects-model');
const yup = require('yup');

function logger(req, res, next) {
console.log(`
${req.method} request to ${req.baseUrl} endpoint
`);
next();
}

async function validateProjectId(req, res, next) {
const project = await Projects.get(req.params.id);
if (!project) {
res.status(404).json({
message: "no project found",
});
} else {
req.project = project;
next();
}
}

const projectSchema = yup.object({
name: yup.string().trim().required(),
description: yup.string().trim().required(),
completed: yup.bool(),
})

async function validateProject(req, res, next) {
try {
const validatedProject = await projectSchema.validate(req.body, {
stripUnknown: true,
});
req.body = validatedProject;
next();
} catch (err) {
res.status(400).json({
message: "name and description fields required"
})
}
}

module.exports = { logger, validateProjectId, validateProject };
2 changes: 1 addition & 1 deletion api/projects/projects-model.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ function get(id) {

function insert(project) {
return db("projects")
.insert(project)
.insert(project, "id")
.then(([id]) => get(id));
}

Expand Down
74 changes: 73 additions & 1 deletion api/projects/projects-router.js
Original file line number Diff line number Diff line change
@@ -1 +1,73 @@
// Write your "projects" router here!
const express = require('express');
const Projects = require('./projects-model');
const { logger, validateProjectId, validateProject } = require('./projects-middleware.js');

const router = express.Router();

// [GET] /api/projects
// returns array of projects as res.body
// responds with empty array if no projects
router.get('/', logger, (req, res, next) => {
Projects.get()
.then(projects => {
res.status(200).json(projects);
})
.catch(next);
});

// [GET] /api/projects/:id
// returns project with given id as res.body
// responds with a 404 if no project with given id
router.get('/:id', logger, validateProjectId, (req, res) => {
res.status(200).json(req.project);
});

// [POST] /api/projects
// returns newly created project as res.body
// responds with a 400 if req.body missing required fields
router.post('/', logger, validateProject, (req, res, next) => {
Projects.insert(req.body)
.then(project => {
res.status(201).json(project)
})
.catch(next);
});

// [PUT] /api/projects/:id
// returns updated project as res.body
// responds with a 404 if no project with given id
// responds with a 400 if req.body missing required fields
router.put('/:id', logger, validateProjectId, validateProject, (req, res, next) => {
Projects.update(req.params.id, req.body)
.then(project => {
res.status(200).json(project);
})
.catch(next);
});

// [DELETE] /api/projects/:id
// returns no res.body
// responds with a 404 if no project with given id
router.delete('/:id', logger, validateProjectId, async (req, res, next) => {
try {
await Projects.remove(req.params.id);
res.status(200).json({
message: "project deleted"
})
} catch (err) {
next(err);
}
});

// [GET] /api/projects/:id/actions
// returns array of actions for project with given id
// responds with a 404 if no project with given id
router.get('/:id/actions', logger, validateProjectId, (req, res, next) => {
Projects.getProjectActions(req.params.id)
.then(actions => {
res.status(200).json(actions);
})
.catch(next);
});

module.exports = router;
27 changes: 23 additions & 4 deletions api/server.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,28 @@
const express = require('express');
const server = express();

// Configure your server here
// Build your actions router in /api/actions/actions-router.js
// Build your projects router in /api/projects/projects-router.js
// Do NOT `server.listen()` inside this file!
const projectsRouter = require('./projects/projects-router');
const actionsRouter = require('./actions/actions-router');

// middlewares
server.use(express.json());

// server error
server.use((err, req, res, next) => { // eslint-disable-line
res.status(err.status || 500).json({
note: `something went terriblly wrong with the server`,
message: err.message,
stack: err.stack,
});
});

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

// defaults
server.use('/', (req, res) => {
res.send(`<h2>Look Ma, I built an API!</h2>`);
});

module.exports = server;
17 changes: 6 additions & 11 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
/*
play this: https://www.youtube.com/watch?v=d-diB65scQU
require('dotenv').config();

Sing along:
const server = require('./api/server');

here's a little code I wrote, please read the README word for word, don't worry, you got this
in every task there may be trouble, but if you worry you make it double, don't worry, you got this
ain't got no sense of what is REST? just concentrate on learning Express, don't worry, you got this
your file is getting way too big, bring a Router and make it thin, don't worry, be crafty
there is no data on that route, just write some code, you'll sort it out… don't worry, just hack it…
I need this code, but don't know where, perhaps should make some middleware, don't worry, just hack it
const PORT = process.env.PORT || 8000;

Pull your server into this file and start it!
*/
server.listen(PORT, () => {
console.log(`server listening on ${PORT}`);
});
Loading