server/tags: add tag deleting
This commit is contained in:
		
							parent
							
								
									e4239a199c
								
							
						
					
					
						commit
						37a86816af
					
				
							
								
								
									
										36
									
								
								API.md
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								API.md
									
									
									
									
									
								
							| @ -17,12 +17,12 @@ | |||||||
|    - [Creating tag](#creating-tag) |    - [Creating tag](#creating-tag) | ||||||
|    - [Updating tag](#updating-tag) |    - [Updating tag](#updating-tag) | ||||||
|    - [Getting tag](#getting-tag) |    - [Getting tag](#getting-tag) | ||||||
|    - [Removing tag](#removing-tag) |    - [Deleting tag](#deleting-tag) | ||||||
|    - [Listing users](#listing-users) |    - [Listing users](#listing-users) | ||||||
|    - [Creating user](#creating-user) |    - [Creating user](#creating-user) | ||||||
|    - [Updating user](#updating-user) |    - [Updating user](#updating-user) | ||||||
|    - [Getting user](#getting-user) |    - [Getting user](#getting-user) | ||||||
|    - [Removing user](#removing-user) |    - [Deleting user](#deleting-user) | ||||||
|    - [Password reset - step 1: mail request](#password-reset---step-2-confirmation) |    - [Password reset - step 1: mail request](#password-reset---step-2-confirmation) | ||||||
|    - [Password reset - step 2: confirmation](#password-reset---step-2-confirmation) |    - [Password reset - step 2: confirmation](#password-reset---step-2-confirmation) | ||||||
| 
 | 
 | ||||||
| @ -183,8 +183,26 @@ Not yet implemented. | |||||||
| Not yet implemented. | Not yet implemented. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| ## Removing tag | ## Deleting tag | ||||||
| Not yet implemented. | - **Request** | ||||||
|  | 
 | ||||||
|  |     `DELETE /tag/<name>` | ||||||
|  | 
 | ||||||
|  | - **Output** | ||||||
|  | 
 | ||||||
|  |     ```json5 | ||||||
|  |     {} | ||||||
|  |     ``` | ||||||
|  | 
 | ||||||
|  | - **Errors** | ||||||
|  | 
 | ||||||
|  |     - the tag does not exist | ||||||
|  |     - the tag is used by some posts | ||||||
|  |     - privileges are too low | ||||||
|  | 
 | ||||||
|  | - **Description** | ||||||
|  | 
 | ||||||
|  |     Deletes existing tag. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| ## Listing users | ## Listing users | ||||||
| @ -253,6 +271,7 @@ Not yet implemented. | |||||||
| 
 | 
 | ||||||
|     None. |     None. | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| ## Creating user | ## Creating user | ||||||
| - **Request** | - **Request** | ||||||
| 
 | 
 | ||||||
| @ -293,7 +312,6 @@ Not yet implemented. | |||||||
|     `default_rank` in the server's configuration. |     `default_rank` in the server's configuration. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| ## Updating user | ## Updating user | ||||||
| - **Request** | - **Request** | ||||||
| 
 | 
 | ||||||
| @ -345,7 +363,6 @@ Not yet implemented. | |||||||
|     file - see [file uploads](#file-uploads) for details. |     file - see [file uploads](#file-uploads) for details. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| ## Getting user | ## Getting user | ||||||
| - **Request** | - **Request** | ||||||
| 
 | 
 | ||||||
| @ -370,8 +387,7 @@ Not yet implemented. | |||||||
|     Retrieves information about an existing user. |     Retrieves information about an existing user. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | ## Deleting user | ||||||
| ## Removing user |  | ||||||
| - **Request** | - **Request** | ||||||
| 
 | 
 | ||||||
|     `DELETE /user/<name>` |     `DELETE /user/<name>` | ||||||
| @ -392,7 +408,6 @@ Not yet implemented. | |||||||
|     Deletes existing user. |     Deletes existing user. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| ## Password reset - step 1: mail request | ## Password reset - step 1: mail request | ||||||
| - **Request** | - **Request** | ||||||
| 
 | 
 | ||||||
| @ -418,7 +433,6 @@ Not yet implemented. | |||||||
|     account. |     account. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| ## Password reset - step 2: confirmation | ## Password reset - step 2: confirmation | ||||||
| - **Request** | - **Request** | ||||||
| 
 | 
 | ||||||
| @ -483,6 +497,8 @@ Not yet implemented. | |||||||
| } | } | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| # Search | # Search | ||||||
| 
 | 
 | ||||||
| Search queries are built of tokens that are separated by spaces. Each token can | Search queries are built of tokens that are separated by spaces. Each token can | ||||||
|  | |||||||
| @ -64,5 +64,16 @@ class TagDetailApi(BaseApi): | |||||||
|         ctx.session.commit() |         ctx.session.commit() | ||||||
|         return {'tag': _serialize_tag(tag)} |         return {'tag': _serialize_tag(tag)} | ||||||
| 
 | 
 | ||||||
|     def delete(self, ctx): |     def delete(self, ctx, tag_name): | ||||||
|         raise NotImplementedError() |         tag = tags.get_by_name(ctx.session, tag_name) | ||||||
|  |         if not tag: | ||||||
|  |             raise tags.TagNotFoundError('Tag %r not found.' % tag_name) | ||||||
|  |         if tag.post_count > 0: | ||||||
|  |             raise tags.TagIsInUseError( | ||||||
|  |                 'Tag has some usages and cannot be deleted. ' + | ||||||
|  |                 'Please untag relevant posts first.') | ||||||
|  | 
 | ||||||
|  |         auth.verify_privilege(ctx.user, 'tags:delete') | ||||||
|  |         ctx.session.delete(tag) | ||||||
|  |         ctx.session.commit() | ||||||
|  |         return {} | ||||||
|  | |||||||
							
								
								
									
										59
									
								
								server/szurubooru/tests/api/test_tag_deleting.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								server/szurubooru/tests/api/test_tag_deleting.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,59 @@ | |||||||
|  | import pytest | ||||||
|  | from datetime import datetime | ||||||
|  | from szurubooru import api, db, errors | ||||||
|  | from szurubooru.util import misc, tags | ||||||
|  | 
 | ||||||
|  | @pytest.fixture | ||||||
|  | def test_ctx( | ||||||
|  |         session, config_injector, context_factory, tag_factory, user_factory): | ||||||
|  |     config_injector({ | ||||||
|  |         'privileges': { | ||||||
|  |             'tags:delete': 'regular_user', | ||||||
|  |         }, | ||||||
|  |         'ranks': ['anonymous', 'regular_user'], | ||||||
|  |     }) | ||||||
|  |     ret = misc.dotdict() | ||||||
|  |     ret.session = session | ||||||
|  |     ret.context_factory = context_factory | ||||||
|  |     ret.user_factory = user_factory | ||||||
|  |     ret.tag_factory = tag_factory | ||||||
|  |     ret.api = api.TagDetailApi() | ||||||
|  |     return ret | ||||||
|  | 
 | ||||||
|  | def test_removing_tags(test_ctx): | ||||||
|  |     test_ctx.session.add(test_ctx.tag_factory(names=['tag'])) | ||||||
|  |     test_ctx.session.commit() | ||||||
|  |     result = test_ctx.api.delete( | ||||||
|  |         test_ctx.context_factory( | ||||||
|  |             user=test_ctx.user_factory(rank='regular_user')), | ||||||
|  |         'tag') | ||||||
|  |     assert result == {} | ||||||
|  |     assert test_ctx.session.query(db.Tag).count() == 0 | ||||||
|  | 
 | ||||||
|  | def test_removing_tags_without_privileges(test_ctx): | ||||||
|  |     test_ctx.session.add(test_ctx.tag_factory(names=['tag'])) | ||||||
|  |     test_ctx.session.commit() | ||||||
|  |     with pytest.raises(errors.AuthError): | ||||||
|  |         test_ctx.api.delete( | ||||||
|  |             test_ctx.context_factory( | ||||||
|  |                 user=test_ctx.user_factory(rank='anonymous')), | ||||||
|  |             'tag') | ||||||
|  |     assert test_ctx.session.query(db.Tag).count() == 1 | ||||||
|  | 
 | ||||||
|  | def test_removing_tags_with_usages(test_ctx): | ||||||
|  |     tag = test_ctx.tag_factory(names=['tag']) | ||||||
|  |     tag.post_count = 5 | ||||||
|  |     test_ctx.session.add(tag) | ||||||
|  |     test_ctx.session.commit() | ||||||
|  |     with pytest.raises(tags.TagIsInUseError): | ||||||
|  |         test_ctx.api.delete( | ||||||
|  |             test_ctx.context_factory( | ||||||
|  |                 user=test_ctx.user_factory(rank='regular_user')), | ||||||
|  |             'tag') | ||||||
|  |     assert test_ctx.session.query(db.Tag).count() == 1 | ||||||
|  | 
 | ||||||
|  | def test_removing_non_existing(test_ctx): | ||||||
|  |     with pytest.raises(tags.TagNotFoundError): | ||||||
|  |         test_ctx.api.delete( | ||||||
|  |             test_ctx.context_factory( | ||||||
|  |                 user=test_ctx.user_factory(rank='regular_user')), 'bad') | ||||||
| @ -149,7 +149,6 @@ def test_mods_trying_to_become_admin(test_ctx): | |||||||
| def test_uploading_avatar(test_ctx, tmpdir): | def test_uploading_avatar(test_ctx, tmpdir): | ||||||
|     config.config['data_dir'] = str(tmpdir.mkdir('data')) |     config.config['data_dir'] = str(tmpdir.mkdir('data')) | ||||||
|     config.config['data_url'] = 'http://example.com/data/' |     config.config['data_url'] = 'http://example.com/data/' | ||||||
| 
 |  | ||||||
|     user = test_ctx.user_factory(name='u1', rank='mod') |     user = test_ctx.user_factory(name='u1', rank='mod') | ||||||
|     test_ctx.session.add(user) |     test_ctx.session.add(user) | ||||||
|     empty_pixel = \ |     empty_pixel = \ | ||||||
|  | |||||||
| @ -9,6 +9,7 @@ class TagAlreadyExistsError(errors.ValidationError): pass | |||||||
| class InvalidNameError(errors.ValidationError): pass | class InvalidNameError(errors.ValidationError): pass | ||||||
| class InvalidCategoryError(errors.ValidationError): pass | class InvalidCategoryError(errors.ValidationError): pass | ||||||
| class RelationError(errors.ValidationError): pass | class RelationError(errors.ValidationError): pass | ||||||
|  | class TagIsInUseError(errors.ValidationError): pass | ||||||
| 
 | 
 | ||||||
| def _verify_name_validity(name): | def _verify_name_validity(name): | ||||||
|     name_regex = config.config['tag_name_regex'] |     name_regex = config.config['tag_name_regex'] | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 rr-
						rr-