Skip to content

Commit faf6c5a

Browse files
committed
First pass implementation of graphql schema in server for metadata
1 parent ca4877c commit faf6c5a

File tree

4 files changed

+172
-5
lines changed

4 files changed

+172
-5
lines changed

lib/pbench/server/api/__init__.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@
66

77
import os
88

9-
from flask import Flask
9+
from flask import Flask, request
1010
from flask_restful import Api
1111
from flask_cors import CORS
1212

1313
from pbench.server import PbenchServerConfig
1414
from pbench.common.exceptions import BadConfig, ConfigFileNotSpecified
1515
from pbench.server.api.resources.upload_api import Upload, HostInfo
16-
from pbench.server.api.resources.graphql_api import GraphQL
16+
from pbench.server.api.resources.graphql_api import GraphQL, UserMetadata
1717
from pbench.common.logger import get_pbench_logger
1818
from pbench.server.api.resources.query_apis.elasticsearch_api import Elasticsearch
1919
from pbench.server.api.resources.query_apis.query_controllers import QueryControllers
@@ -74,6 +74,10 @@ def register_endpoints(api, app, config):
7474
UserAPI, f"{base_uri}/user/<string:username>", resource_class_args=(logger,),
7575
)
7676

77+
api.add_resource(
78+
UserMetadata, f"{base_uri}/user/metadata", resource_class_args=(config, logger),
79+
)
80+
7781

7882
def get_server_config():
7983
cfg_name = os.environ.get("_PBENCH_SERVER_CONFIG")
@@ -107,6 +111,11 @@ def create_app(server_config):
107111

108112
Database.init_db(server_config=server_config, logger=app.logger)
109113

114+
@app.before_request
115+
def before_request():
116+
print(request.path)
117+
print(request.remote_addr)
118+
110119
@app.teardown_appcontext
111120
def shutdown_session(exception=None):
112121
Database.db_session.remove()

lib/pbench/server/api/resources/graphql_api.py

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,87 @@
11
import requests
22
from flask_restful import Resource, abort
3-
from flask import request, make_response
3+
from flask import request, make_response, jsonify
4+
from pbench.server.api.resources.auth import auth
5+
from pbench.server.api.resources.graphql_schema import schema
6+
7+
8+
class UserMetadata(Resource):
9+
"""
10+
Abstracted pbench API for handling user metadata by using graphql schema
11+
"""
12+
13+
def __init__(self, config, logger):
14+
self.server_config = config
15+
self.logger = logger
16+
17+
@auth.login_required()
18+
def post(self):
19+
"""
20+
Post request for creating metadata instance for a user.
21+
This requires a JWT auth token in the header field
22+
23+
This requires a JSON data with required user metadata fields
24+
{
25+
"config": "config",
26+
"description": "description",
27+
}
28+
29+
Required headers include
30+
Authorization: JWT token (user received upon login)
31+
32+
:return: JSON Payload
33+
response_object = {
34+
"status": "success"
35+
"data" {
36+
"id": "metadata_id",
37+
"config": "Config string"
38+
"description": "Description string"
39+
}
40+
}
41+
"""
42+
post_data = request.get_json()
43+
if not post_data:
44+
self.logger.warning("Invalid json object: %s", request.url)
45+
abort(400, message="Invalid json object in request")
46+
47+
config = post_data.get("config")
48+
if not config:
49+
self.logger.warning("Config not provided during metadata creation")
50+
abort(400, message="Please provide a config string")
51+
52+
description = post_data.get("description")
53+
if not description:
54+
self.logger.warning("Description not provided during metadata creation")
55+
abort(400, message="Please provide a description string")
56+
current_user_id = auth.current_user().id
57+
try:
58+
# query GraphQL
59+
query = f"""
60+
mutation {{
61+
createMetadata (input: {{config:"{config}", description:"{description}", user_id:{current_user_id}}}) {{
62+
metadata {{
63+
id
64+
config
65+
description
66+
}}
67+
}}
68+
}}
69+
"""
70+
result = schema.execute(query)
71+
except Exception as e:
72+
self.logger.exception("Exception occurred during Metadata creation")
73+
abort(500, message="INTERNAL ERROR")
74+
else:
75+
data = result.data["createMetadata"]["metadata"]
76+
response_object = {
77+
"status": "success",
78+
"data": {
79+
"id": data["id"],
80+
"config": data["config"],
81+
"description": data["description"],
82+
},
83+
}
84+
return make_response(jsonify(response_object), 201)
485

586

687
class GraphQL(Resource):
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
from pbench.server.api.resources.models import MetadataModel
2+
from pbench.server.api.resources.database import Database
3+
4+
import graphene
5+
from graphene import relay
6+
from graphene_sqlalchemy import SQLAlchemyObjectType
7+
8+
9+
# Define graphql types
10+
class Metadata(SQLAlchemyObjectType):
11+
class Meta:
12+
model = MetadataModel
13+
interfaces = (relay.Node,)
14+
15+
16+
class MetadataAttribute:
17+
id = graphene.ID
18+
user_id = graphene.ID()
19+
created = graphene.DateTime()
20+
updated = graphene.DateTime()
21+
config = graphene.String()
22+
description = graphene.String()
23+
24+
25+
class CreateMetadataInput(graphene.InputObjectType, MetadataAttribute):
26+
pass
27+
28+
29+
# mutations
30+
class CreateMetadata(graphene.Mutation):
31+
metadata = graphene.Field(lambda: Metadata)
32+
ok = graphene.Boolean()
33+
34+
class Arguments:
35+
input = CreateMetadataInput(required=True)
36+
37+
@staticmethod
38+
def mutate(self, info, input):
39+
data = input
40+
metadata = MetadataModel(**data)
41+
Database.db_session.add(metadata)
42+
Database.db_session.commit()
43+
ok = True
44+
return CreateMetadata(metadata=metadata, ok=ok)
45+
46+
47+
class Mutation(graphene.ObjectType):
48+
createMetadata = CreateMetadata.Field()
49+
50+
51+
# Query
52+
class Query(graphene.ObjectType):
53+
node = relay.Node.Field()
54+
55+
metadata_by_id = graphene.List(Metadata, id=graphene.String())
56+
metadata_by_userid = graphene.List(Metadata, userid=graphene.String())
57+
58+
@staticmethod
59+
def resolve_metadata_by_id(parent, info, **args):
60+
q = args.get("id")
61+
62+
metadata_query = Metadata.get_query(info)
63+
return metadata_query.filter(MetadataModel.id == q).all()
64+
65+
@staticmethod
66+
def resolve_metadata_by_userid(parent, info, **args):
67+
q = args.get("userid")
68+
69+
metadata_query = Metadata.get_query(info)
70+
return metadata_query.filter(MetadataModel.user_id == q).all()
71+
72+
73+
# schema
74+
schema = graphene.Schema(query=Query, mutation=Mutation, types=[Metadata])

lib/pbench/server/api/resources/models.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ class UserModel(Database.Base):
1919
registered_on = Column(DateTime, nullable=False)
2020
email = Column(String(255), unique=True, nullable=False)
2121
auth_tokens = relationship("ActiveTokens", backref="users")
22+
user_metadata = relationship("MetadataModel", uselist=False, backref="users")
2223

2324
def __init__(self, bcrypt_log_rounds, **kwargs):
2425
super().__init__(**kwargs)
@@ -76,7 +77,7 @@ def valid(auth_token):
7677
return False
7778

7879

79-
class Metadata(Database.Base):
80+
class MetadataModel(Database.Base):
8081
""" Metadata Model for storing user metadata details """
8182

8283
# TODO: Think about the better name
@@ -87,12 +88,14 @@ class Metadata(Database.Base):
8788
updated = Column(DateTime, nullable=False)
8889
config = Column(String(255), unique=False, nullable=False)
8990
description = Column(String(255), nullable=False)
91+
user_id = Column(Integer, ForeignKey('users.id'))
9092

91-
def __init__(self, created, config, description):
93+
def __init__(self, created, config, description, user_id):
9294
self.created = parser.parse(created)
9395
self.updated = datetime.datetime.now()
9496
self.config = config
9597
self.description = description
98+
self.user_id = user_id
9699

97100
def __str__(self):
98101
return f"Url id: {self.id}, created on: {self.created}, description: {self.description}"

0 commit comments

Comments
 (0)