1
Fork 0
mirror of https://github.com/pds-nest/nest.git synced 2024-11-25 06:24: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>/conditions", view_func=page_repository_conditions,
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_blueprint(swagger_ui_blueprint, url_prefix=SWAGGER_URL)
with app.test_request_context():
print(" * Getting docs ready...")
for fn_name in app.view_functions:
if fn_name == 'static':
continue
view_fn = app.view_functions[fn_name]
spec.path(view=view_fn)
print(" * Docs have been compiled on http://127.0.0.1:5000/docs")
@app.route("/docs/swagger.json")

View file

@ -70,3 +70,7 @@ class ConditionSchema(Schema):
class CreateCondition(Schema):
type = fields.Integer(description="The condition 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("CreateCondition", schema=CreateCondition)
spec.components.schema("Condition", schema=ConditionSchema)
spec.components.schema("ConditionParameter", schema=ConditionParameterSchema)
spec.components.security_scheme("jwt", {"type": "http", "scheme": "bearer", "bearerFormat": "JWT"})
# add swagger tags that are used for endpoint annotation
@ -39,6 +40,9 @@ tags = [
{'name': 'repository-related',
'description': 'Repository related calls of the API.'
},
{'name': 'condition-related',
'description': 'Condition related calls of the API.'
},
{'name': 'admin-only',
'description': 'Admin only calls of the API.'
},

View file

@ -12,7 +12,7 @@ class Alert(Base.Model):
limit = Base.Column(Base.Integer, nullable=False)
window_size = Base.Column(Base.Integer, nullable=False)
# 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
repository = Base.relationship("Repository", back_populates="alerts")
notifications = Base.relationship("Notification", back_populates="alert", cascade="all, delete")

View file

@ -6,8 +6,8 @@ from ..base import Base
class Authorization(Base.Model):
rid = Base.Column(Base.Integer, Base.ForeignKey("repository.id"), primary_key=True)
email = Base.Column(Base.String, Base.ForeignKey("user.email"), 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", ondelete="CASCADE"), primary_key=True)
# Relationships
repository = Base.relationship("Repository", 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)
type = Base.Column(Base.Enum(ConditionType), nullable=False)
content = Base.Column(Base.String, nullable=False)
# FK
repository_id = Base.Column(Base.Integer, Base.ForeignKey("repository.id", ondelete="CASCADE"))
# 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")
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)
# 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
owner = Base.relationship("User", back_populates="owner_of")
authorizations = Base.relationship("Authorization", 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")
uses = Base.relationship("Uses", back_populates="repository", cascade="all, delete")
conditions = Base.relationship("Condition", back_populates="repository")
def to_json(self):
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 .Tweet import Tweet
from .User import User
from .Uses import Uses
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 .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
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 (type_ := request.json.get("type")) is None:
@ -90,12 +90,8 @@ def page_repository_conditions(rid):
if not (content := request.json.get("content")):
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.commit()
use = Uses(cid=condition.id, rid=repository.id)
Base.session.merge(use)
Base.session.commit()
return json_success(condition.to_json()), 200

View file

@ -13,7 +13,7 @@ class MyTestCase(unittest.TestCase):
assert j['result'] == "success"
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})
j = json.loads(r.text)
assert j['result'] == "success"
@ -28,7 +28,7 @@ class MyTestCase(unittest.TestCase):
auth_code = j['data']['access_token']
# 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},
json={'type': 0, 'content': 'MarioDraghi'})
j = json.loads(r.text)
@ -36,7 +36,7 @@ class MyTestCase(unittest.TestCase):
print("Conditions dei Repositories creati correttamente!")
# 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},
json={'type': 1, 'content': 'Roma'})
j = json.loads(r.text)
@ -44,7 +44,7 @@ class MyTestCase(unittest.TestCase):
print("Conditions dei Repositories creati correttamente!")
# 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},
json={'type': 2, 'content': '26/06/2021'})
j = json.loads(r.text)
@ -52,7 +52,7 @@ class MyTestCase(unittest.TestCase):
print("Conditions dei Repositories creati correttamente!")
# 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},
json={'type': 1 and 0, 'content': 'Roma' and 'Totti'})
j = json.loads(r.text)

View file

@ -14,17 +14,17 @@ class MyTestCase(unittest.TestCase):
auth_code = j['data']['access_token']
# 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)
assert j['result'] == "success"
# 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)
assert j['result'] == "success"
# 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})
j = json.loads(r.text)
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',
headers={'authorization': "Bearer " + auth_code})
j = json.loads(r.text)
assert j['result'] == "success"
assert j['result'] == "failure"
print("delete User eseguito correttamente!")

View file

@ -25,7 +25,7 @@ class MyTestCase(unittest.TestCase):
# User autenticato come non-Admin: fallisce
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)
assert j['result'] == "success"
auth_code = j['data']['access_token']