This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to branch feature/SLING-8337 in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-committer-cli.git
commit f884748ff3aafb3868cc63bf01c53984f0723b5d Author: Radu Cotescu <[email protected]> AuthorDate: Fri Apr 19 17:46:25 2019 +0200 SLING-8368 - Name comparisons fail when accents are missing * made name comparisons ignore accents when looking for a member by name * if members use their apache email address for emails, then names will automatically be extracted from Whimsy instead of using the name from the From/Sender email headers --- pom.xml | 10 +++ .../java/org/apache/sling/cli/impl/mail/Email.java | 88 +++++++++++++++++++--- .../apache/sling/cli/impl/mail/EmailThread.java | 31 -------- .../sling/cli/impl/mail/VoteThreadFinder.java | 17 ++++- .../sling/cli/impl/people/MembersFinder.java | 17 ++++- .../sling/cli/impl/release/TallyVotesCommand.java | 33 ++++---- 6 files changed, 135 insertions(+), 61 deletions(-) diff --git a/pom.xml b/pom.xml index 0fc0dbd..6cdcf27 100644 --- a/pom.xml +++ b/pom.xml @@ -34,6 +34,16 @@ <build> <plugins> <plugin> + <artifactId>maven-clean-plugin</artifactId> + <configuration> + <filesets> + <fileset> + <directory>launcher</directory> + </fileset> + </filesets> + </configuration> + </plugin> + <plugin> <groupId>biz.aQute.bnd</groupId> <artifactId>bnd-maven-plugin</artifactId> </plugin> diff --git a/src/main/java/org/apache/sling/cli/impl/mail/Email.java b/src/main/java/org/apache/sling/cli/impl/mail/Email.java index 54ec66e..e11bfaa 100644 --- a/src/main/java/org/apache/sling/cli/impl/mail/Email.java +++ b/src/main/java/org/apache/sling/cli/impl/mail/Email.java @@ -16,33 +16,101 @@ */ package org.apache.sling.cli.impl.mail; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Properties; + +import javax.mail.Address; +import javax.mail.BodyPart; +import javax.mail.Message; +import javax.mail.MessagingException; +import javax.mail.Session; +import javax.mail.internet.InternetAddress; +import javax.mail.internet.MimeMessage; +import javax.mail.internet.MimeMultipart; + +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.utils.URIBuilder; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; + public class Email { - private String from; + + private String id; + private InternetAddress from; private String subject; private String body; - public String getFrom() { - return from; + public Email(String id) { + this.id = id; + try (CloseableHttpClient client = HttpClients.createDefault()) { + URI uri = new URIBuilder("https://lists.apache.org/api/source.lua/" + URLEncoder.encode(id, StandardCharsets.UTF_8)).build(); + HttpGet get = new HttpGet(uri); + try (CloseableHttpResponse response = client.execute(get)) { + try (InputStream content = response.getEntity().getContent()) { + if (response.getStatusLine().getStatusCode() != 200) { + throw new IOException("Status line : " + response.getStatusLine()); + } + MimeMessage message = new MimeMessage(Session.getDefaultInstance(new Properties()), content); + subject = message.getSubject(); + Address[] who = message.getFrom(); + if (who.length > 0) { + from = (InternetAddress) who[0]; + } + body = getContent(message); + } + } + } catch (URISyntaxException | IOException | MessagingException e) { + throw new IllegalArgumentException(e); + } } - public void setFrom(String from) { - this.from = from; + public String getId() { + return id; + } + + public InternetAddress getFrom() { + return from; } public String getSubject() { return subject; } - public void setSubject(String subject) { - this.subject = subject; - } public String getBody() { return body; } - public void setBody(String body) { - this.body = body; + private String getContent(Message message) throws MessagingException, IOException { + String result = ""; + if (message.isMimeType("text/plain")) { + result = message.getContent().toString(); + } else if (message.isMimeType("multipart/*")) { + MimeMultipart mimeMultipart = (MimeMultipart) message.getContent(); + result = getTextFromMimeMultipart(mimeMultipart); + } + return result; + } + + private String getTextFromMimeMultipart( + MimeMultipart mimeMultipart) throws MessagingException, IOException { + StringBuilder result = new StringBuilder(); + int count = mimeMultipart.getCount(); + for (int i = 0; i < count; i++) { + BodyPart bodyPart = mimeMultipart.getBodyPart(i); + if (bodyPart.isMimeType("text/plain") || bodyPart.isMimeType("text/html")) { + result.append("\n").append(bodyPart.getContent()); + } else if (bodyPart.getContent() instanceof MimeMultipart) { + result.append(getTextFromMimeMultipart((MimeMultipart) bodyPart.getContent())); + } + } + return result.toString(); } } diff --git a/src/main/java/org/apache/sling/cli/impl/mail/EmailThread.java b/src/main/java/org/apache/sling/cli/impl/mail/EmailThread.java deleted file mode 100644 index 03ac673..0000000 --- a/src/main/java/org/apache/sling/cli/impl/mail/EmailThread.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.sling.cli.impl.mail; - -import java.util.List; - -public class EmailThread { - private List<Email> emails; - - public List<Email> getEmails() { - return emails; - } - - public void setEmails(List<Email> emails) { - this.emails = emails; - } -} diff --git a/src/main/java/org/apache/sling/cli/impl/mail/VoteThreadFinder.java b/src/main/java/org/apache/sling/cli/impl/mail/VoteThreadFinder.java index 0d39968..f48baa1 100644 --- a/src/main/java/org/apache/sling/cli/impl/mail/VoteThreadFinder.java +++ b/src/main/java/org/apache/sling/cli/impl/mail/VoteThreadFinder.java @@ -21,6 +21,8 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.net.URI; import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.List; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; @@ -29,12 +31,14 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.osgi.service.component.annotations.Component; -import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; @Component(service = VoteThreadFinder.class) public class VoteThreadFinder { - public EmailThread findVoteThread(String releaseName) throws IOException { + public List<Email> findVoteThread(String releaseName) throws IOException { try ( CloseableHttpClient client = HttpClients.createDefault() ) { URI uri = new URIBuilder("https://lists.apache.org/api/stats.lua") @@ -50,8 +54,13 @@ public class VoteThreadFinder { InputStreamReader reader = new InputStreamReader(content)) { if ( response.getStatusLine().getStatusCode() != 200 ) throw new IOException("Status line : " + response.getStatusLine()); - Gson gson = new Gson(); - return gson.fromJson(reader, EmailThread.class); + JsonParser parser = new JsonParser(); + JsonArray emailsArray = parser.parse(reader).getAsJsonObject().get("emails").getAsJsonArray(); + List<Email> emails = new ArrayList<>(); + for (JsonElement email : emailsArray) { + emails.add(new Email(email.getAsJsonObject().get("id").getAsString())); + } + return emails; } } } catch (URISyntaxException e) { diff --git a/src/main/java/org/apache/sling/cli/impl/people/MembersFinder.java b/src/main/java/org/apache/sling/cli/impl/people/MembersFinder.java index 70b5469..c26ab9a 100644 --- a/src/main/java/org/apache/sling/cli/impl/people/MembersFinder.java +++ b/src/main/java/org/apache/sling/cli/impl/people/MembersFinder.java @@ -21,8 +21,10 @@ package org.apache.sling.cli.impl.people; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.text.Collator; import java.util.Collections; import java.util.HashSet; +import java.util.Locale; import java.util.Set; import org.apache.http.client.methods.CloseableHttpResponse; @@ -103,7 +105,7 @@ public class MembersFinder { return members; } - public Member getMemberById(String id) { + public Member findById(String id) { for (Member member : findMembers()) { if (id.equals(member.getId())) { return member; @@ -112,8 +114,19 @@ public class MembersFinder { return null; } + public Member findByNameOrEmail(String name, String email) { + Collator collator = Collator.getInstance(Locale.US); + collator.setDecomposition(Collator.NO_DECOMPOSITION); + for (Member member : findMembers()) { + if (email.equals(member.getEmail()) || collator.compare(name, member.getName()) == 0) { + return member; + } + } + return null; + } + public Member getCurrentMember() { - return getMemberById(credentialsService.getCredentials().getUsername()); + return findById(credentialsService.getCredentials().getUsername()); } } diff --git a/src/main/java/org/apache/sling/cli/impl/release/TallyVotesCommand.java b/src/main/java/org/apache/sling/cli/impl/release/TallyVotesCommand.java index fb3ca95..93dbf0a 100644 --- a/src/main/java/org/apache/sling/cli/impl/release/TallyVotesCommand.java +++ b/src/main/java/org/apache/sling/cli/impl/release/TallyVotesCommand.java @@ -17,14 +17,15 @@ package org.apache.sling.cli.impl.release; import java.io.IOException; +import java.text.Collator; import java.util.Arrays; -import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Locale; import java.util.Set; import java.util.stream.Collectors; import org.apache.sling.cli.impl.Command; import org.apache.sling.cli.impl.mail.Email; -import org.apache.sling.cli.impl.mail.EmailThread; import org.apache.sling.cli.impl.mail.VoteThreadFinder; import org.apache.sling.cli.impl.nexus.StagingRepository; import org.apache.sling.cli.impl.nexus.StagingRepositoryFinder; @@ -84,24 +85,28 @@ public class TallyVotesCommand implements Command { .stream() .map(Release::getFullName) .collect(Collectors.joining(", ")); - EmailThread voteThread = voteThreadFinder.findVoteThread(releaseName); - Set<String> bindingVoters = new HashSet<>(); - Set<String> nonBindingVoters = new HashSet<>(); - for (Email e : voteThread.getEmails()) { - if (isPositiveVote(e)) { - String sender = e.getFrom().replaceAll("<.*>", "").trim(); - for (Member m : membersFinder.findMembers()) { - if (sender.equals(m.getName())) { + Set<String> bindingVoters = new LinkedHashSet<>(); + Set<String> nonBindingVoters = new LinkedHashSet<>(); + Collator collator = Collator.getInstance(Locale.US); + collator.setDecomposition(Collator.NO_DECOMPOSITION); + voteThreadFinder.findVoteThread(releaseName).stream().skip(1).filter(this::isPositiveVote).forEachOrdered( + email -> { + String from = email.getFrom().getAddress(); + String name = email.getFrom().getPersonal(); + Member m = membersFinder.findByNameOrEmail(name, from); + if (m != null) { if (m.isPMCMember()) { - bindingVoters.add(sender); + bindingVoters.add(m.getName()); } else { - nonBindingVoters.add(sender); + nonBindingVoters.add(m.getName()); } + } else { + nonBindingVoters.add(name); } } - } - } + ); + String email = EMAIL_TEMPLATE .replace("##RELEASE_NAME##", releaseFullName) .replace("##BINDING_VOTERS##", String.join(", ", bindingVoters))
