1
Fork 0
mirror of https://github.com/pds-nest/nest.git synced 2024-11-25 22:44:19 +00:00

Updated database structure, added condition management.

This commit is contained in:
Lorenzo Balugani 2021-05-06 14:12:11 +02:00
parent 9d2954bda6
commit 44f4b97ddf
18 changed files with 169 additions and 41 deletions

View file

@ -29,17 +29,20 @@ app.add_url_rule("/api/v1/repositories/", view_func=page_repositories, methods=[
app.add_url_rule("/api/v1/repositories/<int:rid>", view_func=page_repository, methods=["GET", "PATCH", "DELETE"]) app.add_url_rule("/api/v1/repositories/<int:rid>", view_func=page_repository, methods=["GET", "PATCH", "DELETE"])
app.add_url_rule("/api/v1/repositories/<int:rid>/conditions", view_func=page_repository_conditions, app.add_url_rule("/api/v1/repositories/<int:rid>/conditions", view_func=page_repository_conditions,
methods=["GET", "POST"]) methods=["GET", "POST"])
app.add_url_rule("/api/v1/conditions/<int:cid>", view_func=page_condition, methods=["GET", "PATCH", "DELETE"])
app.register_error_handler(Exception, error_handler) app.register_error_handler(Exception, error_handler)
app.register_blueprint(swagger_ui_blueprint, url_prefix=SWAGGER_URL) app.register_blueprint(swagger_ui_blueprint, url_prefix=SWAGGER_URL)
with app.test_request_context(): with app.test_request_context():
print(" * Getting docs ready...")
for fn_name in app.view_functions: for fn_name in app.view_functions:
if fn_name == 'static': if fn_name == 'static':
continue continue
view_fn = app.view_functions[fn_name] view_fn = app.view_functions[fn_name]
spec.path(view=view_fn) spec.path(view=view_fn)
print(" * Docs have been compiled on http://127.0.0.1:5000/docs")
@app.route("/docs/swagger.json") @app.route("/docs/swagger.json")

View file

@ -70,3 +70,7 @@ class ConditionSchema(Schema):
class CreateCondition(Schema): class CreateCondition(Schema):
type = fields.Integer(description="The condition type.") type = fields.Integer(description="The condition type.")
content = fields.String(description="The condition content. Meaning may change according to type.") content = fields.String(description="The condition content. Meaning may change according to type.")
class ConditionParameterSchema(Schema):
cid = fields.Integer(description="The condition id.")

View file

@ -29,6 +29,7 @@ spec.components.schema("RepositoryUpdate", schema=RepositoryUpdate)
spec.components.schema("CreateRepository", schema=CreateRepository) spec.components.schema("CreateRepository", schema=CreateRepository)
spec.components.schema("CreateCondition", schema=CreateCondition) spec.components.schema("CreateCondition", schema=CreateCondition)
spec.components.schema("Condition", schema=ConditionSchema) spec.components.schema("Condition", schema=ConditionSchema)
spec.components.schema("ConditionParameter", schema=ConditionParameterSchema)
spec.components.security_scheme("jwt", {"type": "http", "scheme": "bearer", "bearerFormat": "JWT"}) spec.components.security_scheme("jwt", {"type": "http", "scheme": "bearer", "bearerFormat": "JWT"})
# add swagger tags that are used for endpoint annotation # add swagger tags that are used for endpoint annotation
@ -39,6 +40,9 @@ tags = [
{'name': 'repository-related', {'name': 'repository-related',
'description': 'Repository related calls of the API.' 'description': 'Repository related calls of the API.'
}, },
{'name': 'condition-related',
'description': 'Condition related calls of the API.'
},
{'name': 'admin-only', {'name': 'admin-only',
'description': 'Admin only calls of the API.' 'description': 'Admin only calls of the API.'
}, },

View file

@ -12,7 +12,7 @@ class Alert(Base.Model):
limit = Base.Column(Base.Integer, nullable=False) limit = Base.Column(Base.Integer, nullable=False)
window_size = Base.Column(Base.Integer, nullable=False) window_size = Base.Column(Base.Integer, nullable=False)
# Foreign Keys # Foreign Keys
repository_id = Base.Column(Base.Integer, Base.ForeignKey("repository.id"), nullable=False) repository_id = Base.Column(Base.Integer, Base.ForeignKey("repository.id", ondelete="CASCADE"), nullable=False)
# Relationships # Relationships
repository = Base.relationship("Repository", back_populates="alerts") repository = Base.relationship("Repository", back_populates="alerts")
notifications = Base.relationship("Notification", back_populates="alert", cascade="all, delete") notifications = Base.relationship("Notification", back_populates="alert", cascade="all, delete")

View file

@ -6,8 +6,8 @@ from ..base import Base
class Authorization(Base.Model): class Authorization(Base.Model):
rid = Base.Column(Base.Integer, Base.ForeignKey("repository.id"), primary_key=True) rid = Base.Column(Base.Integer, Base.ForeignKey("repository.id", ondelete="CASCADE"), primary_key=True)
email = Base.Column(Base.String, Base.ForeignKey("user.email"), primary_key=True) email = Base.Column(Base.String, Base.ForeignKey("user.email", ondelete="CASCADE"), primary_key=True)
# Relationships # Relationships
repository = Base.relationship("Repository", back_populates="authorizations") repository = Base.relationship("Repository", back_populates="authorizations")
user = Base.relationship("User", back_populates="authorizations") user = Base.relationship("User", back_populates="authorizations")

View file

@ -11,8 +11,10 @@ class Condition(Base.Model):
id = Base.Column(Base.Integer, primary_key=True) id = Base.Column(Base.Integer, primary_key=True)
type = Base.Column(Base.Enum(ConditionType), nullable=False) type = Base.Column(Base.Enum(ConditionType), nullable=False)
content = Base.Column(Base.String, nullable=False) content = Base.Column(Base.String, nullable=False)
# FK
repository_id = Base.Column(Base.Integer, Base.ForeignKey("repository.id", ondelete="CASCADE"))
# Relationships # Relationships
used = Base.relationship("Uses", back_populates="condition", cascade="all, delete") repository = Base.relationship("Repository", back_populates="conditions", cascade="all, delete")
tweets = Base.relationship("Contains", back_populates="condition") tweets = Base.relationship("Contains", back_populates="condition")
operations = Base.relationship("BoolOperation", back_populates="condition") operations = Base.relationship("BoolOperation", back_populates="condition")

View file

@ -16,14 +16,14 @@ class Repository(Base.Model):
isActive = Base.Column(Base.Boolean, nullable=False, default=False) isActive = Base.Column(Base.Boolean, nullable=False, default=False)
# Foreign Keys # Foreign Keys
owner_id = Base.Column(Base.String, Base.ForeignKey("user.email"), nullable=False) owner_id = Base.Column(Base.String, Base.ForeignKey("user.email", ondelete="CASCADE"), nullable=False)
# Relationships # Relationships
owner = Base.relationship("User", back_populates="owner_of") owner = Base.relationship("User", back_populates="owner_of")
authorizations = Base.relationship("Authorization", back_populates="repository", cascade="all, delete") authorizations = Base.relationship("Authorization", back_populates="repository", cascade="all, delete")
tweets = Base.relationship("Composed", back_populates="repository", cascade="all, delete") tweets = Base.relationship("Composed", back_populates="repository", cascade="all, delete")
alerts = Base.relationship("Alert", back_populates="repository", cascade="all, delete") alerts = Base.relationship("Alert", back_populates="repository", cascade="all, delete")
uses = Base.relationship("Uses", back_populates="repository", cascade="all, delete") conditions = Base.relationship("Condition", back_populates="repository")
def to_json(self): def to_json(self):
return { return {

View file

@ -1,14 +0,0 @@
"""
This module defines the Uses database class.
"""
from ..base import Base
class Uses(Base.Model):
__tablename__ = "uses"
rid = Base.Column(Base.Integer, Base.ForeignKey("repository.id"), primary_key=True)
cid = Base.Column(Base.Integer, Base.ForeignKey("condition.id"), primary_key=True)
# Relationships
repository = Base.relationship("Repository", back_populates="uses")
condition = Base.relationship("Condition", back_populates="used")

View file

@ -12,5 +12,4 @@ from .Notification import Notification
from .Repository import Repository from .Repository import Repository
from .Tweet import Tweet from .Tweet import Tweet
from .User import User from .User import User
from .Uses import Uses
from .Enums import ConditionType, OperationType from .Enums import ConditionType, OperationType

View file

@ -1,3 +1,4 @@
from .repository_conditions import page_repository_conditions from routes.repository.conditions.repository_conditions import page_repository_conditions
from .repository import page_repository from .repository import page_repository
from .repositories import page_repositories from .repositories import page_repositories
from .conditions import *

View file

@ -0,0 +1,2 @@
from .repository_conditions import page_repository_conditions
from .condition import page_condition

View file

@ -0,0 +1,131 @@
from flask import render_template, abort, jsonify, request
from nest_backend.database import *
from flask_jwt_extended import jwt_required
from nest_backend.gestione import *
from flask_cors import cross_origin
@cross_origin()
@jwt_required()
def page_condition(cid):
"""
---
get:
summary: Get the details of a condition.
parameters:
- in: path
schema: ConditionParameterSchema
security:
- jwt: []
responses:
'200':
description: Details about a certain condition.
content:
application/json:
schema: Condition
'401':
description: The user is not logged in.
content:
application/json:
schema: Error
'403':
description: The user is not authorized.
content:
application/json:
schema: Error
'404':
description: The repository could not be found.
content:
application/json:
schema: Error
tags:
- condition-related
delete:
summary: Delete a condition.
parameters:
- in: path
schema: ConditionParameterSchema
security:
- jwt: []
responses:
'200':
description: The deletion was successful.
'401':
description: The user is not logged in.
content:
application/json:
schema: Error
'403':
description: The user is not authorized.
content:
application/json:
schema: Error
'404':
description: The repository could not be found.
content:
application/json:
schema: Error
tags:
- condition-related
patch:
summary: Update a condition.
parameters:
- in: path
schema: ConditionParameterSchema
security:
- jwt: []
requestBody:
required: true
content:
application/json:
schema: CreateCondition
responses:
'200':
description: The user is not logged in.
content:
application/json:
schema: Condition
'401':
description: The user is not logged in.
content:
application/json:
schema: Error
'403':
description: The user is not authorized.
content:
application/json:
schema: Error
'404':
description: The repository could not be found.
content:
application/json:
schema: Error
tags:
- condition-related
"""
condition = Condition.query.filter_by(id=cid).first()
user = find_user(get_jwt_identity())
if not condition:
return json_error("Could not find the condition."), 404
if condition.repository not in [a.repository for a in user.authorizations] + user.owner_of and not user.isAdmin:
return json_error("You lack the authorization to proceed, pal."), 403
if request.method == "GET":
return json_success(condition.to_json()), 200
if condition.repository not in user.owner_or and not user.isAdmin:
return json_error("You lack the authorization to proceed, pal."), 403
if request.method == "PATCH":
if (type_ := request.json.get("type")) is not None:
try:
type_ = ConditionType(type_)
condition.type = type_
except KeyError:
return json_error("Unknown `type` specified."), 400
if content := request.json.get("content"):
condition.content = content
Base.session.commit()
return json_success(condition.to_json()), 200
if request.method == "DELETE":
Base.session.delete(condition)
Base.session.commit()
return json_success("Deleted."), 200

View file

@ -76,7 +76,7 @@ def page_repository_conditions(rid):
return json_error("You are not authorized."), 403 return json_error("You are not authorized."), 403
if request.method == "GET": if request.method == "GET":
return json_success([u.condition.to_json() for u in repository.uses]) return json_success([u.condition.to_json() for u in repository.conditions])
if request.method == "POST": if request.method == "POST":
if (type_ := request.json.get("type")) is None: if (type_ := request.json.get("type")) is None:
@ -90,12 +90,8 @@ def page_repository_conditions(rid):
if not (content := request.json.get("content")): if not (content := request.json.get("content")):
return json_error("Missing `content` parameter."), 400 return json_error("Missing `content` parameter."), 400
condition = Condition(content=content, type=type_) condition = Condition(content=content, type=type_, repository_id=rid)
Base.session.add(condition) Base.session.add(condition)
Base.session.commit() Base.session.commit()
use = Uses(cid=condition.id, rid=repository.id)
Base.session.merge(use)
Base.session.commit()
return json_success(condition.to_json()), 200 return json_success(condition.to_json()), 200

View file

@ -13,7 +13,7 @@ class MyTestCase(unittest.TestCase):
assert j['result'] == "success" assert j['result'] == "success"
auth_code = j['data']['access_token'] auth_code = j['data']['access_token']
r = requests.get(f'http://localhost:5000/api/v1/repositories/16/conditions', r = requests.get(f'http://localhost:5000/api/v1/repositories/1/conditions',
headers={'authorization': "Bearer " + auth_code}) headers={'authorization': "Bearer " + auth_code})
j = json.loads(r.text) j = json.loads(r.text)
assert j['result'] == "success" assert j['result'] == "success"
@ -28,7 +28,7 @@ class MyTestCase(unittest.TestCase):
auth_code = j['data']['access_token'] auth_code = j['data']['access_token']
# uso come filtro un hashtag # uso come filtro un hashtag
r = requests.post(f'http://localhost:5000/api/v1/repositories/16/conditions', r = requests.post(f'http://localhost:5000/api/v1/repositories/1/conditions',
headers={'authorization': "Bearer " + auth_code}, headers={'authorization': "Bearer " + auth_code},
json={'type': 0, 'content': 'MarioDraghi'}) json={'type': 0, 'content': 'MarioDraghi'})
j = json.loads(r.text) j = json.loads(r.text)
@ -36,7 +36,7 @@ class MyTestCase(unittest.TestCase):
print("Conditions dei Repositories creati correttamente!") print("Conditions dei Repositories creati correttamente!")
# uso come filtro un luogo # uso come filtro un luogo
r = requests.post(f'http://localhost:5000/api/v1/repositories/16/conditions', r = requests.post(f'http://localhost:5000/api/v1/repositories/1/conditions',
headers={'authorization': "Bearer " + auth_code}, headers={'authorization': "Bearer " + auth_code},
json={'type': 1, 'content': 'Roma'}) json={'type': 1, 'content': 'Roma'})
j = json.loads(r.text) j = json.loads(r.text)
@ -44,7 +44,7 @@ class MyTestCase(unittest.TestCase):
print("Conditions dei Repositories creati correttamente!") print("Conditions dei Repositories creati correttamente!")
# uso come filtro una data # uso come filtro una data
r = requests.post(f'http://localhost:5000/api/v1/repositories/16/conditions', r = requests.post(f'http://localhost:5000/api/v1/repositories/1/conditions',
headers={'authorization': "Bearer " + auth_code}, headers={'authorization': "Bearer " + auth_code},
json={'type': 2, 'content': '26/06/2021'}) json={'type': 2, 'content': '26/06/2021'})
j = json.loads(r.text) j = json.loads(r.text)
@ -52,7 +52,7 @@ class MyTestCase(unittest.TestCase):
print("Conditions dei Repositories creati correttamente!") print("Conditions dei Repositories creati correttamente!")
# uso una combinazione di filtri # uso una combinazione di filtri
r = requests.post(f'http://localhost:5000/api/v1/repositories/16/conditions', r = requests.post(f'http://localhost:5000/api/v1/repositories/1/conditions',
headers={'authorization': "Bearer " + auth_code}, headers={'authorization': "Bearer " + auth_code},
json={'type': 1 and 0, 'content': 'Roma' and 'Totti'}) json={'type': 1 and 0, 'content': 'Roma' and 'Totti'})
j = json.loads(r.text) j = json.loads(r.text)

View file

@ -14,17 +14,17 @@ class MyTestCase(unittest.TestCase):
auth_code = j['data']['access_token'] auth_code = j['data']['access_token']
# ritorno le info sulla repository speficicata # ritorno le info sulla repository speficicata
r = requests.get(f'http://localhost:5000/api/v1/repositories/17', headers={'authorization': "Bearer " + auth_code}) r = requests.get(f'http://localhost:5000/api/v1/repositories/1', headers={'authorization': "Bearer " + auth_code})
j = json.loads(r.text) j = json.loads(r.text)
assert j['result'] == "success" assert j['result'] == "success"
# cancello la repository specificata # cancello la repository specificata
r = requests.delete(f'http://localhost:5000/api/v1/repositories/17', headers={'authorization': "Bearer " + auth_code}) r = requests.delete(f'http://localhost:5000/api/v1/repositories/1', headers={'authorization': "Bearer " + auth_code})
j = json.loads(r.text) j = json.loads(r.text)
assert j['result'] == "success" assert j['result'] == "success"
# modifico il nome e lo stato della repository specificata # modifico il nome e lo stato della repository specificata
r = requests.patch(f'http://localhost:5000/api/v1/repositories/16', headers={'authorization': "Bearer " + auth_code}, r = requests.patch(f'http://localhost:5000/api/v1/repositories/1', headers={'authorization': "Bearer " + auth_code},
json={'name': 'newname', 'open': True}) json={'name': 'newname', 'open': True})
j = json.loads(r.text) j = json.loads(r.text)
assert j['result'] == "success" assert j['result'] == "success"

View file

@ -49,7 +49,7 @@ class MyTestCase(unittest.TestCase):
r = requests.delete(f'http://localhost:5000/api/v1/users/utente13@nest.com', r = requests.delete(f'http://localhost:5000/api/v1/users/utente13@nest.com',
headers={'authorization': "Bearer " + auth_code}) headers={'authorization': "Bearer " + auth_code})
j = json.loads(r.text) j = json.loads(r.text)
assert j['result'] == "success" assert j['result'] == "failure"
print("delete User eseguito correttamente!") print("delete User eseguito correttamente!")

View file

@ -25,7 +25,7 @@ class MyTestCase(unittest.TestCase):
# User autenticato come non-Admin: fallisce # User autenticato come non-Admin: fallisce
auth_code = "" auth_code = ""
r = requests.post('http://localhost:5000/api/v1/login', json={'email': 'utente20@nest.com', 'password': 'password'}) r = requests.post('http://localhost:5000/api/v1/login', json={'email': 'utente_test@nest.com', 'password': 'password'})
j = json.loads(r.text) j = json.loads(r.text)
assert j['result'] == "success" assert j['result'] == "success"
auth_code = j['data']['access_token'] auth_code = j['data']['access_token']