Merge pull request 'turner_ilya_lab_3' (#125) from turner_ilya_lab_3 into main
Reviewed-on: #125
This commit is contained in:
commit
b92c0ca599
2
turner_ilya_lab_3/.gitignore
vendored
Normal file
2
turner_ilya_lab_3/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/.venv
|
||||||
|
/.idea
|
36
turner_ilya_lab_3/README.md
Normal file
36
turner_ilya_lab_3/README.md
Normal 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
|
27
turner_ilya_lab_3/docker-compose.yaml
Normal file
27
turner_ilya_lab_3/docker-compose.yaml
Normal 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
|
17
turner_ilya_lab_3/faculty_service/Dockerfile
Normal file
17
turner_ilya_lab_3/faculty_service/Dockerfile
Normal 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"]
|
129
turner_ilya_lab_3/faculty_service/faculty_service.py
Normal file
129
turner_ilya_lab_3/faculty_service/faculty_service.py
Normal 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)
|
26
turner_ilya_lab_3/nginx.conf
Normal file
26
turner_ilya_lab_3/nginx.conf
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
BIN
turner_ilya_lab_3/requirements.txt
Normal file
BIN
turner_ilya_lab_3/requirements.txt
Normal file
Binary file not shown.
17
turner_ilya_lab_3/speciality_service/Dockerfile
Normal file
17
turner_ilya_lab_3/speciality_service/Dockerfile
Normal 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"]
|
161
turner_ilya_lab_3/speciality_service/speciality_service.py
Normal file
161
turner_ilya_lab_3/speciality_service/speciality_service.py
Normal 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)
|
Loading…
Reference in New Issue
Block a user