Skip to content

Commit 9efdbe4

Browse files
committed
Remove implementation and create student tasks
1 parent 1aacbe1 commit 9efdbe4

File tree

2 files changed

+32
-113
lines changed

2 files changed

+32
-113
lines changed

Taskfile.yml

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,6 @@ includes:
55
taskfile: ./Requests.yml
66

77
tasks:
8-
run:
9-
desc: Stop, build and run the url shortener container
10-
deps:
11-
- stop
12-
- build
13-
cmds:
14-
- docker run -d -p 8080:8080 -p 8000:8000 --name url-shortener url-shortener
15-
16-
stop:
17-
desc: Stop and remove the url shortener container if it exists
18-
cmds:
19-
- docker stop url-shortener || true
20-
- docker rm url-shortener || true
21-
22-
build:
23-
desc: Build the applications Docker image
24-
cmds:
25-
- docker build -t url-shortener .
26-
278
run-backend-dev:
289
desc: Run the backend container for testing
2910
cmds:

src/backend/main.py

Lines changed: 32 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class ClickStatsData(BaseModel):
1717

1818
class UrlRequest(BaseModel):
1919
url: str
20-
# Frontend-provided hostname of this service (no port)
20+
# Frontend-provided hostname of this service
2121
service_host: str
2222

2323
class ClickStats(BaseModel):
@@ -34,96 +34,35 @@ class ClickStats(BaseModel):
3434
click_stats: Dict[str, ClickStatsData] = {}
3535

3636
def generate_short_code(length: int = 6) -> str:
37-
return ''.join(random.choices(string.ascii_letters + string.digits, k=length))
38-
39-
def is_self_referencing(url: str, service_host: Optional[str]) -> bool:
40-
"""Check if URL points to the same service hostname (ports ignored).
41-
42-
Compares the target URL's hostname with the provided `service_host`.
43-
Hostname comparison is case-insensitive; port is ignored.
44-
"""
45-
try:
46-
# Add protocol if missing so urlparse can extract netloc/port
47-
if not url.startswith(("http://", "https://")):
48-
url = "http://" + url
49-
50-
parsed = urlparse(url)
51-
52-
# Extract target hostname (lower-cased)
53-
target_host = (parsed.hostname or "").lower()
54-
55-
# Determine service hostname (lower-cased). Frontend should send hostname only.
56-
service_hostname = (service_host or "").lower()
57-
58-
# Block when hostnames match (port is irrelevant)
59-
return target_host == service_hostname
60-
except Exception:
61-
# If URL parsing fails, allow it (conservative approach)
62-
return False
37+
return ''.join(random.choices(string.ascii_letters + string.digits, k=length))
6338

6439
@app.post("/shorten")
6540
def shorten_url(request: UrlRequest, http_request: Request):
66-
# Check for self-referencing URLs to prevent infinite redirect loops
67-
# Prefer frontend-provided host; fallback to request host if needed
68-
current_host = request.service_host or http_request.url.hostname
69-
if is_self_referencing(request.url, current_host):
70-
raise HTTPException(
71-
status_code=400,
72-
detail="Cannot shorten URLs that point to this service - this would create an infinite redirect loop. Please use an external URL instead."
73-
)
74-
75-
if request.url in url_store.values():
76-
raise HTTPException(status_code=400, detail="URL was already shortened previously")
77-
78-
short_code = generate_short_code()
79-
while short_code in url_store:
80-
short_code = generate_short_code()
81-
82-
url_store[short_code] = request.url
83-
84-
# Initialize click stats for the new URL
85-
click_stats[short_code] = ClickStatsData(
86-
count=0,
87-
original_url=request.url
88-
)
89-
90-
return short_code
41+
# TODO: implement this function
42+
# 1. Generate short code for long url
43+
# 2. Store the mapping of short code to long url
44+
# 3. Optional: store click_stats using the class ClickStatsData for the short_code with initial values
45+
short_code = ""
46+
return {short_code}
9147

9248
@app.get("/get-long-url/{short_code}")
9349
def get_long_url(short_code: str):
94-
if short_code in url_store:
95-
# Track clicks
96-
if short_code not in click_stats:
97-
click_stats[short_code] = ClickStatsData(
98-
count=0,
99-
original_url=url_store[short_code]
100-
)
101-
102-
click_stats[short_code].count += 1
103-
return url_store[short_code]
104-
105-
raise HTTPException(status_code=404, detail="Short URL not found")
106-
50+
# TODO: implement this function
51+
# 1. Fetch the long URL from the url_store using the short_code
52+
# 2. Return HTTP 404 (not found) if the short_code is not found
53+
# 3. (Optional) Update the click statistics in click_stats for the short_code
54+
long_url = ""
55+
return {long_url}
10756

10857
@app.get("/get-qr-code/{url}")
10958
def get_qr_code(url: str):
110-
# URL decode the parameter to handle special characters
111-
decoded_url = url
112-
113-
qr = qrcode.QRCode(
114-
version=1,
115-
error_correction=qrcode.constants.ERROR_CORRECT_L,
116-
box_size=10,
117-
border=0,
118-
)
119-
qr.add_data(decoded_url)
120-
qr.make(fit=True)
121-
img = qr.make_image(fill_color="black", back_color="white")
122-
buf = io.BytesIO()
123-
img.save(buf, format="PNG")
124-
buf.seek(0)
125-
encoded_string = base64.b64encode(buf.read()).decode("utf-8")
126-
return {"image_base64": f"data:image/png;base64,{encoded_string}"}
59+
# TODO: implement this function
60+
# 1. Generate a QR code for the given url parameter
61+
# 2. Return the QR code image as a base64-encoded string
62+
# Hint: You can use the qrcode library to generate QR codes.
63+
# Take a look at the documentation here: https://pypi.org/project/qrcode/
64+
encoded_string = ""
65+
# return {"image_base64": f"data:image/png;base64,{encoded_string}"}
12766

12867
# Click Statistics Endpoints
12968
@app.get("/stats", response_model=List[ClickStats])
@@ -160,21 +99,20 @@ def get_stats_for_url(short_code: str):
16099
"click_count": data.count,
161100
}
162101

163-
# Returns simple string message
164102
@app.get("/example")
165103
def example_endpoint(param: str):
166-
if param == "error":
167-
raise HTTPException(status_code=400, detail="Invalid parameter value")
168-
return {"message": f"This is an example endpoint with parameter: {param}"}
104+
if param == "error":
105+
raise HTTPException(status_code=400, detail="Invalid parameter value")
106+
return {"message": f"This is an example endpoint with parameter: {param}"}
169107

170108
# Returns image in base64 format
171109
@app.get("/example-image")
172110
def example_image_endpoint():
173-
file_path = "image.jpeg"
174-
if os.path.exists(file_path):
175-
with open(file_path, "rb") as image_file:
176-
encoded_string = base64.b64encode(image_file.read()).decode('utf-8')
177-
prefix = "data:image/jpeg;base64,"
178-
return {"image_base64": prefix + encoded_string}
179-
else:
180-
raise HTTPException(status_code=404, detail="Image not found")
111+
file_path = "image.jpeg"
112+
if os.path.exists(file_path):
113+
with open(file_path, "rb") as image_file:
114+
encoded_string = base64.b64encode(image_file.read()).decode('utf-8')
115+
prefix = "data:image/jpeg;base64,"
116+
return {"image_base64": prefix + encoded_string}
117+
else:
118+
raise HTTPException(status_code=404, detail="Image not found")

0 commit comments

Comments
 (0)