-
Notifications
You must be signed in to change notification settings - Fork 2
Milestone 2 Report
We designed this practice app to learn working as a group on github for future releases.
Aras Güngöre |
Begüm Arslan |
Cahid Enes Keleş |
Egecan Serbester |
Halil İbrahim Gürbüz |
|
Hasan Bingölbali |
Mehmet Emin İpekdal |
Mehmet Kuzulugil |
Merve Gürbüz | Ömer Şahin Albayram | Ramazan Burak Sarıtaş |
We used FastAPI for the backend and next.js for the front end for our practice app. The API's are picked considering future use. We succesfully deployed our docker on AWS.
- URL of practice app code and the tag
- Any information we may need to test your application
- Instructions for building the application with docker. – API URL (the URL should take us to a documented API)
- Links to meeting notes related to practice app
- Lessons learned: evaluation of tools and processes utilized to manage the team project.
- Visual Studio Code
- Github
- FastAPI The easiness with documenting API's were notable.
- MongoDB and PostgreSQL This was a challenge we faced as a group. We decided on using both after careful consideration to test them for our purposes. ``
-
- TimeZoneDB API
- endpoint: http://api.timezonedb.com/v2.1/get-time-zone?key=API_KEY
- POST request
- Description: Get local time of a city by its name, time zone, latitude & longtiude, or IP address.
- Parameters:
- key: Your unique API key you get after register your account.
- by: The method of lookup
- zone: A time zone abbreviation or time zone name. Required if lookup by zone method.
- lat: Latitude of a city. Required if lookup by position method.
- lng: Longitude of a city. Required if lookup by position method.
- city: The name of a city. Use asterisk (*) for wildcard search. Required if lookup by city method.
- Parameters:
- Description: Get local time of a city by its name, time zone, latitude & longtiude, or IP address.
-
- get_timezone
- Route: /timezone/get_timezone
- Description: Convert latitude and longitude data to timezone and date.
- list_timezones
- Route: /tz_conversion/list_timezones
- Description: Retrieve a list of all available timezones from the Timezone DB API.
- convert_time
- Route: /tz_conversion/convert_time
- Description: Convert a given time from one timezone to another.
- saved_conversions
- Route: /tz_conversion/saved_conversions
- Description: Retrieve a list of saved time conversions from the MongoDB database.
- delete_conversion
- Route: /tz_conversion/delete_conversion
- Delete a saved time conversion from the MongoDB database.
- get_timezone
I have written 5 unit tests, one for each API function.
class TimezoneTests(unittest.TestCase):
def test_get_timezone(self):
r = requests.post(f'http://0.0.0.0:8000{api_prefix}/timezone/get_timezone', json={
"latitude": "122",
"longitude": "232",
})
self.assertEqual(r.status_code, 200)
@pytest.mark.parametrize(
"expected_status_code",
[(200)],
)
def test_list_timezones(expected_status_code):
client = TestClient(app)
response = client.get("/tz_conversion/list_timezones")
assert response.status_code == expected_status_code
@pytest.mark.parametrize(
"time, from_tz, to_tz, expected_status_code",
[
("2023-05-12 15:00:00", "America/New_York", "Europe/London", 200),
("2023-05-12 15:00:00", "Invalid timezone", "Europe/London", 400),
("2023-05-12 15:00:00", "America/New_York", "Invalid timezone", 400),
("2023-05-12T15:00:00", "America/New_York", "Europe/London", 400),
],
)
def test_convert_time(time, from_tz, to_tz, expected_status_code):
client = TestClient(app)
response = client.post(
"/tz_conversion/convert_time", json={"time": time, "from_tz": from_tz, "to_tz": to_tz}
)
assert response.status_code == expected_status_code
@pytest.mark.parametrize(
"expected_status_code",
[(200)],
)
def test_get_saved_conversions(expected_status_code):
client = TestClient(app)
response = client.get("/tz_conversion/saved_conversions")
assert response.status_code == expected_status_code
@pytest.mark.parametrize(
"conversion_name, expected_status_code",
[
("Invalid name", 404),
],
)
def test_delete_conversion(conversion_name, expected_status_code):
client = TestClient(app)
response = client.post("/tz_conversion/delete_conversion",
json={"conversion_name": conversion_name})
assert response.status_code == expected_status_code
import requests
uri = 'http://127.0.0.1:8000'
# POST Request Example
response = requests.post(uri + '/timezone/get_timezone', json={
"latitude": 120.1,
"longitude": 23.4
})
print('Headers:', response.headers)
print('Status Code:', response.status_code)
print('Json:', response.json())
Headers: {'date': 'Fri, 12 May 2023 20:37:25 GMT', 'server': 'uvicorn', 'content-length': '61', 'content-type': 'application/json'}
Status Code: 200
Json: {'timezone': 'America/Anchorage', 'date': '2023-05-12 12:37:23'}
# GET Request Example
response = requests.get(uri + '/tz_conversion/list_timezones')
print('Headers:', response.headers)
print('Status Code:', response.status_code)
print('Json:', response.json())
Headers: {'date': 'Fri, 12 May 2023 20:37:25 GMT', 'server': 'uvicorn', 'content-length': '7722', 'content-type': 'application/json'}
Status Code: 200
Json: ['Africa/Abidjan', 'Africa/Accra', 'Africa/Addis_Ababa', 'Africa/Algiers', 'Africa/Asmara', 'Africa/Bamako', 'Africa/Bangui', 'Africa/Banjul', 'Africa/Bissau', 'Africa/Blantyre', 'Africa/Brazzaville', 'Africa/Bujumbura', 'Africa/Cairo', 'Africa/Casablanca', 'Africa/Ceuta', 'Africa/Conakry', 'Africa/Dakar', 'Africa/Dar_es_Salaam', 'Africa/Djibouti', 'Africa/Douala', 'Africa/El_Aaiun', 'Africa/Freetown', 'Africa/Gaborone', 'Africa/Harare', 'Africa/Johannesburg', 'Africa/Juba', 'Africa/Kampala', 'Africa/Khartoum', 'Africa/Kigali', 'Africa/Kinshasa', 'Africa/Lagos', 'Africa/Libreville', 'Africa/Lome', 'Africa/Luanda', 'Africa/Lubumbashi', 'Africa/Lusaka', 'Africa/Malabo', 'Africa/Maputo', 'Africa/Maseru', 'Africa/Mbabane', 'Africa/Mogadishu', 'Africa/Monrovia', 'Africa/Nairobi', 'Africa/Ndjamena', 'Africa/Niamey', 'Africa/Nouakchott', 'Africa/Ouagadougou', 'Africa/Porto-Novo', 'Africa/Sao_Tome', 'Africa/Tripoli', 'Africa/Tunis', 'Africa/Windhoek', 'America/Adak', 'America/Anchorage', 'America/Anguilla', 'America/Antigua', 'America/Araguaina', 'America/Argentina/Buenos_Aires', 'America/Argentina/Catamarca', 'America/Argentina/Cordoba', 'America/Argentina/Jujuy', 'America/Argentina/La_Rioja', 'America/Argentina/Mendoza', 'America/Argentina/Rio_Gallegos', 'America/Argentina/Salta', 'America/Argentina/San_Juan', 'America/Argentina/San_Luis', 'America/Argentina/Tucuman', 'America/Argentina/Ushuaia', 'America/Aruba', 'America/Asuncion', 'America/Atikokan', 'America/Bahia', 'America/Bahia_Banderas', 'America/Barbados', 'America/Belem', 'America/Belize', 'America/Blanc-Sablon', 'America/Boa_Vista', 'America/Bogota', 'America/Boise', 'America/Cambridge_Bay', 'America/Campo_Grande', 'America/Cancun', 'America/Caracas', 'America/Cayenne', 'America/Cayman', 'America/Chicago', 'America/Chihuahua', 'America/Ciudad_Juarez', 'America/Costa_Rica', 'America/Creston', 'America/Cuiaba', 'America/Curacao', 'America/Danmarkshavn', 'America/Dawson', 'America/Dawson_Creek', 'America/Denver', 'America/Detroit', 'America/Dominica', 'America/Edmonton', 'America/Eirunepe', 'America/El_Salvador', 'America/Fort_Nelson', 'America/Fortaleza', 'America/Glace_Bay', 'America/Goose_Bay', 'America/Grand_Turk', 'America/Grenada', 'America/Guadeloupe', 'America/Guatemala', 'America/Guayaquil', 'America/Guyana', 'America/Halifax', 'America/Havana', 'America/Hermosillo', 'America/Indiana/Indianapolis', 'America/Indiana/Knox', 'America/Indiana/Marengo', 'America/Indiana/Petersburg', 'America/Indiana/Tell_City', 'America/Indiana/Vevay', 'America/Indiana/Vincennes', 'America/Indiana/Winamac', 'America/Inuvik', 'America/Iqaluit', 'America/Jamaica', 'America/Juneau', 'America/Kentucky/Louisville', 'America/Kentucky/Monticello', 'America/Kralendijk', 'America/La_Paz', 'America/Lima', 'America/Los_Angeles', 'America/Lower_Princes', 'America/Maceio', 'America/Managua', 'America/Manaus', 'America/Marigot', 'America/Martinique', 'America/Matamoros', 'America/Mazatlan', 'America/Menominee', 'America/Merida', 'America/Metlakatla', 'America/Mexico_City', 'America/Miquelon', 'America/Moncton', 'America/Monterrey', 'America/Montevideo', 'America/Montserrat', 'America/Nassau', 'America/New_York', 'America/Nome', 'America/Noronha', 'America/North_Dakota/Beulah', 'America/North_Dakota/Center', 'America/North_Dakota/New_Salem', 'America/Nuuk', 'America/Ojinaga', 'America/Panama', 'America/Paramaribo', 'America/Phoenix', 'America/Port-au-Prince', 'America/Port_of_Spain', 'America/Porto_Velho', 'America/Puerto_Rico', 'America/Punta_Arenas', 'America/Rankin_Inlet', 'America/Recife', 'America/Regina', 'America/Resolute', 'America/Rio_Branco', 'America/Santarem', 'America/Santiago', 'America/Santo_Domingo', 'America/Sao_Paulo', 'America/Scoresbysund', 'America/Sitka', 'America/St_Barthelemy', 'America/St_Johns', 'America/St_Kitts', 'America/St_Lucia', 'America/St_Thomas', 'America/St_Vincent', 'America/Swift_Current', 'America/Tegucigalpa', 'America/Thule', 'America/Tijuana', 'America/Toronto', 'America/Tortola', 'America/Vancouver', 'America/Whitehorse', 'America/Winnipeg', 'America/Yakutat', 'Antarctica/Casey', 'Antarctica/Davis', 'Antarctica/DumontDUrville', 'Antarctica/Macquarie', 'Antarctica/Mawson', 'Antarctica/McMurdo', 'Antarctica/Palmer', 'Antarctica/Rothera', 'Antarctica/Syowa', 'Antarctica/Troll', 'Antarctica/Vostok', 'Arctic/Longyearbyen', 'Asia/Aden', 'Asia/Almaty', 'Asia/Amman', 'Asia/Anadyr', 'Asia/Aqtau', 'Asia/Aqtobe', 'Asia/Ashgabat', 'Asia/Atyrau', 'Asia/Baghdad', 'Asia/Bahrain', 'Asia/Baku', 'Asia/Bangkok', 'Asia/Barnaul', 'Asia/Beirut', 'Asia/Bishkek', 'Asia/Brunei', 'Asia/Chita', 'Asia/Choibalsan', 'Asia/Colombo', 'Asia/Damascus', 'Asia/Dhaka', 'Asia/Dili', 'Asia/Dubai', 'Asia/Dushanbe', 'Asia/Famagusta', 'Asia/Gaza', 'Asia/Hebron', 'Asia/Ho_Chi_Minh', 'Asia/Hong_Kong', 'Asia/Hovd', 'Asia/Irkutsk', 'Asia/Jakarta', 'Asia/Jayapura', 'Asia/Jerusalem', 'Asia/Kabul', 'Asia/Kamchatka', 'Asia/Karachi', 'Asia/Kathmandu', 'Asia/Khandyga', 'Asia/Kolkata', 'Asia/Krasnoyarsk', 'Asia/Kuala_Lumpur', 'Asia/Kuching', 'Asia/Kuwait', 'Asia/Macau', 'Asia/Magadan', 'Asia/Makassar', 'Asia/Manila', 'Asia/Muscat', 'Asia/Nicosia', 'Asia/Novokuznetsk', 'Asia/Novosibirsk', 'Asia/Omsk', 'Asia/Oral', 'Asia/Phnom_Penh', 'Asia/Pontianak', 'Asia/Pyongyang', 'Asia/Qatar', 'Asia/Qostanay', 'Asia/Qyzylorda', 'Asia/Riyadh', 'Asia/Sakhalin', 'Asia/Samarkand', 'Asia/Seoul', 'Asia/Shanghai', 'Asia/Singapore', 'Asia/Srednekolymsk', 'Asia/Taipei', 'Asia/Tashkent', 'Asia/Tbilisi', 'Asia/Tehran', 'Asia/Thimphu', 'Asia/Tokyo', 'Asia/Tomsk', 'Asia/Ulaanbaatar', 'Asia/Urumqi', 'Asia/Ust-Nera', 'Asia/Vientiane', 'Asia/Vladivostok', 'Asia/Yakutsk', 'Asia/Yangon', 'Asia/Yekaterinburg', 'Asia/Yerevan', 'Atlantic/Azores', 'Atlantic/Bermuda', 'Atlantic/Canary', 'Atlantic/Cape_Verde', 'Atlantic/Faroe', 'Atlantic/Madeira', 'Atlantic/Reykjavik', 'Atlantic/South_Georgia', 'Atlantic/St_Helena', 'Atlantic/Stanley', 'Australia/Adelaide', 'Australia/Brisbane', 'Australia/Broken_Hill', 'Australia/Darwin', 'Australia/Eucla', 'Australia/Hobart', 'Australia/Lindeman', 'Australia/Lord_Howe', 'Australia/Melbourne', 'Australia/Perth', 'Australia/Sydney', 'Europe/Amsterdam', 'Europe/Andorra', 'Europe/Astrakhan', 'Europe/Athens', 'Europe/Belgrade', 'Europe/Berlin', 'Europe/Bratislava', 'Europe/Brussels', 'Europe/Bucharest', 'Europe/Budapest', 'Europe/Busingen', 'Europe/Chisinau', 'Europe/Copenhagen', 'Europe/Dublin', 'Europe/Gibraltar', 'Europe/Guernsey', 'Europe/Helsinki', 'Europe/Isle_of_Man', 'Europe/Istanbul', 'Europe/Jersey', 'Europe/Kaliningrad', 'Europe/Kirov', 'Europe/Kyiv', 'Europe/Lisbon', 'Europe/Ljubljana', 'Europe/London', 'Europe/Luxembourg', 'Europe/Madrid', 'Europe/Malta', 'Europe/Mariehamn', 'Europe/Minsk', 'Europe/Monaco', 'Europe/Moscow', 'Europe/Oslo', 'Europe/Paris', 'Europe/Podgorica', 'Europe/Prague', 'Europe/Riga', 'Europe/Rome', 'Europe/Samara', 'Europe/San_Marino', 'Europe/Sarajevo', 'Europe/Saratov', 'Europe/Simferopol', 'Europe/Skopje', 'Europe/Sofia', 'Europe/Stockholm', 'Europe/Tallinn', 'Europe/Tirane', 'Europe/Ulyanovsk', 'Europe/Vaduz', 'Europe/Vatican', 'Europe/Vienna', 'Europe/Vilnius', 'Europe/Volgograd', 'Europe/Warsaw', 'Europe/Zagreb', 'Europe/Zurich', 'Indian/Antananarivo', 'Indian/Chagos', 'Indian/Christmas', 'Indian/Cocos', 'Indian/Comoro', 'Indian/Kerguelen', 'Indian/Mahe', 'Indian/Maldives', 'Indian/Mauritius', 'Indian/Mayotte', 'Indian/Reunion', 'Pacific/Apia', 'Pacific/Auckland', 'Pacific/Bougainville', 'Pacific/Chatham', 'Pacific/Chuuk', 'Pacific/Easter', 'Pacific/Efate', 'Pacific/Fakaofo', 'Pacific/Fiji', 'Pacific/Funafuti', 'Pacific/Galapagos', 'Pacific/Gambier', 'Pacific/Guadalcanal', 'Pacific/Guam', 'Pacific/Honolulu', 'Pacific/Kanton', 'Pacific/Kiritimati', 'Pacific/Kosrae', 'Pacific/Kwajalein', 'Pacific/Majuro', 'Pacific/Marquesas', 'Pacific/Midway', 'Pacific/Nauru', 'Pacific/Niue', 'Pacific/Norfolk', 'Pacific/Noumea', 'Pacific/Pago_Pago', 'Pacific/Palau', 'Pacific/Pitcairn', 'Pacific/Pohnpei', 'Pacific/Port_Moresby', 'Pacific/Rarotonga', 'Pacific/Saipan', 'Pacific/Tahiti', 'Pacific/Tarawa', 'Pacific/Tongatapu', 'Pacific/Wake', 'Pacific/Wallis']
I have developed a web app so when I enter a location in latitude and longitude, it displays the current time and time zone along with its respective location on the map.
-
- I initially wanted to use Google Maps Directions API for getting directions to a disaster area, however it was too difficult to implement so I switched to a more simple TimeZoneDB API.
- Difference in payload between client-side and server-side caused Unprocessable Entity problem which took me a while to find the source of it.
-
Issues related to research and documentation: #164 #162 #160 #199 #195 related to practice App: #213 #197 #190 #187 #181 #171
-
- Instance ID API
- endpoint: https://iid.googleapis.com/iid/info/IID_TOKEN
- GET request
- Description:
- Authorization: key=YOUR_API_KEY. Set this parameter in the header. *[optional] boolean details: set this query parameter to true to get FCM or GCM topic subscription information (if any) associated with this token. When not specified, defaults to false. On success the call returns HTTP status 200 and a JSON object containing:
- application - package name associated with the token.
- authorizedEntity - projectId authorized to send to the token.
- applicationVersion - version of the application.
- appSigner - sha1 fingerprint for the signature applied to the package.
- Indicates which party signed the app; for example,Play Store.
- platform - returns ANDROID, IOS, or CHROME to indicate the device platform to which the token belongs. If the details flag is set:
- rel - relations associated with the token. For example, a list of topic subscriptions. I used the API for retriveving topics subscribed for client tokens to show it on notifications page.
- Description:
- endpoint: https://iid.googleapis.com/iid/v1/IID_TOKEN/rel/topics/TOPIC_NAME
- POST request
- Works similarly for subscribing a topic. I used the functions for the usage of
this API:
messaging.subscribe_to_topic(subscription.token, subscription.topic)
- Works similarly for subscribing a topic. I used the functions for the usage of
this API:
- Firebase Cloud Messaging API
- Endpoint https://fcm.googleapis.com/v1/{parent=projects/*}/messages:send
- POST request
- parent: It contains the Firebase project id
- data has the message with topic, title and body and headers include an authorization token.
- The api can be used for sending notifications to topic and a client token
-
- create_subscription
- Route: /notifications/subscriptions
- Description: This function uses firebase API to subscribe a client to a topic using client token. Also the subscription is added to database.
- unsubscription
- Route: /notifications/unsubscribe
- Description: This function uses firebase API to unsubscribe a client from a topic using client token. Also the subscription is added to database.
- get_subscriptions
- Route: /notifications/subscriptions
- Description: This function return a list of subscriptions from database.
- get_client_info
- Route: /notifications/subscriptions/{device_token}
- Description: This function return a list of subscriptions of a single client using Instance ID API using device token.
- send_notif
- Route: /notifications/send_notification_to_one
- Description: This function sends a single notification to a device token.
- create_event
- Route: /notifications/send_notification
- Description: This function sends notifications to devices that are subscribed to a topic.
- get_notif
- Route: /notifications/send_notification
- Description: This function gets notifications that were sent.
- create_subscription
I have written 8 tests for the functions that checks if the calls are successfull:
-
- Request:
axios.post(`${process.env.NEXT_PUBLIC_BACKEND_URL}/notifications/send_notification`, JSON.stringify(notificationData), { headers: { 'content-type': 'application/json' } });
- Response:
HTTP/1.1 200 OK content-type: application/json [ {notification: 'Driver Need in Istanbul', message: 'Notification is sended'} ]
- Request:
axios.post(`${process.env.NEXT_PUBLIC_BACKEND_URL}/notifications/subscriptions`, JSON.stringify(subscriptionData), { headers: { 'content-type': 'application/json' } });
- Response:
HTTP/1.1 200 OK content-type: application/json [ {success_count: 1, message: 'Tokens were subscribed successfully'} ]
- Request:
axios.post(`${process.env.NEXT_PUBLIC_BACKEND_URL}/notifications/unsubscribe`, JSON.stringify(subscriptionData), { headers: { 'content-type': 'application/json' } });
- Response:
HTTP/1.1 200 OK content-type: application/json [ {success_count: 1, message: 'Tokens were unsubscribed successfully'} ]
- Request:
axios.get(`${process.env.NEXT_PUBLIC_BACKEND_URL}/notifications/subscriptions/${token}`, { headers: { 'content-type': 'application/json' } });
- Response:
HTTP/1.1 200 OK content-type: application/json ['Driver', 'Food']
- Request:
I have had a hard time with Firebase Cloud Messaging API configuration. I had to find a way to get Bearer token that expires in a short time. I used many secret keys and tokens, which caused some trouble to my group members:) Also I never had an experience with APIs, FastAPI and next.js. For all of those things I got many help from Merve Gürbüz. Also Firebase needs HTTPS connection for sending notification and others, I used ngrok for test purposes. I also had a hard time with git, but I feel more confident at resolving merge conflicts now. The most stressfull part was about database choice, it was decided to use NoSQL on the meeting but later on we decided on using both because it was asked. But on the implementation side it caused problems. I think it should be a good lesson that we should always follow the meetings, And if you are not confident about something and wont take responsibility, please trust other's opinion.
I tried to find APIs and resources for others to learn. Mainly focused on my individual work due to lack of knowledge.
- https://www.uuidtools.com/api/generate/v4: This API generates a UUID version 4 and returns it. There are other options available but this particular route is the one that I used.
I used /addel
for all my endpoints. Following routes adds to /addel
-
/add/resource
: POST with json body: Add resource to the database -
/add/need
: POST with json body: Add need to the database -
/add/event
: POST with json body: Add event to the database -
/add/action
: POST with json body: Add action to the database -
/get/resource
: GET: get the list of resources -
/get/need
: GET: get the list of needs -
/get/event
: GET: get the list of events -
/get/action
: GET: get the list of actions -
/get/resource?id=<id>
: GET: Get resource with id -
/get/need?id=<id>
: GET: Get need with id -
/get/event?id=<id>
: GET: Get event with id -
/get/action?id=<id>
: GET: Get action with id -
/delete/resource?id=<id>
: GET: Delete resource with id -
/delete/need?id=<id>
: GET: Delete need with id -
/delete/event?id=<id>
: GET: Delete event with id -
/delete/action?id=<id>
: GET: Delete action with id
Note: The parameters of the POST requests can be seen and be tested in docs: Swagger or Redoc. Note that these documentations also include UI endpoints as well.
For each combination of (resource, need, event, action) and (add, get, delete) there are related tests, there is no API endpoint that have not any unit test. There are total of 70 unit tests (and 1 mock test to cleanup).
For add tests, there are
- Full arguments tests
- Only required arguments tests
- 1 required argument missing tests for each required argument (should fail)
For get tests, there are
- Empty list tests
- At least 3 tests
- ID tests
- Unpresent ID tests (should return null)
For delete tests, there are
- ID tests
- Unpresent ID tests
An example test is as follows:
def test_full_arguments(self):
r = requests.post(f'http://0.0.0.0:8000{api_prefix}/add/resource', json={
"type": "food",
"location": "1234",
"notes": "this resource is here for a test",
"updated_at": "2020-04-01T12:00:00Z",
"is_active": True,
"upvotes": 0,
"downvotes": 0,
"creator_id": "test",
"creation_date": "2020-04-01T12:00:00Z",
"condition": "good",
"quantity": 1
})
# test response uuid
self.assertEqual(r.status_code, 200)
uuid = r.text[1:-1]
self.assertTrue(uuid4regex.match(uuid), f'uuid {uuid} does not match regex {uuid4regex.pattern}')
return uuid
Two types of requests have two different use of calls. POST requests require a json body, while (some of) GET requests require inline parameters. For each type of requests, the example is as follows:
import requests
uri = 'http://13.49.41.10:8000'
# POST Request Example
response = requests.post(uri + '/add/resource', json={
"type": "Food",
"location": "Istanbul",
"notes": "This is a test resource",
"updated_at": "12.05.2023",
"is_active": True,
"upvotes": 0,
"downvotes": 0,
"creator_id": "293874",
"creation_date": "12.05.2023",
"condition": "New",
"quantity": 1
})
print('Headers:', response.headers)
print('Status Code:', response.status_code)
print('Json:', response.json())
print()
# GET Request Example
response = requests.get(uri + '/get/resource?id=' + response.json())
print('Headers:', response.headers)
print('Status Code:', response.status_code)
print('Json:', response.json())
And the output is:
Headers: {'date': 'Fri, 12 May 2023 20:17:48 GMT', 'server': 'uvicorn', 'content-length': '38', 'content-type': 'application/json'}
Status Code: 200
Json: 787d8f8a-c9df-4fcc-b5fa-5c8d860dc630
Headers: {'date': 'Fri, 12 May 2023 20:17:48 GMT', 'server': 'uvicorn', 'content-length': '268', 'content-type': 'application/json'}
Status Code: 200
Json: {'uuid': '787d8f8a-c9df-4fcc-b5fa-5c8d860dc630', 'type': 'Food', 'location': 'Istanbul', 'notes': 'This is a test resource', 'updated_at': '12.05.2023', 'is_active': True, 'upvotes': 0, 'downvotes': 0, 'creator_id': '293874', 'creation_date': '12.05.2023', 'condition': 'New', 'quantity': 1}
I initially thought that we should make the dockerization and deployment by ourselves, and I did all these things. Here is the API that I deployed myself: http://13.49.41.10:8000/ (later I merged my code with shared code and docker and amazon server) The Dockerfile and other files related to the dockerization and deployment are deleted. But you can see these files in the history. As backend, I (and we) used fastapi. As database, I used PostgreSQL.
There were a lot of new things to learn in a short time, that was very very challenging. I initially tried to use MongoDB for database. I worked very hard and melted down 5 hours just struggling and I was failed. This was very depressing for me. But I recovered tomorrow, and used PostgreSQL (with the suggestions of my teammates: it is better if we could use the advantages of different types of databases).
Opening Discussions for Web Service, APIs, Git Actions
Note taking in Meeting 9
Issue numbers #160, #162, #180
Twilio API I post Twilio API twice once for sending SMS to proper phone number (I checked them if it contains +90 for Turkey and has a length 11 like 0532 123 45 67. However because sending an SMS needs verified outgoing taller numbers I also post another method which adds the number to verified outgoing caller numbers. I also post another method, which adds the number to verified outgoing caller numbers. But because my Twilio account is on trial ı couldn't post for them. Here is the response from Twilio:
-
- Name: SendGrid API
- Route: https://api.sendgrid.com/v3/mail/send
- Description: SendGrid's web API allows our customers to pull information about their email program without having to actually log on to SendGrid.com.
-
- Name: EmailReport
- Route: /emailreport
- Description: This function collects data from report form and sends an email about the report to the admin
# Request
?reporter=John%20Doe&activity=NEED352&reason=Spam&details=same%20need%20sent%20over%20and%20over
# Response
{
"_status_code": 202,
"_body": "",
"_headers": {
"Server": "nginx",
"Date": "Fri, 12 May 2023 22:18:56 GMT",
"Content-Length": "0",
"Connection": "close",
"X-Message-Id": "f_EHbqtoQ9qOny9heZX8sQ",
"Access-Control-Allow-Origin": "https://sendgrid.api-docs.io",
"Access-Control-Allow-Methods": "POST",
"Access-Control-Allow-Headers": "Authorization, Content-Type, On-behalf-of, x-sg-elas-acl",
"Access-Control-Max-Age": "600",
"X-No-CORS-Reason": "https://sendgrid.com/docs/Classroom/Basics/API/cors.html",
"Strict-Transport-Security": "max-age=600; includeSubDomains"
}
}
-
- I tried to add the generated report to the database as well as sending an e-mail, but I could not solve the problem.
-
- I wasted a lot of time looking for an unused api suitable for the project. I tried Wikidata and GoogleMaps apis but I couldn't get the result I wanted.
- Since I lost a lot of time in choosing the api, there is not enough time left for the backend and frontend part. Honestly, this part was more difficult than I thought. I had a hard time in the project as I have not used NextJS and MongoDb before.
- My teammates, especially Egecan and Merve, helped me when I got stuck. Even though I couldn't finish it on time, I learned a lot of new things.
I implemented a tweet processor that recognizes address from a given sentence or paragraph
http://ec2-13-49-231-144.eu-north-1.compute.amazonaws.com/word/word. This is the endpoint function I created. This endpoint functions calls hugging face endpoint deprem-ner endpoint
I implemented unit tests that one checks the behavior of my endpoint in case of missing required fields.
I implemented unit test that simulates the successful behavior.
I implemented unit test that returns error from server.
I really worked hard to install our fastapi server to aws. However, I also made the code modular for my teammates to work comfortably on their local pc. I also set-up ci/cd for immediate deployment and it worked without problem.
My biggest challenge was the server crashes. Whenever you miss a requirement or change the credentials of db connection the system would not respond. We couldn't communicate well through the end of semester and lack of communication ended up with some argue. However our end server functions well and I learned a lot about docker. Especially to debug-logging and managing overall server and security management.
-
- Twitter API
-
The route to create tweets is utilized:
https://api.twitter.com/2/tweets
- Posting tweet texts and chaining tweets (using the reply-to parameter) was possible
-
- Root for route is
/on-twitter
- Functions are:
-
/on-twitter/alive GET
Shows that the API is alive and available -
/on-twitter/check GET
Checks for the database connection etc. Returns OK or Failed. -
/on-twitter/getPublishedTweets/{eventId} GET
Returns the urls of the published tweets about the event described by the eventId -
/on-twitter/publishTweets/{eventId} POST
Publish the event summary messages in a tweet chain.
-
- Root for route is
http://localhost:8000/on-twitter/getPublishedTweets/100124
http://localhost:8000/on-twitter/publishTweets/100124
-
- Used a nosql database for the first time. It was a short experience, an introduction so to say.
- Twitter API has its own problems and troubles. "Free" features are not well defined. My intentions to make use of it in smart ways failed. I had a plan to use the Twitter Spaces (audio chat with transcripting facilitise) as a means of communication with speech to text option. There is a python project for this by the way
-
- Twitter API itself was a challenge
-
-
- get_coord
- Route: /location
- Description: This function collects coordinate values from MongoDB collection and responses it.
- insert_coord
- Route: /location/insert
- Description: This function retrieves new markers and adds it to MongoDB.
- get_coord
-
- Tests similar to the following were made during the implementation:
-
def test_locations_fetch(): response = client.get("/location/") assert response.status_code == 200 assert response.json() == [ {'username': 'ali', 'x_coord': 37.08328368718349, 'y_coord': 36.83810055490806}, {'username': 'kemal', 'x_coord': 38.934396015417725, 'y_coord': 38.32618261611021}, {'username': 'ayşe', 'x_coord': 39.915131681651594, 'y_coord': 37.35510170315383} ] def test_locations_insert(): response = client.post("/location/insert",json={"items": [{'x_coord': 38.64261790634527, 'y_coord': 37.27661132812501}]}) assert response.status_code == 200 assert response.json() == [ {'x_coord': '38.64261790634527', 'y_coord': '37.27661132812501'} ]
-
- Tests similar to the following were made during the implementation:
-
- Request:
axios.get('http://127.0.0.1:8000/location/')
- Response:
HTTP/1.1 200 OK content-type: application/json [ {'username': 'ali', 'x_coord': 37.08328368718349, 'y_coord': 36.83810055490806}, {'username': 'kemal', 'x_coord': 38.934396015417725, 'y_coord': 38.32618261611021}, {'username': 'ayşe', 'x_coord': 39.915131681651594, 'y_coord': 37.35510170315383} ]
- Request:
axios.post("/location/insert",json={"items": [{'x_coord': 38.64261790634527, 'y_coord': 37.27661132812501}]})
- Response:
HTTP/1.1 200 OK content-type: application/json [ {'x_coord': '38.64261790634527', 'y_coord': '37.27661132812501'} ]
- Request:
-
- Frontend: Creating map is very crucial for my API. I chose leaflet for it, however it was very challenging. I had to close the server rendering in order to render it on the browser.
-
- Name: OpenAQ
- Route: https://api.openaq.org/v2/latest?limit=10&page=1&offset=0
- Description: The OpenAQ API provides air quality data from various locations around the world. The API is used to fetch the latest measurements, which are then filtered and sorted as per user request.
-
- get_measurements
- Route: /aqdata
- Description: This function uses the OpenAQ API to fetch the latest air quality measurements. It also handles filtering and sorting of these measurements based on the parameters provided.
- filter_and_sort_resources_mongo
- Route: /resources
- Description: This function retrieves resources from the MongoDB collection 'Resources'. It allows filtering and sorting of these resources based on the parameters provided.
- create_resource
- Route: /resources
- Description: This function creates a new resource in the MongoDB 'Resources' collection.
- delete_resource
- Route: /resources/{resource_id}
- Description: This function deletes a specific resource in the MongoDB 'Resources' collection.
- clear_resources
- Route: /resources/clear
- Description: This function deletes all resources in the MongoDB 'Resources' collection.
- get_measurements
-
- Tests similar to the following were made during the implementation:
-
def test_get_measurements_success(): response = client.get("/aqdata") assert response.status_code == 200 def test_get_measurements_fail(): response = client.get("/aqdata", params={"country": "nonexistent"}) assert response.status_code == 404
-
- Tests similar to the following were made during the implementation:
-
- Request to /aqdata:
curl -X GET "http://localhost:8000/aqdata?country=TR&city=Denizli&sort=desc"
- Response:
HTTP/1.1 200 OK content-type: application/json [ { "country": "TR", "city": "Denizli", "measurement": "pm25", "lastUpdated": "2023-04-12T10:30:00Z" }, ... ]
- Filter & Sort request to /resources:
curl -X GET "http://localhost:8000/resources?type=book&sort_by=upvotes"
- Response:
HTTP/1.1 200 OK content-type: application/json [ { "id": "1", "type": "book", # filtered type "location": "Location1", "notes": "Notes1", "updated_at": "2023-04-12T10:30:00Z", "is_active": true, "upvotes": 10, # sorted field "downvotes": 2, "creator_id": "Creator1", "creation_date": "2023-04-12T10:30:00Z", "condition": "Good", "quantity": 5 }, { "id": "3", "type": "book", # filtered type "location": "Location1", "notes": "Notes1", "updated_at": "2023-04-12T10:30:00Z", "is_active": true, "upvotes": 16, # sorted field "downvotes": 2, "creator_id": "Creator7", "creation_date": "2023-04-10T10:30:00Z", "condition": "Good", "quantity": 4 } ... ]
- Request to /aqdata:
-
- Connecting to Server: One of the initial challenges was to establish a connection to the server. This process involved understanding how to configure the server correctly and working with different settings and configurations. It was a new experience for me and took a considerable amount of time and effort.
- API Implementation: The implementation of the API backend was another significant challenge. Understanding how to correctly use FastAPI, work with MongoDB, and manipulate data according to the requirements was not straightforward for me personally. Despite these challenges, with the collaborative effort and support from my team members, especially Egecan and Merve, I was able to overcome them. Their guidance was instrumental in understanding the concepts and executing the tasks effectively.
- Frontend: Unfortunately, due to time constraints and the complexity of the tasks, I was unable to complete the frontend for this release. This is an area I aim to focus on and improve in future projects.
📌 Communication Plan
📌 Docker and local deployment tutorial
📌 RAM
📌 Test Traceability Matrix