Skip to content

Commit 18b6f89

Browse files
authored
Merge pull request #31 from AryanK1511/issue-13
Add input validation for resume API endpoints
2 parents 1d58759 + 21cd870 commit 18b6f89

File tree

3 files changed

+128
-19
lines changed

3 files changed

+128
-19
lines changed

app.py

Lines changed: 44 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
'''
44
from flask import Flask, jsonify, request
55
from models import Experience, Education, Skill
6+
from utils import validate_data
67

78
app = Flask(__name__)
89

@@ -63,22 +64,24 @@ def experience():
6364
return jsonify(data['experience']), 200
6465

6566
if request.method == 'POST':
66-
experience_data = request.get_json()
67-
# Validate the json data
68-
if not all(key in experience_data for key in ['title', 'company', 'start_date',
69-
'end_date', 'description', 'logo']):
70-
return jsonify({"error": "Missing required fields"}), 400
71-
72-
new_experience = Experience(
73-
experience_data['title'],
74-
experience_data['company'],
75-
experience_data['start_date'],
76-
experience_data['end_date'],
77-
experience_data['description'],
78-
experience_data['logo']
79-
)
80-
data['experience'].append(new_experience)
81-
return jsonify({"id": len(data['experience']) - 1}), 201
67+
try:
68+
experience_data = request.get_json()
69+
is_valid, error_message = validate_data('experience', experience_data)
70+
if not is_valid:
71+
return jsonify({"error": error_message}), 400
72+
73+
new_experience = Experience(
74+
experience_data['title'],
75+
experience_data['company'],
76+
experience_data['start_date'],
77+
experience_data['end_date'],
78+
experience_data['description'],
79+
experience_data['logo']
80+
)
81+
data['experience'].append(new_experience)
82+
return jsonify({"id": len(data['experience']) - 1}), 201
83+
except (TypeError, ValueError, KeyError):
84+
return jsonify({"error": "Invalid data format"}), 400
8285

8386
return jsonify({"error": "Method not allowed"}), 405
8487

@@ -114,7 +117,18 @@ def education():
114117
return jsonify(data['education']), 200
115118

116119
if request.method == 'POST':
117-
return jsonify({}), 201
120+
try:
121+
education_data = request.get_json()
122+
is_valid, error_message = validate_data('education', education_data)
123+
if not is_valid:
124+
return jsonify({"error": error_message}), 400
125+
# pylint: disable=fixme
126+
# TODO: Create new Education object with education_data
127+
# TODO: Append new education to data['education']
128+
# TODO: Return jsonify({"id": len(data['education']) - 1}), 201
129+
return jsonify({}), 201
130+
except (TypeError, ValueError, KeyError):
131+
return jsonify({"error": "Invalid data format"}), 400
118132

119133
return jsonify({})
120134

@@ -155,9 +169,20 @@ def skill():
155169
Returns 405 if method is not allowed.
156170
"""
157171
if request.method == 'GET':
158-
return jsonify({})
172+
return jsonify(data['skill']), 200
159173

160174
if request.method == 'POST':
161-
return jsonify({})
175+
try:
176+
skill_data = request.get_json()
177+
is_valid, error_message = validate_data('skill', skill_data)
178+
if not is_valid:
179+
return jsonify({"error": error_message}), 400
180+
# pylint: disable=fixme
181+
# TODO: Create new Skill object with skill_data
182+
# TODO: Append new skill to data['skill']
183+
# TODO: Return jsonify({"id": len(data['skill']) - 1}), 201
184+
return jsonify({}), 201
185+
except (TypeError, ValueError, KeyError):
186+
return jsonify({"error": "Invalid data format"}), 400
162187

163188
return jsonify({})

test_pytest.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,3 +224,52 @@ def test_skill():
224224

225225
response = app.test_client().get('/resume/skill')
226226
assert response.json[item_id] == example_skill
227+
228+
229+
def test_invalid_input_validation():
230+
'''
231+
Test that the API properly validates input data for POST requests
232+
'''
233+
client = app.test_client()
234+
# Test invalid experience
235+
invalid_experience = {
236+
"title": "Software Developer",
237+
# Missing required fields
238+
}
239+
response = client.post('/resume/experience', json=invalid_experience)
240+
assert response.status_code == 400
241+
assert "error" in response.json
242+
assert "Missing required fields" in response.json["error"]
243+
assert "company" in response.json["error"]
244+
assert "start_date" in response.json["error"]
245+
assert "end_date" in response.json["error"]
246+
assert "description" in response.json["error"]
247+
assert "logo" in response.json["error"]
248+
# Test invalid education
249+
invalid_education = {
250+
"course": "Computer Science",
251+
# Missing required fields
252+
}
253+
response = client.post('/resume/education', json=invalid_education)
254+
assert response.status_code == 400
255+
assert "error" in response.json
256+
assert "Missing required fields" in response.json["error"]
257+
assert "school" in response.json["error"]
258+
assert "start_date" in response.json["error"]
259+
assert "end_date" in response.json["error"]
260+
assert "grade" in response.json["error"]
261+
assert "logo" in response.json["error"]
262+
# Test invalid skill
263+
invalid_skill = {
264+
"name": "Python",
265+
# Missing required fields
266+
}
267+
response = client.post('/resume/skill', json=invalid_skill)
268+
assert response.status_code == 400
269+
assert "error" in response.json
270+
assert "Missing required fields" in response.json["error"]
271+
assert "proficiency" in response.json["error"]
272+
assert "logo" in response.json["error"]
273+
# Test invalid data format
274+
response = client.post('/resume/experience', data="not json")
275+
assert response.status_code == 415

utils.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
'''
2+
Utility functions
3+
'''
4+
5+
# Define required fields for each type
6+
REQUIRED_FIELDS = {
7+
'experience': ['title', 'company', 'start_date', 'end_date', 'description', 'logo'],
8+
'education': ['course', 'school', 'start_date', 'end_date', 'grade', 'logo'],
9+
'skill': ['name', 'proficiency', 'logo']
10+
}
11+
12+
def validate_data(data_type, data):
13+
'''
14+
Validates that all required fields are present in the data
15+
16+
Parameters
17+
----------
18+
data_type : str
19+
The type of data being validated ('experience', 'education', or 'skill')
20+
data : dict
21+
The data to validate
22+
23+
Returns
24+
-------
25+
tuple
26+
(bool, str) - (is_valid, error_message)
27+
'''
28+
if data is None:
29+
return False, "Invalid data format"
30+
if not isinstance(data, dict):
31+
return False, "Invalid data format"
32+
missing_fields = [field for field in REQUIRED_FIELDS[data_type] if field not in data]
33+
if missing_fields:
34+
return False, f"Missing required fields: {', '.join(missing_fields)}"
35+
return True, None

0 commit comments

Comments
 (0)