Merge pull request 'turner_ilya_lab_3' (#125) from turner_ilya_lab_3 into main

Reviewed-on: #125
This commit is contained in:
Alexey 2024-11-25 20:56:30 +04:00
commit b92c0ca599
9 changed files with 415 additions and 0 deletions

2
turner_ilya_lab_3/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/.venv
/.idea

View File

@ -0,0 +1,36 @@
# Лабораторная работа №3 - REST API, Gateway и синхронный обмен между микросервисами
## ПИбд-42 || Тюрнер Илья
### Описание:
В рамках данной лабораторной работы были реализованы две сущности со связью "один-ко-многим", также были разработаны все CRUD-операции: получение списка записей, получение одной записи, создание, редактирование и удаление записи.
У сущности факультета также реализована возможность получения записи со списком всх специальностей. В этом случае сервис взаимодействует с другим сервисом. Еще реализована проверка наличия факультета по id.
У сущности специальности также реализованы возможности: получение списка записей с информацией о факультете, получение списка записей по id факультета и получение одной записи с информацией о факультете.
### Цель лабораторной работы
изучение шаблона проектирования gateway, построения синхронного обмена между микросервисами и архитектурного стиля RESTful API
### Выбранные сущности:
- Факультет. Поля: Uuid, Name и Description
- Специальность. Поля: Uuid, Name, CountPlaces и Faculty_Id
### Инструкция для работы
1. Клонирование репозитория:
```
git clone <ссылка-на-репозиторий>
cd <папка репозитория>
cd <папка лабораторной работы>
```
2. Запуск контейнеров:
```
docker compose up --build
```
3. Результаты:
В результате можно будет применят CRUD операции к сущностям через http запросы. Для демонстрации был выбран знакомый инструмент Postman.
### Видео с демонстрацией работы:
Размещено на платформе VK видео
https://vk.com/video/@tyurner02?z=video303312410_456239079%2Fpl_303312410_-2

View File

@ -0,0 +1,27 @@
services:
faculty_service:
container_name: faculty_service
build:
context: .
dockerfile: ./faculty_service/Dockerfile
expose:
- 10001
speciality_service:
container_name: speciality_service
build:
context: .
dockerfile: ./speciality_service/Dockerfile
expose:
- 10002
nginx:
image: nginx:latest
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- faculty_service
- speciality_service

View File

@ -0,0 +1,17 @@
#Берем базовый образ python
FROM python:3.12
#Устанавливаем рабочую директорию
WORKDIR /app
#Копируем requirements.txt в контейнер
COPY requirements.txt .
#Подтягиваем зависимости
RUN pip install --no-cache-dir -r requirements.txt
#Копируем файлы программы в контейнер
COPY faculty_service/faculty_service.py .
#Задаем команду для выполнения программы
CMD ["python", "faculty_service.py"]

View File

@ -0,0 +1,129 @@
from flask import Flask, jsonify, request
from uuid import uuid4
import uuid
import requests
class Faculty:
def __init__(self, name, description, uuid_: uuid):
if uuid_ is None:
self.uuid_: uuid = uuid4()
else:
self.uuid_: uuid = uuid.UUID(uuid_)
self.name: str = name
self.description: str = description
def to_dict(self):
return {
"uuid": self.uuid_,
"name": self.name,
"description": self.description
}
def to_dict_with_specialities(self, specialities: list):
return {
"uuid": self.uuid_,
"name": self.name,
"description": self.description,
"specialities": specialities
}
app = Flask(__name__)
faculties: list[Faculty] = [
Faculty(name='ФИСТ', description='Факультет информационных систем и технологий', uuid_='3c87a9a1-1a3e-402d-8a53-6bbba478e644'),
Faculty(name='ГФ', description='Гуманитарный факультет', uuid_='a9e36307-02dc-4ee0-a771-e6d39cae9a75'),
Faculty(name='РТФ', description='Радиотехнический факультет', uuid_='3e7fc35d-f3bb-4657-bdcf-cf19191f3bc0'),
Faculty(name='ИЭФ', description='Инженерно-экономический факультет', uuid_='bb8571c4-f4a9-4d6e-b1f3-6efe4f54166e')
]
specialities_url = 'http://speciality_service:10002/'
def list_jsonify():
return jsonify([faculty.to_dict() for faculty in faculties])
# получение списка всех факультетов
@app.route('/', methods=['GET'])
def get_all():
return list_jsonify(), 200
# получение факультета по id
@app.route('/<uuid:uuid_>', methods=['GET'])
def get_one(uuid_):
for faculty in faculties:
if faculty.uuid_ == uuid_:
return faculty.to_dict(), 200
return 'Факультет с указанным uuid не обнаружен', 404
# получение факультета со списком специальностей
@app.route('/with-specialities/<uuid:uuid_>', methods=['GET'])
def get_one_with_specialities(uuid_):
for faculty in faculties:
if faculty.uuid_ == uuid_:
response = requests.get(specialities_url + f'by-faculty/{uuid_}')
print(response.json())
return faculty.to_dict_with_specialities(response.json()), 200
return 'Факультет с указанным uuid не обнаружен', 404
# проверка наличия факультета по id (для специальности)
@app.route('/check/<uuid:uuid_>', methods=['GET'])
def check_exist(uuid_):
for faculty in faculties:
if faculty.uuid_ == uuid_:
return '', 200
return '', 404
# создание факультета
@app.route('/', methods=['POST'])
def create():
data = request.json
name = data.get('name', None)
description = data.get('description', None)
if name is None or description is None:
return 'Не хватает данных для создания нового факультета', 404
new_faculty = Faculty(name, description, None)
faculties.append(new_faculty)
return get_one(new_faculty.uuid_)
# изменение факультета по id
@app.route('/<uuid:uuid_>', methods=['PUT'])
def update_by_id(uuid_):
data = request.json
new_name = data.get('name', None)
new_description = data.get('description', None)
for faculty in faculties:
if faculty.uuid_ == uuid_:
if new_name is not None:
faculty.name = new_name
if new_description is not None:
faculty.description = new_description
return get_one(faculty.uuid_)
return 'Факультет с указанным uuid не обнаружен', 404
# удаление факультета по id
@app.route('/<uuid:uuid_>', methods=['DELETE'])
def delete(uuid_):
for faculty in faculties:
if faculty.uuid_ == uuid_:
faculties.remove(faculty)
return 'Факультет был удален', 200
return 'Факультет с указанным uuid не обнаружен', 404
if __name__ == '__main__':
app.run(host='0.0.0.0', port=10001, debug=True)

View File

@ -0,0 +1,26 @@
events { worker_connections 1024; }
http {
server {
listen 80;
listen [::]:80;
server_name localhost;
location /faculty_service/ {
proxy_pass http://faculty_service:10001/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /speciality_service/ {
proxy_pass http://speciality_service:10002/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
}

Binary file not shown.

View File

@ -0,0 +1,17 @@
#Берем базовый образ python
FROM python:3.12
#Устанавливаем рабочую директорию
WORKDIR /app
#Копируем requirements.txt в контейнер
COPY requirements.txt .
#Подтягиваем зависимости
RUN pip install --no-cache-dir -r requirements.txt
#Копируем файлы программы в контейнер
COPY speciality_service/speciality_service.py .
#Задаем команду для выполнения программы
CMD ["python", "speciality_service.py"]

View File

@ -0,0 +1,161 @@
from flask import Flask, jsonify, request
import requests
from uuid import uuid4
import uuid
class Speciality:
def __init__(self, name, countPlaces, uuid_: uuid, faculty_id: uuid):
if uuid_ is None:
self.uuid_: uuid = uuid4()
else:
self.uuid_: uuid = uuid.UUID(uuid_)
self.name: str = name
self.countPlaces: int = countPlaces
self.faculty_id: uuid = uuid.UUID(faculty_id)
def to_dict(self):
return {
'name': self.name,
'countPlaces': self.countPlaces,
'faculty_id': self.faculty_id,
'uuid': self.uuid_
}
def to_dict_for_faculties(self):
return {
'name': self.name,
'countPlaces': self.countPlaces,
'uuid': self.uuid_
}
def to_dict_with_info(self, faculty: dict):
return {
'name': self.name,
'countPlaces': self.countPlaces,
'faculty_id': self.faculty_id,
'faculty_info': faculty,
'uuid': self.uuid_
}
app = Flask(__name__)
specialities: list[Speciality] = [
Speciality(name='Программная инженерия', countPlaces=39, uuid_='a6233a8e-7b54-4927-954a-cafbdbf142fe',
faculty_id='3c87a9a1-1a3e-402d-8a53-6bbba478e644'),
Speciality(name='Прикладная информатика', countPlaces=53, uuid_='c1863dee-7b27-4252-9327-9226dd10eebe',
faculty_id='3c87a9a1-1a3e-402d-8a53-6bbba478e644'),
Speciality(name='Лингвистика', countPlaces=23, uuid_='da1f1594-39c7-4407-8c54-883e9a8565ea',
faculty_id='a9e36307-02dc-4ee0-a771-e6d39cae9a75'),
Speciality(name='Радиотехника', countPlaces=46, uuid_='a7156dbf-b102-40bd-9eb9-d53c110cfe0d',
faculty_id='603e552f-2b53-4d60-b13b-700efa4dfbb9'),
Speciality(name='Управление качеством', countPlaces=17, uuid_='1a24d9b7-72d2-4918-8a86-b80608c855f6',
faculty_id='3e7fc35d-f3bb-4657-bdcf-cf19191f3bc0')
]
facuties_url = 'http://faculty_service:10001/'
def list_jsonify():
return jsonify([speciality.to_dict() for speciality in specialities])
# получить список специальностей
@app.route('/', methods=['GET'])
def get_all():
return list_jsonify(), 200
# получить список специальностей с информацией о факультете
@app.route('/full', methods=['GET'])
def get_all_full():
faculties: list[dict] = requests.get(facuties_url).json()
response = []
for speciality in specialities:
for faculty in faculties:
if speciality.faculty_id == uuid.UUID(faculty.get('uuid')):
response.append(speciality.to_dict_with_info(faculty))
return response, 200
# получение списка специальностей по id факультета
@app.route('/by-faculty/<uuid:faculty_uuid>', methods=['GET'])
def get_by_faculty_id(faculty_uuid):
return [speciality.to_dict_for_faculties() for speciality in specialities if speciality.faculty_id == faculty_uuid], 200
# получение специальности по id
@app.route('/<uuid:uuid_>', methods=['GET'])
def get_one(uuid_):
for speciality in specialities:
if speciality.uuid_ == uuid_:
return speciality.to_dict(), 200
return 'Специальность с указанным uuid не обнаружена', 404
# получение специальности по id с информацией о факультете
@app.route('/full/<uuid:uuid_>', methods=['GET'])
def get_one_full(uuid_):
for speciality in specialities:
if speciality.uuid_ == uuid_:
response = requests.get(facuties_url + str(speciality.faculty_id))
return speciality.to_dict_with_info(response.json()), 200
return 'Специальность с указанным uuid не обнаружена', 404
# создание новой специальности
@app.route('/', methods=['POST'])
def create():
data = request.json
name = data.get('name', None)
countPlaces = data.get('countPlaces', None)
faculty_id = data.get('faculty_id', None)
checking = requests.get(facuties_url + f'/check/{faculty_id}')
print(checking)
if checking.status_code == 200:
new_speciality = Speciality(name, countPlaces, None, faculty_id)
specialities.append(new_speciality)
return get_one(new_speciality.uuid_)
if checking.status_code == 404:
return 'Факультета с таким uuid не существует', 404
return 'Неизвестная ошибка', 500
# изменение специальности по id
@app.route('/<uuid:uuid_>', methods=['PUT'])
def update_by_id(uuid_):
data = request.json
new_name = data.get('name', None)
new_countPlaces = data.get('countPlaces', None)
for speciality in specialities:
print(speciality.uuid_)
if speciality.uuid_ == uuid_:
if new_name is not None:
speciality.name = new_name
if new_countPlaces is not None:
speciality.countPlaces = new_countPlaces
return get_one(speciality.uuid_)
return 'Специальность с указанным uuid не обнаружена', 404
# удаление специальности по id
@app.route('/<uuid:uuid_>', methods=['DELETE'])
def delete(uuid_):
for speciality in specialities:
if speciality.uuid_ == uuid_:
specialities.remove(speciality)
return 'Специальность была удалена', 200
return 'Специальность с указанным uuid не обнаружена', 404
if __name__ == '__main__':
app.run(host='0.0.0.0', port=10002, debug=True)