diff --git a/tools/aggregatable_report_converter/README.md b/tools/aggregatable_report_converter/README.md new file mode 100644 index 00000000..37efb77e --- /dev/null +++ b/tools/aggregatable_report_converter/README.md @@ -0,0 +1,61 @@ +# Aggregatable Report Converter + +## Purpose +To provide Aggregation Service testers tools to create debug aggregatable reports that can be used for Local Testing and AWS Aggregation Service testing. + +## Guide +1. Clone the privacy-sandbox-demos repository. +2. Go to \/tools/aggregatable_report_converter +3. Options for usage: + 1. Use the jar file available in \/tools/aggregatable_report_converter/out/artifacts/aggregatable_report_converter_jar. + 2. Build jar file for the project `aggregatable_report_converter` using Eclipse or Intellij. + +### Build your jar file in Intellij +1. Open Project in Intellij +2. Go to `Build` > `Build Project`. +3. Go to `File` > `Project Structure`. Select `Artifacts` under `Project Settings`. +4. Click `+` > `JAR` > `From modules with dependencies...`. +5. Ensure `module` selected is `aggregatable_report_converter`. +6. In Main Class, select `Main`. Click `OK` > `Apply`. + + +## How to use + +Once the jar file is created, you can get options available for this tool using the command `java -jar aggregatable_report_converter.jar --help`. + +To convert json reports to debug aggregatable reports, you can use the below command: +```angular2html +java -jar aggregatable_report_converter.jar \ +--request_type convertToAvro \ +--input_file [filename] \ +--debug +``` + +To create output domain avro file, you can use the below command: +```angular2html +java -jar aggregatable_report_converter.jar \ +--request_type createDomainAvro \ +--bucket_key [bucket key] +``` + +## Options + +--request_type [request type] \ +Type of request. Options available: +- convertToAvro: converts a json report to an avro aggregatable report +- convertToJson: converts the avro summary report to a json report +- createDomainAvro: creates an output_domain.avro file from provided bucket key + +--input_file [file name] \ +This will be the file that will be encoded. + +--debug \ +This will signify that the request is for debug. \ +If the type of request is convertToAvro, this will create a debug avro report for local testing. + +--bucket_key [bucket key] \ +Provide your bucket key here. + +--output_file [file name] \ +Output filename the report will be written to. + diff --git a/tools/aggregatable_report_converter/out/artifacts/aggregatable_report_converter_jar/aggregatable_report_converter.jar b/tools/aggregatable_report_converter/out/artifacts/aggregatable_report_converter_jar/aggregatable_report_converter.jar new file mode 100644 index 00000000..4b917ba0 Binary files /dev/null and b/tools/aggregatable_report_converter/out/artifacts/aggregatable_report_converter_jar/aggregatable_report_converter.jar differ diff --git a/tools/aggregatable_report_converter/pom.xml b/tools/aggregatable_report_converter/pom.xml new file mode 100644 index 00000000..0550f4f2 --- /dev/null +++ b/tools/aggregatable_report_converter/pom.xml @@ -0,0 +1,96 @@ + + + 4.0.0 + + groupId + aggregatable_report_converter + 1.0-SNAPSHOT + + + 11 + 11 + UTF-8 + 5.8.2 + + + + + org.apache.avro + avro + 1.11.1 + + + org.junit.jupiter + junit-jupiter-api + ${junit.version} + test + + + org.junit.jupiter + junit-jupiter-engine + ${junit.version} + test + + + + org.json + json + 20220924 + + + + co.nstant.in + cbor + 0.9 + + + org.slf4j + slf4j-api + 2.0.7 + + + org.slf4j + slf4j-simple + 2.0.7 + + + + + + + + + org.apache.maven.plugins + maven-war-plugin + 3.3.2 + + + org.apache.avro + avro-maven-plugin + 1.11.1 + + + generate-sources + + schema + + + ${project.basedir}/src/main/java/ + ${project.basedir}/src/main/java/ + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + + \ No newline at end of file diff --git a/tools/aggregatable_report_converter/src/main/java/AggregatableReport.java b/tools/aggregatable_report_converter/src/main/java/AggregatableReport.java new file mode 100644 index 00000000..9e2ecf94 --- /dev/null +++ b/tools/aggregatable_report_converter/src/main/java/AggregatableReport.java @@ -0,0 +1,34 @@ +import java.lang.reflect.Array; +import java.util.ArrayList; +import org.apache.avro.data.Json; +import org.json.JSONArray; +import org.json.JSONObject; + +public class AggregatableReport { + private String sharedInfo; + private ArrayList aggregationServicePayloads; + + public AggregatableReport(String reportString){ + JSONObject aggregatableJson = new JSONObject(reportString); + sharedInfo = aggregatableJson.getString("shared_info"); + aggregationServicePayloads = getPayloads((JSONArray) aggregatableJson.get("aggregation_service_payloads")); + } + + private static ArrayList getPayloads(JSONArray payloads){ + ArrayList aggregatePayloads = new ArrayList<>(); + for (Object aggregationReport : payloads){ + JSONObject report = (JSONObject) aggregationReport; + Payload payload = new Payload(report); + aggregatePayloads.add(payload); + } + return aggregatePayloads; + } + + public String getSharedInfo() { + return sharedInfo; + } + + public ArrayList getAggregationServicePayloads() { + return aggregationServicePayloads; + } +} diff --git a/tools/aggregatable_report_converter/src/main/java/AvroEncoder.java b/tools/aggregatable_report_converter/src/main/java/AvroEncoder.java new file mode 100644 index 00000000..c59fcff2 --- /dev/null +++ b/tools/aggregatable_report_converter/src/main/java/AvroEncoder.java @@ -0,0 +1,79 @@ +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Base64; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Scanner; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.apache.avro.Schema; +import org.apache.avro.file.DataFileReader; +import org.apache.avro.file.DataFileWriter; +import org.apache.avro.generic.GenericData; +import org.apache.avro.generic.GenericData.Record; +import org.apache.avro.generic.GenericDatumWriter; +import org.apache.avro.generic.GenericRecord; +import org.apache.avro.io.DatumWriter; +import org.json.JSONArray; +import org.json.JSONObject; + +public class AvroEncoder { + + static String REPORT_SCHEMA = "{\n" + + " \"type\": \"record\",\n" + + " \"name\": \"AggregatableReport\",\n" + + " \"fields\": [\n" + + " {\n" + + " \"name\": \"payload\",\n" + + " \"type\": \"bytes\"\n" + + " },\n" + + " {\n" + + " \"name\": \"key_id\",\n" + + " \"type\": \"string\"\n" + + " },\n" + + " {\n" + + " \"name\": \"shared_info\",\n" + + " \"type\": \"string\"\n" + + " }\n" + + " ]\n" + + "}"; + + private static String readFileAsString(String file) throws IOException { + return new String(Files.readAllBytes(Paths.get(file))); + } + + public static void convertToAvroReport(HashMap requestParameters) throws IOException { + String fileName = Tools.getFileName(requestParameters); + String file = requestParameters.get("inputFile"); + File outputAvroReport = new File (fileName); + // check if the report is debug mode. If yes, the payload class will use the debug cleartext payload. + boolean isDebug = Boolean.parseBoolean(requestParameters.get("debugMode")); + Schema schema = new Schema.Parser().parse(REPORT_SCHEMA); + DatumWriter avroWriter = new GenericDatumWriter(schema); + DataFileWriter avroFileWriter = new DataFileWriter(avroWriter); + avroFileWriter.create(schema, outputAvroReport); + String reportJsonString = readFileAsString(file); + try { + AggregatableReport report = new AggregatableReport(reportJsonString); + for (Payload payload : report.getAggregationServicePayloads()) { + GenericRecord avroReport = new GenericData.Record(schema); + avroReport.put("key_id", payload.getKeyId()); + avroReport.put("shared_info", report.getSharedInfo()); + // the getPayload will check if the request is debug or not. + avroReport.put("payload", payload.getPayload(isDebug)); + avroFileWriter.append(avroReport); + } + avroFileWriter.close(); + System.out.println("Avro Report created: " + outputAvroReport); + } catch (Exception e){ + System.out.println(e); + } + } + +} diff --git a/tools/aggregatable_report_converter/src/main/java/AvroFileReader.java b/tools/aggregatable_report_converter/src/main/java/AvroFileReader.java new file mode 100644 index 00000000..a2dc57d5 --- /dev/null +++ b/tools/aggregatable_report_converter/src/main/java/AvroFileReader.java @@ -0,0 +1,46 @@ +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.sql.SQLOutput; +import java.util.Arrays; +import java.util.HashMap; +import org.apache.avro.Schema; +import org.apache.avro.file.DataFileReader; +import org.apache.avro.generic.GenericDatumReader; +import org.apache.avro.generic.GenericRecord; +import org.apache.avro.io.DatumReader; +import org.apache.avro.reflect.ReflectData; +import java.util.Scanner; +import org.json.JSONObject; + +public class AvroFileReader { + + public static void avroReader(HashMap requestParameters) throws IOException { + String fileName = Tools.getFileName(requestParameters); + String file = requestParameters.get("inputFile"); + + DataFileReader dataFileReader = new DataFileReader( + new File(file), new GenericDatumReader<>()); + + GenericRecord report = null; + FileWriter writer = new FileWriter(fileName); + + while (dataFileReader.hasNext()){ + report = dataFileReader.next(report); + ByteBuffer byteBuffer = (ByteBuffer) report.get("bucket"); + StringBuilder hexString = new StringBuilder(); + for (int i=0; i requestParams = Tools.getRequestParams(args); + Tools.processRequest(requestParams); + } + +} \ No newline at end of file diff --git a/tools/aggregatable_report_converter/src/main/java/OutputDomain.java b/tools/aggregatable_report_converter/src/main/java/OutputDomain.java new file mode 100644 index 00000000..2d9ca49b --- /dev/null +++ b/tools/aggregatable_report_converter/src/main/java/OutputDomain.java @@ -0,0 +1,66 @@ +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Scanner; +import org.apache.avro.Schema; +import org.apache.avro.file.DataFileWriter; +import org.apache.avro.generic.GenericData; +import org.apache.avro.generic.GenericData.Record; +import org.apache.avro.generic.GenericDatumWriter; +import org.apache.avro.generic.GenericRecord; +import org.apache.avro.io.DatumWriter; +import org.json.JSONObject; + +public class OutputDomain { + + static String OUTPUT_DOMAIN_SCHEMA = "{\n" + + " \"type\": \"record\",\n" + + " \"name\": \"AggregationBucket\",\n" + + " \"fields\": [\n" + + " {\n" + + " \"name\": \"bucket\",\n" + + " \"type\": \"bytes\",\n" + + " \"doc\": \"A single bucket that appears in the aggregation service output. 128-bit integer encoded as a 16-byte big-endian bytestring.\"\n" + + " }\n" + + " ]\n" + + "}"; + + public static void createOutputDomain(HashMap requestParams) throws IOException { + BigInteger bucket = new BigInteger(requestParams.get("bucketKey")); + String hex = bucket.toString(16); + String bucketString = ""; + if((hex.length()%2) > 0){ + hex = "0" + hex; + } + + List hexList = new ArrayList(); + int index = 0; + while (index < hex.length()){ + hexList.add(hex.substring(index, index+2)); + index += 2; + } + byte[] bucketByteArray = new byte[hexList.size()]; + for (int i=0; i < hexList.size(); i++){ + bucketByteArray[i] = (byte) Integer.parseInt(hexList.get(i), 16); + } + ByteBuffer bucketByteBuffer = ByteBuffer.wrap(bucketByteArray); + + String fileName = Tools.getFileName(requestParams); + File outputDomainAvro = new File (fileName); + Schema schema = new Schema.Parser().parse(OUTPUT_DOMAIN_SCHEMA); + DatumWriter avroWriter = new GenericDatumWriter(schema); + DataFileWriter avroFileWriter = new DataFileWriter(avroWriter); + avroFileWriter.create(schema, outputDomainAvro); + GenericRecord bucketKey = new GenericData.Record(schema); + bucketKey.put("bucket", bucketByteBuffer); + avroFileWriter.append(bucketKey); + avroFileWriter.close(); + System.out.println("Output Domain Avro created: " + fileName); + } + +} diff --git a/tools/aggregatable_report_converter/src/main/java/Payload.java b/tools/aggregatable_report_converter/src/main/java/Payload.java new file mode 100644 index 00000000..84efe5b4 --- /dev/null +++ b/tools/aggregatable_report_converter/src/main/java/Payload.java @@ -0,0 +1,44 @@ +import java.nio.ByteBuffer; +import java.util.Base64; +import java.util.EmptyStackException; +import org.json.JSONObject; + +public class Payload { + private ByteBuffer debugCleartextPayload; + private String keyId; + private ByteBuffer payload; + + Payload(JSONObject jsonPayload){ + boolean debugPayloadExists = jsonPayload.has("debug_cleartext_payload"); + if (debugPayloadExists) { + debugCleartextPayload = convertToBuffer(jsonPayload.getString("debug_cleartext_payload")); + } + keyId = jsonPayload.getString("key_id"); + payload = convertToBuffer(jsonPayload.getString("payload")); + } + + private static ByteBuffer convertToBuffer (String payload){ + byte [] bytePayload = Base64.getDecoder().decode(payload); + ByteBuffer decodedPayload = ByteBuffer.wrap(bytePayload); + return decodedPayload; + } + + public String getKeyId() { + return keyId; + } + + private ByteBuffer getDebugCleartextPayload() throws Exception { + if (debugCleartextPayload == null) { + throw new Exception("\"debug_cleartext_payload\" does not exist in report"); + } + return debugCleartextPayload; + } + + public ByteBuffer getPayload(boolean isDebug) throws Exception { + if (isDebug){ + return getDebugCleartextPayload(); + } + return payload; + + } +} diff --git a/tools/aggregatable_report_converter/src/main/java/Tools.java b/tools/aggregatable_report_converter/src/main/java/Tools.java new file mode 100644 index 00000000..bcc08421 --- /dev/null +++ b/tools/aggregatable_report_converter/src/main/java/Tools.java @@ -0,0 +1,140 @@ +import java.io.IOException; +import java.util.HashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Tools { + public static HashMap getRequestParams(String[] requestArgs){ + int lengthOfInput = requestArgs.length; + HashMap requestParams = new HashMap<>(); + int i = 0; + while (i < lengthOfInput) { + switch (requestArgs[i]){ + case "--request_type": + String requestType = requestArgs[i+1]; + requestParams.put("requestType", requestType); + i += 2; + break; + + case "--input_file": + String inputFile = requestArgs[i+1]; + requestParams.put("inputFile", inputFile); + i += 2; + break; + + case "--debug": + requestParams.put("debugMode", "true"); + i += 1; + break; + + case "--bucket_key": + String bucketKey = requestArgs[i+1]; + requestParams.put("bucketKey", bucketKey); + i += 2; + break; + + case "--output_file": + String outputFile = requestArgs[i+1]; + requestParams.put("outputFile", outputFile); + i += 2; + break; + + default: + System.out.println("Problematic Request. Please type \"--help\" if help is needed."); + i += lengthOfInput; + break; + } + } + return requestParams; + } + + public static void processRequest(HashMap requestParams) throws Exception { + String requestType = requestParams.get("requestType"); + boolean hasInputFile = requestParams.containsKey("inputFile"); + switch (requestType){ + case "convertToAvro": + if (!hasInputFile) { + throw new Exception("requestType convertToAvro missing inputFile. Include \"--input_file [filename]\" in arguments."); + } + try { + + // AvroEncoder.encodeReport(requestParams); + AvroEncoder.convertToAvroReport(requestParams); + } catch (Exception e) { + System.out.println(e); + } + break; + + case "convertToJson": + if(!hasInputFile) { + throw new Exception("requestType convertToJson missing inputFile. Include \"--input_file [filename]\" in arguments."); + } + try { + AvroFileReader.avroReader(requestParams); + } catch (Exception e) { + System.out.println(e); + } + break; + + case "createDomainAvro": + boolean hasBucketKey = requestParams.containsKey("bucketKey"); + if (!hasBucketKey) { + throw new Exception("requestType createDomainAvro missing bucketKey. Include \"--bucket_key [bucket key]\" in arguments."); + } + OutputDomain.createOutputDomain(requestParams); + break; + + default: + System.out.println("Invalid request type"); + break; + + } + } + + public static String getFileName(HashMap requestParameters){ + String fileName = "output_domain"; + String fileExtension = ".avro"; + if (requestParameters.containsKey("inputFile")){ + String inputFile = requestParameters.get("inputFile"); + fileName = getFileNameFromPath(inputFile); + fileExtension = getFileExtension(inputFile); + } + + if (requestParameters.containsKey("outputFile")) { + fileName = requestParameters.get("outputFile"); + } else { + fileName = fileName + fileExtension; + } + return fileName; + } + + private static String getFileExtension(String path){ + String fileExtension = ".avro"; + // define the regular expression + Pattern pattern = Pattern.compile("(^\\/.+\\/)?(.*).(json|avro)"); + + // match regular expression with the file path + Matcher matcher = pattern.matcher(path); + matcher.find(); + + String inputFileExtension = matcher.group(3); + if (inputFileExtension.equals("avro")){ + fileExtension = ".json"; + } + return fileExtension; + } + + private static String getFileNameFromPath(String path){ + // define the regular expression + Pattern pattern = Pattern.compile("(^\\/.+\\/)?(.*).(json|avro)"); + + // match regular expression with the file path + Matcher matcher = pattern.matcher(path); + matcher.find(); + + String fileName = matcher.group(2); + + return fileName; + } + +} diff --git a/tools/aggregatable_report_converter/src/main/resources/META-INF/MANIFEST.MF b/tools/aggregatable_report_converter/src/main/resources/META-INF/MANIFEST.MF new file mode 100644 index 00000000..37197ef4 --- /dev/null +++ b/tools/aggregatable_report_converter/src/main/resources/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: Main + diff --git a/tools/aggregatable_report_converter/target/aggregatable_report_converter-1.0-SNAPSHOT.jar b/tools/aggregatable_report_converter/target/aggregatable_report_converter-1.0-SNAPSHOT.jar new file mode 100644 index 00000000..d3dca439 Binary files /dev/null and b/tools/aggregatable_report_converter/target/aggregatable_report_converter-1.0-SNAPSHOT.jar differ diff --git a/tools/aggregatable_report_converter/target/classes/AggregatableReport.class b/tools/aggregatable_report_converter/target/classes/AggregatableReport.class new file mode 100644 index 00000000..d0edd386 Binary files /dev/null and b/tools/aggregatable_report_converter/target/classes/AggregatableReport.class differ diff --git a/tools/aggregatable_report_converter/target/classes/AvroEncoder.class b/tools/aggregatable_report_converter/target/classes/AvroEncoder.class new file mode 100644 index 00000000..7a1705da Binary files /dev/null and b/tools/aggregatable_report_converter/target/classes/AvroEncoder.class differ diff --git a/tools/aggregatable_report_converter/target/classes/AvroFileReader.class b/tools/aggregatable_report_converter/target/classes/AvroFileReader.class new file mode 100644 index 00000000..35719c86 Binary files /dev/null and b/tools/aggregatable_report_converter/target/classes/AvroFileReader.class differ diff --git a/tools/aggregatable_report_converter/target/classes/HelpOption.class b/tools/aggregatable_report_converter/target/classes/HelpOption.class new file mode 100644 index 00000000..a24983af Binary files /dev/null and b/tools/aggregatable_report_converter/target/classes/HelpOption.class differ diff --git a/tools/aggregatable_report_converter/target/classes/META-INF/MANIFEST.MF b/tools/aggregatable_report_converter/target/classes/META-INF/MANIFEST.MF new file mode 100644 index 00000000..37197ef4 --- /dev/null +++ b/tools/aggregatable_report_converter/target/classes/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: Main + diff --git a/tools/aggregatable_report_converter/target/classes/Main.class b/tools/aggregatable_report_converter/target/classes/Main.class new file mode 100644 index 00000000..e3dfbd64 Binary files /dev/null and b/tools/aggregatable_report_converter/target/classes/Main.class differ diff --git a/tools/aggregatable_report_converter/target/classes/OutputDomain.class b/tools/aggregatable_report_converter/target/classes/OutputDomain.class new file mode 100644 index 00000000..3b4effa2 Binary files /dev/null and b/tools/aggregatable_report_converter/target/classes/OutputDomain.class differ diff --git a/tools/aggregatable_report_converter/target/classes/Payload.class b/tools/aggregatable_report_converter/target/classes/Payload.class new file mode 100644 index 00000000..b57c0890 Binary files /dev/null and b/tools/aggregatable_report_converter/target/classes/Payload.class differ diff --git a/tools/aggregatable_report_converter/target/classes/Tools.class b/tools/aggregatable_report_converter/target/classes/Tools.class new file mode 100644 index 00000000..20c1536d Binary files /dev/null and b/tools/aggregatable_report_converter/target/classes/Tools.class differ diff --git a/tools/aggregatable_report_converter/target/classes/main/resources/META-INF/MANIFEST.MF b/tools/aggregatable_report_converter/target/classes/main/resources/META-INF/MANIFEST.MF new file mode 100644 index 00000000..37197ef4 --- /dev/null +++ b/tools/aggregatable_report_converter/target/classes/main/resources/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: Main + diff --git a/tools/aggregatable_report_converter/target/maven-archiver/pom.properties b/tools/aggregatable_report_converter/target/maven-archiver/pom.properties new file mode 100644 index 00000000..4d553d49 --- /dev/null +++ b/tools/aggregatable_report_converter/target/maven-archiver/pom.properties @@ -0,0 +1,5 @@ +#Generated by Maven +#Wed Sep 27 10:38:51 PDT 2023 +groupId=groupId +artifactId=aggregatable_report_converter +version=1.0-SNAPSHOT diff --git a/tools/aggregatable_report_converter/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/tools/aggregatable_report_converter/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 00000000..cf44d98c --- /dev/null +++ b/tools/aggregatable_report_converter/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1,8 @@ +AggregatableReport.class +Main.class +HelpOption.class +Tools.class +OutputDomain.class +AvroEncoder.class +AvroFileReader.class +Payload.class diff --git a/tools/aggregatable_report_converter/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/tools/aggregatable_report_converter/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 00000000..d67efb1f --- /dev/null +++ b/tools/aggregatable_report_converter/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1,8 @@ +/usr/local/google/home/maybellineb/IdeaProjects/aggregatable_report_converter/src/main/java/Tools.java +/usr/local/google/home/maybellineb/IdeaProjects/aggregatable_report_converter/src/main/java/AvroEncoder.java +/usr/local/google/home/maybellineb/IdeaProjects/aggregatable_report_converter/src/main/java/Main.java +/usr/local/google/home/maybellineb/IdeaProjects/aggregatable_report_converter/src/main/java/OutputDomain.java +/usr/local/google/home/maybellineb/IdeaProjects/aggregatable_report_converter/src/main/java/Payload.java +/usr/local/google/home/maybellineb/IdeaProjects/aggregatable_report_converter/src/main/java/HelpOption.java +/usr/local/google/home/maybellineb/IdeaProjects/aggregatable_report_converter/src/main/java/AggregatableReport.java +/usr/local/google/home/maybellineb/IdeaProjects/aggregatable_report_converter/src/main/java/AvroFileReader.java