server/general: improve versioning effectiveness
...by integrating it with sqlalchemy that adds WHERE conditions for each UPDATE and DELETE statement.
This commit is contained in:
parent
ef4af697c4
commit
0320a0b55b
|
@ -1,7 +1,7 @@
|
|||
import datetime
|
||||
from szurubooru import search
|
||||
from szurubooru.func import auth, comments, posts, scores, util
|
||||
from szurubooru.rest import routes
|
||||
from szurubooru.func import auth, comments, posts, scores, util, versions
|
||||
|
||||
|
||||
_search_executor = search.Executor(search.configs.CommentSearchConfig())
|
||||
|
@ -43,12 +43,12 @@ def get_comment(ctx, params):
|
|||
@routes.put('/comment/(?P<comment_id>[^/]+)/?')
|
||||
def update_comment(ctx, params):
|
||||
comment = comments.get_comment_by_id(params['comment_id'])
|
||||
util.verify_version(comment, ctx)
|
||||
versions.verify_version(comment, ctx)
|
||||
versions.bump_version(comment)
|
||||
infix = 'own' if ctx.user.user_id == comment.user_id else 'any'
|
||||
text = ctx.get_param_as_string('text', required=True)
|
||||
auth.verify_privilege(ctx.user, 'comments:edit:%s' % infix)
|
||||
comments.update_comment_text(comment, text)
|
||||
util.bump_version(comment)
|
||||
comment.last_edit_time = datetime.datetime.utcnow()
|
||||
ctx.session.commit()
|
||||
return _serialize(ctx, comment)
|
||||
|
@ -57,7 +57,7 @@ def update_comment(ctx, params):
|
|||
@routes.delete('/comment/(?P<comment_id>[^/]+)/?')
|
||||
def delete_comment(ctx, params):
|
||||
comment = comments.get_comment_by_id(params['comment_id'])
|
||||
util.verify_version(comment, ctx)
|
||||
versions.verify_version(comment, ctx)
|
||||
infix = 'own' if ctx.user.user_id == comment.user_id else 'any'
|
||||
auth.verify_privilege(ctx.user, 'comments:delete:%s' % infix)
|
||||
ctx.session.delete(comment)
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import datetime
|
||||
import os
|
||||
from szurubooru import config
|
||||
from szurubooru.func import posts, users, util
|
||||
from szurubooru.rest import routes
|
||||
from szurubooru.func import posts, users, util
|
||||
|
||||
|
||||
_cache_time = None
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from szurubooru import config, errors
|
||||
from szurubooru.func import auth, mailer, users, util
|
||||
from szurubooru.rest import routes
|
||||
from szurubooru.func import auth, mailer, users, versions
|
||||
|
||||
|
||||
MAIL_SUBJECT = 'Password reset for {name}'
|
||||
|
@ -40,6 +40,6 @@ def finish_password_reset(ctx, params):
|
|||
if token != good_token:
|
||||
raise errors.ValidationError('Invalid password reset token.')
|
||||
new_password = users.reset_user_password(user)
|
||||
util.bump_version(user)
|
||||
versions.bump_version(user)
|
||||
ctx.session.commit()
|
||||
return {'password': new_password}
|
||||
|
|
|
@ -2,7 +2,7 @@ import datetime
|
|||
from szurubooru import search
|
||||
from szurubooru.rest import routes
|
||||
from szurubooru.func import (
|
||||
auth, tags, posts, snapshots, favorites, scores, util)
|
||||
auth, tags, posts, snapshots, favorites, scores, util, versions)
|
||||
|
||||
|
||||
_search_executor = search.Executor(search.configs.PostSearchConfig())
|
||||
|
@ -68,7 +68,8 @@ def get_post(ctx, params):
|
|||
@routes.put('/post/(?P<post_id>[^/]+)/?')
|
||||
def update_post(ctx, params):
|
||||
post = posts.get_post_by_id(params['post_id'])
|
||||
util.verify_version(post, ctx)
|
||||
versions.verify_version(post, ctx)
|
||||
versions.bump_version(post)
|
||||
if ctx.has_file('content'):
|
||||
auth.verify_privilege(ctx.user, 'posts:edit:content')
|
||||
posts.update_post_content(post, ctx.get_file('content'))
|
||||
|
@ -97,7 +98,6 @@ def update_post(ctx, params):
|
|||
if ctx.has_file('thumbnail'):
|
||||
auth.verify_privilege(ctx.user, 'posts:edit:thumbnail')
|
||||
posts.update_post_thumbnail(post, ctx.get_file('thumbnail'))
|
||||
util.bump_version(post)
|
||||
post.last_edit_time = datetime.datetime.utcnow()
|
||||
ctx.session.flush()
|
||||
snapshots.save_entity_modification(post, ctx.user)
|
||||
|
@ -110,7 +110,7 @@ def update_post(ctx, params):
|
|||
def delete_post(ctx, params):
|
||||
auth.verify_privilege(ctx.user, 'posts:delete')
|
||||
post = posts.get_post_by_id(params['post_id'])
|
||||
util.verify_version(post, ctx)
|
||||
versions.verify_version(post, ctx)
|
||||
snapshots.save_entity_deletion(post, ctx.user)
|
||||
posts.delete(post)
|
||||
ctx.session.commit()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from szurubooru import search
|
||||
from szurubooru.func import auth, snapshots
|
||||
from szurubooru.rest import routes
|
||||
from szurubooru.func import auth, snapshots
|
||||
|
||||
|
||||
_search_executor = search.Executor(search.configs.SnapshotSearchConfig())
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import datetime
|
||||
from szurubooru import db, search
|
||||
from szurubooru.func import auth, tags, util, snapshots
|
||||
from szurubooru.rest import routes
|
||||
from szurubooru.func import auth, tags, snapshots, util, versions
|
||||
|
||||
|
||||
_search_executor = search.Executor(search.configs.TagSearchConfig())
|
||||
|
@ -66,7 +66,8 @@ def get_tag(ctx, params):
|
|||
@routes.put('/tag/(?P<tag_name>[^/]+)/?')
|
||||
def update_tag(ctx, params):
|
||||
tag = tags.get_tag_by_name(params['tag_name'])
|
||||
util.verify_version(tag, ctx)
|
||||
versions.verify_version(tag, ctx)
|
||||
versions.bump_version(tag)
|
||||
if ctx.has_param('names'):
|
||||
auth.verify_privilege(ctx.user, 'tags:edit:names')
|
||||
tags.update_tag_names(tag, ctx.get_param_as_list('names'))
|
||||
|
@ -88,7 +89,6 @@ def update_tag(ctx, params):
|
|||
implications = ctx.get_param_as_list('implications')
|
||||
_create_if_needed(implications, ctx.user)
|
||||
tags.update_tag_implications(tag, implications)
|
||||
util.bump_version(tag)
|
||||
tag.last_edit_time = datetime.datetime.utcnow()
|
||||
ctx.session.flush()
|
||||
snapshots.save_entity_modification(tag, ctx.user)
|
||||
|
@ -100,7 +100,7 @@ def update_tag(ctx, params):
|
|||
@routes.delete('/tag/(?P<tag_name>[^/]+)/?')
|
||||
def delete_tag(ctx, params):
|
||||
tag = tags.get_tag_by_name(params['tag_name'])
|
||||
util.verify_version(tag, ctx)
|
||||
versions.verify_version(tag, ctx)
|
||||
auth.verify_privilege(ctx.user, 'tags:delete')
|
||||
snapshots.save_entity_deletion(tag, ctx.user)
|
||||
tags.delete(tag)
|
||||
|
@ -115,12 +115,12 @@ def merge_tags(ctx, _params=None):
|
|||
target_tag_name = ctx.get_param_as_string('mergeTo', required=True) or ''
|
||||
source_tag = tags.get_tag_by_name(source_tag_name)
|
||||
target_tag = tags.get_tag_by_name(target_tag_name)
|
||||
util.verify_version(source_tag, ctx, 'removeVersion')
|
||||
util.verify_version(target_tag, ctx, 'mergeToVersion')
|
||||
versions.verify_version(source_tag, ctx, 'removeVersion')
|
||||
versions.verify_version(target_tag, ctx, 'mergeToVersion')
|
||||
versions.bump_version(target_tag)
|
||||
auth.verify_privilege(ctx.user, 'tags:merge')
|
||||
tags.merge_tags(source_tag, target_tag)
|
||||
snapshots.save_entity_deletion(source_tag, ctx.user)
|
||||
util.bump_version(target_tag)
|
||||
ctx.session.commit()
|
||||
tags.export_to_json()
|
||||
return _serialize(ctx, target_tag)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from szurubooru.rest import routes
|
||||
from szurubooru.func import auth, tags, tag_categories, util, snapshots
|
||||
from szurubooru.func import (
|
||||
auth, tags, tag_categories, snapshots, util, versions)
|
||||
|
||||
|
||||
def _serialize(ctx, category):
|
||||
|
@ -40,7 +41,8 @@ def get_tag_category(ctx, params):
|
|||
@routes.put('/tag-category/(?P<category_name>[^/]+)/?')
|
||||
def update_tag_category(ctx, params):
|
||||
category = tag_categories.get_category_by_name(params['category_name'])
|
||||
util.verify_version(category, ctx)
|
||||
versions.verify_version(category, ctx)
|
||||
versions.bump_version(category)
|
||||
if ctx.has_param('name'):
|
||||
auth.verify_privilege(ctx.user, 'tag_categories:edit:name')
|
||||
tag_categories.update_category_name(
|
||||
|
@ -49,7 +51,6 @@ def update_tag_category(ctx, params):
|
|||
auth.verify_privilege(ctx.user, 'tag_categories:edit:color')
|
||||
tag_categories.update_category_color(
|
||||
category, ctx.get_param_as_string('color'))
|
||||
util.bump_version(category)
|
||||
ctx.session.flush()
|
||||
snapshots.save_entity_modification(category, ctx.user)
|
||||
ctx.session.commit()
|
||||
|
@ -60,7 +61,7 @@ def update_tag_category(ctx, params):
|
|||
@routes.delete('/tag-category/(?P<category_name>[^/]+)/?')
|
||||
def delete_tag_category(ctx, params):
|
||||
category = tag_categories.get_category_by_name(params['category_name'])
|
||||
util.verify_version(category, ctx)
|
||||
versions.verify_version(category, ctx)
|
||||
auth.verify_privilege(ctx.user, 'tag_categories:delete')
|
||||
tag_categories.delete_category(category)
|
||||
snapshots.save_entity_deletion(category, ctx.user)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from szurubooru import search
|
||||
from szurubooru.func import auth, users, util
|
||||
from szurubooru.rest import routes
|
||||
from szurubooru.func import auth, users, util, versions
|
||||
|
||||
|
||||
_search_executor = search.Executor(search.configs.UserSearchConfig())
|
||||
|
@ -52,7 +52,8 @@ def get_user(ctx, params):
|
|||
@routes.put('/user/(?P<user_name>[^/]+)/?')
|
||||
def update_user(ctx, params):
|
||||
user = users.get_user_by_name(params['user_name'])
|
||||
util.verify_version(user, ctx)
|
||||
versions.verify_version(user, ctx)
|
||||
versions.bump_version(user)
|
||||
infix = 'self' if ctx.user.user_id == user.user_id else 'any'
|
||||
if ctx.has_param('name'):
|
||||
auth.verify_privilege(ctx.user, 'users:edit:%s:name' % infix)
|
||||
|
@ -74,7 +75,6 @@ def update_user(ctx, params):
|
|||
user,
|
||||
ctx.get_param_as_string('avatarStyle'),
|
||||
ctx.get_file('avatar'))
|
||||
util.bump_version(user)
|
||||
ctx.session.commit()
|
||||
return _serialize(ctx, user)
|
||||
|
||||
|
@ -82,7 +82,7 @@ def update_user(ctx, params):
|
|||
@routes.delete('/user/(?P<user_name>[^/]+)/?')
|
||||
def delete_user(ctx, params):
|
||||
user = users.get_user_by_name(params['user_name'])
|
||||
util.verify_version(user, ctx)
|
||||
versions.verify_version(user, ctx)
|
||||
infix = 'self' if ctx.user.user_id == user.user_id else 'any'
|
||||
auth.verify_privilege(ctx.user, 'users:delete:%s' % infix)
|
||||
ctx.session.delete(user)
|
||||
|
|
|
@ -48,3 +48,8 @@ class Comment(Base):
|
|||
.query(func.sum(CommentScore.score)) \
|
||||
.filter(CommentScore.comment_id == self.comment_id) \
|
||||
.one()[0] or 0
|
||||
|
||||
__mapper_args__ = {
|
||||
'version_id_col': version,
|
||||
'version_id_generator': False,
|
||||
}
|
||||
|
|
|
@ -237,3 +237,8 @@ class Post(Base):
|
|||
(PostRelation.parent_id == post_id)
|
||||
| (PostRelation.child_id == post_id))
|
||||
.correlate_except(PostRelation))
|
||||
|
||||
__mapper_args__ = {
|
||||
'version_id_col': version,
|
||||
'version_id_generator': False,
|
||||
}
|
||||
|
|
|
@ -115,3 +115,8 @@ class Tag(Base):
|
|||
.where(TagImplication.parent_id == tag_id)
|
||||
.as_scalar(),
|
||||
deferred=True)
|
||||
|
||||
__mapper_args__ = {
|
||||
'version_id_col': version,
|
||||
'version_id_generator': False,
|
||||
}
|
||||
|
|
|
@ -21,3 +21,8 @@ class TagCategory(Base):
|
|||
select([func.count('Tag.tag_id')])
|
||||
.where(Tag.category_id == tag_category_id)
|
||||
.correlate_except(table('Tag')))
|
||||
|
||||
__mapper_args__ = {
|
||||
'version_id_col': version,
|
||||
'version_id_generator': False,
|
||||
}
|
||||
|
|
|
@ -75,3 +75,8 @@ class User(Base):
|
|||
.filter(PostScore.user_id == self.user_id)
|
||||
.filter(PostScore.score == -1)
|
||||
.one()[0] or 0)
|
||||
|
||||
__mapper_args__ = {
|
||||
'version_id_col': version,
|
||||
'version_id_generator': False,
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
import os
|
||||
import logging
|
||||
import coloredlogs
|
||||
import sqlalchemy.orm.exc
|
||||
from szurubooru import config, errors, rest
|
||||
# pylint: disable=unused-import
|
||||
from szurubooru import api, middleware
|
||||
|
@ -38,6 +39,11 @@ def _on_processing_error(ex):
|
|||
title='Processing error', description=str(ex))
|
||||
|
||||
|
||||
def _on_stale_data_error(_ex):
|
||||
raise rest.errors.HttpConflict(
|
||||
'Someone else modified this in the meantime. Please try again.')
|
||||
|
||||
|
||||
def validate_config():
|
||||
'''
|
||||
Check whether config doesn't contain errors that might prove
|
||||
|
@ -83,5 +89,6 @@ def create_app():
|
|||
rest.errors.handle(errors.IntegrityError, _on_integrity_error)
|
||||
rest.errors.handle(errors.NotFoundError, _on_not_found_error)
|
||||
rest.errors.handle(errors.ProcessingError, _on_processing_error)
|
||||
rest.errors.handle(sqlalchemy.orm.exc.StaleDataError, _on_stale_data_error)
|
||||
|
||||
return rest.application
|
||||
|
|
|
@ -154,16 +154,3 @@ def value_exceeds_column_size(value, column):
|
|||
if max_length is None:
|
||||
return False
|
||||
return len(value) > max_length
|
||||
|
||||
|
||||
def verify_version(entity, context, field_name='version'):
|
||||
actual_version = context.get_param_as_int(field_name, required=True)
|
||||
expected_version = entity.version
|
||||
if actual_version != expected_version:
|
||||
raise errors.InvalidParameterError(
|
||||
'Someone else modified this in the meantime. ' +
|
||||
'Please try again.')
|
||||
|
||||
|
||||
def bump_version(entity):
|
||||
entity.version += 1
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
from szurubooru import errors
|
||||
|
||||
|
||||
def verify_version(entity, context, field_name='version'):
|
||||
actual_version = context.get_param_as_int(field_name, required=True)
|
||||
expected_version = entity.version
|
||||
if actual_version != expected_version:
|
||||
raise errors.InvalidParameterError(
|
||||
'Someone else modified this in the meantime. ' +
|
||||
'Please try again.')
|
||||
|
||||
|
||||
def bump_version(entity):
|
||||
entity.version = entity.version + 1
|
|
@ -136,6 +136,7 @@ def test_trying_to_create_tags_without_privileges(
|
|||
params={'suggestions': ['tag1', 'tag2'], 'version': 1},
|
||||
user=user_factory(rank=db.User.RANK_REGULAR)),
|
||||
{'tag_name': 'tag'})
|
||||
db.session.rollback()
|
||||
with pytest.raises(errors.AuthError):
|
||||
api.tag_api.update_tag(
|
||||
context_factory(
|
||||
|
|
Loading…
Reference in New Issue