diff --git a/CTFd/api/__init__.py b/CTFd/api/__init__.py index 3e3fadf4..3c70f839 100644 --- a/CTFd/api/__init__.py +++ b/CTFd/api/__init__.py @@ -5,7 +5,6 @@ from CTFd.api.v1.awards import awards_namespace from CTFd.api.v1.challenges import challenges_namespace from CTFd.api.v1.comments import comments_namespace from CTFd.api.v1.config import configs_namespace -from CTFd.api.v1.fields import fields_namespace from CTFd.api.v1.files import files_namespace from CTFd.api.v1.flags import flags_namespace from CTFd.api.v1.hints import hints_namespace @@ -51,4 +50,3 @@ CTFd_API_v1.add_namespace(pages_namespace, "/pages") CTFd_API_v1.add_namespace(unlocks_namespace, "/unlocks") CTFd_API_v1.add_namespace(tokens_namespace, "/tokens") CTFd_API_v1.add_namespace(comments_namespace, "/comments") -CTFd_API_v1.add_namespace(fields_namespace, "/fields") diff --git a/CTFd/api/v1/config.py b/CTFd/api/v1/config.py index f4472982..9be0824c 100644 --- a/CTFd/api/v1/config.py +++ b/CTFd/api/v1/config.py @@ -8,8 +8,9 @@ from CTFd.api.v1.helpers.schemas import sqlalchemy_to_pydantic from CTFd.api.v1.schemas import APIDetailedSuccessResponse, APIListSuccessResponse from CTFd.cache import clear_config, clear_standings from CTFd.constants import RawEnum -from CTFd.models import Configs, db +from CTFd.models import Fields, Configs, db from CTFd.schemas.config import ConfigSchema +from CTFd.schemas.fields import FieldSchema from CTFd.utils import set_config from CTFd.utils.decorators import admins_only from CTFd.utils.helpers.models import build_model_filters @@ -189,3 +190,89 @@ class Config(Resource): clear_standings() return {"success": True} + + +@configs_namespace.route("/fields") +class FieldList(Resource): + @admins_only + @validate_args( + { + "type": (str, None), + "q": (str, None), + "field": (RawEnum("FieldFields", {"description": "description"}), None), + }, + location="query", + ) + def get(self, query_args): + q = query_args.pop("q", None) + field = str(query_args.pop("field", None)) + filters = build_model_filters(model=Fields, query=q, field=field) + + fields = Fields.query.filter_by(**query_args).filter(*filters).all() + schema = FieldSchema(many=True) + + response = schema.dump(fields) + + if response.errors: + return {"success": False, "errors": response.errors}, 400 + + return {"success": True, "data": response.data} + + @admins_only + def post(self): + req = request.get_json() + schema = FieldSchema() + response = schema.load(req, session=db.session) + + if response.errors: + return {"success": False, "errors": response.errors}, 400 + + db.session.add(response.data) + db.session.commit() + + response = schema.dump(response.data) + db.session.close() + + return {"success": True, "data": response.data} + + +@configs_namespace.route("/fields/") +class Field(Resource): + @admins_only + def get(self, field_id): + field = Fields.query.filter_by(id=field_id).first_or_404() + schema = FieldSchema() + + response = schema.dump(field) + + if response.errors: + return {"success": False, "errors": response.errors}, 400 + + return {"success": True, "data": response.data} + + @admins_only + def patch(self, field_id): + field = Fields.query.filter_by(id=field_id).first_or_404() + schema = FieldSchema() + + req = request.get_json() + + response = schema.load(req, session=db.session, instance=field) + if response.errors: + return {"success": False, "errors": response.errors}, 400 + + db.session.commit() + + response = schema.dump(response.data) + db.session.close() + + return {"success": True, "data": response.data} + + @admins_only + def delete(self, field_id): + field = Fields.query.filter_by(id=field_id).first_or_404() + db.session.delete(field) + db.session.commit() + db.session.close() + + return {"success": True} diff --git a/CTFd/api/v1/fields.py b/CTFd/api/v1/fields.py deleted file mode 100644 index 3a649f37..00000000 --- a/CTFd/api/v1/fields.py +++ /dev/null @@ -1,101 +0,0 @@ -from typing import List - -from flask import request, session -from flask_restx import Namespace, Resource - -from CTFd.api.v1.helpers.models import build_model_filters -from CTFd.api.v1.helpers.request import validate_args -from CTFd.api.v1.helpers.schemas import sqlalchemy_to_pydantic -from CTFd.api.v1.schemas import APIDetailedSuccessResponse, APIListSuccessResponse -from CTFd.constants import RawEnum -from CTFd.models import Fields, db -from CTFd.schemas.fields import FieldSchema -from CTFd.utils.decorators import admins_only - -fields_namespace = Namespace("fields", description="Endpoint to retrieve Fields") - - -@fields_namespace.route("") -class FieldList(Resource): - @admins_only - @validate_args( - { - "type": (str, None), - "q": (str, None), - "field": (RawEnum("FieldFields", {"description": "description"}), None), - }, - location="query", - ) - def get(self, query_args): - q = query_args.pop("q", None) - field = str(query_args.pop("field", None)) - filters = build_model_filters(model=Fields, query=q, field=field) - - fields = Fields.query.filter_by(**query_args).filter(*filters).all() - schema = FieldSchema(many=True) - - response = schema.dump(fields) - - if response.errors: - return {"success": False, "errors": response.errors}, 400 - - return {"success": True, "data": response.data} - - @admins_only - def post(self): - req = request.get_json() - schema = FieldSchema() - response = schema.load(req, session=db.session) - - if response.errors: - return {"success": False, "errors": response.errors}, 400 - - db.session.add(response.data) - db.session.commit() - - response = schema.dump(response.data) - db.session.close() - - return {"success": True, "data": response.data} - - -@fields_namespace.route("/") -class Field(Resource): - @admins_only - def get(self, field_id): - field = Fields.query.filter_by(id=field_id).first_or_404() - schema = FieldSchema() - - response = schema.dump(field) - - if response.errors: - return {"success": False, "errors": response.errors}, 400 - - return {"success": True, "data": response.data} - - @admins_only - def patch(self, field_id): - field = Fields.query.filter_by(id=field_id).first_or_404() - schema = FieldSchema() - - req = request.get_json() - - response = schema.load(req, session=db.session, instance=field) - if response.errors: - return {"success": False, "errors": response.errors}, 400 - - db.session.commit() - - response = schema.dump(response.data) - db.session.close() - - return {"success": True, "data": response.data} - - @admins_only - def delete(self, field_id): - field = Fields.query.filter_by(id=field_id).first_or_404() - db.session.delete(field) - db.session.commit() - db.session.close() - - return {"success": True}