Skip to content

Commit

Permalink
simplified and tested endpoints with Postman
Browse files Browse the repository at this point in the history
  • Loading branch information
venuswku committed May 31, 2022
1 parent 42a5b48 commit 2bf5327
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 68 deletions.
63 changes: 43 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,6 @@ Available at https://venuswku.github.io/cherish-api.

## Endpoints
### Actions
GET `/actions`
- Returns a list of all acts of kindness.
- Optional query parameters:
- `for`: who the act of kindness is for (e.g. friends, family, yourself, coworkers, strangers)

GET `/actions/:id`
- Returns detailed information about a single act of kindness with the specified object id.

GET `/actions/random`
- Returns information about a single random act of kindness.

POST `/actions/suggest`
- Suggests a new act of kindness, which needs to be approved before being added to our database.
- Requires authentication.
Expand All @@ -26,13 +15,38 @@ POST `/actions/suggest`
- `did`: whether suggester did their suggested act of kindness (required boolean)
- `suggester`: id of person who suggested this act of kindness (required object id)
- `img`: link to an image that relates to this act of kindness (optional string)
- Example request body:
```json
{
"act": "Send a handwritten letter or postcard to someone",
"desc": "Brighten a loved one's day by sharing what you appreciate about them!",
"for": ["family", "friends", "yourself"],
"like": true,
"did": true,
"suggester": "62957314cb99993a91f07ce8",
"img": ""
}
```

GET `/actions`
- Returns a list of all acts of kindness.
- If `for` query parameters are provided, then acts of kindness containing at least one of the provided query values will be returned.
- Optional query parameters:
- `for`: who the act of kindness is for (e.g. friends, family, yourself, coworkers, strangers)
- Example request link: http://localhost:5000/actions?for=family&for=yourself

GET `/actions/get/:id`
- Returns detailed information about a single act of kindness with the specified object id.

POST `/actions/like/:id`
GET `/actions/random`
- Returns information about a single random act of kindness.

PUT `/actions/like/:id`
- Either adds or removes a like for the act of kindness with the specified id.
- If the user's id is not in the list of people who liked the act, then it increments the number of likes by adding the id of the user.
- Else it decrements the number of likes because the user's id is already in the list and would like their id to be removed.

POST `/actions/done/:id`
PUT `/actions/done/:id`
- Either increments or decrements the number of people who did the act of kindness with the specified id.
- If the user's id is not in the list of people who did the act, then it increments the number of people who did the act by adding the id of the user.
- Else it decrements the number of people who did the act because the user's id is already in the list and would like their id to be removed.
Expand All @@ -41,14 +55,23 @@ DELETE `/actions/:id`
- Removes the act of kindness with the specified id from our database.

## Users
GET `/users`
- Returns a list of all users who have either suggested an act of kindness or contributed to number of likes/done.

GET `/users/:id`
- Returns detailed information about a single user with the specified object id.

POST `/users/add`
- Creates a new user in our database if they haven't been saved before.
- The request body needs to be in JSON format and includes the following properties:
- `email`: email of user (required string)
- `name`: user's name (optional string)
- `name`: user's name (optional string)
- Example request body:
```json
{
"email": "[email protected]",
"name": "Venus Ku"
}
```

GET `/users`
- Returns a list of all users who have either suggested an act of kindness or contributed to number of likes/done.
- If either one or both of the following query parameters are included in the request, returns detailed information about user(s) with the specified object id or email.
- Optional query parameters:
- `id`: object id of a user
- `email`: email of a user
- Example request link: http://localhost:5000/[email protected]
Binary file added images/handwritten-letter.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
"name": "cherish-api",
"version": "1.0.0",
"description": "My first REST API!",
"main": "index.js",
"main": "server.js",
"scripts": {
"start": "nodemon server",
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
Expand Down
90 changes: 59 additions & 31 deletions routes/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,80 +30,108 @@ router.route("/suggest").post((req, res) => {

// Save the new Action instance to the database.
newAction.save()
.then(() => res.json("Your suggested act of kindness has been submitted for approval!"))
.catch(err => res.status(400).json("Error: " + err));
.then(() => res.status(201).json("Your suggested act of kindness has been submitted for approval!"))
.catch(err => res.status(400).json("Error adding your act of kindness: " + err));
});

// Route: /actions/
// Reads and returns all actions from the MongoDB Atlas database.
// If query parameter values with the key "for" are provided, we only return filtered actions.
router.route("/").get((req, res) => {
Action.find() // find() returns a promise
.then(actions => res.json(actions)) // then returns actions in JSON format
.catch(err => res.status(400).json("Error: " + err));
const forFilters = req.query.for;
if (forFilters) {
Action.find({ "for": { $in: forFilters } }) // find() returns actions that match at least one of the given `for` parameter values
.then(filteredActions => res.json(filteredActions)) // then returns actions in JSON format
.catch(err => res.status(400).json("Error getting filtered acts of kindness: " + err));
} else {
Action.find()
.then(actions => res.json(actions))
.catch(err => res.status(400).json("Error getting all acts of kindness: " + err));
}
});

// Route: /actions/:id
// Route: /actions/get/:id
// Reads and returns an action with the specified object id, which is automatically generated by MongoDB.
router.route("/:id").get((req, res) => {
router.route("/get/:id").get((req, res) => {
Action.findById(req.params.id)
.then(action => res.json(action))
.catch(err => res.status(400).json("Error: " + err));
.catch(err => res.status(400).json("Error getting an act of kindness with the specified id: " + err));
});

// Route: /actions/random
// Reads and returns a random action from the MongoDB Atlas database.
// Reads and returns a random approved action from the MongoDB Atlas database.
router.route("/random").get((req, res) => {
Action.estimatedDocumentCount((err, totalActions) => {
// Calculate a random number of documents to skip over before finding an action document to return.
const randomSkips = Math.floor(Math.random() * totalActions);
Action.findOne().skip(randomSkips)
Action.countDocuments({ approved: true }, (err, totalApprovedActions) => {
// Calculate a random number of approved documents to skip over before finding an action document to return.
const randomSkips = Math.floor(Math.random() * totalApprovedActions);
Action.findOne({ approved: true }).skip(randomSkips)
.then(randomAction => res.json(randomAction))
.catch(err => res.status(400).json("Error: " + err));
.catch(err => res.status(400).json("Error getting a random act of kindness: " + err));
});
});

// Route: /actions/like/:id
// Updates the number of likes for an existing action with the specified object id.
router.route("/like/:id").post((req, res) => {
router.route("/like/:id").put((req, res) => {
const userObjectId = req.body.userId;
Action.findById(req.params.id)
.then(existingAction => {
const userObjectId = req.body.userId;
if (!existingAction.likes.includes(userObjectId)) {
// If we've don't see the user's object id in the list of people who liked the specified act of kindness, add them.
existingAction.likes.append(userObjectId);
// console.log("before update:", existingAction.likes);
const likeExists = existingAction.likes.includes(userObjectId);
if (likeExists) {
// If the user's id is already in the list and they want to remove their like, so remove their object id.
existingAction.likes.pull(userObjectId);
} else {
// Else the user's id is already in the list and they want to remove their like, so remove their object id.
const likerIndex = existingAction.likes.indexOf(userObjectId);
existingAction.likes.splice(likerIndex, 1);
// Else we don't see the user's object id in the list of people who liked the specified act of kindness, so add them.
existingAction.likes.push(userObjectId);
}
// console.log("after update:", existingAction.likes);

// Make sure to save update for likes.
existingAction.save()
.then(() => {
if (likeExists) { res.json("Your like for an act of kindness has successfully been removed."); }
else { res.json("Your like for an act of kindness has successfully been added."); }
})
.catch(err => res.status(400).json("Error updating likes for an act of kindness: " + err));
})
.catch(err => res.status(400).json("Error: " + err));
.catch(err => res.status(400).json("Error finding the specified act of kindness to update likes: " + err));
});

// Route: /actions/done/:id
// Updates the number of people who did an existing action with the specified object id.
router.route("/done/:id").post((req, res) => {
router.route("/done/:id").put((req, res) => {
const userObjectId = req.body.userId;
Action.findById(req.params.id)
.then(existingAction => {
const userObjectId = req.body.userId;
if (!existingAction.done.includes(userObjectId)) {
// console.log("before update:", existingAction.done);
const doneExists = existingAction.done.includes(userObjectId);
if (doneExists) {
// If we've don't see the user's object id in the list of people who did the specified act of kindness, add them.
existingAction.done.append(userObjectId);
existingAction.done.pull(userObjectId);
} else {
// Else the user's id is already in the list and they want to remove their done vote, so remove their object id.
const donerIndex = existingAction.done.indexOf(userObjectId);
existingAction.done.splice(donerIndex, 1);
existingAction.done.push(userObjectId);
}
// console.log("after update:", existingAction.done);

// Make sure to save update for done.
existingAction.save()
.then(() => {
if (doneExists) { res.json("Your done vote for an act of kindness has successfully been removed."); }
else { res.json("Your done vote for an act of kindness has successfully been added."); }
})
.catch(err => res.status(400).json("Error updating done votes for an act of kindness: " + err));
})
.catch(err => res.status(400).json("Error: " + err));
.catch(err => res.status(400).json("Error finding the specified act of kindness to update done votes: " + err));
});

// Route: /actions/:id
// Deletes an action with the specified object id, which is automatically generated by MongoDB.
router.route("/:id").delete((req, res) => {
Action.findByIdAndDelete(req.params.id)
.then(() => res.json("Specified act of kindness has successfully been deleted."))
.catch(err => res.status(400).json("Error: " + err));
.catch(err => res.status(400).json("Error deleting an act of kindness: " + err));
});

module.exports = router;
30 changes: 16 additions & 14 deletions routes/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ let User = require("../models/user.model");

// Route: /users/add
// Creates a new user.
router.route("/add").post((res, req) => {
router.route("/add").post((req, res) => {
// Only add user to database if it doesn't exist yet.
User.find({ email: req.body.email })
User.findOne({ email: req.body.email })
.then(existingUser => res.json(`User with the email ${existingUser.email} has already been added to our database.`))
.catch(err => {
const userData = { email: req.body.email };
if (req.body.hasOwnProperty("name")) {
Expand All @@ -15,24 +16,25 @@ router.route("/add").post((res, req) => {

newUser.save()
.then(() => res.json("You are saved to our database!"))
.catch(err => res.status(400).json("Error: " + err));
.catch(err => res.status(400).json("Error saving you to our database: " + err));
});
});

// Route: /users/
// Reads and returns all users from the MongoDB Atlas database.
// If query parameter values with the key "id" or "email" are provided, reads and returns user(s) with the specified object id (automatically generated by MongoDB) or email, both of which should be unique to a single user.
router.route("/").get((req, res) => {
User.find()
.then(users => res.json(users))
.catch(err => res.status(400).json("Error: " + err));
});

// Route: /users/:id
// Reads and returns a user with the specified object id, which is automatically generated by MongoDB.
router.route("/:id").get((req, res) => {
User.findById(req.params.id)
.then(user => res.json(user))
.catch(err => res.status(400).json("Error: " + err));
const idFilter = req.query.id;
const emailFilter = req.query.email;
if (idFilter || emailFilter) {
User.find({ $or: [{_id: idFilter}, {email: emailFilter}] })
.then(user => res.json(user))
.catch(err => res.status(400).json("No user with the specified id or email was found: " + err));
} else {
User.find()
.then(users => res.json(users))
.catch(err => res.status(400).json("Error getting all users from database: " + err));
}
});

module.exports = router;
7 changes: 5 additions & 2 deletions server.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ const port = process.env.PORT || 5000;
app.use(cors());
// Add Express middleware to parse JSON data, which is sent to and from our server.
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// Connect server to MongoDB database.
const uri = process.env.ATLAS_URI; // uri = where Mongo database is stored (get from MongoDB dashboard)
mongoose.connect(uri, { useNewParser: true, useCreateIndex: true }); // useNewParse and useCreateIndex are flags used to handle updates to MongoDB
mongoose.connect(uri);
// Print in terminal once MongoDB connection is open.
const connection = mongoose.connection;
connection.once("open", () => {
Expand All @@ -25,10 +26,12 @@ connection.once("open", () => {

// Require/import server routers.
const actionsRouter = require("./routes/actions");
const usersRouter = require("./routes/users");
// Load all possible routes (endpoints for HTTP requests) for each server router.
app.use("/actions", actionsRouter);
app.use("/users", usersRouter);

// Tell server to start listening to a certain port.
app.listen(port, () => {
console.log(`Server is running on port: ${port}.`);
console.log(`Server is running at: http://localhost:${port}/.`);
});

0 comments on commit 2bf5327

Please sign in to comment.