Skip to content

Commit

Permalink
Deploy price tracker on a 1 hour schedule
Browse files Browse the repository at this point in the history
  • Loading branch information
jordansimsmith committed Nov 3, 2024
1 parent 7044c38 commit a68ace3
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 7 deletions.
1 change: 0 additions & 1 deletion immersion_tracker_api/infra/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,6 @@ data "aws_iam_policy_document" "lambda_dynamodb_allow_policy_document" {
actions = [
"dynamodb:PutItem",
"dynamodb:UpdateItem",
"dynamodb:DeleteItem",
"dynamodb:BatchWriteItem",
"dynamodb:GetItem",
"dynamodb:BatchGetItem",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.jordansimsmith.lib.notifications;

import com.google.common.collect.Iterables;
import software.amazon.awssdk.services.sns.SnsClient;
import software.amazon.awssdk.services.sns.model.ListTopicsRequest;
import software.amazon.awssdk.services.sns.model.PublishRequest;

public class SnsNotificationPublisher implements NotificationPublisher {
Expand All @@ -12,8 +14,16 @@ public SnsNotificationPublisher(SnsClient snsClient) {

@Override
public void publish(String topic, String subject, String message) {
var req = PublishRequest.builder().topicArn(topic).subject(subject).message(message).build();
var topicArn = getTopicArn(topic);
var req = PublishRequest.builder().topicArn(topicArn).subject(subject).message(message).build();

snsClient.publish(req);
}

private String getTopicArn(String topic) {
var req = ListTopicsRequest.builder().build();
var res = snsClient.listTopics(req);
var topics = res.topics().stream().filter(t -> t.topicArn().endsWith(":" + topic)).toList();
return Iterables.getOnlyElement(topics).topicArn();
}
}
2 changes: 2 additions & 0 deletions price_tracker_api/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ java_binary(
":lib",
"//lib/notifications:lib",
"//lib/time:lib",
"@maven//:ch_qos_logback_logback_classic",
"@maven//:ch_qos_logback_logback_core",
"@maven//:com_amazonaws_aws_lambda_java_core",
"@maven//:com_amazonaws_aws_lambda_java_events",
"@maven//:com_google_code_findbugs_jsr305",
Expand Down
131 changes: 129 additions & 2 deletions price_tracker_api/infra/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ locals {
handler = "com.jordansimsmith.pricetracker.UpdatePricesHandler"
}
}

subscriptions = ["[email protected]"]
}

data "aws_iam_policy_document" "lambda_sts_allow_policy_document" {
Expand All @@ -57,6 +59,112 @@ resource "aws_iam_role_policy_attachment" "lambda_basic" {
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}

resource "aws_dynamodb_table" "price_tracker" {
name = "price_tracker"
billing_mode = "PAY_PER_REQUEST"
hash_key = "pk"
range_key = "sk"

attribute {
name = "pk"
type = "S"
}

attribute {
name = "sk"
type = "S"
}

point_in_time_recovery {
enabled = true
}

deletion_protection_enabled = true

tags = local.tags
}

data "aws_iam_policy_document" "lambda_dynamodb" {
statement {
effect = "Allow"

resources = [
aws_dynamodb_table.price_tracker.arn
]

actions = [
"dynamodb:PutItem",
"dynamodb:UpdateItem",
"dynamodb:BatchWriteItem",
"dynamodb:GetItem",
"dynamodb:BatchGetItem",
"dynamodb:Scan",
"dynamodb:Query",
"dynamodb:ConditionCheckItem",
]
}
}

resource "aws_iam_policy" "lambda_dynamodb" {
name = "${local.application_id}_lambda_dynamodb"
policy = data.aws_iam_policy_document.lambda_dynamodb.json
tags = local.tags
}

resource "aws_iam_role_policy_attachment" "lambda_dynamodb" {
role = aws_iam_role.lambda_role.name
policy_arn = aws_iam_policy.lambda_dynamodb.arn
}

resource "aws_sns_topic" "price_updates" {
name = "${local.application_id}_price_updates"
}

resource "aws_sns_topic_subscription" "price_updates" {
for_each = toset(local.subscriptions)
topic_arn = aws_sns_topic.price_updates.arn
protocol = "email"
endpoint = each.value
}

data "aws_iam_policy_document" "lambda_sns" {
statement {
effect = "Allow"

resources = [
aws_sns_topic.price_updates.arn,
]

actions = [
"SNS:Publish",
"SNS:GetTopicAttributes",
]
}

statement {
effect = "Allow"

resources = [
"*"
]

actions = [
"SNS:ListTopics"
]
}
}

resource "aws_iam_policy" "lambda_sns" {
name = "${local.application_id}_lambda_sns"
policy = data.aws_iam_policy_document.lambda_sns.json
tags = local.tags
}

resource "aws_iam_role_policy_attachment" "lambda_sns" {
role = aws_iam_role.lambda_role.name
policy_arn = aws_iam_policy.lambda_sns.arn
}

data "external" "handler_location" {
for_each = local.lambdas

Expand All @@ -82,7 +190,26 @@ resource "aws_lambda_function" "lambda" {
source_code_hash = data.local_file.handler_file[each.key].content_base64sha256
handler = each.value.handler
runtime = "java17"
memory_size = 512
timeout = 10
memory_size = 1024
timeout = 30
tags = local.tags
}

resource "aws_cloudwatch_event_rule" "trigger" {
name = "${local.application_id}_trigger"
schedule_expression = "rate(1 hour)"
}

resource "aws_cloudwatch_event_target" "trigger" {
rule = aws_cloudwatch_event_rule.trigger.name
target_id = "lambda"
arn = aws_lambda_function.lambda["update_prices"].arn
}

resource "aws_lambda_permission" "cloudwatch_trigger" {
statement_id = "AllowExecutionFromCloudWatch"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.lambda["update_prices"].function_name
principal = "events.amazonaws.com"
source_arn = aws_cloudwatch_event_rule.trigger.arn
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ public List<Product> findChemistWarehouseProducts() {
new Product(
URI.create(
"https://www.chemistwarehouse.co.nz/buy/111309/inc-high-protein-bar-peanut-butter-fudge-100g"),
"Chemist Warehouse - INC High Protein Bar Peanut Butter Fudge 100g"));
"Chemist Warehouse - INC High Protein Bar Peanut Butter Fudge 100g"),
new Product(
URI.create(
"https://www.chemistwarehouse.co.nz/buy/120088/musashi-electrolytes-blue-raspberry-300g"),
"Musashi Electrolytes Blue Raspberry 300g"),
new Product(
URI.create(
"https://www.chemistwarehouse.co.nz/buy/101969/musashi-electrolytes-watermelon-300g"),
"Musashi Electrolytes Watermelon 300g"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import software.amazon.awssdk.enhanced.dynamodb.model.QueryEnhancedRequest;

public class UpdatePricesHandler implements RequestHandler<ScheduledEvent, Void> {
@VisibleForTesting static final String TOPIC = "my topic";
@VisibleForTesting static final String TOPIC = "price_tracker_api_price_updates";

private final Clock clock;
private final NotificationPublisher notificationPublisher;
Expand Down Expand Up @@ -93,7 +93,7 @@ private Void doHandleRequest(ScheduledEvent event, Context context) throws Excep
priceChanges.size() == 1
? "1 price updated"
: "%d prices updated".formatted(priceChanges.size());
var message = new StringJoiner("\\n");
var message = new StringJoiner("\r\n\r\n");
for (var priceChange : priceChanges) {
var line =
"%s $%.2f -> $%.2f %s"
Expand Down

0 comments on commit a68ace3

Please sign in to comment.