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:
parent
9d2954bda6
commit
44f4b97ddf
18 changed files with 169 additions and 41 deletions
|
@ -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")
|
||||
|
|
|
@ -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.")
|
|
@ -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.'
|
||||
},
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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")
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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")
|
|
@ -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
|
|
@ -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 *
|
|
@ -0,0 +1,2 @@
|
|||
from .repository_conditions import page_repository_conditions
|
||||
from .condition import page_condition
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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!")
|
||||
|
||||
|
|
|
@ -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']
|
||||
|
|
Loading…
Reference in a new issue