Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
David Huntsperger committed May 20, 2020
1 parent 93ec3d7 commit 289c365
Show file tree
Hide file tree
Showing 11 changed files with 305 additions and 5 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
target/
dependency-reduced-pom.xml

.idea
*.iml
54 changes: 49 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,55 @@
## My Project
# Amazon CodeGuru Reviewer Example App

TODO: Fill this README out!
This application is intended to demonstrate Amazon CodeGuru Reviewer, a service that uses program analysis and machine learning to detect potential defects in code. The implementation of this example is deliberately suboptimal, to showcase CodeGuru Reviewer's ability to provide recommendations. This example code should not be used in production.

Be sure to:
To get started with CodeGuru Reviewer, follow the steps below.

* Change the title in this README
* Edit your repository description on GitHub
## Try CodeGuru Reviewer

### 1) Fork this repo

Log in to GitHub and choose **Fork** to fork this example app to your GitHub account.

### 2) Associate the forked repo

1. Log in to the [CodeGuru dashboard](https://console.aws.amazon.com/codeguru/home?region=us-east-1).
1. Choose **Reviewer** from the left panel and choose **Associated repositories**.
1. Choose **Associate repository**.
1. Make sure **GitHub** is selected and choose **Connect to GitHub**.
1. To allow CodeGuru Reviewer to access your account, choose **Authorize aws-codesuite**. If prompted, confirm your GitHub password.
1. Select the **amazon-codeguru-reviewer-sample-app** repository and choose **Associate**.

CodeGuru Reviewer is now associated with the repo and listening for pull requests.

### 3) Push a change to the code

Clone the forked repo:

git clone https://github.com/<your-userid>/amazon-codeguru-reviewer-sample-app.git

Check out a new branch:

cd amazon-codeguru-reviewer-sample-app
git checkout -b dev

Copy the Java class at **src/main/java/com/shipmentEvents/handlers/EventHandler.java** into **src/main/java/com/shipmentEvents/demo**. Git and CodeGuru Reviewer will treat this as a new file.

Push your changes:

git add --all
git commit -m 'new demo file'
git push --set-upstream origin dev

### 4) Create a pull request

1. In your forked GitHub repo, choose **New pull request**.
1. On the left side of the comparison (**base**), select **<your-userid>/amazon-codeguru-reviewer-sample-app** and leave the branch at **master**.
1. On the right side of the comparison (**compare**), change the branch to **dev**. They should be shown as mergeable ("Able to merge").
1. Choose **Create pull request** and, again, **Create pull request**.

### 5) Review recommendations

CodeGuru Reviewer will issue recommendations on the same GitHub page where the pull request was created. You can review the recommendations and provide feedback on them.

## License

Expand Down
42 changes: 42 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>sample-applications</groupId>
<artifactId>Guru-Sample-application</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Guru-Sample-application</name>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-core</artifactId>
<version>1.2.0</version>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-events</artifactId>
<version>2.2.6</version>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
<version>1.11.624</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.8</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.1</version>
</plugin>
</plugins>
</build>
</project>
Empty file.
174 changes: 174 additions & 0 deletions src/main/java/com/shipmentEvents/handlers/EventHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
package com.shipmentEvents.handlers;

import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import com.amazonaws.regions.Regions;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.LambdaLogger;
import com.amazonaws.services.lambda.runtime.events.ScheduledEvent;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.DeleteObjectsRequest;
import com.amazonaws.services.s3.model.DeleteObjectsRequest.KeyVersion;
import com.amazonaws.services.s3.model.ObjectListing;
import com.amazonaws.services.s3.model.S3ObjectSummary;
import com.shipmentEvents.util.Constants;

import org.apache.commons.lang3.tuple.MutablePair;
import org.apache.commons.lang3.tuple.Pair;


public class EventHandler implements RequestHandler<ScheduledEvent, String> {

/**
* Shipment events for a carrier are uploaded to separate S3 buckets based on the source of events. E.g., events originating from
* the hand-held scanner are stored in a separate bucket than the ones from mobile App. The Lambda processes events from multiple
* sources and updates the latest status of the package in a summary S3 bucket every 15 minutes.
*
* The events are stored in following format:
* - Each status update is a file, where the name of the file is tracking number + random id.
* - Each file has status and time-stamp as the first 2 lines respectively.
* - The time at which the file is stored in S3 is not an indication of the time-stamp of the event.
* - Once the status is marked as DELIVERED, we can stop tracking the package.
*
* A Sample files looks as below:
* FILE-NAME-> '8787323232232332--55322798-dd29-4a04-97f4-93e18feed554'
* >status:IN TRANSIT
* >timestamp: 1573410202
* >Other fields like...tracking history and address
*/
public String handleRequest(ScheduledEvent scheduledEvent, Context context) {

final LambdaLogger logger = context.getLogger();
try {
processShipmentUpdates(logger);
return "SUCCESS";
} catch (final Exception ex) {
logger.log(String.format("Failed to process shipment Updates in %s due to %s", scheduledEvent.getAccount(), ex.getMessage()));
throw new RuntimeException(ex);
}
}


private void processShipmentUpdates(final LambdaLogger logger) throws InterruptedException {

final List<String> bucketsToProcess = Constants.BUCKETS_TO_PROCESS;
final Map<String, Pair<Long, String>> latestStatusForTrackingNumber = new HashMap<String, Pair<Long, String>>();
final Map<String, List<KeyVersion>> filesToDelete = new HashMap<String, List<DeleteObjectsRequest.KeyVersion>>();
for (final String bucketName : bucketsToProcess) {
final List<KeyVersion> filesProcessed = processEventsInBucket(bucketName, logger, latestStatusForTrackingNumber);
filesToDelete.put(bucketName, filesProcessed);
}
final AmazonS3 s3Client = EventHandler.getS3Client();

//Create a new file in the Constants.SUMMARY_BUCKET
logger.log("Map of statuses -> " + latestStatusForTrackingNumber);
String summaryUpdateName = Long.toString(System.currentTimeMillis());

EventHandler.getS3Client().putObject(Constants.SUMMARY_BUCKET, summaryUpdateName, latestStatusForTrackingNumber.toString());

long expirationTime = System.currentTimeMillis() + Duration.ofMinutes(1).toMillis();
while(System.currentTimeMillis() < expirationTime) {
if (s3Client.doesObjectExist(Constants.SUMMARY_BUCKET, summaryUpdateName)) {
break;
}
logger.log("waiting for file to be created " + summaryUpdateName);
Thread.sleep(1000);
}

// Before we delete the shipment updates make sure the summary update file exists
if (EventHandler.getS3Client().doesObjectExist(Constants.SUMMARY_BUCKET, summaryUpdateName)) {
deleteProcessedFiles(filesToDelete);
logger.log("All updates successfully processed");
} else {
throw new RuntimeException("Failed to write summary status, will be retried in 15 minutes");
}

}

private List<KeyVersion> processEventsInBucket(String bucketName, LambdaLogger logger, Map<String, Pair<Long, String>> latestStatusForTrackingNumber) {
final AmazonS3 s3Client = EventHandler.getS3Client();
logger.log("Processing Bucket: " + bucketName);

ObjectListing files = s3Client.listObjects(bucketName);
List<KeyVersion> filesProcessed = new ArrayList<DeleteObjectsRequest.KeyVersion>();

for (Iterator<?> iterator = files.getObjectSummaries().iterator(); iterator.hasNext(); ) {
S3ObjectSummary summary = (S3ObjectSummary) iterator.next();
logger.log("Reading Object: " + summary.getKey());

String trackingNumber = summary.getKey().split("--")[0];
Pair<Long, String> lastKnownStatus = latestStatusForTrackingNumber.get(trackingNumber);

// Check if this shipment has already been delivered, skip this file
if (lastKnownStatus != null && "DELIVERED".equals(lastKnownStatus.getRight())) {
continue;
}

String fileContents = s3Client.getObjectAsString(bucketName, summary.getKey());

if (!isValidFile(fileContents)) {
logger.log(String.format("Skipping invalid file %s", summary.getKey()));
continue;
}

if (!fileContents.contains("\n")) {

}
String[] lines = fileContents.split("\n");
String line1 = lines[0];
String line2 = lines[1];

String status = line1.split(":")[1];
Long timeStamp = Long.parseLong(line2.split(":")[1]);


if (null == lastKnownStatus || lastKnownStatus.getLeft() < timeStamp) {
lastKnownStatus = new MutablePair<Long, String>(timeStamp, status);
latestStatusForTrackingNumber.put(trackingNumber, lastKnownStatus);
}

//Add to list of processed files
filesProcessed.add(new KeyVersion(summary.getKey()));
logger.log("logging Contents of the file" + fileContents);
}
return filesProcessed;
}


private void deleteProcessedFiles(Map<String, List<KeyVersion>> filesToDelete) {
final AmazonS3 s3Client = EventHandler.getS3Client();
for (Entry<String, List<KeyVersion>> entry : filesToDelete.entrySet()) {
final DeleteObjectsRequest deleteRequest = new DeleteObjectsRequest(entry.getKey()).withKeys(entry.getValue()).withQuiet(false);
s3Client.deleteObjects(deleteRequest);
}
}

private boolean isValidFile(String fileContents) {
if (!fileContents.contains("\n")) {
return false;
}
String[] lines = fileContents.split("\n");
for (String l: lines) {
if (!l.contains(":")) {
return false;
}
}
return true;
}

public static AmazonS3 getS3Client() {
return AmazonS3ClientBuilder.standard().withRegion(Regions.DEFAULT_REGION).build();
}


}


10 changes: 10 additions & 0 deletions src/main/java/com/shipmentEvents/util/Constants.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.shipmentEvents.util;

import java.util.Arrays;
import java.util.List;

public final class Constants {

public static final List<String> BUCKETS_TO_PROCESS = Arrays.asList("shipment-events-from-scanner", "shipment-events-from-mobile-app");
public static final String SUMMARY_BUCKET = "shipment-events-summary";
}
13 changes: 13 additions & 0 deletions src/main/java/com/shipmentEvents/util/S3ClientUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.shipmentEvents.util;

import com.amazonaws.regions.Regions;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;

public class S3ClientUtil {

public static AmazonS3 getS3Client() {
return AmazonS3ClientBuilder.standard().withRegion(Regions.DEFAULT_REGION).build();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
status:IN_TRANSIT
timestamp:1573410202
tackingHistory: testinggg
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
status:IN_TRANSIT
timestamp:1573410203
tackingHistory: testinggg
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
status:DELIVERED
timestamp:1573410206
tackingHistory: testinggg
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
status:IN_TRANSIT
timestamp:1573410209
tackingHistory: testinggg

0 comments on commit 289c365

Please sign in to comment.