Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Corrected GitHub stats #140

Merged
6 commits merged into from
Dec 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 7 additions & 7 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
<modelVersion>4.0.0</modelVersion>
<groupId>edu.tamu</groupId>
<artifactId>product-management-service</artifactId>
<version>0.2.0</version>
<version>1.0.1</version>

<name>Product-Management-Service</name>
<description>A Webservice to manage Products</description>

<parent>
<groupId>edu.tamu.weaver</groupId>
<artifactId>webservice-parent</artifactId>
<version>2.0.2</version>
<version>2.1.0</version>
</parent>

<properties>
Expand All @@ -37,31 +37,31 @@
<dependency>
<groupId>edu.tamu.weaver</groupId>
<artifactId>auth</artifactId>
<version>2.0.2</version>
<version>2.1.0</version>
</dependency>

<dependency>
<groupId>edu.tamu.weaver</groupId>
<artifactId>token-provider</artifactId>
<version>2.0.2</version>
<version>2.1.0</version>
</dependency>

<dependency>
<groupId>edu.tamu.weaver</groupId>
<artifactId>validation</artifactId>
<version>2.0.2</version>
<version>2.1.0</version>
</dependency>

<dependency>
<groupId>edu.tamu.weaver</groupId>
<artifactId>email</artifactId>
<version>2.0.2</version>
<version>2.1.0</version>
</dependency>

<dependency>
<groupId>edu.tamu.weaver</groupId>
<artifactId>reporting</artifactId>
<version>2.0.2</version>
<version>2.1.0</version>
</dependency>

<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public void update() {
if (remoteProjectManagers.isPresent()){
for (RemoteProjectManager remoteProjectManager : remoteProjectManagers.get()) {
RemoteProjectManagerBean remoteProjectManagerBean = (RemoteProjectManagerBean) managementBeanRegistry
.getService(remoteProjectManager.getName());
.getService(remoteProjectManager.getName());
try {
remoteProjects.put(remoteProjectManager.getId(), remoteProjectManagerBean.getRemoteProject());
} catch (Exception e) {
Expand Down
208 changes: 95 additions & 113 deletions src/main/java/edu/tamu/app/service/manager/AbstractGitHubService.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import java.net.URL;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -43,46 +42,52 @@ public abstract class AbstractGitHubService extends MappingRemoteProjectManagerB

protected static final Logger logger = Logger.getLogger(GitHubProjectService.class);

protected static final String ORGANIZATION = "TAMULib";
protected static final String REQUEST_LABEL = "request";
protected static final String ISSUE_LABEL = "issue";
protected static final String FEATURE_LABEL = "feature";
protected static final String DEFECT_LABEL = "bug";
protected static final String SPRINT = "SPRINT";
static final String ORGANIZATION = "TAMULib";
static final String SPRINT = "SPRINT";

protected final ManagementService managementService;
static final String ISSUE_LABEL = "issue";
static final String REQUEST_LABEL = "request";
static final String FEATURE_LABEL = "feature";
static final String DEFECT_LABEL = "bug";

protected final GitHubBuilder ghBuilder;
private static final List<String> MATCHING_LABELS = Arrays.asList(
REQUEST_LABEL,
FEATURE_LABEL,
DEFECT_LABEL
);

protected final GitHub github;

protected final Map<String, Member> members;
private final ManagementService managementService;

protected final RestTemplate restTemplate;
private final RestTemplate restTemplate;

private final GitHubBuilder ghBuilder;

private final Map<String, Member> members;

protected AbstractGitHubService(final ManagementService managementService) throws IOException {
this.managementService = managementService;
ghBuilder = new GitHubBuilder();
github = getGitHubInstance();
restTemplate = getRestTemplate();
members = new HashMap<String, Member>();
this.restTemplate = getRestTemplate();
this.ghBuilder = new GitHubBuilder();
this.members = new HashMap<String, Member>();
this.github = getGitHubInstance();
}

@Override
public List<RemoteProject> getRemoteProject() throws Exception {
logger.info("Fetching remote projects");
final GHOrganization org = github.getOrganization(ORGANIZATION);
return org.getRepositories().values().stream()
.map(repo -> exceptionHandlerWrapper(repo, r-> buildRemoteProject(r, r.listLabels().asList())))
.map(repo -> exceptionHandlerWrapper(repo, this::buildRemoteProject))
.collect(Collectors.toList());
}

@Override
public RemoteProject getRemoteProjectByScopeId(final String scopeId) throws Exception {
logger.info("Fetching remote project by scope id " + scopeId);
final GHRepository repo = github.getRepositoryById(scopeId);
final List<GHLabel> labels = repo.listLabels().asList();
return buildRemoteProject(repo, labels);
return buildRemoteProject(repo);
}

@Override
Expand All @@ -97,7 +102,7 @@ public String push(final FeatureRequest request) throws Exception {
return Long.toString(repo.createIssue(title).body(body).create().getId());
}

protected GitHub getGitHubInstance() throws IOException {
GitHub getGitHubInstance() throws IOException {
final Optional<String> endpoint = Optional.of(managementService.getUrl());
final Optional<String> token = Optional.of(managementService.getToken());

Expand All @@ -115,77 +120,7 @@ protected GitHub getGitHubInstance() throws IOException {
.build();
}

protected RestTemplate getRestTemplate() {
return new TokenAuthRestTemplate(managementService.getToken());
}

protected RemoteProject buildRemoteProject(final GHRepository repo, final List<GHLabel> labels) throws IOException {
final String scopeId = String.valueOf(repo.getId());
final String name = repo.getName();

final long requestCount = getPrimaryWorkItemCount(REQUEST_LABEL, repo, labels);
final long issueCount = getPrimaryWorkItemCount(ISSUE_LABEL, repo, labels);
final long featureCount = getPrimaryWorkItemCount(FEATURE_LABEL, repo, labels);
final long defectCount = getPrimaryWorkItemCount(DEFECT_LABEL, repo, labels);

return new RemoteProject(scopeId, name, requestCount, issueCount, featureCount, defectCount, 0L);
}

protected long getPrimaryWorkItemCount(final String type, final GHRepository repo, final List<GHLabel> labels)
throws IOException {
final Optional<GHLabel> label = getLabelByName(labels, type);
if (!label.isPresent()) {
return 0;
}
return repo.listIssues(GHIssueState.OPEN).asList().stream()
.filter(card -> cardIsLabelType(card, label.get()))
.count();
}

protected Optional<GHLabel> getLabelByName(final List<GHLabel> labels, final String name) {
return labels.stream()
.filter(label -> label.getName().equals(name))
.findFirst();
}

protected boolean cardIsLabelType(GHIssue card, GHLabel label) {
try {
Collection<GHLabel> labels = card.getLabels();
if (label.getName().equals(ISSUE_LABEL) && isAnIssue(card)) {
return true;
}
return hasLabelByName(labels, label.getName());
} catch (IOException e) {
throw new RuntimeException(e);
}
}

protected boolean isAnIssue(final GHIssue card) throws IOException {
final Collection<GHLabel> labels = card.getLabels();
return !hasLabelByName(labels, REQUEST_LABEL)
&& !hasLabelByName(labels, DEFECT_LABEL)
&& !hasLabelByName(labels, FEATURE_LABEL);
}

protected boolean hasLabelByName(final Collection<GHLabel> labels, final String name) {
return labels.parallelStream()
.filter(cardLabel -> cardLabel.getName().equals(name))
.findAny()
.isPresent();
}

protected String getCardType(final GHIssue content) throws IOException {
final List<GHLabel> labels = (List<GHLabel>) content.getLabels();
final Optional<GHLabel> label = labels.stream()
.filter(l -> l.getName().equals(DEFECT_LABEL) ||
l.getName().equals(FEATURE_LABEL) ||
l.getName().equals(ISSUE_LABEL) ||
l.getName().equals(REQUEST_LABEL)
).findFirst();
return label.isPresent() ? label.get().getName() : null;
}

protected Member getMember(final GHUser user) throws IOException {
Member getMember(final GHUser user) throws IOException {
Member member;
final String memberId = String.valueOf(user.getId());
final Optional<Member> cachedMember = getCachedMember(memberId);
Expand All @@ -207,15 +142,73 @@ protected Member getMember(final GHUser user) throws IOException {
return member;
}

protected Optional<Member> getCachedMember(final String id) {
String toProductName(GHProject project) {
return String.format("%s/%s", ORGANIZATION, project.getName());
}

Card toCard(GHProjectCard card, GHIssue issue) throws IOException {
String type = getCardType(issue);
String status = card.getColumn().getName();
// TODO: Figure out how we want to handle sizes
String estimate = null;
return new Card(
String.valueOf(card.getId()),
String.valueOf(issue.getNumber()),
mapCardType(type),
issue.getTitle(),
issue.getBody(),
mapStatus(status),
mapEstimate(estimate),
getAssignees(issue)
);
}

private RestTemplate getRestTemplate() {
return new TokenAuthRestTemplate(managementService.getToken());
}

private RemoteProject buildRemoteProject(final GHRepository repo) throws IOException {
final String scopeId = String.valueOf(repo.getId());
final String name = repo.getName();

final List<GHIssue> issues = repo.getIssues(GHIssueState.OPEN);

long featureCount = 0;
long defectCount = 0;
long requestCount = 0;
long issueCount = 0;

for (GHIssue issue : issues) {
if (issue.isPullRequest()) {
continue;
}
Optional<String> label = issue.getLabels().stream()
.filter(l -> MATCHING_LABELS.contains(l.getName()))
.map(GHLabel::getName)
.findFirst();
if (label.isPresent()) {
switch (label.get()) {
case FEATURE_LABEL: featureCount++; break;
case DEFECT_LABEL: defectCount++; break;
case REQUEST_LABEL: requestCount++; break;
}
} else {
issueCount++;
}
}

return new RemoteProject(scopeId, name, requestCount, issueCount, featureCount, defectCount, 0L);
}

private Optional<Member> getCachedMember(final String id) {
return Optional.ofNullable(members.get(id));
}

protected String getAvatarPath(final String url) {
private String getAvatarPath(final String url) {
return url.substring(url.indexOf("/u/") + 3, url.indexOf("?"));
}

protected void storeAvatar(final String avatarUrl) throws IOException {
private void storeAvatar(final String avatarUrl) throws IOException {
final URL imagesPath = getClass().getResource("/images/");
final HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM));
Expand All @@ -227,29 +220,17 @@ protected void storeAvatar(final String avatarUrl) throws IOException {
}
}

protected void cacheMember(final String id, final Member member) {
private void cacheMember(final String id, final Member member) {
members.put(id, member);
}

String toProductName(GHProject project) {
return String.format("%s/%s", ORGANIZATION, project.getName());
}

Card toCard(GHProjectCard card, GHIssue issue) throws IOException {
String type = getCardType(issue);
String status = card.getColumn().getName();
// TODO: Figure out how we want to handle sizes
String estimate = null;
return new Card(
String.valueOf(card.getId()),
String.valueOf(issue.getNumber()),
mapCardType(type),
issue.getTitle(),
issue.getBody(),
mapStatus(status),
mapEstimate(estimate),
getAssignees(issue)
);
private String getCardType(final GHIssue issue) throws IOException {
final List<GHLabel> labels = (List<GHLabel>) issue.getLabels();
final Optional<String> label = labels.stream()
.filter(l -> MATCHING_LABELS.contains(l.getName()))
.map(GHLabel::getName)
.findFirst();
return label.isPresent() ? label.get() : ISSUE_LABEL;
}

private List<Member> getAssignees(GHIssue issue) {
Expand All @@ -270,4 +251,5 @@ static <T, R> R exceptionHandlerWrapper(T t, ExceptionHandler<T, R, Exception> f
throw new RuntimeException(e);
}
}

}
2 changes: 1 addition & 1 deletion src/main/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ app:
# edu.tamu.weaver.auth.service.CryptoService
secret: verysecretsecret
# edu.tamu.weaver.filter.CorsFilter
allow-access: http://localhost,http://localhost:8080,http://machuff.tamu.edu,http://janus.evans.tamu.edu,http://savell.evans.tamu.edu,http://jmicah.tamu.edu
allow-access: "*"
# edu.tamu.weaver.email.config.WeaverEmailConfig
email:
host: relay.tamu.edu
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,8 +217,8 @@ public void setUp() throws Exception {
when(TEST_REPOSITORY1.createIssue(any(String.class)).body(any(String.class)).create()).thenReturn(TEST_ISSUE1);
when(TEST_REPOSITORY1.listProjects(any(ProjectStateFilter.class)).asList()).thenReturn(TEST_PROJECTS);
when(TEST_REPOSITORY1.listProjects().asList()).thenReturn(TEST_PROJECTS);
when(TEST_REPOSITORY1.listIssues(any(GHIssueState.class)).asList()).thenReturn(TEST_ISSUE_LIST);
when(TEST_REPOSITORY2.listIssues(any(GHIssueState.class)).asList()).thenReturn(TEST_ISSUE_LIST);
when(TEST_REPOSITORY1.getIssues(any(GHIssueState.class))).thenReturn(TEST_ISSUE_LIST);
when(TEST_REPOSITORY2.getIssues(any(GHIssueState.class))).thenReturn(TEST_ISSUE_LIST);
when(TEST_REPOSITORY2.listProjects().asList()).thenReturn(TEST_PROJECTS);
when(TEST_REPOSITORY1.listLabels().asList()).thenReturn(ALL_TEST_LABELS);
when(TEST_REPOSITORY2.listLabels().asList()).thenReturn(ALL_TEST_LABELS);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,8 +212,8 @@ public void setUp() throws Exception {
when(TEST_REPOSITORY1.createIssue(any(String.class)).body(any(String.class)).create()).thenReturn(TEST_ISSUE1);
when(TEST_REPOSITORY1.listProjects(any(ProjectStateFilter.class)).asList()).thenReturn(TEST_PROJECTS);
when(TEST_REPOSITORY1.listProjects().asList()).thenReturn(TEST_PROJECTS);
when(TEST_REPOSITORY1.listIssues(any(GHIssueState.class)).asList()).thenReturn(TEST_ISSUE_LIST);
when(TEST_REPOSITORY2.listIssues(any(GHIssueState.class)).asList()).thenReturn(TEST_ISSUE_LIST);
when(TEST_REPOSITORY1.getIssues(any(GHIssueState.class))).thenReturn(TEST_ISSUE_LIST);
when(TEST_REPOSITORY2.getIssues(any(GHIssueState.class))).thenReturn(TEST_ISSUE_LIST);
when(TEST_REPOSITORY2.listProjects().asList()).thenReturn(TEST_PROJECTS);
when(TEST_REPOSITORY1.listLabels().asList()).thenReturn(ALL_TEST_LABELS);
when(TEST_REPOSITORY2.listLabels().asList()).thenReturn(ALL_TEST_LABELS);
Expand Down