Skip to content

Commit bc4a422

Browse files
Documentation (#5)
* updated README.md * added JSDOC for functions * updated README.md
1 parent 67bedaa commit bc4a422

File tree

9 files changed

+156
-21
lines changed

9 files changed

+156
-21
lines changed

README.md

+57-16
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,75 @@
1-
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
1+
# Short URL
2+
3+
This is a simple URL shortener project built as a part of the John Crickett's Coding Challenges [codingchallenges.fyi](https://codingchallenges.fyi/challenges/challenge-url-shortener).
4+
5+
## Table of Contents
6+
7+
- [Tech Stack](#tech-stack)
8+
- [Installation](#installation)
9+
- [Getting Started](#getting-started)
10+
- [Testing](#testing)
11+
- [Deployment](#deployment)
12+
- [Extra commands](#extra-commands)
13+
14+
## Tech Stack
15+
16+
[Next.js](https://nextjs.org/), [NextAuth.js](https://next-auth.js.org/), [TypeScript](https://www.typescriptlang.org/), [React MUI](https://mui.com/), [Tailwind CSS](https://tailwindcss.com/), [Cypress](https://www.cypress.io/) and [MongoDB](https://www.mongodb.com/)
17+
18+
## Installation
19+
20+
```bash
21+
# To install dependencies
22+
npm ci
23+
24+
# To install and setup husky
25+
npm husky install
26+
```
227

328
## Getting Started
429

5-
First, run the development server:
30+
Before starting the server make sure you have a `.env.local` file in the root directory with the same template as mentioned in `.env.template` file.
631

732
```bash
33+
# Prisma migration
34+
npm run prisma-dev-push
35+
36+
# To start the development server
837
npm run dev
9-
# or
10-
yarn dev
11-
# or
12-
pnpm dev
1338
```
1439

1540
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
1641

17-
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
42+
## Testing
1843

19-
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
44+
Before starting the test, make sure you have a `cypress.env.json` file in the root directory with the same template as mentioned in `cypress.env.template.json` file.
2045

21-
## Learn More
46+
```bash
47+
# To use the Cypress GUI
48+
npm run test
2249

23-
To learn more about Next.js, take a look at the following resources:
50+
# To run Cypress in headless mode
51+
npm run test:headless
52+
```
2453

25-
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
26-
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
54+
## Deployment
2755

28-
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
56+
```bash
57+
# Build the optimized production build
58+
npm run build
2959

30-
## Deploy on Vercel
60+
# Start the production server
61+
npm run start
62+
```
3163

32-
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
64+
## Extra commands
3365

34-
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
66+
```bash
67+
# To lint the code
68+
npm run lint
69+
70+
# To check formatting using Prettier
71+
npm run check-format
72+
73+
# To format the code using Prettier
74+
npm run format
75+
```

src/app/dashboard/page.tsx

+8-4
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,12 @@ export default function Profile() {
105105
setCurrentPage(page)
106106
}
107107

108+
/**
109+
* Function used to add a URL.
110+
* The form contains an input with name='url' containing the original URL.
111+
*
112+
* @param {*} e - The Form DOM element
113+
*/
108114
function addUrl(e: any) {
109115
setAddingUrl(true)
110116
e.preventDefault()
@@ -171,6 +177,8 @@ export default function Profile() {
171177
if (res.status === 200) {
172178
urlList.splice(index, 1)
173179
setUrlList(urlList)
180+
181+
// If the urlList is empty, then we need to go back one page if possible
174182
if (urlList.length === 0) {
175183
const newPage = Math.max(0, currentPage - 1)
176184
setCurrentPage(newPage)
@@ -245,10 +253,6 @@ export default function Profile() {
245253
)
246254
}
247255

248-
if (!session?.user) {
249-
return <div>Invalid User</div>
250-
}
251-
252256
return (
253257
<>
254258
<div className="flex w-full flex-col gap-4">

src/components/Header.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ export function Header() {
2323
></Image>
2424
<span>Home</span>
2525
</Link>
26+
{/* Show content is user is authenticated or not authenticated.
27+
Show nothing when the status is loading */}
2628
<div className="ml-auto mr-5">
2729
{status === 'loading' ? (
2830
<></>

src/lib/authOptions.ts

+11
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@ import GoogleProvider from 'next-auth/providers/google'
44
import { AuthOptions } from 'next-auth'
55
import { prisma } from './prisma'
66

7+
/**
8+
* The authOptions used for NextAuth.
9+
* We are using a MongoDB adapter along with GoogleProvider to store user data.
10+
* In the callbacks we are making sure that the ID of the user is available in the Session object.
11+
*
12+
* @type {AuthOptions}
13+
*/
714
export const authOptions: AuthOptions = {
815
secret: process.env.NEXTAUTH_SECRET,
916
adapter: PrismaAdapter(prisma),
@@ -15,15 +22,19 @@ export const authOptions: AuthOptions = {
1522
],
1623
callbacks: {
1724
async jwt({ token, user }) {
25+
// Update the token to have user id available in session callback
1826
if (user) {
1927
token.id = user.id
2028
}
2129
return token
2230
},
2331
async session({ session, token, user }) {
32+
// If token is not null, use the id present in the Token
2433
if (token) {
2534
session.user.id = token.id as string
2635
}
36+
37+
// If user object is available, use the id present in it
2738
if (user) {
2839
session.user.id = user.id
2940
}

src/lib/copyToClipboard.ts

+6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1+
/**
2+
* Function to copy the text provided in the input to the User's clipboard
3+
*
4+
* @param {string} text
5+
*/
16
const copyToClipboard = (text: string) => {
27
navigator.clipboard.writeText(text)
38
}
9+
410
export default copyToClipboard

src/lib/prisma.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { PrismaClient } from '@prisma/client'
22

3+
// Make sure that the PrismaClient is created only once
34
const globalForPrisma = global as unknown as { prisma: PrismaClient }
45

56
export const prisma =

src/lib/url.ts

+40-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@ import { Session } from 'next-auth'
22
import crypto from 'crypto'
33
import { prisma } from './prisma'
44

5+
/**
6+
* Get URL by its ID otherwise
7+
*
8+
* @async
9+
* @param {string} id
10+
*/
511
export const getUrlById = async (id: string) => {
612
const url = await prisma.url.findUnique({
713
where: {
@@ -14,6 +20,15 @@ export const getUrlById = async (id: string) => {
1420
return url
1521
}
1622

23+
/**
24+
* Creates a new URL corresponding to the User ID present in the Session object.
25+
* The short URL is created by making sure that it is unique in the database.
26+
* Returns the URL object along with the attached User.
27+
*
28+
* @async
29+
* @param {string} originalUrl
30+
* @param {Session} session
31+
*/
1732
export const createUrl = async (originalUrl: string, session: Session) => {
1833
if (!session?.user?.email) {
1934
throw new Error('No email present in Session object')
@@ -45,6 +60,15 @@ export const createUrl = async (originalUrl: string, session: Session) => {
4560
return url
4661
}
4762

63+
/**
64+
* Function to get paginated URLs from the DB.
65+
*
66+
* @async
67+
* @param {string} id - ID of the User
68+
* @param {number} [skip=0] - Records to skip
69+
* @param {number} [take=10] - Records to take after skipping
70+
* @returns {Promise<IPaginatedUrls>}
71+
*/
4872
export const getUrlsForUser = async (
4973
id: string,
5074
skip: number = 0,
@@ -77,6 +101,14 @@ export const getUrlsForUser = async (
77101
}
78102
}
79103

104+
/**
105+
* Deletes a URL given its ID and the User ID
106+
*
107+
* @async
108+
* @param {string} id - ID of the URL object
109+
* @param {string} userId - User ID
110+
* @returns {Promise<boolean>} - true if record was found and deleted successfully, otherwise false
111+
*/
80112
export const deleteUrl = async (
81113
id: string,
82114
userId: string
@@ -90,7 +122,14 @@ export const deleteUrl = async (
90122
return true
91123
}
92124

93-
export const getOriginalUrlFromShortUrl = async (
125+
export /**
126+
* Given a short URL, find if a URL object exists otherwise throw an error.
127+
*
128+
* @async
129+
* @param {string} shortUrl
130+
* @returns {Promise<string>} - the original URL corresponding to the short URL
131+
*/
132+
const getOriginalUrlFromShortUrl = async (
94133
shortUrl: string
95134
): Promise<string> => {
96135
const url = await prisma.url.findFirstOrThrow({

src/types/next-auth.d.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { DefaultUser } from 'next-auth'
22

3+
// To make the User ID available on the client side,
4+
// we need to extend the Session interface provided by NextAuth
35
declare module 'next-auth' {
46
interface Session {
57
user: DefaultUser & {

src/types/url.d.ts

+29
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,35 @@
1+
/**
2+
* Interface for the Response of /api/urls GET call.
3+
*
4+
* @interface IPaginatedUrls
5+
* @typedef {IPaginatedUrls}
6+
*/
17
interface IPaginatedUrls {
8+
/**
9+
* List of URLS.
10+
*
11+
* @type {Url[]}
12+
*/
213
results: Url[]
14+
15+
/**
16+
* Total URL count for the user.
17+
*
18+
* @type {number}
19+
*/
320
totalCount: number
21+
22+
/**
23+
* The skip parameter used for the Prisma query.
24+
*
25+
* @type {number}
26+
*/
427
skip: number
28+
29+
/**
30+
* The take parameter used for the Prisma query.
31+
*
32+
* @type {number}
33+
*/
534
take: number
635
}

0 commit comments

Comments
 (0)