server/general: move extra details to resources

This commit is contained in:
rr- 2016-05-30 22:07:44 +02:00
parent 48bcbbff83
commit d0314813cb
27 changed files with 213 additions and 236 deletions

67
API.md
View file

@ -1391,7 +1391,8 @@ A single user.
- `<name>`: the user name.
- `<email>`: the user email. It is available only if the request is
authenticated by the same user, or the authenticated user can change the
email.
email. If it's unavailable, the server returns `false`. If the user hasn't
specified an email, the server returns `null`.
- `<rank>`: the user rank, which effectively affects their privileges.
Possible values:
@ -1444,7 +1445,12 @@ experience.
"name": <name>,
"color": <color>,
"usages": <usages>
"default": <is-default>
"default": <is-default>,
"snapshots": [
<snapshot>,
<snapshot>,
<snapshot>
]
}
```
@ -1454,6 +1460,8 @@ experience.
- `<color>`: the category color.
- `<usages>`: how many tags is the given category used with.
- `<is-default>`: whether the tag category is the default one.
- `<snapshot>`: a [snapshot resource](#snapshot) that contains the tag
category's earlier versions.
## Detailed tag category
**Description**
@ -1465,19 +1473,12 @@ A tag category with extra information.
```json5
{
"tagCategory": <tag-category>,
"snapshots": [
<snapshot>,
<snapshot>,
<snapshot>
]
}
```
**Field meaning**
- `<tag-category>`: a [tag category resource](#tag-category)
- `<snapshot>`: a [snapshot resource](#snapshot) that contains the tag
category's earlier versions.
## Tag
**Description**
@ -1494,7 +1495,12 @@ A single tag. Tags are used to let users search for posts.
"suggestions": <suggestions>,
"creationTime": <creation-time>,
"lastEditTime": <last-edit-time>,
"usages": <usage-count>
"usages": <usage-count>,
"snapshots": [
<snapshot>,
<snapshot>,
<snapshot>
]
}
```
@ -1510,6 +1516,8 @@ A single tag. Tags are used to let users search for posts.
- `<creation-time>`: time the tag was created, formatted as per RFC 3339.
- `<last-edit-time>`: time the tag was edited, formatted as per RFC 3339.
- `<usage-count>`: the number of posts the tag was used in.
- `<snapshot>`: a [snapshot resource](#snapshot) that contains the tag's
earlier versions.
## Detailed tag
**Description**
@ -1521,18 +1529,11 @@ A tag with extra information.
```json5
{
"tag": <tag>,
"snapshots": [
<snapshot>,
<snapshot>,
<snapshot>
]
}
```
**Field meaning**
- `<tag>`: a [tag resource](#tag)
- `<snapshot>`: a [snapshot resource](#snapshot) that contains the tag's
earlier versions.
## Post
**Description**
@ -1566,6 +1567,16 @@ One file together with its metadata posted to the site.
"favoritedBy": <favorited-by>,
"hasCustomThumbnail": <has-custom-thumbnail>,
"mimeType": <mime-type>
"snapshots": [
<snapshot>,
<snapshot>,
<snapshot>
],
"comments": {
<comment>,
<comment>,
<comment>
}
}
```
@ -1617,6 +1628,9 @@ One file together with its metadata posted to the site.
- `<has-custom-thumbnail>`: whether the post uses custom thumbnail.
- `<mime-type>`: subsidiary to `<type>`, used to tell exact content format;
useful for `<video>` tags for instance.
- `<snapshot>`: a [snapshot resource](#snapshot) that contains the post's
earlier versions.
- `<comment>`: a [comment resource](#comment) for given post.
## Detailed post
**Description**
@ -1628,24 +1642,11 @@ A post with extra information.
```json5
{
"post": <post>,
"snapshots": [
<snapshot>,
<snapshot>,
<snapshot>
],
"comments": {
<comment>,
<comment>,
<comment>
}
}
```
**Field meaning**
- `<post>`: a [post resource](#post).
- `<snapshot>`: a [snapshot resource](#snapshot) that contains the post's
earlier versions.
- `<comment>`: a [comment resource](#comment) for given post.
## Note
**Description**
@ -1678,7 +1679,7 @@ A comment under a post.
```json5
{
"id": <id>,
"post": <post>,
"postId": <post-id>,
"user": <author>
"text": <text>,
"creationTime": <creation-time>,
@ -1690,9 +1691,9 @@ A comment under a post.
**Field meaning**
- `<id>`: the comment identifier.
- `<post>`: a post resource the post is linked with.
- `<post-id>`: an id of the post the comment is for.
- `<text>`: the comment content. The client should render is as Markdown.
- `<author>`: a user resource the post is created by.
- `<author>`: a user resource the comment is created by.
- `<creation-time>`: time the comment was created, formatted as per RFC 3339.
- `<last-edit-time>`: time the comment was edited, formatted as per RFC 3339.
- `<score>`: the collective score (+1/-1 rating) of the given comment.

View file

@ -23,13 +23,13 @@ class CommentListApi(BaseApi):
comment = comments.create_comment(ctx.user, post, text)
ctx.session.add(comment)
ctx.session.commit()
return comments.serialize_comment_with_details(comment, ctx.user)
return {'comment': comments.serialize_comment(comment, ctx.user)}
class CommentDetailApi(BaseApi):
def get(self, ctx, comment_id):
auth.verify_privilege(ctx.user, 'comments:view')
comment = comments.get_comment_by_id(comment_id)
return comments.serialize_comment_with_details(comment, ctx.user)
return {'comment': comments.serialize_comment(comment, ctx.user)}
def put(self, ctx, comment_id):
comment = comments.get_comment_by_id(comment_id)
@ -39,7 +39,7 @@ class CommentDetailApi(BaseApi):
comment.last_edit_time = datetime.datetime.now()
comments.update_comment_text(comment, text)
ctx.session.commit()
return comments.serialize_comment_with_details(comment, ctx.user)
return {'comment': comments.serialize_comment(comment, ctx.user)}
def delete(self, ctx, comment_id):
comment = comments.get_comment_by_id(comment_id)
@ -56,11 +56,11 @@ class CommentScoreApi(BaseApi):
comment = comments.get_comment_by_id(comment_id)
scores.set_score(comment, ctx.user, score)
ctx.session.commit()
return comments.serialize_comment_with_details(comment, ctx.user)
return {'comment': comments.serialize_comment(comment, ctx.user)}
def delete(self, ctx, comment_id):
auth.verify_privilege(ctx.user, 'comments:score')
comment = comments.get_comment_by_id(comment_id)
scores.delete_score(comment, ctx.user)
ctx.session.commit()
return comments.serialize_comment_with_details(comment, ctx.user)
return {'comment': comments.serialize_comment(comment, ctx.user)}

View file

@ -38,13 +38,13 @@ class PostListApi(BaseApi):
snapshots.save_entity_creation(post, ctx.user)
ctx.session.commit()
tags.export_to_json()
return posts.serialize_post_with_details(post, ctx.user)
return {'post': posts.serialize_post(post, ctx.user)}
class PostDetailApi(BaseApi):
def get(self, ctx, post_id):
auth.verify_privilege(ctx.user, 'posts:view')
post = posts.get_post_by_id(post_id)
return posts.serialize_post_with_details(post, ctx.user)
return {'post': posts.serialize_post(post, ctx.user)}
def put(self, ctx, post_id):
post = posts.get_post_by_id(post_id)
@ -79,7 +79,7 @@ class PostDetailApi(BaseApi):
snapshots.save_entity_modification(post, ctx.user)
ctx.session.commit()
tags.export_to_json()
return posts.serialize_post_with_details(post, ctx.user)
return {'post': posts.serialize_post(post, ctx.user)}
def delete(self, ctx, post_id):
auth.verify_privilege(ctx.user, 'posts:delete')
@ -104,11 +104,11 @@ class PostFeatureApi(BaseApi):
snapshots.save_entity_modification(featured_post, ctx.user)
snapshots.save_entity_modification(post, ctx.user)
ctx.session.commit()
return posts.serialize_post_with_details(post, ctx.user)
return {'post': posts.serialize_post(post, ctx.user)}
def get(self, ctx):
post = posts.try_get_featured_post()
return posts.serialize_post_with_details(post, ctx.user)
return {'post': posts.serialize_post(post, ctx.user)}
class PostScoreApi(BaseApi):
def put(self, ctx, post_id):
@ -117,14 +117,14 @@ class PostScoreApi(BaseApi):
score = ctx.get_param_as_int('score', required=True)
scores.set_score(post, ctx.user, score)
ctx.session.commit()
return posts.serialize_post_with_details(post, ctx.user)
return {'post': posts.serialize_post(post, ctx.user)}
def delete(self, ctx, post_id):
auth.verify_privilege(ctx.user, 'posts:score')
post = posts.get_post_by_id(post_id)
scores.delete_score(post, ctx.user)
ctx.session.commit()
return posts.serialize_post_with_details(post, ctx.user)
return {'post': posts.serialize_post(post, ctx.user)}
class PostFavoriteApi(BaseApi):
def post(self, ctx, post_id):
@ -132,11 +132,11 @@ class PostFavoriteApi(BaseApi):
post = posts.get_post_by_id(post_id)
favorites.set_favorite(post, ctx.user)
ctx.session.commit()
return posts.serialize_post_with_details(post, ctx.user)
return {'post': posts.serialize_post(post, ctx.user)}
def delete(self, ctx, post_id):
auth.verify_privilege(ctx.user, 'posts:favorite')
post = posts.get_post_by_id(post_id)
favorites.unset_favorite(post, ctx.user)
ctx.session.commit()
return posts.serialize_post_with_details(post, ctx.user)
return {'post': posts.serialize_post(post, ctx.user)}

View file

@ -41,13 +41,13 @@ class TagListApi(BaseApi):
snapshots.save_entity_creation(tag, ctx.user)
ctx.session.commit()
tags.export_to_json()
return tags.serialize_tag_with_details(tag)
return {'tag': tags.serialize_tag(tag)}
class TagDetailApi(BaseApi):
def get(self, ctx, tag_name):
auth.verify_privilege(ctx.user, 'tags:view')
tag = tags.get_tag_by_name(tag_name)
return tags.serialize_tag_with_details(tag)
return {'tag': tags.serialize_tag(tag)}
def put(self, ctx, tag_name):
tag = tags.get_tag_by_name(tag_name)
@ -73,7 +73,7 @@ class TagDetailApi(BaseApi):
snapshots.save_entity_modification(tag, ctx.user)
ctx.session.commit()
tags.export_to_json()
return tags.serialize_tag_with_details(tag)
return {'tag': tags.serialize_tag(tag)}
def delete(self, ctx, tag_name):
tag = tags.get_tag_by_name(tag_name)
@ -101,7 +101,7 @@ class TagMergeApi(BaseApi):
tags.merge_tags(source_tag, target_tag)
ctx.session.commit()
tags.export_to_json()
return tags.serialize_tag_with_details(target_tag)
return {'tag': tags.serialize_tag(target_tag)}
class TagSiblingsApi(BaseApi):
def get(self, ctx, tag_name):

View file

@ -21,13 +21,13 @@ class TagCategoryListApi(BaseApi):
snapshots.save_entity_creation(category, ctx.user)
ctx.session.commit()
tags.export_to_json()
return tag_categories.serialize_category_with_details(category)
return {'tagCategory': tag_categories.serialize_category(category)}
class TagCategoryDetailApi(BaseApi):
def get(self, ctx, category_name):
auth.verify_privilege(ctx.user, 'tag_categories:view')
category = tag_categories.get_category_by_name(category_name)
return tag_categories.serialize_category_with_details(category)
return {'tagCategory': tag_categories.serialize_category(category)}
def put(self, ctx, category_name):
category = tag_categories.get_category_by_name(category_name)
@ -43,7 +43,7 @@ class TagCategoryDetailApi(BaseApi):
snapshots.save_entity_modification(category, ctx.user)
ctx.session.commit()
tags.export_to_json()
return tag_categories.serialize_category_with_details(category)
return {'tagCategory': tag_categories.serialize_category(category)}
def delete(self, ctx, category_name):
category = tag_categories.get_category_by_name(category_name)
@ -69,4 +69,4 @@ class DefaultTagCategoryApi(BaseApi):
snapshots.save_entity_modification(category, ctx.user)
ctx.session.commit()
tags.export_to_json()
return tag_categories.serialize_category_with_details(category)
return {'tagCategory': tag_categories.serialize_category(category)}

View file

@ -28,14 +28,14 @@ class UserListApi(BaseApi):
ctx.get_file('avatar'))
ctx.session.add(user)
ctx.session.commit()
return users.serialize_user_with_details(
user, ctx.user, force_show_email=True)
return {'user': users.serialize_user(
user, ctx.user, force_show_email=True)}
class UserDetailApi(BaseApi):
def get(self, ctx, user_name):
auth.verify_privilege(ctx.user, 'users:view')
user = users.get_user_by_name(user_name)
return users.serialize_user_with_details(user, ctx.user)
return {'user': users.serialize_user(user, ctx.user)}
def put(self, ctx, user_name):
user = users.get_user_by_name(user_name)
@ -61,7 +61,7 @@ class UserDetailApi(BaseApi):
ctx.get_param_as_string('avatarStyle'),
ctx.get_file('avatar'))
ctx.session.commit()
return users.serialize_user_with_details(user, ctx.user)
return {'user': users.serialize_user(user, ctx.user)}
def delete(self, ctx, user_name):
user = users.get_user_by_name(user_name)

View file

@ -1,25 +1,22 @@
import datetime
from szurubooru import db, errors
from szurubooru.func import users, posts, scores
from szurubooru.func import users, scores, util
class CommentNotFoundError(errors.NotFoundError): pass
class EmptyCommentTextError(errors.ValidationError): pass
def serialize_comment(comment, authenticated_user):
ret = {
'id': comment.comment_id,
'user': users.serialize_user(comment.user, authenticated_user),
'post': posts.serialize_post(comment.post, authenticated_user),
'text': comment.text,
'creationTime': comment.creation_time,
'lastEditTime': comment.last_edit_time,
}
if authenticated_user:
ret['ownScore'] = scores.get_score(comment, authenticated_user)
return ret
def serialize_comment_with_details(comment, authenticated_user):
return {'comment': serialize_comment(comment, authenticated_user)}
return util.serialize_entity(
comment,
{
'id': lambda: comment.comment_id,
'user': lambda: users.serialize_user(comment.user, authenticated_user),
'postId': lambda: comment.post.post_id,
'text': lambda: comment.text,
'creationTime': lambda: comment.creation_time,
'lastEditTime': lambda: comment.last_edit_time,
'ownScore': lambda: scores.get_score(comment, authenticated_user),
})
def try_get_comment_by_id(comment_id):
return db.session \

View file

@ -62,54 +62,43 @@ def serialize_note(note):
}
def serialize_post(post, authenticated_user):
if not post:
return None
ret = {
'id': post.post_id,
'creationTime': post.creation_time,
'lastEditTime': post.last_edit_time,
'safety': SAFETY_MAP[post.safety],
'source': post.source,
'type': TYPE_MAP[post.type],
'checksum': post.checksum,
'fileSize': post.file_size,
'canvasWidth': post.canvas_width,
'canvasHeight': post.canvas_height,
'contentUrl': get_post_content_url(post),
'thumbnailUrl': get_post_thumbnail_url(post),
'flags': post.flags,
'tags': [tag.names[0].name for tag in post.tags],
'relations': [rel.post_id for rel in post.relations],
'notes': sorted(
[ serialize_note(note) for note in post.notes],
key=lambda x: x['polygon']),
'user': users.serialize_user(post.user, authenticated_user),
'score': post.score,
'featureCount': post.feature_count,
'lastFeatureTime': post.last_feature_time,
'favoritedBy': [users.serialize_user(rel.user, authenticated_user) \
for rel in post.favorited_by],
'hasCustomThumbnail': files.has(get_post_thumbnail_backup_path(post)),
'mimeType': post.mime_type,
}
if authenticated_user:
ret['ownScore'] = scores.get_score(post, authenticated_user)
return ret
def serialize_post_with_details(post, authenticated_user):
comment_list = []
if post:
for comment in post.comments:
comment_list.append(
comments.serialize_comment(comment, authenticated_user))
return {
'post': serialize_post(post, authenticated_user),
'snapshots': snapshots.get_serialized_history(post),
'comments': comment_list,
}
return util.serialize_entity(
post,
{
'id': lambda: post.post_id,
'creationTime': lambda: post.creation_time,
'lastEditTime': lambda: post.last_edit_time,
'safety': lambda: SAFETY_MAP[post.safety],
'source': lambda: post.source,
'type': lambda: TYPE_MAP[post.type],
'mimeType': lambda: post.mime_type,
'checksum': lambda: post.checksum,
'fileSize': lambda: post.file_size,
'canvasWidth': lambda: post.canvas_width,
'canvasHeight': lambda: post.canvas_height,
'contentUrl': lambda: get_post_content_url(post),
'thumbnailUrl': lambda: get_post_thumbnail_url(post),
'flags': lambda: post.flags,
'tags': lambda: [tag.names[0].name for tag in post.tags],
'relations': lambda: [rel.post_id for rel in post.relations],
'user': lambda: users.serialize_user(post.user, authenticated_user),
'score': lambda: post.score,
'ownScore': lambda: scores.get_score(post, authenticated_user),
'featureCount': lambda: post.feature_count,
'lastFeatureTime': lambda: post.last_feature_time,
'favoritedBy': lambda: [
users.serialize_user(rel.user, authenticated_user) \
for rel in post.favorited_by],
'hasCustomThumbnail':
lambda: files.has(get_post_thumbnail_backup_path(post)),
'notes': lambda: sorted(
[serialize_note(note) for note in post.notes],
key=lambda x: x['polygon']),
'comments': lambda: [
comments.serialize_comment(comment, authenticated_user) \
for comment in post.comments],
'snapshots': lambda: snapshots.get_serialized_history(post),
})
def get_post_count():
return db.session.query(sqlalchemy.func.count(db.Post.post_id)).one()[0]

View file

@ -15,18 +15,15 @@ def _verify_name_validity(name):
'Name must satisfy regex %r.' % name_regex)
def serialize_category(category):
return {
'name': category.name,
'color': category.color,
'usages': category.tag_count,
'default': category.default,
}
def serialize_category_with_details(category):
return {
'tagCategory': serialize_category(category),
'snapshots': snapshots.get_serialized_history(category),
}
return util.serialize_entity(
category,
{
'name': lambda: category.name,
'color': lambda: category.color,
'usages': lambda: category.tag_count,
'default': lambda: category.default,
'snapshots': lambda: snapshots.get_serialized_history(category),
})
def create_category(name, color):
category = db.TagCategory()

View file

@ -37,23 +37,20 @@ def _get_default_category_name():
return DEFAULT_CATEGORY_NAME
def serialize_tag(tag):
return {
'names': [tag_name.name for tag_name in tag.names],
'category': tag.category.name,
'suggestions': [
relation.names[0].name for relation in tag.suggestions],
'implications': [
relation.names[0].name for relation in tag.implications],
'creationTime': tag.creation_time,
'lastEditTime': tag.last_edit_time,
'usages': tag.post_count,
}
def serialize_tag_with_details(tag):
return {
'tag': serialize_tag(tag),
'snapshots': snapshots.get_serialized_history(tag),
}
return util.serialize_entity(
tag,
{
'names': lambda: [tag_name.name for tag_name in tag.names],
'category': lambda: tag.category.name,
'creationTime': lambda: tag.creation_time,
'lastEditTime': lambda: tag.last_edit_time,
'usages': lambda: tag.post_count,
'suggestions': lambda: [
relation.names[0].name for relation in tag.suggestions],
'implications': lambda: [
relation.names[0].name for relation in tag.implications],
'snapshots': lambda: snapshots.get_serialized_history(tag),
})
def export_to_json():
output = {

View file

@ -12,36 +12,34 @@ class InvalidPasswordError(errors.ValidationError): pass
class InvalidRankError(errors.ValidationError): pass
class InvalidAvatarError(errors.ValidationError): pass
def serialize_user(user, authenticated_user, force_show_email=False):
if not user:
return {}
ret = {
'name': user.name,
'rank': user.rank,
'creationTime': user.creation_time,
'lastLoginTime': user.last_login_time,
'avatarStyle': user.avatar_style,
'email': user.email,
}
def _get_avatar_url(user):
if user.avatar_style == user.AVATAR_GRAVATAR:
ret['avatarUrl'] = 'http://gravatar.com/avatar/%s?d=retro&s=%d' % (
return 'http://gravatar.com/avatar/%s?d=retro&s=%d' % (
util.get_md5((user.email or user.name).lower()),
config.config['thumbnails']['avatar_width'])
else:
ret['avatarUrl'] = '%s/avatars/%s.png' % (
return '%s/avatars/%s.png' % (
config.config['data_url'].rstrip('/'), user.name.lower())
if authenticated_user.user_id != user.user_id \
and not force_show_email \
and not auth.has_privilege(authenticated_user, 'users:edit:any:email'):
del ret['email']
def _get_email(user, authenticated_user, force_show_email):
if not force_show_email \
and authenticated_user.user_id != user.user_id \
and not auth.has_privilege(authenticated_user, 'users:edit:any:email'):
return False
return user.email
return ret
def serialize_user_with_details(user, authenticated_user, **kwargs):
return {'user': serialize_user(user, authenticated_user, **kwargs)}
def serialize_user(user, authenticated_user, force_show_email=False):
return util.serialize_entity(
user,
{
'name': lambda: user.name,
'rank': lambda: user.rank,
'creationTime': lambda: user.creation_time,
'lastLoginTime': lambda: user.last_login_time,
'avatarStyle': lambda: user.avatar_style,
'avatarUrl': lambda: _get_avatar_url(user),
'email': lambda: _get_email(user, authenticated_user, force_show_email),
})
def get_user_count():
return db.session.query(db.User).count()

View file

@ -6,6 +6,14 @@ import tempfile
from contextlib import contextmanager
from szurubooru.errors import ValidationError
def serialize_entity(entity, field_factories):
if not entity:
return None
ret = {}
for key, factory in field_factories.items():
ret[key] = factory()
return ret
@contextmanager
def create_temp_file(**kwargs):
(handle, path) = tempfile.mkstemp(**kwargs)

View file

@ -32,9 +32,8 @@ def test_creating_comment(test_ctx, fake_datetime):
assert result['comment']['text'] == 'input'
assert 'id' in result['comment']
assert 'user' in result['comment']
assert 'post' in result['comment']
assert 'name' in result['comment']['user']
assert 'id' in result['comment']['post']
assert 'postId' in result['comment']
comment = db.session.query(db.Comment).one()
assert comment.text == 'input'
assert comment.creation_time == datetime.datetime(1997, 1, 1)

View file

@ -60,8 +60,7 @@ def test_retrieving_single(test_ctx):
assert 'text' in result['comment']
assert 'user' in result['comment']
assert 'name' in result['comment']['user']
assert 'post' in result['comment']
assert 'id' in result['comment']['post']
assert 'postId' in result['comment']
def test_trying_to_retrieve_single_non_existing(test_ctx):
with pytest.raises(comments.CommentNotFoundError):

View file

@ -25,12 +25,12 @@ def test_creating_minimal_posts(
unittest.mock.patch('szurubooru.func.posts.update_post_notes'), \
unittest.mock.patch('szurubooru.func.posts.update_post_flags'), \
unittest.mock.patch('szurubooru.func.posts.update_post_thumbnail'), \
unittest.mock.patch('szurubooru.func.posts.serialize_post_with_details'), \
unittest.mock.patch('szurubooru.func.posts.serialize_post'), \
unittest.mock.patch('szurubooru.func.tags.export_to_json'), \
unittest.mock.patch('szurubooru.func.snapshots.save_entity_creation'):
posts.create_post.return_value = post
posts.serialize_post_with_details.return_value = 'serialized post'
posts.serialize_post.return_value = 'serialized post'
result = api.PostListApi().post(
context_factory(
@ -44,7 +44,7 @@ def test_creating_minimal_posts(
},
user=auth_user))
assert result == 'serialized post'
assert result == {'post': 'serialized post'}
posts.create_post.assert_called_once_with(
'post-content', ['tag1', 'tag2'], auth_user)
posts.update_post_thumbnail.assert_called_once_with(post, 'post-thumbnail')
@ -54,7 +54,7 @@ def test_creating_minimal_posts(
posts.update_post_notes.assert_called_once_with(post, [])
posts.update_post_flags.assert_called_once_with(post, [])
posts.update_post_thumbnail.assert_called_once_with(post, 'post-thumbnail')
posts.serialize_post_with_details.assert_called_once_with(post, auth_user)
posts.serialize_post.assert_called_once_with(post, auth_user)
tags.export_to_json.assert_called_once_with()
snapshots.save_entity_creation.assert_called_once_with(post, auth_user)
@ -70,12 +70,12 @@ def test_creating_full_posts(context_factory, post_factory, user_factory):
unittest.mock.patch('szurubooru.func.posts.update_post_relations'), \
unittest.mock.patch('szurubooru.func.posts.update_post_notes'), \
unittest.mock.patch('szurubooru.func.posts.update_post_flags'), \
unittest.mock.patch('szurubooru.func.posts.serialize_post_with_details'), \
unittest.mock.patch('szurubooru.func.posts.serialize_post'), \
unittest.mock.patch('szurubooru.func.tags.export_to_json'), \
unittest.mock.patch('szurubooru.func.snapshots.save_entity_creation'):
posts.create_post.return_value = post
posts.serialize_post_with_details.return_value = 'serialized post'
posts.serialize_post.return_value = 'serialized post'
result = api.PostListApi().post(
context_factory(
@ -92,7 +92,7 @@ def test_creating_full_posts(context_factory, post_factory, user_factory):
},
user=auth_user))
assert result == 'serialized post'
assert result == {'post': 'serialized post'}
posts.create_post.assert_called_once_with(
'post-content', ['tag1', 'tag2'], auth_user)
posts.update_post_safety.assert_called_once_with(post, 'safe')
@ -100,7 +100,7 @@ def test_creating_full_posts(context_factory, post_factory, user_factory):
posts.update_post_relations.assert_called_once_with(post, [1, 2])
posts.update_post_notes.assert_called_once_with(post, ['note1', 'note2'])
posts.update_post_flags.assert_called_once_with(post, ['flag1', 'flag2'])
posts.serialize_post_with_details.assert_called_once_with(post, auth_user)
posts.serialize_post.assert_called_once_with(post, auth_user)
tags.export_to_json.assert_called_once_with()
snapshots.save_entity_creation.assert_called_once_with(post, auth_user)
@ -114,7 +114,7 @@ def test_creating_from_url_saves_source(
with unittest.mock.patch('szurubooru.func.net.download'), \
unittest.mock.patch('szurubooru.func.tags.export_to_json'), \
unittest.mock.patch('szurubooru.func.snapshots.save_entity_creation'), \
unittest.mock.patch('szurubooru.func.posts.serialize_post_with_details'), \
unittest.mock.patch('szurubooru.func.posts.serialize_post'), \
unittest.mock.patch('szurubooru.func.posts.create_post'), \
unittest.mock.patch('szurubooru.func.posts.update_post_source'):
config_injector({
@ -145,7 +145,7 @@ def test_creating_from_url_with_source_specified(
with unittest.mock.patch('szurubooru.func.net.download'), \
unittest.mock.patch('szurubooru.func.tags.export_to_json'), \
unittest.mock.patch('szurubooru.func.snapshots.save_entity_creation'), \
unittest.mock.patch('szurubooru.func.posts.serialize_post_with_details'), \
unittest.mock.patch('szurubooru.func.posts.serialize_post'), \
unittest.mock.patch('szurubooru.func.posts.create_post'), \
unittest.mock.patch('szurubooru.func.posts.update_post_source'):
config_injector({

View file

@ -26,7 +26,7 @@ def test_no_featured_post(test_ctx):
result = test_ctx.api.get(
test_ctx.context_factory(
user=test_ctx.user_factory(rank=db.User.RANK_REGULAR)))
assert result == {'post': None, 'snapshots': [], 'comments': []}
assert result == {'post': None}
def test_featuring(test_ctx):
db.session.add(test_ctx.post_factory(id=1))
@ -41,15 +41,15 @@ def test_featuring(test_ctx):
assert posts.get_post_by_id(1).is_featured
assert 'post' in result
assert 'id' in result['post']
assert 'snapshots' in result
assert 'comments' in result
assert 'snapshots' in result['post']
assert 'comments' in result['post']
result = test_ctx.api.get(
test_ctx.context_factory(
user=test_ctx.user_factory(rank=db.User.RANK_REGULAR)))
assert 'post' in result
assert 'id' in result['post']
assert 'snapshots' in result
assert 'comments' in result
assert 'snapshots' in result['post']
assert 'comments' in result['post']
def test_trying_to_feature_the_same_post_twice(test_ctx):
db.session.add(test_ctx.post_factory(id=1))

View file

@ -81,8 +81,8 @@ def test_retrieving_single(test_ctx):
user=test_ctx.user_factory(rank=db.User.RANK_REGULAR)), 1)
assert 'post' in result
assert 'id' in result['post']
assert 'snapshots' in result
assert 'comments' in result
assert 'snapshots' in result['post']
assert 'comments' in result['post']
def test_trying_to_retrieve_single_non_existing(test_ctx):
with pytest.raises(posts.PostNotFoundError):

View file

@ -33,11 +33,11 @@ def test_post_updating(
unittest.mock.patch('szurubooru.func.posts.update_post_relations'), \
unittest.mock.patch('szurubooru.func.posts.update_post_notes'), \
unittest.mock.patch('szurubooru.func.posts.update_post_flags'), \
unittest.mock.patch('szurubooru.func.posts.serialize_post_with_details'), \
unittest.mock.patch('szurubooru.func.posts.serialize_post'), \
unittest.mock.patch('szurubooru.func.tags.export_to_json'), \
unittest.mock.patch('szurubooru.func.snapshots.save_entity_modification'):
posts.serialize_post_with_details.return_value = 'serialized post'
posts.serialize_post.return_value = 'serialized post'
with fake_datetime('1997-01-01'):
result = api.PostDetailApi().put(
@ -57,7 +57,7 @@ def test_post_updating(
user=auth_user),
post.post_id)
assert result == 'serialized post'
assert result == {'post': 'serialized post'}
posts.create_post.assert_not_called()
posts.update_post_tags.assert_called_once_with(post, ['tag1', 'tag2'])
posts.update_post_content.assert_called_once_with(post, 'post-content')
@ -67,7 +67,7 @@ def test_post_updating(
posts.update_post_relations.assert_called_once_with(post, [1, 2])
posts.update_post_notes.assert_called_once_with(post, ['note1', 'note2'])
posts.update_post_flags.assert_called_once_with(post, ['flag1', 'flag2'])
posts.serialize_post_with_details.assert_called_once_with(post, auth_user)
posts.serialize_post.assert_called_once_with(post, auth_user)
tags.export_to_json.assert_called_once_with()
snapshots.save_entity_modification.assert_called_once_with(post, auth_user)
assert post.last_edit_time == datetime.datetime(1997, 1, 1)
@ -83,7 +83,7 @@ def test_uploading_from_url_saves_source(
with unittest.mock.patch('szurubooru.func.net.download'), \
unittest.mock.patch('szurubooru.func.tags.export_to_json'), \
unittest.mock.patch('szurubooru.func.snapshots.save_entity_modification'), \
unittest.mock.patch('szurubooru.func.posts.serialize_post_with_details'), \
unittest.mock.patch('szurubooru.func.posts.serialize_post'), \
unittest.mock.patch('szurubooru.func.posts.update_post_content'), \
unittest.mock.patch('szurubooru.func.posts.update_post_source'):
net.download.return_value = b'content'
@ -110,7 +110,7 @@ def test_uploading_from_url_with_source_specified(
with unittest.mock.patch('szurubooru.func.net.download'), \
unittest.mock.patch('szurubooru.func.tags.export_to_json'), \
unittest.mock.patch('szurubooru.func.snapshots.save_entity_modification'), \
unittest.mock.patch('szurubooru.func.posts.serialize_post_with_details'), \
unittest.mock.patch('szurubooru.func.posts.serialize_post'), \
unittest.mock.patch('szurubooru.func.posts.update_post_content'), \
unittest.mock.patch('szurubooru.func.posts.update_post_source'):
net.download.return_value = b'content'

View file

@ -21,13 +21,14 @@ def test_creating_category(test_ctx):
test_ctx.context_factory(
input={'name': 'meta', 'color': 'black'},
user=test_ctx.user_factory(rank=db.User.RANK_REGULAR)))
assert len(result['tagCategory']['snapshots']) == 1
del result['tagCategory']['snapshots']
assert result['tagCategory'] == {
'name': 'meta',
'color': 'black',
'usages': 0,
'default': True,
}
assert len(result['snapshots']) == 1
category = db.session.query(db.TagCategory).one()
assert category.name == 'meta'
assert category.color == 'black'

View file

@ -43,8 +43,8 @@ def test_retrieving_single(test_ctx):
'color': 'dummy',
'usages': 0,
'default': False,
'snapshots': [],
},
'snapshots': [],
}
def test_trying_to_retrieve_single_non_existing(test_ctx):

View file

@ -37,13 +37,14 @@ def test_simple_updating(test_ctx):
},
user=test_ctx.user_factory(rank=db.User.RANK_REGULAR)),
'name')
assert len(result['tagCategory']['snapshots']) == 1
del result['tagCategory']['snapshots']
assert result['tagCategory'] == {
'name': 'changed',
'color': 'white',
'usages': 0,
'default': False,
}
assert len(result['snapshots']) == 1
assert tag_categories.try_get_category_by_name('name') is None
category = tag_categories.get_category_by_name('changed')
assert category is not None

View file

@ -38,6 +38,8 @@ def test_creating_simple_tags(test_ctx, fake_datetime):
'implications': [],
},
user=test_ctx.user_factory(rank=db.User.RANK_REGULAR)))
assert len(result['tag']['snapshots']) == 1
del result['tag']['snapshots']
assert result['tag'] == {
'names': ['tag1', 'tag2'],
'category': 'meta',
@ -47,7 +49,6 @@ def test_creating_simple_tags(test_ctx, fake_datetime):
'lastEditTime': None,
'usages': 0,
}
assert len(result['snapshots']) == 1
tag = tags.get_tag_by_name('tag1')
assert [tag_name.name for tag_name in tag.names] == ['tag1', 'tag2']
assert tag.category.name == 'meta'

View file

@ -33,6 +33,8 @@ def test_merging_without_usages(test_ctx, fake_datetime):
'mergeTo': 'target',
},
user=test_ctx.user_factory(rank=db.User.RANK_REGULAR)))
assert 'snapshots' in result['tag']
del result['tag']['snapshots']
assert result['tag'] == {
'names': ['target'],
'category': 'meta',
@ -42,7 +44,6 @@ def test_merging_without_usages(test_ctx, fake_datetime):
'lastEditTime': None,
'usages': 0,
}
assert 'snapshots' in result
assert tags.try_get_tag_by_name('source') is None
tag = tags.get_tag_by_name('target')
assert tag is not None

View file

@ -56,8 +56,8 @@ def test_retrieving_single(test_ctx):
'suggestions': [],
'implications': [],
'usages': 0,
'snapshots': [],
},
'snapshots': [],
}
def test_trying_to_retrieve_single_non_existing(test_ctx):

View file

@ -46,6 +46,8 @@ def test_simple_updating(test_ctx, fake_datetime):
},
user=test_ctx.user_factory(rank=db.User.RANK_REGULAR)),
'tag1')
assert len(result['tag']['snapshots']) == 1
del result['tag']['snapshots']
assert result['tag'] == {
'names': ['tag3'],
'category': 'character',
@ -55,7 +57,6 @@ def test_simple_updating(test_ctx, fake_datetime):
'lastEditTime': datetime.datetime(1997, 12, 1),
'usages': 0,
}
assert len(result['snapshots']) == 1
assert tags.try_get_tag_by_name('tag1') is None
assert tags.try_get_tag_by_name('tag2') is None
tag = tags.get_tag_by_name('tag3')

View file

@ -56,6 +56,7 @@ def test_retrieving_single(test_ctx):
'avatarStyle': 'gravatar',
'avatarUrl': 'http://gravatar.com/avatar/' +
'275876e34cf609db118f3d84b799a790?d=retro&s=200',
'email': False,
}
}

View file

@ -64,11 +64,17 @@ def test_serialize_note():
def test_serialize_empty_post():
assert posts.serialize_post(None, None) is None
def test_serialize_post(post_factory, user_factory, tag_factory, config_injector):
def test_serialize_post(
post_factory, user_factory, comment_factory, tag_factory, config_injector):
config_injector({'data_url': 'http://example.com/'})
with unittest.mock.patch('szurubooru.func.users.serialize_user'), \
unittest.mock.patch('szurubooru.func.posts.files.has', return_value=True):
with unittest.mock.patch('szurubooru.func.comments.serialize_comment'), \
unittest.mock.patch('szurubooru.func.users.serialize_user'), \
unittest.mock.patch('szurubooru.func.posts.files.has', return_value=True), \
unittest.mock.patch('szurubooru.func.snapshots.get_serialized_history'):
users.serialize_user.side_effect = lambda user, auth_user: user.name
comments.serialize_comment.side_effect \
= lambda comment, auth_user: comment.user.name
snapshots.get_serialized_history.return_value = 'snapshot history'
auth_user = user_factory(name='auth user')
post = db.Post()
@ -90,6 +96,11 @@ def test_serialize_post(post_factory, user_factory, tag_factory, config_injector
post.canvas_height = 300
post.flags = ['loop']
db.session.add(post)
post.comments = [
comment_factory(user=user_factory(name='commenter1')),
comment_factory(user=user_factory(name='commenter2')),
]
db.session.flush()
db.session.add_all([
db.PostFavorite(
@ -144,31 +155,6 @@ def test_serialize_post(post_factory, user_factory, tag_factory, config_injector
'favoritedBy': ['fav1'],
'hasCustomThumbnail': True,
'mimeType': 'image/jpeg',
}
def test_serialize_post_with_details(post_factory, comment_factory, user_factory):
with unittest.mock.patch('szurubooru.func.comments.serialize_comment'), \
unittest.mock.patch('szurubooru.func.snapshots.get_serialized_history'), \
unittest.mock.patch('szurubooru.func.posts.serialize_post'):
comments.serialize_comment.side_effect \
= lambda comment, auth_user: comment.user.name
posts.serialize_post.side_effect \
= lambda post, auth_user: post.post_id
snapshots.get_serialized_history.return_value = 'snapshot history'
auth_user = user_factory(name='auth user')
post = post_factory()
post.comments = [
comment_factory(user=user_factory(name='commenter1')),
comment_factory(user=user_factory(name='commenter2')),
]
db.session.add(post)
db.session.flush()
result = posts.serialize_post_with_details(post, auth_user)
assert result == {
'post': post.post_id,
'snapshots': 'snapshot history',
'comments': ['commenter1', 'commenter2'],
}