Skip to content

Commit

Permalink
Add a key and client defined value column to metadata table
Browse files Browse the repository at this point in the history
  • Loading branch information
npalaska committed Mar 11, 2021
1 parent ba71d77 commit 062daa0
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 111 deletions.
4 changes: 2 additions & 2 deletions lib/pbench/server/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,12 @@ def register_endpoints(api, app, config):

api.add_resource(
UserMetadata,
f"{base_uri}/metadata",
f"{base_uri}/metadata/<string:key>",
resource_class_args=(config, logger, token_auth),
)
api.add_resource(
QueryMetadata,
f"{base_uri}/metadata/<string:id>",
f"{base_uri}/metadata/<int:id>",
resource_class_args=(config, logger),
)

Expand Down
146 changes: 85 additions & 61 deletions lib/pbench/server/api/resources/metadata_api.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from flask_restful import Resource, abort
from flask import request, make_response, jsonify
from pbench.server.database.models.metadata import Metadata
from pbench.server.database.models.metadata import Metadata, MetadataKeys
from pbench.server.api.auth import Auth


Expand All @@ -14,58 +14,70 @@ def __init__(self, config, logger, auth):
self.logger = logger
self.auth = auth

@Auth.token_auth.login_required()
def post(self):
@Auth.token_auth.login_required(optional=True)
def post(self, key):
"""
Post request for creating metadata instance for a user.
This requires a Pbench auth token in the header field
The url requires a metadata session key such as /metadata/<key>
the only keys acceptable to create the metadata sessions are favourite/saved
This requires a JSON data with required user metadata fields
{
"config": "config",
"description": "description",
"value": "blog text" <Client defined text, can be a string of json>,
}
Example: {"value": '{"config": "config", "description": "description"}'}
Required headers include
Authorization header can be included as
Authorization: Bearer <Pbench authentication token (user received upon login)>
If the authorization header is not present, the created metadata session becomes public by default
:return: JSON Payload
response_object = {
"message": "success"
"data" {
"id": "metadata_id",
"config": "Config string"
"description": "Description string"
"value": "client text blob",
"created": "datetime string",
"updated": "datetime string",
"key": favorite/saved
}
}
"""
metadata_key = key.upper()
if metadata_key not in [key.name for key in MetadataKeys]:
self.logger.warning(
"Invalid Metadata key provided during metadata creation. Key: {}",
metadata_key,
)
abort(
400,
message="Invalid metadata key. Key need to be either Favorite/Saved",
)

post_data = request.get_json()
if not post_data:
self.logger.warning("Invalid json object: {}", request.url)
abort(400, message="Invalid json object in request")

current_user_id = self.auth.token_auth.current_user().id

config = post_data.get("config")
if not config:
self.logger.warning(
"Config not provided during metadata creation. user_id: {}",
current_user_id,
)
abort(400, message="Config field missing")
current_user = self.auth.token_auth.current_user()
if current_user:
current_user_id = current_user.id
else:
current_user_id = None

description = post_data.get("description")
if not description:
value = post_data.get("value")
if not value:
self.logger.warning(
"Description not provided during metadata creation by user: {}",
"value not provided during metadata creation. user_id: {}",
current_user_id,
)
abort(400, message="Description field missing")
abort(400, message="Value field missing")

try:
# Create a new metadata session
metadata_session = Metadata(
config=config, description=description, user_id=current_user_id
value=value, key=metadata_key, user_id=current_user_id
)
# insert the metadata session for a user
metadata_session.add()
Expand All @@ -78,16 +90,12 @@ def post(self):
else:
response_object = {
"message": "success",
"data": {
"id": metadata_session.id,
"config": metadata_session.config,
"description": metadata_session.description,
},
"data": metadata_session.get_json(),
}
return make_response(jsonify(response_object), 201)

@Auth.token_auth.login_required()
def get(self):
@Auth.token_auth.login_required(optional=True)
def get(self, key):
"""
Get request for querying all the metadata sessions for a user.
returns the list of all the metadata sessions associated with a logged in user.
Expand All @@ -98,20 +106,38 @@ def get(self):
:return: JSON Payload
response_object = {
"status": "success"
"message": "success"
"data": {
"sessions": [
{"id": "metadata_id",
"config": "Config string"
"description": "Description string"},
"value": "client text blob",
"key": "key string",
"created": "datetime string",
"updated": "datetime string", },
{}]
}
}
"""
current_user_id = self.auth.token_auth.current_user().id
if not key:
self.logger.warning("Metadata key not provided during metadata query")
abort(400, message="Missing metadata key in the query url")

current_user = self.auth.token_auth.current_user()
if current_user:
current_user_id = current_user.id
else:
current_user_id = None
print(current_user_id)
try:
# Fetch the metadata session
metadata_sessions = Metadata.query(user_id=current_user_id)
# Fetch the metadata sessions
# If the key is favorite, we return only favorite sessions,
# else we return all the saved and favorite sessions
if key.upper() == "FAVORITE":
metadata_sessions = Metadata.query(
user_id=current_user_id, key=MetadataKeys[key.upper()].value
)
else:
metadata_sessions = Metadata.query(user_id=current_user_id)
data = [session.get_json() for session in metadata_sessions]
except Exception:
self.logger.exception(
Expand Down Expand Up @@ -153,18 +179,20 @@ def get(self, id=None):
Get request for querying a metadata session of a user given a metadata id.
This requires a Pbench auth token in the header field
The url requires a metadata session id such as /user/metadata/<string:id>
The url requires a metadata session id such as /user/metadata/<int:id>
Required headers include
Authorization: Bearer <Pbench authentication token (user received upon login)>
:return: JSON Payload
response_object = {
"message": "success"
"data" {
"message": "success",
"data": {
"id": "metadata_id",
"config": "Config string"
"description": "Description string"
"value": "client text blob"
"created": "Datetime string"
"updated": "Datetime String"
"key": "key string"
}
}
"""
Expand All @@ -174,7 +202,7 @@ def get(self, id=None):

try:
# Fetch the metadata session
metadata_session = Metadata.query(id=id)
metadata_session = Metadata.query(id=id)[0]
except Exception:
self.logger.exception(
"Exception occurred in the GET request while querying the Metadata model, id: {}",
Expand All @@ -187,11 +215,7 @@ def get(self, id=None):

response_object = {
"message": "success",
"data": {
"id": metadata_session.id,
"config": metadata_session.config,
"description": metadata_session.description,
},
"data": metadata_session.get_json(),
}
return make_response(jsonify(response_object), 200)

Expand All @@ -201,23 +225,27 @@ def put(self, id=None):
Put request for updating a metadata session of a user given a metadata id.
This requires a Pbench auth token in the header field
The url requires a metadata session id such as /user/metadata/<string:id>
The url requires a metadata session id such as /user/metadata/<int:id>
This requires a JSON data with required user metadata fields to update
{
"description": "description",
"value": "new text value",
...
}
To update the value field, it is required to send the complete text blob again
Required headers include
Authorization: Bearer <Pbench authentication token (user received upon login)>
:return: JSON Payload
response_object = {
"message": "success"
"data" {
"message": "success",
"data": {
"id": "metadata_id",
"config": "Config string"
"description": "Description string"
"value": "client text blob"
"created": "Datetime string"
"updated": "Datetime String"
"key": "key string"
}
}
"""
Expand All @@ -231,7 +259,7 @@ def put(self, id=None):
abort(400, message="Invalid json object in request")

try:
metadata_session = Metadata.query(id=id)
metadata_session = Metadata.query(id=id)[0]
except Exception:
self.logger.exception(
"Exception occurred in the PUT request while querying the Metadata model, id: {}",
Expand Down Expand Up @@ -274,11 +302,7 @@ def put(self, id=None):
else:
response_object = {
"message": "success",
"data": {
"id": metadata_session.id,
"config": metadata_session.config,
"description": metadata_session.description,
},
"data": metadata_session.get_json(),
}
return make_response(jsonify(response_object), 200)

Expand All @@ -288,7 +312,7 @@ def delete(self, id=None):
Delete request for deleting a metadata session of a user given a metadata id.
This requires a Pbench auth token in the header field
The url requires a metadata session id such as /user/metadata/<string:id>
The url requires a metadata session id such as /user/metadata/<int:id>
Required headers include
Authorization: Bearer <Pbench authentication token (user received upon login)>
Expand All @@ -304,7 +328,7 @@ def delete(self, id=None):

try:
# Fetch the metadata session
metadata_session = Metadata.query(id=id)
metadata_session = Metadata.query(id=id)[0]
except Exception:
self.logger.exception(
"Exception occurred in the Delete request while querying the Metadata model, id: {}",
Expand Down
43 changes: 23 additions & 20 deletions lib/pbench/server/database/models/metadata.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import enum
import datetime
from pbench.server.database.database import Database
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Text
from sqlalchemy import Column, Integer, DateTime, ForeignKey, Text
from sqlalchemy.orm import validates


class MetadataKeys(enum.Enum):
FAVORITE = 1
SAVED = 2


class Metadata(Database.Base):
Expand All @@ -12,38 +19,34 @@ class Metadata(Database.Base):
id = Column(Integer, primary_key=True, autoincrement=True)
created = Column(DateTime, nullable=False, default=datetime.datetime.now())
updated = Column(DateTime, nullable=False, default=datetime.datetime.now())
config = Column(Text, unique=False, nullable=False)
description = Column(String(255), nullable=False)
user_id = Column(Integer, ForeignKey("users.id"))
value = Column(Text, unique=False, nullable=False)
key = Column(Integer, nullable=False)
user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=True)

def __str__(self):
return f"Url id: {self.id}, created on: {self.created}, description: {self.description}"
@validates("key")
def evaluate_key(self, key, value):
return MetadataKeys[value].value

def get_json(self):
return {
"id": self.id,
"config": self.config,
"description": self.description,
"value": self.value,
"created": self.created,
"updated": self.updated,
"key": MetadataKeys(self.key).name,
}

@staticmethod
def get_protected():
return ["id", "created"]
return ["id", "created", "user_id"]

@staticmethod
def query(id=None, user_id=None):
# Currently we would only query with single argument. Argument need to be either id/user_id
if id:
metadata = Database.db_session.query(Metadata).filter_by(id=id).first()
elif user_id:
# If the query parameter is user_id then we return the list of all the metadata linked to that user
metadata = Database.db_session.query(Metadata).filter_by(user_id=user_id)
else:
metadata = None

return metadata
def query(**kwargs):
query = Database.db_session.query(Metadata)
for attr, value in kwargs.items():
print(getattr(Metadata, attr), value)
query = query.filter(getattr(Metadata, attr) == value)
return query.all()

def add(self):
"""
Expand Down
Loading

0 comments on commit 062daa0

Please sign in to comment.