Merge "Add MetadataParser for inbound email"
This commit is contained in:
@@ -28,6 +28,7 @@ import com.google.gwtorm.server.OrmException;
|
||||
import org.eclipse.jgit.revwalk.FooterKey;
|
||||
import org.eclipse.jgit.revwalk.FooterLine;
|
||||
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -35,6 +36,9 @@ import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class MailUtil {
|
||||
public static DateTimeFormatter rfcDateformatter =
|
||||
DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss ZZZ");
|
||||
|
||||
public static MailRecipients getRecipientsFromFooters(
|
||||
ReviewDb db, AccountResolver accountResolver, boolean draftPatchSet,
|
||||
List<FooterLine> footerLines) throws OrmException {
|
||||
|
@@ -0,0 +1,34 @@
|
||||
// Copyright (C) 2016 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.server.mail;
|
||||
|
||||
public final class MetadataName {
|
||||
public static final String CHANGE_ID = "Gerrit-Change-Id";
|
||||
public static final String PATCH_SET = "Gerrit-PatchSet";
|
||||
public static final String MESSAGE_TYPE = "Gerrit-MessageType";
|
||||
public static final String TIMESTAMP = "Gerrit-Comment-Date";
|
||||
|
||||
public static String toHeader(String metadataName) {
|
||||
return "X-" + metadataName;
|
||||
}
|
||||
|
||||
public static String toHeaderWithDelimiter(String metadataName) {
|
||||
return toHeader(metadataName) + ": ";
|
||||
}
|
||||
|
||||
public static String toFooterWithDelimiter(String metadataName) {
|
||||
return metadataName + ": ";
|
||||
}
|
||||
}
|
@@ -0,0 +1,45 @@
|
||||
// Copyright (C) 2016 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.server.mail.receive;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
|
||||
/** MailMetadata represents metadata parsed from inbound email. */
|
||||
public class MailMetadata {
|
||||
public String changeId;
|
||||
public Integer patchSet;
|
||||
public String author; // Author of the email
|
||||
public Timestamp timestamp;
|
||||
public String messageType; // we expect comment here
|
||||
|
||||
|
||||
public boolean hasRequiredFields() {
|
||||
return changeId != null && patchSet != null && author != null &&
|
||||
timestamp != null && messageType != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(this)
|
||||
.add("Change-Id", changeId)
|
||||
.add("Patch-Set", patchSet)
|
||||
.add("Author", author)
|
||||
.add("Timestamp", timestamp)
|
||||
.add("Message-Type", messageType)
|
||||
.toString();
|
||||
}
|
||||
}
|
@@ -0,0 +1,110 @@
|
||||
// Copyright (C) 2016 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.server.mail.receive;
|
||||
|
||||
import static com.google.gerrit.server.mail.MetadataName.toHeaderWithDelimiter;
|
||||
import static com.google.gerrit.server.mail.MetadataName.toFooterWithDelimiter;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.primitives.Ints;
|
||||
|
||||
import com.google.gerrit.server.mail.MailUtil;
|
||||
import com.google.gerrit.server.mail.MetadataName;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.time.Instant;
|
||||
|
||||
/** Parse metadata from inbound email */
|
||||
public class MetadataParser {
|
||||
public static MailMetadata parse(MailMessage m) {
|
||||
MailMetadata metadata = new MailMetadata();
|
||||
// Find author
|
||||
metadata.author = m.from().getEmail();
|
||||
|
||||
// Check email headers for X-Gerrit-<Name>
|
||||
for (String header : m.additionalHeaders()) {
|
||||
if (header.startsWith(toHeaderWithDelimiter(MetadataName.CHANGE_ID))) {
|
||||
metadata.changeId = header
|
||||
.substring(toHeaderWithDelimiter(MetadataName.CHANGE_ID).length());
|
||||
} else if (header.startsWith(
|
||||
toHeaderWithDelimiter(MetadataName.PATCH_SET))) {
|
||||
String ps = header.substring(
|
||||
toHeaderWithDelimiter(MetadataName.PATCH_SET).length());
|
||||
metadata.patchSet = Ints.tryParse(ps);
|
||||
} else if (header.startsWith(
|
||||
toHeaderWithDelimiter(MetadataName.TIMESTAMP))) {
|
||||
String ts = header.substring(
|
||||
toHeaderWithDelimiter(MetadataName.TIMESTAMP).length());
|
||||
metadata.timestamp = Timestamp.from(
|
||||
MailUtil.rfcDateformatter.parse(ts, Instant::from));
|
||||
} else if (header.startsWith(
|
||||
toHeaderWithDelimiter(MetadataName.MESSAGE_TYPE))) {
|
||||
metadata.messageType = header.substring(
|
||||
toHeaderWithDelimiter(MetadataName.MESSAGE_TYPE).length());
|
||||
}
|
||||
}
|
||||
if (metadata.hasRequiredFields()) {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
// If the required fields were not yet found, continue to parse the text
|
||||
if (!Strings.isNullOrEmpty(m.textContent())) {
|
||||
String[] lines = m.textContent().split("\n");
|
||||
extractFooters(lines, metadata);
|
||||
if (metadata.hasRequiredFields()) {
|
||||
return metadata;
|
||||
}
|
||||
}
|
||||
|
||||
// If the required fields were not yet found, continue to parse the HTML
|
||||
// HTML footer are contained inside a <p> tag
|
||||
if (!Strings.isNullOrEmpty(m.htmlContent())) {
|
||||
String[] lines = m.htmlContent().split("</p>");
|
||||
extractFooters(lines, metadata);
|
||||
if (metadata.hasRequiredFields()) {
|
||||
return metadata;
|
||||
}
|
||||
}
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
private static void extractFooters(String[] lines, MailMetadata metadata) {
|
||||
for (String line : lines) {
|
||||
if (metadata.changeId == null && line.contains(MetadataName.CHANGE_ID)) {
|
||||
metadata.changeId =
|
||||
extractFooter(toFooterWithDelimiter(MetadataName.CHANGE_ID), line);
|
||||
} else if (metadata.patchSet == null &&
|
||||
line.contains(MetadataName.PATCH_SET)) {
|
||||
metadata.patchSet = Ints.tryParse(
|
||||
extractFooter(toFooterWithDelimiter(MetadataName.PATCH_SET), line));
|
||||
} else if (metadata.timestamp == null &&
|
||||
line.contains(MetadataName.TIMESTAMP)) {
|
||||
String ts =
|
||||
extractFooter(toFooterWithDelimiter(MetadataName.TIMESTAMP), line);
|
||||
metadata.timestamp = Timestamp.from(
|
||||
MailUtil.rfcDateformatter.parse(ts, Instant::from));
|
||||
} else if (metadata.messageType == null &&
|
||||
line.contains(MetadataName.MESSAGE_TYPE)) {
|
||||
metadata.messageType = extractFooter(
|
||||
toFooterWithDelimiter(MetadataName.MESSAGE_TYPE), line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String extractFooter(String key, String line) {
|
||||
return line.substring(line.indexOf(key) + key.length(), line.length());
|
||||
}
|
||||
}
|
@@ -0,0 +1,121 @@
|
||||
// Copyright (C) 2016 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.server.mail.receive;
|
||||
|
||||
import static com.google.gerrit.server.mail.MetadataName.toFooterWithDelimiter;
|
||||
import static com.google.gerrit.server.mail.MetadataName.toHeaderWithDelimiter;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import com.google.gerrit.server.mail.Address;
|
||||
import com.google.gerrit.server.mail.MetadataName;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.junit.Test;
|
||||
|
||||
public class MetadataParserTest {
|
||||
@Test
|
||||
public void testParseMetadataFromHeader() {
|
||||
// This tests if the metadata parser is able to parse metadata from the
|
||||
// email headers of the message.
|
||||
MailMessage.Builder b = MailMessage.builder();
|
||||
b.id("");
|
||||
b.dateReceived(new DateTime());
|
||||
b.subject("");
|
||||
|
||||
b.addAdditionalHeader(
|
||||
toHeaderWithDelimiter(MetadataName.CHANGE_ID) + "cid");
|
||||
b.addAdditionalHeader(toHeaderWithDelimiter(MetadataName.PATCH_SET) + "1");
|
||||
b.addAdditionalHeader(
|
||||
toHeaderWithDelimiter(MetadataName.MESSAGE_TYPE) +"comment");
|
||||
b.addAdditionalHeader(toHeaderWithDelimiter(MetadataName.TIMESTAMP) +
|
||||
"Tue, 25 Oct 2016 02:11:35 -0700");
|
||||
|
||||
Address author = new Address("Diffy", "test@gerritcodereview.com");
|
||||
b.from(author);
|
||||
|
||||
MailMetadata meta = MetadataParser.parse(b.build());
|
||||
assertThat(meta.author).isEqualTo(author.getEmail());
|
||||
assertThat(meta.changeId).isEqualTo("cid");
|
||||
assertThat(meta.patchSet).isEqualTo(1);
|
||||
assertThat(meta.messageType).isEqualTo("comment");
|
||||
assertThat(meta.timestamp.getTime()).isEqualTo(
|
||||
new DateTime(2016, 10, 25, 9, 11, 35, 0, DateTimeZone.UTC).getMillis());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseMetadataFromText() {
|
||||
// This tests if the metadata parser is able to parse metadata from the
|
||||
// the text body of the message.
|
||||
MailMessage.Builder b = MailMessage.builder();
|
||||
b.id("");
|
||||
b.dateReceived(new DateTime());
|
||||
b.subject("");
|
||||
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
stringBuilder.append(
|
||||
toFooterWithDelimiter(MetadataName.CHANGE_ID) + "cid" + "\n");
|
||||
stringBuilder.append(
|
||||
toFooterWithDelimiter(MetadataName.PATCH_SET) + "1" + "\n");
|
||||
stringBuilder.append(
|
||||
toFooterWithDelimiter(MetadataName.MESSAGE_TYPE) + "comment" + "\n");
|
||||
stringBuilder.append(toFooterWithDelimiter(MetadataName.TIMESTAMP) +
|
||||
"Tue, 25 Oct 2016 02:11:35 -0700" + "\n");
|
||||
b.textContent(stringBuilder.toString());
|
||||
|
||||
Address author = new Address("Diffy", "test@gerritcodereview.com");
|
||||
b.from(author);
|
||||
|
||||
MailMetadata meta = MetadataParser.parse(b.build());
|
||||
assertThat(meta.author).isEqualTo(author.getEmail());
|
||||
assertThat(meta.changeId).isEqualTo("cid");
|
||||
assertThat(meta.patchSet).isEqualTo(1);
|
||||
assertThat(meta.messageType).isEqualTo("comment");
|
||||
assertThat(meta.timestamp.getTime()).isEqualTo(
|
||||
new DateTime(2016, 10, 25, 9, 11, 35, 0, DateTimeZone.UTC).getMillis());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseMetadataFromHTML() {
|
||||
// This tests if the metadata parser is able to parse metadata from the
|
||||
// the HTML body of the message.
|
||||
MailMessage.Builder b = MailMessage.builder();
|
||||
b.id("");
|
||||
b.dateReceived(new DateTime());
|
||||
b.subject("");
|
||||
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
stringBuilder.append("<p>" +
|
||||
toFooterWithDelimiter(MetadataName.CHANGE_ID) + "cid" + "</p>");
|
||||
stringBuilder.append("<p>" + toFooterWithDelimiter(MetadataName.PATCH_SET) +
|
||||
"1" + "</p>");
|
||||
stringBuilder.append("<p>" +
|
||||
toFooterWithDelimiter(MetadataName.MESSAGE_TYPE) + "comment" + "</p>");
|
||||
stringBuilder.append("<p>" + toFooterWithDelimiter(MetadataName.TIMESTAMP) +
|
||||
"Tue, 25 Oct 2016 02:11:35 -0700" + "</p>");
|
||||
b.htmlContent(stringBuilder.toString());
|
||||
|
||||
Address author = new Address("Diffy", "test@gerritcodereview.com");
|
||||
b.from(author);
|
||||
|
||||
MailMetadata meta = MetadataParser.parse(b.build());
|
||||
assertThat(meta.author).isEqualTo(author.getEmail());
|
||||
assertThat(meta.changeId).isEqualTo("cid");
|
||||
assertThat(meta.patchSet).isEqualTo(1);
|
||||
assertThat(meta.messageType).isEqualTo("comment");
|
||||
assertThat(meta.timestamp.getTime()).isEqualTo(
|
||||
new DateTime(2016, 10, 25, 9, 11, 35, 0, DateTimeZone.UTC).getMillis());
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user