سلیب پس از ترجمهی نقشه متوجه شد که گنج جایی در آبادی «بلهآباد» است که در پشت کوههای جزیره پنهان شدهاست. او سریعاً حرکت کرد و هنگامیکه به پشت کوههای «بلهآباد» رسید، متوجه شد که مردمان این آبادی، سامانهای شبیه سیستم بلاگ Medium ندارند. برای سلیب این موقعیت خوبی بود تا به جای جستوجوی شبانهروزی به دنبال گنج، از این طریق ثروتی کسب کند و بیخیال گنج بشود؛ پس درخواست پیادهسازی این سامانه را با مشخصاتی که در ادامه توضیح میدهیم، از شما دارد. :)
جزئیات پروژه
پروژهی اولیه را از این لینک دانلود کنید.
در این سؤال، ما با دو بخش کلی در ارتباط هستیم: بخش کاربران و بخش بلاگها. در ادامه جزئیات هر بخش را بیان میکنیم.
بخش کاربران
برای این بخش نیاز است تعدادی REST API شامل endpointهای زیر پیادهسازی شوند:
آدرس | عنوان |
---|---|
GET / |
بررسی up بودن سرویس |
POST /auth/signup/ |
ثبتنام |
POST /auth/login/ |
ورود به حساب کاربری |
POST /auth/logout/ |
خروج از حساب کاربری |
در این API هر کاربر باید یک توکن داشته باشد. این توکن برای هر کاربر ثابت است.
پیادهسازی endpointهای موردنیاز بخش کاربر
در همهی endpointها، پاسخ باید بهصورت JSON باشد.
اکیداً توصیه میگردد برای پیادهسازی بخش کاربر از JWT استفاده کنید.
اطلاعات ورودی بهصورت application/x-www-form-urlencoded
به endpointها ارسال میشوند.
بررسی up بودن سرویس
پاسخ این endpoint باید بهصورت زیر باشد:
- کد وضعیت:
200
- بدنه:
{"message":Welcome to Medium API}
ثبتنام
سه پارامتر username
و password
و email
به این endpoint ارسال میشوند. درصورتیکه حداقل یکی از پارامترهای username
و password
ارسال نشده باشد یا برابر با رشتهی خالی باشد، پاسخ باید بهصورت زیر باشد:
هر دو پارامتر username
و password
خالی:
- کد وضعیت:
400
- بدنه:
{
"error": {
"username": ["This field may not be blank."],
"password": ["This field may not be blank."],
}
}
پارامتر username
خالی:
- کد وضعیت:
400
- بدنه:
{
"error": {
"username": ["This field may not be blank."]
}
}
پارامتر password
خالی:
- کد وضعیت:
400
- بدنه:
{
"error": {
"password": ["This field may not be blank."]
}
}
اگر کاربری با نام کاربری واردشده، از قبل موجود باشد، پاسخ باید بهصورت زیر باشد:
- کد وضعیت:
400
- بدنه:
{
"error": {
"username": ["A user with that username already exists."],
}
}
در غیر این صورت، کاربر باید ساخته شود، یک توکن یکتا برایش تولید شود و پاسخ بهصورت زیر باشد:
- کد وضعیت:
201
- بدنه:
{
"refresh": "[REFRESH_TOKEN]",
"access": "[ACCESS_TOKEN]",
}
ورود به حساب کاربری
دو پارامتر username
و password
باید به این endpoint ارسال شوند. درصورتیکه حداقل یکی از این پارامترها ارسال نشده باشد یا برابر با رشتهی خالی باشد، پاسخ باید بهصورت زیر باشد:
- کد وضعیت:
401
- بدنه:
{"error": "Invalid credentials"}
اگر نام کاربری یا رمز عبور نادرست باشد، پاسخ باید بهصورت زیر باشد:
- کد وضعیت:
401
- بدنه:
{"error": "Invalid credentials"}
در غیر این صورت، پاسخ باید بهصورت زیر باشد:
- کد وضعیت:
200
- بدنه:
{
"refresh": "[REFRESH_TOKEN]",
"access": "[ACCESS_TOKEN]",
}
خروج از حساب کاربری
تنها پارامتر refresh
باید به این endpoint ارسال شود. درصورتیکه این پارامتر ارسال نشده باشد یا برابر با رشتهی خالی باشد یا حتی مقدار درستی نداشته باشد، پاسخ باید بهصورت زیر باشد:
- کد وضعیت:
400
- بدنه:
{}
در غیر این صورت، پاسخ باید بهصورت زیر باشد:
- کد وضعیت:
205
- بدنه:
{}
بخش بلاگ
برای این بخش نیاز است تا تعدادی REST API شامل endpointهای زیر باید پیادهسازی شوند:
آدرس | عنوان |
---|---|
GET /blogs/ |
دریافت تمامی بلاگهای ثبت شده |
POST /blogs/create/ |
ایجاد بلاگ جدید |
PUT /blogs/<int:pk>/ |
آپدیت بلاگ |
DELETE /blogs/<int:pk>/ |
حذف بلاگ |
GET /blogs/<int:pk>/detail/ |
مشاهده بلاگ |
POST /blogs/<int:pk>/like/ |
لایک کردن بلاگ |
POST /blogs/analytics/ |
اوضاع کلی حساب نویسنده |
پیادهسازی endpointهای موردنیاز بخش بلاگ
در همهی endpointها، پاسخ باید بهصورت JSON باشد.
اطلاعات ورودی بهصورت application/x-www-form-urlencoded
به endpointها ارسال میشوند.
دریافت تمامی بلاگهای ثبتشده
این endpoint نیازمند authentication نیست.
نیازی به ارسال هیچ پارامتری به این endpoint نیست و در همهی حالات باید جواب برابر با مقدار زیر باشد:
- کد وضعیت:
200
- بدنه:
[
{
"id": "[BLOG_ID]",
"title": "[BLOG_TITLE]",
"content": "[BLOG_CONTENT]",
"views": "[BLOG_VIEWS]",
"likes": "[BLOG_LIKES]",
},
...
]
بلاگهای برگرداندهشده باید به ترتیب زمان ثبت بلاگ باشند.
ایجاد بلاگ جدید
این endpoint نیازمند authentication است. در ریکوئست ارسالی، مقدار هدر Authorization
باید برابر با توکن کاربر باشد (توکن همواره همراه با Bearer
ارسال میشود.).
پارامتر title
و content
باید به این endpoint ارسال شود. درصورتیکه یکی از این پارامترها ارسال نشده باشد یا برابر با رشتهی خالی باشد، پاسخ باید بهصورت زیر باشد:
- کد وضعیت:
400
- بدنه:
{
"error": {
"title": ["This field may not be blank."],
"content": ["This field may not be blank."],
}
}
در غیر این صورت، بلاگ باید ثبت شود و پاسخ بهصورت زیر باشد:
- کد وضعیت:
201
- بدنه:
{
"id": "[BLOG_ID]",
"title": "[BLOG_TITLE]",
"content": "[BLOG_CONTENT]",
"views": "[BLOG_VIEWS]",
"likes": "[BLOG_LIKES]",
}
آپدیت بلاگ
این endpoint نیازمند authentication است. در ریکوئست ارسالی مقدار هدر Authorization
باید برابر با توکن کاربر باشد (توکن همواره همراه با Bearer
ارسال میشود.).
پارامتر title
و content
باید به این endpoint ارسال شود. درصورتیکه یکی از این پارامترها ارسال نشده باشد یا برابر با رشتهی خالی باشد، پاسخ باید بهصورت زیر باشد:
- کد وضعیت:
400
- بدنه:
{
"error": {
"title": ["This field may not be blank."],
"content": ["This field may not be blank."],
}
}
درصورتیکه pk
موجود در URL در دیتابیس وجود نداشته باشد، پاسخ باید بهصورت زیر باشد:
- کد وضعیت:
404
- بدنه:
{}
درصورتیکه کاربر ارسالکنندهی ریکوئست برابر با نویسندهی بلاگ نباشد، پاسخ باید بهصورت زیر باشد:
- کد وضعیت:
403
- بدنه:
{}
در غیر این صورت، بلاگ باید آپدیت شود و پاسخ بهصورت زیر باشد:
- کد وضعیت:
200
- بدنه:
{
"id": "[BLOG_ID]",
"title": "[BLOG_TITLE]",
"content": "[BLOG_CONTENT]",
"views": "[BLOG_VIEWS]",
"likes": "[BLOG_LIKES]",
}
حذف بلاگ
این endpoint نیازمند authentication است. در ریکوئست ارسالی مقدار هدر Authorization
باید برابر با توکن کاربر باشد (توکن همواره همراه با Bearer
ارسال میشود.).
پارامتری به این endpoint ارسال نمیشود. در صورتی که pk
موجود در URL در دیتابیس وجود نداشته باشد، پاسخ باید بهصورت زیر باشد:
- کد وضعیت:
404
- بدنه:
{}
در صورتی که کاربر ارسالکنندهی ریکوئست برابر با نویسنده بلاگ نباشد، پاسخ باید بهصورت زیر باشد:
- کد وضعیت:
403
- بدنه:
{}
در غیر این صورت، بلاگ باید حذف شود و پاسخ بهصورت زیر باشد:
- کد وضعیت:
204
- بدنه:
{}
مشاهدهی بلاگ
این endpoint نیازمند authentication نیست.
نیازی به ارسال هیچ پارامتری به این endpoint نیست. در همهی حالات، پاسخ باید برابر با مقادیر زیر باشد:
درصورتیکه pk
موجود در URL در دیتابیس وجود نداشته باشد، پاسخ باید بهصورت زیر باشد:
- کد وضعیت:
404
- بدنه:
{}
در غیر اینصورت، بلاگ باید ثبت شود و پاسخ بهصورت زیر باشد:
- کد وضعیت:
200
- بدنه:
{
"id": "[BLOG_ID]",
"title": "[BLOG_TITLE]",
"content": "[BLOG_CONTENT]",
"views": "[BLOG_VIEWS]",
"likes": "[BLOG_LIKES]",
}
توجه داشته باشید که درصورتیکه بلاگ موردنظر وجود داشت و کاربر بدون مشکل میتوانست آن را مشاهده کند، باید به مقدار view
ی بلاگ، یک واحد اضافه کنید و سپس بلاگ را برگردانید. یعنی بازدید فعلی کاربر در میزان بازدیدهای بلاگ برگرداندهشده باید محاسبه شده باشد.
نکتهی ریت لیمیت: در اینجا نیاز به پیادهسازی سازوکاری برای ریت لیمیت داریم. میخواهیم هر device_id
و ip
بتواند تنها 10 بار هر بلاگ را ببیند تا میزان ویوهای هر بلاگ، شهودی واقعی از میزان دیدهشدن بلاگ بدهد. تا 10 ریکوئست برای گرفتن یک بلاگ از یک device_id
و ip
را بدون مشکل باز گردانید و مقدار ویوی بلاگ را هم اضافه کنید. به محض عبور از 10 نیاز است که پاسخ برابر زیر باشد و به مقدار ویوی بلاگ هیچ عددی اضافه نشود.
- کد وضعیت:
429
- بدنه:
{}
لایک کردن بلاگ
این endpoint نیازمند authentication است. در ریکوئست ارسالی مقدار هدر Authorization
باید برابر با توکن کاربر باشد (توکن همواره همراه با Bearer
ارسال میشود.).
پارامتری به این endpoint ارسال نمیشود. درصورتیکه pk
موجود در URL در دیتابیس وجود نداشته باشد، پاسخ باید بهصورت زیر باشد:
- کد وضعیت:
404
- بدنه:
{}
درصورتیکه توکن Authorization
وجود نداشت و در اصل یوزر لاگین نبود، پاسخ باید بهصورت زیر باشد:
- کد وضعیت:
401
- بدنه:
{}
در غیر این صورت، باید یک عدد به لایکهای بلاگ اضافه شود و پاسخ بهصورت زیر باشد:
- کد وضعیت:
200
- بدنه:
{
"id": "[BLOG_ID]",
"title": "[BLOG_TITLE]",
"content": "[BLOG_CONTENT]",
"views": "[BLOG_VIEWS]",
"likes": "[BLOG_LIKES]",
}
اوضاع کلی حساب نویسنده
این endpoint نیازمند authentication است. در ریکوئست ارسالی مقدار هدر Authorization
باید برابر با توکن کاربر باشد (توکن همواره همراه با Bearer
ارسال میشود.).
درصورتیکه توکن Authorization
وجود نداشت و در اصل یوزر لاگین نبود، پاسخ باید بهصورت زیر باشد:
- کد وضعیت:
401
- بدنه:
{}
در غیر این صورت، باید مجموع تمام view
و like
های تمام بلاگهای نویسنده را محاسبه کند و پاسخ بهصورت زیر باشد:
- کد وضعیت:
200
- بدنه:
{
"total_views": "int",
"total_likes": "int"
}
امتیاز ویژه: در این مسئله داکر فایل و داکر کامپوز در اختیار شماست و نحوهی پیادهسازی کاملاً بر عهدهی خودتان است. اگر دیتابیس مورداستفادهتان را ماندگار و یا persist
کنید، امتیاز بیشتری از مسئله دریافت میکنید.
شما تنها مجاز به استفاده از ایمیجهای موجود در این لینک در داکر فایل و داکر کامپوز خود هستید.
نکات تکمیلی
نصب نیازمندیها و اجرا
برای حل این سؤال میتوانید از هر زبان و هر تکنولوژیای که میخواهید استفاده کنید. بهصورتیکه در یک پوشه به نام medium
کد برنامه را بنویسید. توجه کنید که حتماً باید Dockerfile
مربوط به پروژهی خود را برای ما ارسال کنید.
- نیازی به persistent بودن دادهها نیست! اما برای دریافت امتیاز کامل مسئله باید دیتاها را persistent کنید.
- سیستم داوری
docker-compose.yml
تحویلی شما را که در خارج از فولدرmedium
قرار دارد، با دستورdocker-compose up --build
اجرا میکند.
version: "3"
# add your services here
- شما مجاز به تغییر یا ارسال
docker-compose.yml
دلخواه هستید. - نام سرویس و کانتیر کد سرور شما در فایل
docker-compose.yml
حتما باید برابر باmedium
باشد. - سرویس شما باید روی پورت
80
آدرسlocalhost
قابل دسترسی باشد. - توصیه میکنیم خود APIتان را روی
0.0.0.0:80
اجرا کنید. - ورژن فایل داکرکامپوز شما باید حتما برابر با
3
باشد.
نحوهی ارسال پاسخ
شما میتوانید تمامی محتوای موجود در پوشهی medium
را تغییر دهید و هر فایلی که میخواهید اضافه یا کم کنید.
├── medium
├──├── [ALL_YOUR_PROJECT] # or main.go somefile.js anyfile.php name.any ...
├──├── Dockerfile
└── docker-compose.yml
توجه کنید که نام فایل کد شما برای سیستم داوری اهمیتی ندارد و این خود شما هستید که در داکر فایل و داکر کامپوزتان از نام آن برای اجرای پروژه استفاده میکنید.
در نهایت پوشه medium
را به همراه docker-compose.yml
ZIP کرده و ارسال کنید. توجه کنید که پس از extract کردن فایل ZIP شما، باید فایل docker-compose.yml
پوشهی medium
را ببینیم که درون آن Dockerfile
وجود دارد.
ارسال پاسخ برای این سؤال