Skip to content
Open

PR #2

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
4a8a848
updates instructions
luishrd Jun 22, 2018
33585cd
Update README.md
Jul 23, 2018
a3c98b0
updates readme
luishrd Jul 27, 2018
7848b4e
adds link to styled components
luishrd Jul 27, 2018
a093e90
updates readme
luishrd Jul 27, 2018
f103080
updates review questions
luishrd Jul 27, 2018
54cd659
updates readme
luishrd Jul 27, 2018
598615f
Finalizes README
ryan-hamblin Dec 22, 2018
5121fc8
fixes title
ryan-hamblin Dec 28, 2018
ea26597
Update README.md
ryan-hamblin Jan 10, 2019
bbba393
Update README.md
ryan-hamblin Jan 10, 2019
aeef435
updates instructions
luishrd Feb 15, 2019
b2151f7
updates instructions
luishrd Feb 15, 2019
84b5cb1
updates package.json
luishrd Feb 15, 2019
b28a09d
adds cascading and foreign key enforcing
luishrd Mar 27, 2019
85200da
Create .travis.yml
justsml Apr 7, 2019
99a3cff
updates dependencies
luishrd Apr 26, 2019
309ea47
ingnores react's .env flavors
luishrd Apr 26, 2019
d0edd2a
adds sing along
luishrd Apr 26, 2019
b48a33c
fixes model error when passing id of non existing project
luishrd Apr 26, 2019
3b2fc71
Merge branch 'master' of https://github.com/LambdaSchool/webapi-chall…
luishrd Apr 26, 2019
c41ec0e
removes travis.yml file
luishrd Apr 26, 2019
9261463
updates dependencies
luishrd May 31, 2019
62b2247
fixes #625
luishrd Aug 9, 2019
335ad5f
updates dependencies
luishrd Aug 9, 2019
0a38da4
rewrite instructions
luishrd Aug 9, 2019
b059d7b
upates instructions
luishrd Aug 9, 2019
c3ddfb7
clarifies instructions
luishrd Aug 9, 2019
e72cadf
Merge branch 'master' into v1
luishrd Aug 12, 2019
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
32 changes: 29 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# Runtime data
pids
Expand All @@ -20,7 +24,7 @@ coverage
# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
Expand All @@ -29,14 +33,14 @@ bower_components
# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# Typescript v1 declaration files
# TypeScript v1 declaration files
typings/

# Optional npm cache directory
Expand All @@ -56,6 +60,28 @@ typings/

# dotenv environment variables file
.env
.env.test

# parcel-bundler cache (https://parceljs.org/)
.cache

# next.js build output
.next

# nuxt.js build output
.nuxt

# vuepress build output
.vuepress/dist

# Serverless directories
.serverless/

# FuseBox cache
.fusebox/

# DynamoDB Local files
.dynamodb/

# Mac files
.DS_Store
Expand Down
128 changes: 84 additions & 44 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,71 +1,111 @@
# Express and Node.js Sprint Challenge
# Sprint Challenge: Express and Node.js - Projects & Actions

## Assignments
This challenge allows you to practice the concepts and techniques learned over the past Sprint and apply them in a concrete project. This Sprint explored Building RESTful Web APIs with Express and Node.js, Server-side Routing, Express Middleware & Deployment and Good Practices. In your challenge for this Sprint, you will demonstrate proficiency by creating an Web API using Node.js and Express.

Please open the `Review.md` file and answer the questions.
## Instructions

Use Node.js and Express to design and build an API that performs CRUD operations on **projects** and **actions**.
**Read these instructions carefully. Understand exactly what is expected _before_ starting this Sprint Challenge.**

### Download Project Files and Install Dependencies
This is an individual assessment. All work must be your own. Your challenge score is a measure of your ability to work independently using the material covered through this sprint. You need to demonstrate proficiency in the concepts and objectives introduced and practiced in preceding days.

* **Fork** and **Clone** this repository.
* **CD into the folder** where you cloned the repository.
* Code!
You are not allowed to collaborate during the Sprint Challenge. However, you are encouraged to follow the twenty-minute rule and seek support from your PM and Instructor in your cohort help channel on Slack. Your work reflects your proficiency Building RESTful APIs with Node.js and Express; and your command of the concepts and techniques taught during the Express and Node.js, Server-side Routing, Express Middleware & Deployment and Good Practices modules.

### Implement Requirements
You have three hours to complete this challenge. Plan your time accordingly.

* Take the steps necessary to create a `package.json` to keep a record of all dependencies.
* use _yarn_ or _npm_ to add **knex** and **sqlite3** as dependencies to the project. **This is required for database access**.
* Configure an _npm script_ named _"start"_ that will execute your code using _nodemon_ so that the **server restarts on changes**. Make _nodemon_ be a development time dependency only, it shouldn't be deployed to production.
* Design and build a set of endpoints that satisfy the API requirements.
* **Use _Postman_ to test the API as you work through the exercises.**
## Commits

### Database Persistence Helpers
Commit your code regularly and meaningfully. This helps both you (in case you ever need to return to old code for any number of reasons and your project manager).

The `/data/helpers` folder includes helper files that you can use to manage the persistence of _project_ and _action_ data. These files are `projectModel.js` and `actionModel.js`. Both files publish the following api, which you can use to store, modify and retrieve each resource:
## Description

* `get()`: calling get returns an array of all the resources contained in the database. If you pass an `id` to this method it will return the resource with that id if one is found.
* `insert()`: calling insert passing it a resource object will add it to the database and return the newly created resource.
* `update()`: accepts two arguments, the first is the `id` of the resource to update, and the second is an object with the `changes` to apply. It returns the updated resource. If a resource with the provided `id` is not found, the method returns `null`.
* `remove()`: the remove method accepts an `id` as it's first parameter and, upon successfully deleting the resource from the database, returns the number of records deleted.
In this challenge, create a web API around the following resources: `Projects` and `Actions`.

The `projectModel.js` helper includes an extra method called `getProjectActions()` that when passed a _project id_ as it's only argument, returns a list of all the _actions_ for the _project_.
## Self-Study/Essay Questions

**All these helper methods return a promise.**
Demonstrate your understanding of this Sprint's concepts by answering the following free-form questions. Edit this document to include your answers after each question. Make sure to leave a blank line above and below your answer so it is clear and easy to read by your project manager.

#### Database Schemas
- [ ] Mention two parts of Express that you learned about this week.

The _schemas_ (properties and data type of each property) used to store and retrieve the resources inside the included database (`lambda.sqlite3`) is described below.
- [ ] Describe Middleware?

##### Projects
- [ ] Describe a Resource?

* `id`: number, no need to provide it when creating projects, the database will generate it.
* `name`: string, up to 128 characters long, required.
* `description`: string, up to 128 characters long, required.
* `completed`: boolean to indicate if the project has been completed, not required
- [ ] What can the API return to help clients know if a request was successful?

##### Actions
- [ ] How can we partition our application into sub-applications?

* `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, not required. Used to record additional notes ore requirements to complete the action.
* `completed`: boolean to indicate if the action has been completed, not required
## Project Setup

We have provided test data for all the resources.
Follow these steps to set up and work on your project:

- [ ] Create a forked copy of this project.
- [ ] Add your _Project Manager_ as collaborator on Github.
- [ ] Clone your forked version of the Repository.
- [ ] Create a new Branch on the clone: git checkout -b `<firstName-lastName>`.
- [ ] Implement the project on this Branch, committing changes regularly.
- [ ] Push commits: git push origin `<firstName-lastName>`.

Follow these steps for completing your project.

- [ ] Submit a Pull-Request to merge <firstName-lastName> Branch into master on your fork. **Please don't merge your own pull request.**
- [ ] Add your _Project Manager_ as a Reviewer on the Pull-request
- [ ] Your _Project Manager_ will count the challenge as done by merging the branch into _master_.

## Database Persistence Helpers

Please read the following before implementing the Minimum Viable Product:

The `/data/helpers` folder includes files you can use to manage the persistence of _project_ and _action_ data. These files are `projectModel.js` and `actionModel.js`. Both files publish the following api, which you can use to store, modify and retrieve each resource:

**All these helper methods return a promise. Please you .then().catch() or async/await**

- `get()`: calling get returns an array of all the resources contained in the database. If you pass an `id` to this method it will return the resource with that id if one is found.
- `insert()`: calling insert passing it a resource object will add it to the database and return the newly created resource.
- `update()`: accepts two arguments, the first is the `id` of the resource to update, and the second is an object with the `changes` to apply. It returns the updated resource. If a resource with the provided `id` is not found, the method returns `null`.
- `remove()`: the remove method accepts an `id` as it's first parameter and, upon successfully deleting the resource from the database, returns the number of records deleted.

The `projectModel.js` helper includes an extra method called `getProjectActions()` that takes a _project id_ as it's only argument and returns a list of all the _actions_ for the _project_.

Now that we have a way to add, update, remove and retrieve data from the provided database, it is time to work on the API.
## Minimum Viable Product

### Design and Build Endpoints
- [ ] Configure an _npm script_ named _"server"_ that will execute your code using _nodemon_. Make _nodemon_ be a development time dependency only, it shouldn't be deployed to production.
- [ ] Configure an _npm script_ named _"start"_ that will execute your code using _node_.

Design and build the necessary endpoints to:

* perform CRUD operations on _projects_ and _actions_.
* retrieve the list of actions for a project.
- [ ] Perform CRUD operations on _projects_ and _actions_. When adding an action, make sure the `project_id` provided belongs to an existing `project`. If you try to add an action with an `id` of 3 and there is no project with that `id` the database will return an error.
- [ ] Retrieve the list of actions for a project.

### Database Schemas

The _schemas_ (properties and data type of each property) used to store and retrieve the resources inside the included database (`lambda.sqlite3`) is described below.

#### Projects

| Field | Data Type | Metadata |
| ----------- | --------- | --------------------------------------------------------------------------- |
| id | number | no need to provide it when creating projects, the database will generate it |
| name | string | required. |
| description | string | required. |
| completed | boolean | used to indicate if the project has been completed, not required |

#### 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 |

We have provided test data for all the resources.

Now that we have a way to add, update, remove and retrieve data from the provided database, it's time to work on the API.

## Stretch Goal

* Use `create-react-app` to create an application in a separate folder (outside the API project folder). Name it anything you want.
* From the React application show a list of all _projects_ using the API you built.
* Add functionality to show the details of a project, including its actions, when clicking a project name in the list. Use React Router to navigate to a separate route to show the project details.
* Add styling! Perhaps with `styled-components`.
- Use `create-react-app` to create an application in a separate folder (outside the API project folder). Name it anything you want.
- From the React application show a list of all _projects_ using the API you built.
- Add functionality to show the details of a project, including its actions, when clicking a project name in the list. Use React Router to navigate to a separate route to show the project details.
- Add styling! Perhaps with [`styled-components`](https://www.styled-components.com/).
17 changes: 0 additions & 17 deletions Review.md

This file was deleted.

8 changes: 7 additions & 1 deletion data/helpers/actionModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@ module.exports = {
return query
.where('id', id)
.first()
.then(action => mappers.actionToBody(action));
.then(action => {
if (action) {
return mappers.actionToBody(action);
} else {
return action;
}
});
}

return query.then(actions => {
Expand Down
85 changes: 50 additions & 35 deletions data/helpers/projectModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,60 @@ const db = require('../dbConfig.js');
const mappers = require('./mappers');

module.exports = {
get: function(id) {
let query = db('projects as p');
get,
insert,
update,
remove,
getProjectActions,
};

function get(id) {
let query = db('projects as p');

if (id) {
query.where('p.id', id).first();

if (id) {
query.where('p.id', id).first();
const promises = [query, this.getProjectActions(id)]; // [ projects, actions ]

const promises = [query, this.getProjectActions(id)]; // [ projects, actions ]
return Promise.all(promises).then(function(results) {
let [project, actions] = results;

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

return mappers.projectToBody(project);
});
}

return query.then(projects => {
return projects.map(project => mappers.projectToBody(project));
} else {
return null;
}
});
},
getProjectActions: function(projectId) {
return db('actions')
.where('project_id', projectId)
.then(actions => actions.map(action => mappers.actionToBody(action)));
},
insert: function(project) {
return db('projects')
.insert(project)
.then(([id]) => this.get(id));
},
update: function(id, changes) {
return db('projects')
.where('id', id)
.update(changes)
.then(count => (count > 0 ? this.get(id) : null));
},
remove: function(id) {
return db('projects')
.where('id', id)
.del();
},
};
}

return query.then(projects => {
return projects.map(project => mappers.projectToBody(project));
});
}

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

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

function remove(id) {
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)));
}
Binary file renamed data/lambda.sqlite3 → data/lambda.db3
Binary file not shown.
8 changes: 5 additions & 3 deletions data/migrations/20180405083157_actions.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
exports.up = function(knex, Promise) {
exports.up = function(knex) {
return knex.schema.createTable('actions', function(actions) {
actions.increments();

Expand All @@ -7,14 +7,16 @@ exports.up = function(knex, Promise) {
.unsigned()
.notNullable()
.references('id')
.inTable('projects');
.inTable('projects')
.onDelete('CASCADE')
.onUpdate('CASCADE');

actions.string('description', 128).notNullable();
actions.text('notes').notNullable();
actions.boolean('completed').defaultTo(false);
});
};

exports.down = function(knex, Promise) {
exports.down = function(knex) {
return knex.schema.dropTableIfExists('actions');
};
5 changes: 5 additions & 0 deletions data/seeds/00-cleanup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const cleaner = require('knex-cleaner');

exports.seed = function(knex) {
return cleaner.clean(knex);
};
Loading