Skip to content

Commit c996880

Browse files
authored
Merge pull request #130 from matsumatsu20/feature/create-comment-liked-show
Feature/create comment liked show
2 parents d1cc155 + b873c7e commit c996880

File tree

11 files changed

+461
-5
lines changed

11 files changed

+461
-5
lines changed

.github/pull_request_template.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
## DBやDBへのクエリに対する変更
4141

4242
* DBのスキーマに変更があるか
43-
- [ ] 変更がある場合は[ドキュメント](https://alis-dev.esa.io/posts/58)に反映
43+
- [ ] 変更がある場合は[ドキュメント](https://alismedia.atlassian.net/wiki/spaces/DEV/pages/9273460)に反映
4444
* 変更されるクエリ (変更前、変更後)
4545
* 新規に追加されるクエリ
4646
* 観点:

api-template.yaml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -728,6 +728,38 @@ Resources:
728728
passthroughBehavior: when_no_templates
729729
httpMethod: POST
730730
type: aws_proxy
731+
732+
/me/articles/{article_id}/comments/likes:
733+
get:
734+
description: '指定された記事のコメントの中で、自分がいいねを実行したコメントのIDの一覧を取得する'
735+
parameters:
736+
- name: 'article_id'
737+
in: 'path'
738+
description: '対象記事を指定するために使用'
739+
required: true
740+
type: 'string'
741+
responses:
742+
'200':
743+
description: 'いいねを実施したコメントのIDの一覧'
744+
schema:
745+
type: object
746+
properties:
747+
comment_ids:
748+
type: array
749+
items:
750+
type: 'string'
751+
enum:
752+
- "comment_id"
753+
security:
754+
- cognitoUserPool: []
755+
x-amazon-apigateway-integration:
756+
responses:
757+
default:
758+
statusCode: "200"
759+
uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MeArticlesCommentsLikesIndex.Arn}/invocations
760+
passthroughBehavior: when_no_templates
761+
httpMethod: POST
762+
type: aws_proxy
731763
/me/comments/{comment_id}:
732764
delete:
733765
description: '指定されたコメントを削除する'
@@ -1404,6 +1436,19 @@ Resources:
14041436
Path: /me/articles/{article_id}/comments
14051437
Method: post
14061438
RestApiId: !Ref RestApi
1439+
MeArticlesCommentsLikesIndex:
1440+
Type: AWS::Serverless::Function
1441+
Properties:
1442+
Handler: handler.lambda_handler
1443+
Role: !GetAtt LambdaRole.Arn
1444+
CodeUri: ./deploy/me_articles_comments_likes_index.zip
1445+
Events:
1446+
Api:
1447+
Type: Api
1448+
Properties:
1449+
Path: /me/articles/{article_id}/comments/likes
1450+
Method: get
1451+
RestApiId: !Ref RestApi
14071452
CommentsLikesShow:
14081453
Type: AWS::Serverless::Function
14091454
Properties:

database-template.yaml

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,11 +426,23 @@ Resources:
426426
AttributeType: S
427427
- AttributeName: user_id
428428
AttributeType: S
429+
- AttributeName: article_id
430+
AttributeType: S
429431
KeySchema:
430432
- AttributeName: comment_id
431433
KeyType: HASH
432434
- AttributeName: user_id
433435
KeyType: RANGE
436+
GlobalSecondaryIndexes:
437+
- IndexName: article_id-index
438+
KeySchema:
439+
- AttributeName: article_id
440+
KeyType: HASH
441+
Projection:
442+
ProjectionType: ALL
443+
ProvisionedThroughput:
444+
ReadCapacityUnits: !Ref MinDynamoReadCapacitty
445+
WriteCapacityUnits: !Ref MinDynamoWriteCapacitty
434446
ProvisionedThroughput:
435447
ReadCapacityUnits: !Ref MinDynamoReadCapacitty
436448
WriteCapacityUnits: !Ref MinDynamoWriteCapacitty
@@ -1576,6 +1588,50 @@ Resources:
15761588
ScaleOutCooldown: 60
15771589
PredefinedMetricSpecification:
15781590
PredefinedMetricType: DynamoDBWriteCapacityUtilization
1591+
CommentLikedUserArticleIdIndexReadCapacityScalableTarget:
1592+
Type: AWS::ApplicationAutoScaling::ScalableTarget
1593+
DependsOn: ScalingRole
1594+
Properties:
1595+
MaxCapacity: !Ref MaxDynamoReadCapacitty
1596+
MinCapacity: !Ref MinDynamoReadCapacitty
1597+
ResourceId: !Sub 'table/${CommentLikedUser}/index/article_id-index'
1598+
RoleARN: !GetAtt ScalingRole.Arn
1599+
ScalableDimension: dynamodb:index:ReadCapacityUnits
1600+
ServiceNamespace: dynamodb
1601+
CommentLikedUserArticleIdIndexWriteCapacityScalableTarget:
1602+
Type: 'AWS::ApplicationAutoScaling::ScalableTarget'
1603+
DependsOn: ScalingRole
1604+
Properties:
1605+
MaxCapacity: !Ref MaxDynamoWriteCapacitty
1606+
MinCapacity: !Ref MinDynamoWriteCapacitty
1607+
ResourceId: !Sub 'table/${CommentLikedUser}/index/article_id-index'
1608+
RoleARN: !GetAtt ScalingRole.Arn
1609+
ScalableDimension: dynamodb:index:WriteCapacityUnits
1610+
ServiceNamespace: dynamodb
1611+
CommentLikedUserArticleIdIndexReadScalingPolicy:
1612+
Type: 'AWS::ApplicationAutoScaling::ScalingPolicy'
1613+
Properties:
1614+
PolicyName: ReadAutoScalingPolicy
1615+
PolicyType: TargetTrackingScaling
1616+
ScalingTargetId: !Ref CommentLikedUserArticleIdIndexReadCapacityScalableTarget
1617+
TargetTrackingScalingPolicyConfiguration:
1618+
TargetValue: 50.0
1619+
ScaleInCooldown: 60
1620+
ScaleOutCooldown: 60
1621+
PredefinedMetricSpecification:
1622+
PredefinedMetricType: DynamoDBReadCapacityUtilization
1623+
CommentLikedUserArticleIdIndexWriteScalingPolicy:
1624+
Type: 'AWS::ApplicationAutoScaling::ScalingPolicy'
1625+
Properties:
1626+
PolicyName: WriteAutoScalingPolicy
1627+
PolicyType: TargetTrackingScaling
1628+
ScalingTargetId: !Ref CommentLikedUserArticleIdIndexWriteCapacityScalableTarget
1629+
TargetTrackingScalingPolicyConfiguration:
1630+
TargetValue: 50.0
1631+
ScaleInCooldown: 60
1632+
ScaleOutCooldown: 60
1633+
PredefinedMetricSpecification:
1634+
PredefinedMetricType: DynamoDBWriteCapacityUtilization
15791635
DeletedCommentTableReadCapacityScalableTarget:
15801636
Type: AWS::ApplicationAutoScaling::ScalableTarget
15811637
DependsOn: ScalingRole

database.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,11 +399,23 @@ Resources:
399399
AttributeType: S
400400
- AttributeName: user_id
401401
AttributeType: S
402+
- AttributeName: article_id
403+
AttributeType: S
402404
KeySchema:
403405
- AttributeName: comment_id
404406
KeyType: HASH
405407
- AttributeName: user_id
406408
KeyType: RANGE
409+
GlobalSecondaryIndexes:
410+
- IndexName: article_id-index
411+
KeySchema:
412+
- AttributeName: article_id
413+
KeyType: HASH
414+
Projection:
415+
ProjectionType: ALL
416+
ProvisionedThroughput:
417+
ReadCapacityUnits: 1
418+
WriteCapacityUnits: 1
407419
ProvisionedThroughput:
408420
ReadCapacityUnits: 1
409421
WriteCapacityUnits: 1

src/common/db_util.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,16 @@ def items_values_empty_to_none(values):
7272
for k, v in values.items():
7373
if v == '':
7474
values[k] = None
75+
76+
@staticmethod
77+
def query_all_items(dynamodb_table, query_params):
78+
79+
response = dynamodb_table.query(**query_params)
80+
items = response['Items']
81+
82+
while 'LastEvaluatedKey' in response:
83+
query_params.update({'ExclusiveStartKey': response['LastEvaluatedKey']})
84+
response = dynamodb_table.query(**query_params)
85+
items.extend(response['Items'])
86+
87+
return items
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# -*- coding: utf-8 -*-
2+
import boto3
3+
from me_articles_comments_likes_index import MeArticlesCommentsLikesIndex
4+
5+
dynamodb = boto3.resource('dynamodb')
6+
7+
8+
def lambda_handler(event, context):
9+
me_articles_comments_likes_index = MeArticlesCommentsLikesIndex(event=event, context=context, dynamodb=dynamodb)
10+
return me_articles_comments_likes_index.main()
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# -*- coding: utf-8 -*-
2+
import json
3+
import os
4+
import settings
5+
6+
from boto3.dynamodb.conditions import Key
7+
from db_util import DBUtil
8+
from lambda_base import LambdaBase
9+
from jsonschema import validate
10+
11+
12+
class MeArticlesCommentsLikesIndex(LambdaBase):
13+
def get_schema(self):
14+
return {
15+
'type': 'object',
16+
'properties': {
17+
'article_id': settings.parameters['article_id']
18+
},
19+
'required': ['article_id']
20+
}
21+
22+
def validate_params(self):
23+
validate(self.params, self.get_schema())
24+
DBUtil.validate_article_existence(self.dynamodb, self.params['article_id'], status='public')
25+
26+
def exec_main_proc(self):
27+
user_id = self.event['requestContext']['authorizer']['claims']['cognito:username']
28+
29+
comment_liked_user_table = self.dynamodb.Table(os.environ['COMMENT_LIKED_USER_TABLE_NAME'])
30+
31+
query_params = {
32+
'IndexName': 'article_id-index',
33+
'KeyConditionExpression': Key('article_id').eq(self.params['article_id']),
34+
}
35+
36+
result = DBUtil.query_all_items(comment_liked_user_table, query_params)
37+
38+
comment_ids = [liked_user['comment_id'] for liked_user in result if liked_user['user_id'] == user_id]
39+
40+
return {
41+
'statusCode': 200,
42+
'body': json.dumps({'comment_ids': comment_ids})
43+
}

src/handlers/me/comments/likes/create/me_comments_likes_create.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,20 @@ def validate_params(self):
2828
def exec_main_proc(self):
2929
user_id = self.event['requestContext']['authorizer']['claims']['cognito:username']
3030

31-
comment_liked_user_table = self.dynamodb.Table(os.environ['COMMENT_LIKED_USER_TABLE_NAME'])
31+
comment_table = self.dynamodb.Table(os.environ['COMMENT_TABLE_NAME'])
32+
comment = comment_table.get_item(Key={'comment_id': self.params['comment_id']}).get('Item')
3233

33-
comment = {
34-
'comment_id': self.params['comment_id'],
34+
comment_liked_user_table = self.dynamodb.Table(os.environ['COMMENT_LIKED_USER_TABLE_NAME'])
35+
comment_liked_user = {
36+
'comment_id': comment['comment_id'],
3537
'user_id': user_id,
38+
'article_id': comment['article_id'],
3639
'created_at': int(time.time())
3740
}
3841

3942
try:
4043
comment_liked_user_table.put_item(
41-
Item=comment,
44+
Item=comment_liked_user,
4245
ConditionExpression='attribute_not_exists(comment_id)'
4346
)
4447
except ClientError as e:

tests/common/test_db_util.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import os
2+
3+
from boto3.dynamodb.conditions import Key
24
from db_util import DBUtil
35
from tests_util import TestsUtil
46
from unittest import TestCase
@@ -53,6 +55,50 @@ def setUpClass(cls):
5355
]
5456
TestsUtil.create_table(cls.dynamodb, os.environ['COMMENT_TABLE_NAME'], cls.comment_items)
5557

58+
article_pv_user_items = [
59+
{
60+
'article_id': 'article01',
61+
'user_id': 'one_day_before_user1',
62+
'article_user_id': 'article_user_1',
63+
'target_date': '2018-05-01',
64+
'created_at': 1520035200,
65+
'sort_key': 1520035200000000
66+
},
67+
{
68+
'article_id': 'one_day_before_article',
69+
'user_id': 'one_day_before_user1',
70+
'article_user_id': 'article_user_1',
71+
'target_date': '2018-05-01',
72+
'created_at': 1520035200,
73+
'sort_key': 1520035200000000
74+
},
75+
{
76+
'article_id': 'article01',
77+
'user_id': 'a1_user1',
78+
'article_user_id': 'article_user_1',
79+
'target_date': '2018-05-01',
80+
'created_at': 1520121600,
81+
'sort_key': 1520121600000000
82+
},
83+
{
84+
'article_id': 'article01',
85+
'user_id': 'a1_user2',
86+
'article_user_id': 'article_user_1',
87+
'target_date': '2018-05-01',
88+
'created_at': 1520125200,
89+
'sort_key': 1520125200000000
90+
},
91+
{
92+
'article_id': 'article02',
93+
'user_id': 'a1_user2',
94+
'article_user_id': 'article_user_2',
95+
'target_date': '2018-05-02',
96+
'created_at': 1520125200,
97+
'sort_key': 1520125200000000
98+
}
99+
]
100+
TestsUtil.create_table(cls.dynamodb, os.environ['ARTICLE_PV_USER_TABLE_NAME'], article_pv_user_items)
101+
56102
@classmethod
57103
def tearDownClass(cls):
58104
TestsUtil.delete_all_tables(cls.dynamodb)
@@ -236,3 +282,28 @@ def test_items_values_empty_to_none_ok(self):
236282

237283
self.assertEqual(values['test'], 'test')
238284
self.assertEqual(values['empty'], None)
285+
286+
def test_query_all_items_with_limit(self):
287+
article_pv_user_table = self.dynamodb.Table(os.environ['ARTICLE_PV_USER_TABLE_NAME'])
288+
# ユースケースとしては1MBを超え、レスポンスにLastEvaluatedKeyが付与されて返ってくる場合だが
289+
# Limitを付与した際も同じレスポンスなのでLimitで代用している
290+
query_params = {
291+
'IndexName': 'target_date-sort_key-index',
292+
'KeyConditionExpression': Key('target_date').eq('2018-05-01'),
293+
'Limit': 1
294+
}
295+
296+
response = DBUtil.query_all_items(article_pv_user_table, query_params)
297+
298+
self.assertEqual(len(response), 4)
299+
300+
def test_query_all_items_with_no_limit(self):
301+
article_pv_user_table = self.dynamodb.Table(os.environ['ARTICLE_PV_USER_TABLE_NAME'])
302+
query_params = {
303+
'IndexName': 'target_date-sort_key-index',
304+
'KeyConditionExpression': Key('target_date').eq('2018-05-01')
305+
}
306+
307+
response = DBUtil.query_all_items(article_pv_user_table, query_params)
308+
309+
self.assertEqual(len(response), 4)

0 commit comments

Comments
 (0)