---
title: Assignment 2 | Anchors & Links
tags: assignment
---
<span style="font-size: 50px;">**Assignment 2: Anchors & Links**</span>
:::info
**Released: September 29th**
**Due: October 12th, 11:59pm ET**
:::
:::danger
⚠️ **Do not clone the assignment 2 stencil code until you have submitted assignment 1 for the last time to Gradescope.** We will be checking to make sure you do not submit assignment 1 after cloning assignment 2.
:::
<!-- INFO BOXES
:::success GREEN
This is for any step by step instructions that students should follow.
:::
:::info BLUE
This is for important assignment information throughout the assignment:
**Released: September 8th, 6:00pm ET**
**Due: September 15th, 11:59pm ET**
:::
:::warning YELLOW
Use this info box for disclaimers.
:::
:::danger
Use this info box for important points that students should not miss!
:::
:::spoiler Dropdown list
Use this
:::
-->
<!-- TYPESCRIPT CODE BLOCKS
```typescript
const list = [10, 20];
console.log(list.map(x => (x * x)))
```
-->
<!-- HOW TO CHANGE COLOR IN MARKDOWN
<span style="background:aliceblue">some text with a **lightblue** background</span>
<span style="color:red">some **red** text</span>
-->
<!-- These are a list of shortcuts available. -->
*[HTML]: Hyper Text Markup Language
*[W3C]: World Wide Web Consortium
*[NPM]: Node Package Manager
*[IDE]: Integrated Development Environment
*[MERN]: MongoDB, Express, React, NodeJS
*[Yarn]: Yet Another Resource Negotiator
# Introduction
Now that we have finished making and preparing the dough for our donuts, it's time to cook them 🧑🍳! In this assignment, you will be building upon the concepts introduced in Lab 2 to add anchor and linking support to a standard node file system. At the end, you will have built a full-stack application that will allow users to create, delete and navigate with hypertext links via the frontend. You will be graded on the functionality of your backend via a test suite and the functionality of your frontend manually.
:::warning
We also want to re-emphasize the 30-minute rule – if you can’t get something that appears straightforward to work in 30 minutes, it may not be your problem. Don’t be afraid to send a message in Slack if something unexpected comes up.
:::
This assignment places a heavy emphasis on the frontend, and is a heavier workload than Assignment 1: Nodes. **Remember: Start early, start today, start yesterday!**
:::success
**Note**: Assignment 2 builds on the code from Lab 2; make sure to complete Lab 2 before starting
:::
## Checklist
**Backend:**
- [ ] Implement `AnchorCollectionConnection` and `LinkCollectionConnection`
**Frontend:**
- [ ] Implement `FrontendNodeGateway.deleteNode()`
- [ ] Load anchors from the database instead of randomly generating anchors in `TextContent.tsx` and `ImageContent.tsx`
- [ ] Implement `handleStartLinkClick` and `handleCompleteLinkClick` in `NodeView.tsx`
- [ ] Design the `CompleteLinkModal`
- [ ] Design and implement the `NodeLinkMenu`
**Deployment:**
- [ ] Deploy the backend to Heroku
- [ ] Deploy the frontend to Firebase
**Graduate/Capstone Requirement:**
- [ ] Implement additional node type
- [ ] Add anchoring and linking functionality for new node type
# Architecture
The following architecture diagram should give you a better understanding of the new structure of our full-stack application. A few important things to note:
- Additional functionality is added to our application through more backend services that do not depend on each other.
- Our frontend is a single application that consumes all of our backend services.
- Although all of our MongoDB collections belong to the same cluster, each collection is entirely independent of the other collections in the cluster.
In your next technical interview, you can refer to this project as a Monolithic Front End (a.k.a a single, large component) that interacts with a microservice (a.k.a. multiple, smaller components) backend. For more information on the tradeoffs between monolithic applications and microservices take a look at [this article](https://www.n-ix.com/microservices-vs-monolith-which-architecture-best-choice-your-business/).

## Tips and essential shortcuts for large codebases
### Typescript tips
TypeScript/JavaScript has powerful operators that can make a programmer's life easier. Here are some tips for making your code cleaner!
1. #### **Logical AND (&&) operator**
```typescript=
donut && console.log('delicious')
```
is the same thing as
```typescript=
if (donut) {
console.log('delicious')
}
```
2. #### **Nullish coalescing operator (`??`)**
It returns its right-hand side operand when its left-hand side operand is `null` or `undefined`, and otherwise returns its left-hand side operand. When used properly, it ensures that the variable we are assigning value to is never `undefined` or `null`.
```typescript=
// variable left-hand right-hand
const anchorId: string = currentValue ?? 'default string'
```
3. #### **Conditional ternary operator** (`x ? y : z`)
**`isDonut ? 'coffee' : 'milk'`** is the same as writing
```
if (isDonut){
return 'coffee'
} else {
return 'milk'
}
```
4. #### **`===` vs. `==`**
- `==` converts the variable values to the same type before performing comparison. This is called type coercion.
- `===` does not do any type conversion (coercion) and returns true only if both values and types are identical for the two variables being compared.
**Note:** Gains in performance between these two operations is negligable.
5. #### **Optional Chaining**
Introduced in 2020, the optional chaining operator (?.) enables you to read the value of a property located deep within a chain of connected objects without having to check that each reference in the chain is valid.
In the following example we need to first check for `donut.frosting` before we can access the `flavor` of the frosting.
`let nestedProp = donut.frosting && donut.frosting.flavor`
However, with optional chaining you don't have to explicitly test and short-circuit based on the state of donut.frosting before trying to access donut.frosting.flavor:
`let nestedProp = donut.frosting?.flavor`
### Essential VS Code shortcuts
- `cmd + shift + P`: VS Code commands
- `cmd + left click`: navigate to implementation/usage
- `cmd + P`: search by filename
- `cmd + F`: search current file for term
- `cmd + shift + F`: search entire code base for term
- `cmd + shift + L`: select all instances of selected term in the current file
- `option + left click`: add additional cursor at left click location
- `option + ↓` or `option + ↑`: move line of code up or down
Note, these commands are for Mac. Please replace all `cmd` with `ctrl` and all `option` with `alt` for Windows.
# Demo
You can find a demo of the MyHypermedia application [here](https://basic-hypermedia.web.app).
Note: It is entirely possible that there will be some unexpected behavior in this application as it is not set up to handle concurrency properly. This means that the application might behave weirdly if multiple users are interacting with the demo at the same time.
<!-- ### Demo Specs
**Node Functionality**
- Node should preserve all functionality outlined in Assignment 1 (add, delete, move).
- Additionally, the `Folder` node should support different viewspecs (`listView` and `gridView`).
**Anchor Functionality**
- Create an anchor upon clicking `Start Link` button. Created anchor becomes identifiable in the node immediately.
- When no region/text is selected, anchor is created with `null` as its `Extent`, which represents the whole node.
- Upon clicking an anchor, the related link in link list is highlighted.
- Image node anchor should contain the information for selected region.
- Text node anchor should contain the selected text and its start/end index.
**Link Functionality**
- Supports linking from an anchor to another anchor. Link list is updated immediately.
- Supports linking from an anchor to an anchor on the same node.
- User can stop linking process after initiating.
- On single click, link should highlight all related anchors in current node.
- On double click, link should navigate to the node that contains other anchor.
- Supports deleting a link. Link list is updated immediately.
-->
# Backend
Continuing from the lab, you will be implementing your own `MongoDB` queries.
:::warning
**The backend portion is worth 30% of the assignment grade.**
:::
:::danger
**Don't forget to `yarn install` and add your `.env` file!**
:::
## To Dos
Your objective for the backend assignment is to fill in the stencil code found at `AnchorCollectionConnection.ts` and`LinkCollectionConnection.ts`. We hope that writing these files will get you more familiar with creating your own MongoDB queries.
:::info
**Tips:**
1. We highly recommend looking at the Node version of these files as a close reference for the Anchor/Link versions.
2. If you are ever confused about how we have set up the MongoDB environment, please look at the Lab 2 handout for an explanation.
:::
## Running Tests
Once finished, you should be able to pass all unit (`mock`) and end-to-end (`e2e`) tests for `Links` and `Anchors`. Note that your `Nodes` tests should still all pass.
:::info
**Jest Commands:**
Here are the following commands to run tests on a select group of Jest files:
- Running a single file: `yarn test <file-name>`
- e.g. `yarn test findAnchorById.spec.ts` will run `findAnchorById.spec.ts`
- You can remove the file type suffix, so `yarn test findanchorbyid` works too - it is case insensitive
- Running a folder: `yarn test <folder-name>`
- e.g. `yarn test Anchors/Mock/AnchorCollectionConnection`
- e.g. `yarn test Anchors` will run all tests found in `Anchors`
- e.g. `yarn test e2e` will run all `e2e` unit tests
:::
:::warning
**Stencil code changes**
- The function signature for BackendNodeGateway.moveNode should be
`async moveNode(
nodeToMoveId: string,
newParentNodeId: string
): Promise<IServiceResponse<INode>>`
- Line 50 of updateNode.spec.ts should be `{ fieldName: 'asdf', value: 'new content' } as any,`
- The AnchorRouter.ts file should be replaced with [this file ](https://github.com/CS1951V-F22/anchorRouter/blob/main/AnchorRouter.ts)
:::
## Custom Methods
Feel free to add your own custom methods in any of the `<x>CollectionConnections`, gateways, or router files, as long as the test suite still passes.
The stencil only provides methods the TAs need to power our frontend, but if you think that your frontend implementation would benefit from another endpoint or support from the backend, feel free to add your custom code!
# Frontend
:::warning
**The frontend portion is worth 60% of the assignment grade.**
:::
## Introduction
In the last assignment you handled passing in `props` to the `TextContent` and `ImageContent` components so that they would render the content. You also implemented the `NodeBreadcrumb` component. The changes that you made were specific to single components rather than the overall system.
When implementing `links` and `anchors` we need different components to interact with each other, so in this assignment you will get more familiar with the frontend system as a whole.
### Frontend Roadmap
- Make sure that you've completed **Lab 2**. It's important to understand extents before you begin this assignment.
- Read and understand the implementation of`FrontendLinkGateway`, `FrontendAnchorGateway`, and `FrontendNodeGateway`.
- You will be using methods from these files in the rest of the assigment.
- Implement `FrontendNodeGateway.deleteNode`
- Load anchors from the database instead of randomly generating anchors in `TextContent.tsx` and `ImageContent.tsx`
- Implement the ability to `Start Link`
- Implement the ability to `Complete Link` and design the `CompleteLinkModal`
- Design your `NodeLinkMenu`. Ensure the menu is always up-to-date.
### Folder Structure

Remember, you can search for a file in VSCode using `cmd + P` (Mac) or `ctrl + P` (Windows). These commands are very useful to navigate between files, which you will have to do in this assignment!
## Gateways
Now that we are supporting Links and Anchors, our frontend gateways have gotten more complex. `FrontendNodeGateway.deleteNode()` now has to delete all links and anchors associated with the node before deleting the node itself.
:::warning
**Frontend gateway interacting with backend microservices**
We have designed this project such that each of the backend microservices (router, gateway, collectionConnection) **do not** interact with one another. This means that there is absolutely no cross talk between the link, anchor and node backends.
So how do we delete nodes if the deletion of a node relies on the deletion of anchors and links as well? For the purposes of our assignmments (you can do this differently in your final project) we will be using the frontend gateways to handle the cross talk. For example, this is what happens in the frontend's `FrontendNodeGateway.deleteNode()` method:
1. Make request to `AnchorRouter` to get anchors by nodeId
2. If step 1 was successful, make request to `LinkRouter` to get links by `anchorIds`
3. If step 2 was successful, call frontend `LinkGateway.deleteLinks()` to delete links and orphan anchors
4. If step 3 was successful, make request to `NodeRouter` to delete node
:::
You can make calls to other gateways by importing them, for example in FrontendLinkGateway, we already import FrontendAnchorGateway so that we can make a call to an FrontendAnchorGateway and delete the `orphanAnchors`. You will want to do this when you are implementing `FrontendNodeGateway.deleteNode()`!
```
import { FrontendAnchorGateway } from '../anchors'
await FrontendAnchorGateway.deleteAnchors(orphanAnchors)
```
:::success
**TODO:** Implement `FrontendNodeGateway.deleteNode()`
:::
## Loading Anchors
During the lab, the functions `generateRandomTextAnchors` and `generateRandomImageAnchors` were used to randomly generate anchors to display. Now that we have a working backend, we can instead load and display the node's anchors from the database using FrontendAnchorGateway.
:::success
**TODO:** Load anchors from the database for a given node instead of randomly generating anchors in `TextContent.tsx` and `ImageContent.tsx`
:::
## `Start Link` and `Complete Link`
In `Lab 2` you handled selecting an extent on the node that you are currently on. Once you handled the case of selecting the extent, you used `setSelectedExtent`. There are a few other global state variables that are essential when implementing `Start Link` and `Complete Link`.
To use a global state variable, import the atom (e.g. `isLinkingState`) from `global/Atoms` and import `useRecoilState` from `recoil`.
### App States
- **isLinking**: `boolean` that indicates whether we are linking from a node.
```
const [isLinking, setIsLinking] = useRecoilState(isLinkingState)
```
### Link States
- **startAnchor**: `IAnchor` that indicates the particular anchor that we are **linking from**.
- **endAnchor**: Indicates the anchor that we are linking to. This is important when we want to complete the link.
```
const [startAnchor, setStartAnchor] = useRecoilState(startAnchorState)
const [endAnchor, setEndAnchor] = useRecoilState(endAnchorState)
```
### Anchor States
- **selectedAnchors**: Indicates the list of anchors that are currently selected.
```
const [selectedAnchors, setSelectedAnchors] = useRecoilState(selectedAnchorsState)
```
- **selectedExtent**
```
const [selectedExtent, setSelectedExtent] = useRecoilState(selectedExtentState)
```
:::info
Note: If the **Extent** is `null` then it should link the entire node. If it is `undefined` then it should return an error.
:::
In `Lab 2`, we `setSelectedExtent` in `ImageContent.tsx` and `FolderContent.tsx` respectively. Since`selectedExtentState` is an atom in Recoil, we can access and mutate the state from anywhere in our codebase!
:::success
**TODO:**
- Implement `handleStartLinkClick` and `handleCompleteLinkClick` in `NodeView.tsx` and ensure that the functionality meets the expectations defined below.
- Design the `CompleteLinkModal`
:::
:::warning
**Stencil code fix**: In `NodeView.tsx`, the line `const [isAlertOpen, setAlertIsOpen] = React.useState(false)` should be changed to `const [isAlertOpen, setAlertIsOpen] = useRecoilState(alertOpenState)`
:::
### Functionality Expectations
1. You should be able to start a link from the currently selected extent of any node to go into linking mode.
2. It should be clearly indicated when the user is in linking mode.
- There are no TODOs which indicate where or how to indicate this mode - we leave that up to you.
2. There should be a way to cancel / escape linking mode.
- Again, it's up to you how you'd like to implement this.
4. When you are in linking mode, you should be able to complete the link based on the currently selected extent. This should open up a `CompleteLinkModal`.
5. Design the `CompleteLinkModal`. From this modal, the user should be able to set the link `Title` and `Explainer`.
- If you want, you can also change the `ILink` interface in `Types > ILink` and add additional properties to be added to the database. Make sure to change the interface in both the server and client folders if you do this.
6. Make requests via the frontend gateways so that the Anchors and Links are succesfully created and in the database.
## `NodeLinkMenu`
Now that we have the ability to create links between any two `Extents` on two documents (or within the same document). Your next task is to design the `NodeLinkMenu`, which is where you show the links for a particular node, and handle the user interactions with that link list.
:::success
**TODO**: Design and implement your NodeLinkMenu. You can find this in `src > components > NodeView > NodeLinkMenu`
:::
:::info
The `NodeLinkMenu` in the demo is only one possible design for this menu. Feel free to relocate it, adjust it, and make it your own, as long as it is usable, well designed, and the following three expectations are clear.
:::
### Click Events
You will want to use `onClick` when you are designing the NodeLinkMenu component. The following code snippet is an example of how that could look.
```typescript=
const clickDonut = (e: React.MouseEvent, flavor: IFlavor) => {
switch(e.detail){
case 1:
console.log('Left click', flavor.sprinkles)
case 2:
console.log('Right click', flavor.dough)
}
}
return (
<div className="donut" onClick={(e) => clickDonut(e, flavor)}>
</div>
)
```
Note that `e.detail` is how you can specify the difference between left click and right click. The above method would print the type of `sprinkles` on left click, and the `dough` on right click.
:::info
Don't forget to set the `css cursor` property. This is an important thing to set when it comes to usability with onClick elements!
:::
### Functionality Expectations
1. **You should be able to select an item in this menu, which selects the anchors in the currentNode.** Think about how we can use the `setSelectedAnchors` state variable to do this! But how do we get the anchors on either end of the link? Look at the ILink interface and REMEMBER, we can always make calls to the FrontendAnchorGateway.
2. **You should be able to follow a link.** This means that you should fetch the respective node on the other end of the link and navigates to that node
3. **You should be able to delete a link (and delete both of the link's anchors).** You should make gateway calls to the AnchorGateway and LinkGateway to do this!
This component should be well designed and usable. We recommend creating a mockup on paper or in `Adobe Xd` or `Figma`. Feel free to check in with a TA to ask for feedback on your design!
### Helper functions you may find useful
- **`loadAnchorToLinksMap`**: returns a map from an anchorID to the anchor and its links
- **`fetchNodeFromLink`**: returns the nodeId to navigate to for a given link and set of properties
- **`history`.`push(nodeId)`**: navigates to a given nodeId
- **`includesAnchorId`**: returns a boolean representing whether an anchorID is in the selected anchors
# Deployment
:::warning
**Deployment is worth 10% of the assignment grade.**
:::
The deployment steps are the same as for Assignment 1.
:::info
Dont forget to remove `console.log()` debug statements before deploying!
:::
## Deploying Backend
We can start deploying our backend code. Since we have our server and client in one repo, it's crucial that we only deploy a subfolder of the whole repository.
:::success
**Creating instance and deploying**
- Create a new Heroku instance with `heroku create`
- Commit your changes
- In the root directory of the repository, deploy the `server` subfolder to Heroku instance:
`git subtree push --prefix server heroku master`
:::
We have deployed our code to Heroku! But now it will not work yet, because we have not configured environment variables and our application does not know what port to start on.
:::success
**Configuring environment variables**
- Login to Heroku web portal: https://id.heroku.com/login
- Navigate to the instance you just created, and select *Settings* tab.
- Navigate to `Config Vars` section, and click `Reveal Config Vars`.
- Fill the table with your own `DB_URI`, keeping everything else the same as the image below.
:::

Now, if you refresh your backend instance, you should see that "MyHypermedia Backend Service" is rendered on the DOM. Moving on to deploying frontend!
:::warning
**Commonly Asked Questions**
*Q: The app works locally but Heroku says "Application Error". What's wrong?*
A: Go to Heroku dashboard and select your instance. Click `More` button and select `View logs` option. You will see the console output and debug from there.
Check whether you have configured environmental variables (config vars) correctly.
Note that Heroku does not install `devDependencies` if you have used your own npm package; consider moving it to `dependencies` in `package.json`.
*Q: I'm getting the error `fatal: 'heroku' does not appear to be a git repository`. What should I do?*
A: Try running `heroku git:remote -a herokuAppName`
:::
:::success
**TODO:** Put your backend Heroku URL in the assignment `README.md`
:::
## Deploying Frontend
Before we deploy the frontend, we need to change the endpoint in the frontend codebase to point to the backend service we just deployed.
:::info
**Where can I find my backend deployment link?**
Click the "Open App" button in Heroku instance page, your backend app will open in a new window.
:::
The first step is to change the endpoint in Frontend to point to the remote backend service.
:::success
**Changing the endpoint**
- Navigate to `client/src/global/endpoint.ts`
- Update the `prodEndpoint` variable to point to your backend deployment. Make sure to **include** the trailing `/` in the URL. This will change the end point for all frontend gateways you wrote when you deploy.
For example, `const prodEndpoint = 'https://vast-caverns-62320.herokuapp.com/'`
- Commit your changes
:::
Next, let's create and configure the Firebase app!
:::success
**Create and configure Firebase app**
Note: if you haven't used this account for Firebase before (i.e. in the first project), you will need to accept the terms on the [Firebase console](https://console.firebase.google.com/) first before proceeding.
- Navigate to `client` folder in terminal. This is important!
- Create an Firebase app using `firebase init`
- When CLI prompts: `Are you ready to proceed?`, enter `Y` and hit enter
- In the next step, use up/down arrow and space to select option `Hosting: Configure files for
Firebase Hosting and (optionally) set up GitHub Action deploys`
- In the next step, select `Create a new project`
- Specify your project id and project name.
- When CLI prompts: `What do you want to use as your public directory?`, enter `build`.
- When CLI prompts: `Configure as a single-page app?`, enter `Y`.
- When CLI prompts: `Set up automatic builds and deploys with GitHub?`, enter `N`.
- Wait for the initialization to complete.
:::
Now we have created a Firebase instance on the cloud! Since we are deploying our React application, we will use the `production` version of React. How do we do that?
:::success
**Build production React and deploy**
- Navigate to `client` folder in terminal.
- Build production version of React using `yarn build`. This would compile your React to HTML and JS files to a folder called `build`. We will deploy `build` folder.
- Deploy your compiled frontend to Firebase using `firebase deploy`.
:::
(You can repeat the `yarn build` and `firebase deploy` commands if you need to re-deploy in the future.)
All done, have fun! If everything runs smoothly, you should be seeing your app deployed on Firebase. Firebase would give you the URL of the app upon successful deployment.
You just deployed a fully-fledged hypertext system. **Open your frontend deployment, and test it as a user!**
:::success
**TODO:** Put your frontend Firebase URL in the assignment `README.md`
:::
# Graduate/Capstone Requirement
:::warning
**These features are required for graduate and capstone students.** For undergraduates who are not capstone you can complete these features for a maximum of 15 extra credit points.
:::
Now that you have implemented anchors and links for basic image and text nodes, it's time to implement your own node types. These can be anything ranging from a `PDFNode`, `SpreadsheetNode`, `AudioNode`, `VideoNode` or any other custom node type you want to add. For rendering audio, PDFs, feel free to use any package(s) of your choice!
If you are taking this course for master's/capstone credit, you are expected to create a new node type/view and add linking/anchor functionality to the view.
If you are **not** taking this course for master's/capstone credit, you can add a new node type/view and add linking/anchor functionality to the view for extra credit.
You should briefly explain your added functionality in your `README.md` so we know where to look for it.
# Handin
You should fill out your `README.md` with the following information:
- Notable design choices
- Including any packages that you used.
- Frontend Firebase URL
- Backend Heroku URL
- Known bugs
- Only report bugs related to what you have implemented. We will be more lenient with grading if bugs are reported!
- Time taken
- Master's/Capstone Requirement or Extra Credit (if applicable)
After completing your README, you should submit your GitHub repo to the Gradescope assignment.
# Grading Breakdown
## Backend Tests - 30pts
| Task | Grade |
| ----------- | ----------- |
| Implement `AnchorCollectionConnection.ts` | 15 Pts - yarn unit-test - all tests pass |
| Implement `LinkCollectionConnection.ts` | 15 Pts - yarn unit-test - all tests pass |
## Frontend Functionality - 60pts
| Task | Grade |
| ----------- | ----------- |
| Implement frontend `NodeGateway.deleteNode()` should delete node as well as relevant anchors and links | 10 pts - TA will verify |
| `Linking` works as expected | 25 pts - TA will verify |
| `NodeLinkMenu` meets expected functionality | 25 pts - TA will verify |
Rubric specifications for `Linking` and `NodeLinkMenu`:
| Sub-rubric | Criterion |
| ----------- | ----------- |
| Methods implementation | **10 pts** - correctly implemented<br/>5 pts - partially correct </br> 0 pts - not implemented |
| Design and usability | **10 pts** - clicks behave as expected, items are usable and operate as expected </br> 5 pts - clicks are not clear, poorly designed and not easily accessible </br> 0 pts - no design consideration, buttons are not functional |
| Overall design and usability | **5 pts** - TA will verify |
## Deployment - 10pts
| Task | Grade |
| ----------- | ----------- |
| Deploy backend | **5 Pts** - TA will verify |
| Deploy frontend | **5 Pts** - TA will verify |
#### Total = 100 pts
## Graduate/Captone Requirement - 15pts
| Task | Grade |
| ----------- | ----------- |
| Support an additional node type in your `MyHypermedia` app | 7.5 Pts - TA will verify |
| Add linking and anchoring functionality for the additional node type that you support | 7.5 Pts - TA will verify |