diff --git a/api-template.yaml b/api-template.yaml index cdfdd9f4..d259a16f 100644 --- a/api-template.yaml +++ b/api-template.yaml @@ -214,6 +214,17 @@ Resources: type: string created_at: type: integer + MeArticlesFraudCreate: + type: object + properties: + reason: + type: string + plagiarism_url: + type: string + plagiarism_description: + type: string + illegal_content: + type: string paths: /search/articles: get: @@ -883,6 +894,11 @@ Resources: description: '対象記事の指定するために使用' required: true type: 'string' + - name: 'FraudContents' + in: 'body' + description: '違反報告の内容' + schema: + $ref: '#/definitions/MeArticlesFraudCreate' responses: '200': description: '不正報告の実施成功' diff --git a/src/common/settings.py b/src/common/settings.py index d901f314..2b413eff 100644 --- a/src/common/settings.py +++ b/src/common/settings.py @@ -95,6 +95,32 @@ 'type': 'string', 'minLength': 1, 'maxLength': 150 + }, + 'fraud_user': { + 'reason': { + 'type': 'string', + 'enum': [ + 'violence', + 'spam', + 'plagiarism', + 'slander', + 'illegal', + 'other' + ] + }, + 'plagiarism_url': { + 'type': 'string', + 'format': 'uri', + 'maxLength': 2048 + }, + 'plagiarism_description': { + 'type': 'string', + 'maxLength': 1000 + }, + 'illegal_content': { + 'type': 'string', + 'maxLength': 1000 + } } } @@ -146,7 +172,6 @@ 'xml', 'year' ] - LIKED_RETRY_COUNT = 3 ARTICLE_IMAGE_MAX_WIDTH = 3840 @@ -160,3 +185,7 @@ LIKE_NOTIFICATION_TYPE = 'like' COMMENT_NOTIFICATION_TYPE = 'comment' + +FRAUD_NEED_ORIGINAL_REASONS = ['plagiarism'] + +FRAUD_NEED_DETAIL_REASONS = ['illegal', 'other'] diff --git a/src/handlers/me/articles/fraud/create/me_articles_fraud_create.py b/src/handlers/me/articles/fraud/create/me_articles_fraud_create.py index 33e1472c..b68e4846 100644 --- a/src/handlers/me/articles/fraud/create/me_articles_fraud_create.py +++ b/src/handlers/me/articles/fraud/create/me_articles_fraud_create.py @@ -6,7 +6,7 @@ from db_util import DBUtil from botocore.exceptions import ClientError from lambda_base import LambdaBase -from jsonschema import validate, ValidationError +from jsonschema import validate, ValidationError, FormatChecker class MeArticlesFraudCreate(LambdaBase): @@ -14,7 +14,11 @@ def get_schema(self): return { 'type': 'object', 'properties': { - 'article_id': settings.parameters['article_id'] + 'article_id': settings.parameters['article_id'], + 'reason': settings.parameters['fraud_user']['reason'], + 'plagiarism_url': settings.parameters['fraud_user']['plagiarism_url'], + 'plagiarism_description': settings.parameters['fraud_user']['plagiarism_description'], + 'illegal_content': settings.parameters['fraud_user']['illegal_content'] }, 'required': ['article_id'] } @@ -23,7 +27,8 @@ def validate_params(self): # single if self.event.get('pathParameters') is None: raise ValidationError('pathParameters is required') - validate(self.event.get('pathParameters'), self.get_schema()) + validate(self.params, self.get_schema(), format_checker=FormatChecker()) + self.__validate_reason_dependencies(self.params) # relation DBUtil.validate_article_existence( self.dynamodb, @@ -52,9 +57,31 @@ def __create_article_fraud_user(self, article_fraud_user_table): article_fraud_user = { 'article_id': self.event['pathParameters']['article_id'], 'user_id': self.event['requestContext']['authorizer']['claims']['cognito:username'], + 'reason': self.params.get('reason'), + 'plagiarism_url': self.params.get('plagiarism_url'), + 'plagiarism_description': self.params.get('plagiarism_description'), + 'illegal_content': self.params.get('illegal_content'), 'created_at': int(time.time()) } + DBUtil.items_values_empty_to_none(article_fraud_user) + article_fraud_user_table.put_item( Item=article_fraud_user, ConditionExpression='attribute_not_exists(article_id)' ) + + def __validate_reason_dependencies(self, params): + reason = params.get('reason') + if reason in settings.FRAUD_NEED_ORIGINAL_REASONS: + self.__validate_plagiarism_dependencies(params) + + if reason in settings.FRAUD_NEED_DETAIL_REASONS: + self.__validate_illegal_content_dependencies(params) + + def __validate_plagiarism_dependencies(self, params): + if not params.get('plagiarism_url') and not params.get('plagiarism_description'): + raise ValidationError('plagiarism_url or plagiarism_description is required') + + def __validate_illegal_content_dependencies(self, params): + if not params.get('illegal_content'): + raise ValidationError('illegal_content is required') diff --git a/tests/handlers/me/articles/fraud/create/test_me_articles_fraud_create.py b/tests/handlers/me/articles/fraud/create/test_me_articles_fraud_create.py index 75c8e00d..91afad86 100644 --- a/tests/handlers/me/articles/fraud/create/test_me_articles_fraud_create.py +++ b/tests/handlers/me/articles/fraud/create/test_me_articles_fraud_create.py @@ -1,4 +1,5 @@ import os +import json from tests_util import TestsUtil from unittest import TestCase from me_articles_fraud_create import MeArticlesFraudCreate @@ -29,7 +30,22 @@ def setUpClass(cls): { 'article_id': 'testid000002', 'user_id': 'test02', - 'created_at': 1520150273 + 'created_at': 1520150274 + }, + { + 'article_id': 'testid000003', + 'user_id': 'test02', + 'created_at': 1520150275 + }, + { + 'article_id': 'testid000004', + 'user_id': 'test02', + 'created_at': 1520150276 + }, + { + 'article_id': 'testid000005', + 'user_id': 'test02', + 'created_at': 1520150277 } ] TestsUtil.create_table( @@ -54,7 +70,22 @@ def setUpClass(cls): 'article_id': 'testid000002', 'status': 'draft', 'sort_key': 1520150272000002 - } + }, + { + 'article_id': 'testid000003', + 'status': 'public', + 'sort_key': 1520150272000003 + }, + { + 'article_id': 'testid000004', + 'status': 'public', + 'sort_key': 1520150272000004 + }, + { + 'article_id': 'testid000005', + 'status': 'public', + 'sort_key': 1520150272000005 + }, ] TestsUtil.create_table( cls.dynamodb, @@ -112,6 +143,171 @@ def test_main_ok_exist_article_id(self): for key in article_fraud_user_param_names: self.assertEqual(expected_items[key], article_fraud_user[key]) + @patch('time.time', MagicMock(return_value=1520150272000003)) + def test_main_ok_added_reason(self): + params = { + 'pathParameters': { + 'article_id': self.article_fraud_user_table_items[1]['article_id'] + }, + 'body': json.dumps({ + 'reason': 'violence' + }), + 'requestContext': { + 'authorizer': { + 'claims': { + 'cognito:username': 'test03' + } + } + } + } + + article_fraud_user_table = self.dynamodb.Table(os.environ['ARTICLE_FRAUD_USER_TABLE_NAME']) + article_fraud_user_before = article_fraud_user_table.scan()['Items'] + + article_fraud_user = MeArticlesFraudCreate(event=params, context={}, dynamodb=self.dynamodb) + response = article_fraud_user.main() + + article_fraud_user_after = article_fraud_user_table.scan()['Items'] + + target_article_id = params['pathParameters']['article_id'] + target_user_id = params['requestContext']['authorizer']['claims']['cognito:username'] + body = json.loads(params['body']) + target_reason = body.get('reason') + + article_fraud_user = self.get_article_fraud_user(target_article_id, target_user_id) + + expected_items = { + 'article_id': target_article_id, + 'user_id': target_user_id, + 'reason': target_reason, + 'created_at': 1520150272000003 + } + + self.assertEqual(response['statusCode'], 200) + self.assertEqual(len(article_fraud_user_after), len(article_fraud_user_before) + 1) + article_fraud_user_param_names = [ + 'article_id', + 'user_id', + 'reason', + 'created_at' + ] + for key in article_fraud_user_param_names: + self.assertEqual(expected_items[key], article_fraud_user[key]) + + @patch('time.time', MagicMock(return_value=1520150272000003)) + def test_main_ok_reason_is_plagiarism(self): + params = { + 'pathParameters': { + 'article_id': self.article_fraud_user_table_items[3]['article_id'] + }, + 'body': json.dumps({ + 'reason': 'plagiarism', + 'plagiarism_url': 'http://test.com', + 'plagiarism_description': 'plagiarism description', + }), + 'requestContext': { + 'authorizer': { + 'claims': { + 'cognito:username': 'test03' + } + } + } + } + + article_fraud_user_table = self.dynamodb.Table(os.environ['ARTICLE_FRAUD_USER_TABLE_NAME']) + article_fraud_user_before = article_fraud_user_table.scan()['Items'] + + article_fraud_user = MeArticlesFraudCreate(event=params, context={}, dynamodb=self.dynamodb) + response = article_fraud_user.main() + + article_fraud_user_after = article_fraud_user_table.scan()['Items'] + + target_article_id = params['pathParameters']['article_id'] + target_user_id = params['requestContext']['authorizer']['claims']['cognito:username'] + body = json.loads(params['body']) + target_reason = body.get('reason') + target_plagiarism_url = body.get('plagiarism_url') + target_plagiarism_description = body.get('plagiarism_description') + + article_fraud_user = self.get_article_fraud_user(target_article_id, target_user_id) + + expected_items = { + 'article_id': target_article_id, + 'user_id': target_user_id, + 'reason': target_reason, + 'plagiarism_url': target_plagiarism_url, + 'plagiarism_description': target_plagiarism_description, + 'created_at': 1520150272000003 + } + + self.assertEqual(response['statusCode'], 200) + self.assertEqual(len(article_fraud_user_after), len(article_fraud_user_before) + 1) + article_fraud_user_param_names = [ + 'article_id', + 'user_id', + 'reason', + 'plagiarism_url', + 'plagiarism_description', + 'created_at' + ] + for key in article_fraud_user_param_names: + self.assertEqual(expected_items[key], article_fraud_user[key]) + + @patch('time.time', MagicMock(return_value=1520150272000003)) + def test_main_ok_reason_is_other(self): + params = { + 'pathParameters': { + 'article_id': self.article_fraud_user_table_items[4]['article_id'] + }, + 'body': json.dumps({ + 'reason': 'other', + 'illegal_content': 'illegal content' + }), + 'requestContext': { + 'authorizer': { + 'claims': { + 'cognito:username': 'test03' + } + } + } + } + + article_fraud_user_table = self.dynamodb.Table(os.environ['ARTICLE_FRAUD_USER_TABLE_NAME']) + article_fraud_user_before = article_fraud_user_table.scan()['Items'] + + article_fraud_user = MeArticlesFraudCreate(event=params, context={}, dynamodb=self.dynamodb) + response = article_fraud_user.main() + + article_fraud_user_after = article_fraud_user_table.scan()['Items'] + + target_article_id = params['pathParameters']['article_id'] + target_user_id = params['requestContext']['authorizer']['claims']['cognito:username'] + body = json.loads(params['body']) + target_reason = body.get('reason') + target_illegal_content = body.get('illegal_content') + + article_fraud_user = self.get_article_fraud_user(target_article_id, target_user_id) + + expected_items = { + 'article_id': target_article_id, + 'user_id': target_user_id, + 'reason': target_reason, + 'illegal_content': target_illegal_content, + 'created_at': 1520150272000003 + } + + self.assertEqual(response['statusCode'], 200) + self.assertEqual(len(article_fraud_user_after), len(article_fraud_user_before) + 1) + article_fraud_user_param_names = [ + 'article_id', + 'user_id', + 'reason', + 'illegal_content', + 'created_at' + ] + for key in article_fraud_user_param_names: + self.assertEqual(expected_items[key], article_fraud_user[key]) + def test_call_validate_article_existence(self): params = { 'pathParameters': { @@ -177,9 +373,137 @@ def test_validation_article_id_min(self): self.assert_bad_request(params) + def test_validation_invalid_reason(self): + body = { + 'body': json.dumps({'reason': 'abcde'}) + } + params = dict(self.get_parameters_other_than_body_for_validate(), **body) + self.assert_bad_request(params) + + def test_validation_required_plagiarism_detail_when_reason_is_plagiarism(self): + body = { + 'body': json.dumps( + { + 'reason': 'plagiarism' + } + ) + } + params = dict(self.get_parameters_other_than_body_for_validate(), **body) + self.assert_bad_request(params) + + def test_validation_empty_plagiarism_detail_when_reason_is_plagiarism(self): + body = { + 'body': json.dumps( + { + 'reason': 'plagiarism', + 'plagiarism_url': '', + 'plagiarism_description': '' + } + ) + } + params = dict(self.get_parameters_other_than_body_for_validate(), **body) + self.assert_bad_request(params) + + def test_validation_invalid_plagiarism_url_when_reason_is_plagiarism(self): + body = { + 'body': json.dumps( + { + 'reason': 'plagiarism', + 'plagiarism_url': 'aaa' + } + ) + } + params = dict(self.get_parameters_other_than_body_for_validate(), **body) + self.assert_bad_request(params) + + def test_validation_required_illegal_content_when_reason_is_illegal(self): + body = { + 'body': json.dumps( + { + 'reason': 'illegal' + } + ) + } + params = dict(self.get_parameters_other_than_body_for_validate(), **body) + self.assert_bad_request(params) + + def test_validation_empty_illegal_content_when_reason_is_illegal(self): + body = { + 'body': json.dumps( + { + 'reason': 'illegal', + 'illegal_content': '' + } + ) + } + params = dict(self.get_parameters_other_than_body_for_validate(), **body) + self.assert_bad_request(params) + + def test_validation_required_illegal_content_when_reason_is_other(self): + body = { + 'body': json.dumps( + { + 'reason': 'other' + } + ) + } + params = dict(self.get_parameters_other_than_body_for_validate(), **body) + self.assert_bad_request(params) + + def test_validation_empty_illegal_content_when_reason_is_other(self): + body = { + 'body': json.dumps( + { + 'reason': 'other', + 'illegal_content': '' + } + ) + } + params = dict(self.get_parameters_other_than_body_for_validate(), **body) + self.assert_bad_request(params) + + def test_validation_plagiarism_description_max(self): + body = { + 'body': json.dumps( + { + 'reason': 'plagiarism', + 'plagiarism_description': u'あ' * 1001 + } + ) + } + params = dict(self.get_parameters_other_than_body_for_validate(), **body) + self.assert_bad_request(params) + + def test_validation_illegal_content_max(self): + body = { + 'body': json.dumps( + { + 'reason': 'illegal', + 'illegal_content': u'あ' * 1001 + } + ) + } + params = dict(self.get_parameters_other_than_body_for_validate(), **body) + self.assert_bad_request(params) + def get_article_fraud_user(self, article_id, user_id): query_params = { 'KeyConditionExpression': Key('article_id').eq(article_id) & Key('user_id').eq(user_id) } article_fraud_user_table = self.dynamodb.Table(os.environ['ARTICLE_FRAUD_USER_TABLE_NAME']) return article_fraud_user_table.query(**query_params)['Items'][0] + + def get_parameters_other_than_body_for_validate(self): + basic_params = { + 'pathParameters': { + 'article_id': self.article_fraud_user_table_items[5]['article_id'] + }, + 'requestContext': { + 'authorizer': { + 'claims': { + 'cognito:username': 'test03' + } + } + } + } + return basic_params