Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: get adsnew #360

Merged
merged 2 commits into from
Oct 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions lib/constructs/business/rest-api-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export class ForumAdsApiService extends RestApiService {
{
envVars: {
TABLE_NAME: props.dataSource!,
BUCKET_NAME: 'wasedatime-ads',
},
},
);
Expand All @@ -89,8 +90,8 @@ export class ForumAdsApiService extends RestApiService {
allowMethods: [apigw2.HttpMethod.GET, apigw2.HttpMethod.POST],
});

const getImgsList = root.addMethod(apigw2.HttpMethod.GET, getIntegration, {
operationName: 'GetImgsList',
const getAds = root.addMethod(apigw2.HttpMethod.GET, getIntegration, {
operationName: 'GetAds',
methodResponses: [
{
statusCode: '200',
Expand All @@ -102,7 +103,7 @@ export class ForumAdsApiService extends RestApiService {

this.resourceMapping = {
'/adsImgs': {
[apigw2.HttpMethod.GET]: getImgsList,
[apigw2.HttpMethod.GET]: getAds,
[apigw2.HttpMethod.OPTIONS]: optionsAdsImgs,
},
};
Expand Down
29 changes: 15 additions & 14 deletions lib/constructs/common/lambda-functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -984,42 +984,43 @@ export class AdsImageProcessFunctionsAPI extends Construct {
constructor(scope: Construct, id: string, props: FunctionsProps) {
super(scope, id);

const DBReadRole: iam.LazyRole = new iam.LazyRole(
//! Update to put role
const DBPutRole: iam.LazyRole = new iam.LazyRole(
this,
'dynamodb-s3-lambda-ads-imgs-read',
'dynamo-s3-put-role',
{
assumedBy: new iam.ServicePrincipal(AwsServicePrincipal.LAMBDA),
description:
'Allow lambda function to perform read operation on dynamodb and s3',
'Allow lambda function to perform crud operation on dynamodb and s3',
path: `/service-role/${AwsServicePrincipal.LAMBDA}/`,
roleName: 'dynamodb-s3-lambda-ads-imgs-read',
roleName: 'dynamodb-s3-put-role',
managedPolicies: [
iam.ManagedPolicy.fromManagedPolicyArn(
this,
'basic-exec',
'basic-exec1',
'arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole',
),
iam.ManagedPolicy.fromManagedPolicyArn(
this,
'db-read-only',
'arn:aws:iam::aws:policy/AmazonDynamoDBReadOnlyAccess',
'db-full-access',
'arn:aws:iam::aws:policy/AmazonDynamoDBFullAccess',
),
iam.ManagedPolicy.fromManagedPolicyArn(
this,
's3-read-only',
'arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess',
's3-full-access',
'arn:aws:iam::aws:policy/AmazonS3FullAccess',
),
],
},
);

this.getFunction = new lambda_py.PythonFunction(this, 'get-imgs-list', {
entry: 'src/lambda/get-imgs-list',
description: 'get imgs list from the database.',
functionName: 'get-imgs-list',
this.getFunction = new lambda_py.PythonFunction(this, 'get-ads', {
entry: 'src/lambda/get-ads',
description: 'get ads list or specific url from the database.',
functionName: 'get-ads',
logRetention: logs.RetentionDays.ONE_MONTH,
memorySize: 128,
role: DBReadRole,
role: DBPutRole, //! Change to put role since we now have to read and write
runtime: lambda.Runtime.PYTHON_3_9,
timeout: Duration.seconds(3),
environment: props.envVars,
Expand Down
2 changes: 1 addition & 1 deletion lib/stacks/persistence.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export class WasedaTimePersistenceLayer extends PersistenceLayer {
dynamoDatabase.tables[Collection.COMMENT].tableName,
);

//! new endpoint for adsPipeline
//! new endpoint for ads
this.dataInterface.setEndpoint(
DataEndpoint.ADS,
dynamoDatabase.tables[Collection.ADS].tableName,
Expand Down
52 changes: 52 additions & 0 deletions src/lambda/get-ads/index.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from boto3.dynamodb.conditions import Key
from utils import JsonPayloadBuilder
from utils import resp_handler, table, bucket, generate_url

# typeI api call => no query parameter -> scan and return the whole table
# typeII api call => only have board_id -> return all items with matching board_id.
# typeIII api call => have both board and ad ids -> return only the url.


@resp_handler
def get_imgs_list(board_id, ad_id):

# typeIII
if board_id and ad_id:
# Create the key and url when typeII api call
key = "/".join([board_id, ad_id])
bucket_name = bucket
ad_url = generate_url(bucket_name, key)
results = ad_url

# typeII
elif board_id:
response = table.query(KeyConditionExpression=Key(
"board_id").eq(board_id), ScanIndexForward=False)
results = response

# typeI
else:
response = table.scan(ConsistentRead=False)
results = response

# response = table.scan()
# results = response.get('Items', [])

body = JsonPayloadBuilder().add_status(
True).add_data(results).add_message('').compile()
return body


def handler(event, context):

# params = event["queryStringParameters"]
# board_id = params.get("board_id", "")

params = {
"board_id": event["queryStringParameters"]["board_id"],
}
if "ad_id" in event["queryStringParameters"]:
params["ad_id"] = event["queryStringParameters"]["ad_id"]


return get_imgs_list(**params)
80 changes: 80 additions & 0 deletions src/lambda/get-ads/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import boto3
import json
import logging
import os
from decimal import Decimal

db = boto3.resource("dynamodb", region_name="ap-northeast-1")
table = db.Table(os.getenv('TABLE_NAME'))

s3_client = boto3.client('s3')
bucket = os.getenv('BUCKET_NAME')

class DecimalEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, Decimal):
return float(obj)
return json.JSONEncoder.default(self, obj)


class JsonPayloadBuilder:
payload = {}

def add_status(self, success):
self.payload['success'] = success
return self

def add_data(self, data):
self.payload['data'] = data
return self

def add_message(self, msg):
self.payload['message'] = msg
return self

def compile(self):
return json.dumps(self.payload, cls=DecimalEncoder, ensure_ascii=False).encode('utf8')


def api_response(code, body):
return {
"isBase64Encoded": False,
"statusCode": code,
'headers': {
"Access-Control-Allow-Origin": '*',
"Content-Type": "application/json",
"Referrer-Policy": "origin"
},
"multiValueHeaders": {"Access-Control-Allow-Methods": ["POST", "OPTIONS", "GET", "PATCH", "DELETE"]},
"body": body
}


def resp_handler(func):
def handle(*args, **kwargs):
try:
resp = func(*args, **kwargs)
return api_response(200, resp)
except LookupError:
resp = JsonPayloadBuilder().add_status(False).add_data(None) \
.add_message("Not found").compile()
return api_response(404, resp)
except Exception as e:
logging.error(str(e))
resp = JsonPayloadBuilder().add_status(False).add_data(None) \
.add_message("Internal error, please contact [email protected].").compile()
return api_response(500, resp)

return handle

def generate_url(bucket_name, object_key, expiration=3600):
try:
response = s3_client.generate_presigned_url('get_object',
Params={'Bucket': bucket_name,
'Key': object_key},
ExpiresIn=expiration)
except Exception as e:
logging.error(str(e))
return None

return response
Loading