From 6f193da89d33a3f663555efc2d3d1cf38b46bb78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 6 May 2016 12:04:22 +0200 Subject: [PATCH] Added entry point /p//quotas At the moment this entry point only returns the total file size of all files belonging to the project. It can be extended to return more info, such as nr. of nodes/groups/etc. --- pillar/application/modules/mongo_utils.py | 27 ++++++++++++++++ pillar/application/modules/projects.py | 38 +++++++++++++++++++++-- 2 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 pillar/application/modules/mongo_utils.py diff --git a/pillar/application/modules/mongo_utils.py b/pillar/application/modules/mongo_utils.py new file mode 100644 index 00000000..cfeb1046 --- /dev/null +++ b/pillar/application/modules/mongo_utils.py @@ -0,0 +1,27 @@ +"""Utility functions for MongoDB stuff.""" + +from bson import ObjectId +from flask import current_app +from werkzeug.exceptions import NotFound + + +def find_one_or_404(collection_name, object_id): + """Returns the found object from the collection, or raises a NotFound exception. + + :param collection_name: name of the collection, such as 'users' or 'files' + :type collection_name: str + :param object_id: ID of the object to find. + :type object_id: str or bson.ObjectId + :returns: the found object + :rtype: dict + + :raises: werkzeug.exceptions.NotFound + """ + + collection = current_app.data.driver.db[collection_name] + found = collection.find_one(ObjectId(object_id)) + + if found is None: + raise NotFound() + + return found diff --git a/pillar/application/modules/projects.py b/pillar/application/modules/projects.py index 516e2c0d..50148668 100644 --- a/pillar/application/modules/projects.py +++ b/pillar/application/modules/projects.py @@ -7,9 +7,10 @@ from eve.methods.post import post_internal from eve.methods.patch import patch_internal from flask import g, Blueprint, request, abort, current_app +from application.modules import mongo_utils from application.utils import remove_private_keys, authorization, jsonify from application.utils.gcs import GoogleCloudStorageBucket -from application.utils.authorization import user_has_role, check_permissions +from application.utils.authorization import user_has_role, check_permissions, require_login from manage_extra.node_types.asset import node_type_asset from manage_extra.node_types.comment import node_type_comment from manage_extra.node_types.group import node_type_group @@ -49,7 +50,6 @@ def before_inserting_override_is_private_field(projects): def before_edit_check_permissions(document, original): - # Allow admin users to do whatever they want. # TODO: possibly move this into the check_permissions function. if user_has_role(u'admin'): @@ -316,6 +316,40 @@ def abort_with_error(status): abort(status if status // 100 >= 4 else 500) +@blueprint.route('//quotas') +@require_login() +def project_quotas(project_id): + """Returns information about the project's limits.""" + + # Check that the user has GET permissions on the project itself. + project = mongo_utils.find_one_or_404('projects', project_id) + check_permissions(project, 'GET') + + file_size_used = _project_total_file_size(project_id) + + info = { + 'file_size_quota': None, # TODO: implement this later. + 'file_size_used': file_size_used, + } + + return jsonify(info) + + +def _project_total_file_size(project_id): + """Returns the total number of bytes used by files of this project.""" + + files = current_app.data.driver.db['files'] + file_size_used = files.aggregate([ + {'$match': {'project': ObjectId(project_id)}}, + {'$project': {'length_aggregate_in_bytes': 1}}, + {'$group': {'_id': None, + 'all_files': {'$sum': '$length_aggregate_in_bytes'}}} + ]) + + # The aggregate function returns a cursor, not a document. + return next(file_size_used)['all_files'] + + def setup_app(app, url_prefix): app.on_replace_projects += override_is_private_field app.on_replace_projects += before_edit_check_permissions