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:
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>", 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")
|
||||||
|
|
|
@ -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.")
|
|
@ -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.'
|
||||||
},
|
},
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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")
|
|
@ -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")
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 .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
|
|
@ -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 *
|
|
@ -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
|
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
|
|
@ -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)
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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!")
|
||||||
|
|
||||||
|
|
|
@ -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']
|
||||||
|
|
Loading…
Reference in a new issue