This repository has been archived by the owner on Oct 13, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapp.py
217 lines (186 loc) · 8.73 KB
/
app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# =============================================================================
""" 1 - Carga Inicial.
"""
# =============================================================================
from flask_openapi3 import OpenAPI, Info, Tag
from flask import redirect, request
from urllib.parse import unquote
from flask import jsonify
from sqlalchemy.exc import IntegrityError
from sqlalchemy import func
from model import Session, Obra
from schemas import *
from flask_cors import CORS
from logger import setup_logger
# ===============================================================================
""" 2 - Inicializa variáveis de Informações gerais de identificação do serviço.
"""
# ==============================================================================
info = Info(title="API Obras de Arte", version="1.0.1")
app = OpenAPI(__name__, info=info)
home_tag = Tag(name="Documentação", description="Apresentação da documentação via Swagger.")
obra_tag = Tag(name="Rotas em app", description="Deleção, Adição, Consulta e Busca de obras da base")
doc_tag = Tag(name="Rota em app", description="Documentação da API de manipulação da BD Obras de Arte")
# ==============================================================================
""" 3 - Inicializa "service_name" para fins de geração de arquivo de log.
"""
# ==============================================================================
service_name = "app"
logger = setup_logger(service_name)
# ==============================================================================
""" 4 - Configurações de "Cross-Origin Resource Sharing" (CORS).
# Foi colocado "supports_credentials=False" para evitar possíveis conflitos com
# algum tipo de configuração de browser. Mas não é a melhor recomendação por
# segurança. Para melhorar a segurança desta API, o mais indicado segue nas
# linhas abaixo comentadas.
#> origins_permitidas = ["Obras de Arte"]
#> Configurando o CORS com suporte a credenciais
#> CORS(app, origins=origins_permitidas, supports_credentials=True)
#> CORS(app, supports_credentials=True, expose_headers=["Authorization"])
#> Adicionalmente utilizar da biblioteca PyJWT
"""
# ==============================================================================
CORS(app, supports_credentials=False)
# ================================================================================
""" 5.1 - DOCUMENTAÇÂO: Rota "/" para geração da documentação via Swagger.
"""
# ================================================================================
@app.get('/', tags=[home_tag])
def home():
"""Redireciona para /openapi/swagger.
"""
return redirect('/openapi/swagger')
# ================================================================================
""" 5.2 - DOCUMENTAÇÂO: Rota "/doc" para documentação via github.
"""
# ================================================================================
@app.get('/doc', tags=[doc_tag])
def doc():
"""Redireciona para documentação no github.
"""
return redirect('https://github.com/Moriblo/app')
# ========================================================================================
""" 6.1 - Rota /obra para tratar o fetch de `POST`.
"""
# ========================================================================================
@app.post('/obra', tags=[obra_tag],
responses={"200": ObraViewSchema, "409": ErrorSchema, "400": ErrorSchema})
def add_obra(form: ObraSchema):
"""Adiciona uma nova obra à base de dados.
"""
obra = Obra(
nome=form.nome,
artista=form.artista,
estilo=form.estilo,
tipo=form.tipo,
link=form.link)
logger.debug(f"Adicionando obra de nome: '{obra.nome}' e artista '{obra.artista}'")
try:
# Criando conexão com a base
session = Session()
# Adicionando obra
session.add(obra)
# Efetivando o comando de adição de novo item na tabela
session.commit()
logger.debug(f"Adicionada obra de nome: '{obra.nome}' e artista '{obra.artista}'")
return apresenta_obra(obra), 200
except IntegrityError as e:
""" A duplicidade é verificada pela obra e pelo artista. Vide "UniqueConstraint"
no model.obra.py.
"""
error_msg = "Obra de mesmo nome já salva na base para este artista :/"
logger.warning(f"Erro ao adicionar obra '{obra.nome}' e artista '{obra.artista}'. {error_msg}")
return {"mesage": error_msg}, 409
except Exception as e:
# Caso um erro fora do previsto
error_msg = "Não foi possível salvar nova obra :/"
logger.warning(f"Erro: {error_msg}")
return {"mesage": error_msg}, 400
# Comando de fechamento da sessão.
finally:
session.close()
# ========================================================================================
""" 6.2 - Rota /obras para tratar o fetch de `GET`.
"""
# ========================================================================================
@app.get('/obras', tags=[obra_tag],
responses={"200": ListagemObrasSchema, "404": ErrorSchema})
def get_obras():
"""Faz a busca por todas as obras cadastradas.
"""
logger.debug(f"Coletando obras ")
# Criando conexão com a base
session = Session()
# Fazendo a busca
obras = session.query(Obra).all()
if not obras:
# Se não há obras cadastradas
logger.debug(f"Não há obras na base")
return {"obras": []}, 200
else:
logger.debug(f"%d obras econtradas" % len(obras))
# Retorna a representação de obra
print(obras)
return apresenta_obras(obras), 200
# ========================================================================================
""" 6.3 - Rota /obra para tratar o fetch de `DELETE`.
"""
# ========================================================================================
@app.delete('/obra', tags=[obra_tag],
responses={"200": ObraDelSchema, "404": ErrorSchema})
def del_obra(query: ObraBuscaSchema):
"""Deleta uma obra a partir da obra e do artista informados.
"""
obra_nome = unquote(query.nome)
obra_artista = unquote(query.artista)
print(obra_nome, obra_artista)
logger.debug(f"Deletando dados sobre obra {obra_nome} e artista {obra_artista}")
# Criando conexão com a base
session = Session()
# Fazendo a deleção da obra relacionada ao artista especificado
count = session.query(Obra).filter(Obra.nome == obra_nome, Obra.artista == obra_artista).delete()
session.commit()
if count:
# Retorna a representação da mensagem de confirmação
logger.debug(f"Deletada obra {obra_nome} e {obra_artista}")
return {"mesage": "Obra removida", "nome": obra_nome, "artista" : obra_artista}
else:
# Se a obra não foi encontrada
error_msg = "Erro: Obra + Artista não encontradados na base :/"
error_code = "404"
logger.warning(f"Erro ao deletar obra '{obra_nome}' e artista '{obra_artista}', {error_msg}")
return {"mesage": error_msg}, error_code
# ========================================================================================
""" 6.4 - Rota /obrart para tratar o fetch de GET para consulta obra + artista.
2ª Regra de negócio (RN2) para evitar tupla com obra + artista repetida
"""
# ========================================================================================
@app.get('/obrart', methods=['GET'], tags=[obra_tag],
responses={"200": ObrartSchema, "404": ErrorSchema})
def get_obrart(query: ObraBuscaSchema):
"""Verifica se a compinação Obra + Artista existe na base de dados.
"""
obra_nome = request.args.get('nome')
obra_artista = request.args.get('artista')
logger.debug(f"*** Consultando Obra ***")
# Criando conexão com a base
session = Session()
# Fazendo a busca
result = session.query(func.count(Obra.artista)).filter(Obra.artista == obra_artista, Obra.nome == obra_nome).scalar()
logger.debug(f"Resultado da busca {obra_nome}, {obra_artista}, {result}")
session.commit()
if result:
# Retorna o resultado da busca com "result" com o número de ocorrências
return jsonify(obra_nome, obra_artista, result)
else:
# Se a obra não foi encontrada
error_msg = "Erro: Obra + Artista não encontradados na base :/"
error_code = "404"
logger.warning(f"´{error_msg}´ '{obra_nome}' + '{obra_artista}´")
return jsonify(obra_nome, obra_artista, result), 404
# ===============================================================================
""" 7 - Garante a disponibilidade da API em "suspenso".
"""
# ===============================================================================
if __name__ == '__main__':
app.run(port=5000, debug=True)