Skip to content

Commit

Permalink
Merge 790b4aa into f335bf2
Browse files Browse the repository at this point in the history
  • Loading branch information
rladdusaw committed Dec 11, 2019
2 parents f335bf2 + 790b4aa commit 173114e
Show file tree
Hide file tree
Showing 11 changed files with 711 additions and 9 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ target/
.project
.settings
.springBeans
.vscode
bin/
logs/
*.log*
24 changes: 24 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,24 @@
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.10.1</version>
</dependency>

<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.1</version>
</dependency>

<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.10.1</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
Expand All @@ -110,6 +128,12 @@
<version>16.1.0</version>
</dependency>

<dependency>
<groupId>org.kohsuke</groupId>
<artifactId>github-api</artifactId>
<version>1.101</version>
</dependency>

</dependencies>

<build>
Expand Down
4 changes: 3 additions & 1 deletion src/main/java/edu/tamu/app/model/ServiceType.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@

public enum ServiceType {

VERSION_ONE("Version One");
VERSION_ONE("Version One"),
GITHUB("GitHub");

private String gloss;

Expand Down Expand Up @@ -38,6 +39,7 @@ public void setGloss(String gloss) {
public List<Setting> getScaffold() {
List<Setting> scaffold = new ArrayList<Setting>();
switch (this) {
case GITHUB:
case VERSION_ONE:
scaffold.add(new Setting("text", "url", "URL", true));
scaffold.add(new Setting("text", "username", "Username", false));
Expand Down
262 changes: 262 additions & 0 deletions src/main/java/edu/tamu/app/service/manager/GitHubService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
package edu.tamu.app.service.manager;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.log4j.Logger;
import org.kohsuke.github.GHBlob;
import org.kohsuke.github.GHIssue;
import org.kohsuke.github.GHLabel;
import org.kohsuke.github.GHOrganization;
import org.kohsuke.github.GHProject;
import org.kohsuke.github.GHProjectCard;
import org.kohsuke.github.GHProjectColumn;
import org.kohsuke.github.GHRepository;
import org.kohsuke.github.GHUser;
import org.kohsuke.github.GitHub;
import org.kohsuke.github.GitHubBuilder;

import edu.tamu.app.cache.model.Card;
import edu.tamu.app.cache.model.Member;
import edu.tamu.app.cache.model.RemoteProject;
import edu.tamu.app.cache.model.Sprint;
import edu.tamu.app.model.ManagementService;
import edu.tamu.app.model.request.FeatureRequest;

public class GitHubService extends MappingRemoteProjectManagerBean {

private static final Logger logger = Logger.getLogger(GitHubService.class);

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

private final ManagementService managementService;

private final GitHubBuilder ghBuilder;

private final GitHub github;

private final Map<String, Member> members;

private GHLabel label;

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

@Override
public List<RemoteProject> getRemoteProjects() throws Exception {
logger.info("Fetching remote projects");
final List<RemoteProject> remoteProjects = new ArrayList<RemoteProject>();
final GHOrganization org = github.getOrganization(ORGANIZATION);
for (GHRepository repo : org.getRepositories().values()) {
final List<GHLabel> labels = repo.listLabels().asList();
remoteProjects.add(buildRemoteProject(repo, labels));
}
return remoteProjects;
}

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

@Override
public List<Sprint> getActiveSprintsByProjectId(final String projectScopeId) throws Exception {
logger.info("Fetching active sprints for project with scope id " + projectScopeId);
List<Sprint> activeSprints = new ArrayList<Sprint>();
GHRepository repo = github.getRepositoryById(projectScopeId);
List<GHProject> projects = repo.listProjects().asList();
for (GHProject project : projects) {
String sprintId = String.valueOf(project.getId());
String name = project.getName();
String projectName = repo.getName();
List<Card> cards = getCards(project);
activeSprints.add(new Sprint(sprintId, name, projectName, cards));
}
return activeSprints;
}

@Override
public Object push(final FeatureRequest request) throws Exception {
logger.info("Submitting feature request " + request.getTitle() + " to project with scope id " + request.getScopeId());
String repoId = String.valueOf(request.getProjectId());
String title = request.getTitle();
String body = request.getDescription();
GHRepository repo = github.getRepositoryById(repoId);
return repo.createIssue(title).body(body).create();
}

protected GitHub getGitHubInstance() throws IOException {
GitHub githubInstance;
final Optional<String> endpoint = managementService.getSettingValue("url");
final Optional<String> token = managementService.getSettingValue("token");
if (!endpoint.isPresent()) {
throw new RuntimeException("GitHub service enpoint was not defined");
}
if (token.isPresent()) {
githubInstance = ghBuilder
.withEndpoint(endpoint.get())
.withOAuthToken(token.get())
.build();
} else {
githubInstance = ghBuilder
.withEndpoint(endpoint.get())
.withPassword(getSettingValue("username"), getSettingValue("password"))
.build();
}
return githubInstance;
}

private String getSettingValue(final String key) {
final Optional<String> setting = managementService.getSettingValue(key);
if (setting.isPresent()) {
return setting.get();
}

throw new RuntimeException("No setting " + key + " found in settings for service " + managementService.getName());
}

private RemoteProject buildRemoteProject(GHRepository repo, List<GHLabel> labels) throws IOException {
List<GHProject> projects = repo.listProjects().asList();
final String scopeId = String.valueOf(repo.getId());
final String name = repo.getName();
int requestCount = 0;
int issueCount = 0;
int featureCount = 0;
int defectCount = 0;

for (GHProject project : projects) {
requestCount += getPrimaryWorkItemCount(REQUEST_LABEL, project, labels);
issueCount += getPrimaryWorkItemCount(ISSUE_LABEL, project, labels);
featureCount += getPrimaryWorkItemCount(FEATURE_LABEL, project, labels);
defectCount += getPrimaryWorkItemCount(DEFECT_LABEL, project, labels);
}

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

private int getPrimaryWorkItemCount(final String type, final GHProject project, final List<GHLabel> labels)
throws IOException {
label = getLabelByName(labels, type);
return project.listColumns()
.asList()
.stream()
.map(this::countCardsOnColumn)
.reduce(0L, (a, b) -> a + b)
.intValue();
}

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

private long countCardsOnColumn(GHProjectColumn column) {
try {
return column.listCards()
.asList()
.stream()
.filter(this::cardContainsLabel)
.count();
} catch (IOException e) {
throw new RuntimeException(e);
}
}

private boolean cardContainsLabel(GHProjectCard card) {
try {
return card.getContent().getLabels().contains(label);
} catch (IOException e) {
throw new RuntimeException(e);
}
}

private List<Card> getCards(GHProject project) throws IOException {
List<Card> cards = new ArrayList<Card>();
for (GHProjectColumn column : project.listColumns().asList()) {
List<GHProjectCard> projectCards = column.listCards().asList();
AtomicInteger cardNumber = new AtomicInteger();
for (GHProjectCard card : projectCards) {
GHIssue content = card.getContent();

String id = String.valueOf(card.getId());
String name = content.getTitle();
String number = String.valueOf(cardNumber.getAndIncrement());
String type = getCardType(content);
String description = content.getBody();
String status = card.getColumn().getName();
// TODO: Figure out how we want to handle sizes
String estimate = null;
List<Member> assignees = new ArrayList<Member>();
for (GHUser user : content.getAssignees()) {
assignees.add(getMember(user));
}
cards.add(new Card(id, number, mapCardType(type), name, description, mapStatus(status), mapEstimate(estimate), assignees));
}
}
return cards;
}

private String getCardType(GHIssue content) throws IOException {
List<GHLabel> labels = (List<GHLabel>) content.getLabels();
GHLabel label = labels.stream()
.filter(label1 -> label1.getName().equals(DEFECT_LABEL))
.findFirst()
.orElseGet(() -> labels.stream()
.filter(label2 -> label2.getName().equals(FEATURE_LABEL))
.findFirst()
.orElseGet(() -> labels.stream()
.filter(label3 -> label3.getName().equals(ISSUE_LABEL))
.findFirst()
.orElseGet(() -> labels.stream()
.filter(label4 -> label4.getName().equals(REQUEST_LABEL))
.findFirst()
.orElse(null)
)
)
);
return label == null ? null : label.getName();
}

Member getMember(GHUser user) throws IOException {
Member member;
String memberId = String.valueOf(user.getId());
Optional<Member> cachedMember = getCachedMember(memberId);
if (cachedMember.isPresent()) {
member = cachedMember.get();
} else {
String name = user.getName();
String avatarPath = user.getAvatarUrl();
member = new Member(memberId, name, avatarPath);

cacheMember(memberId, member);
}
return member;
}

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

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

}
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public VersionOneService(ManagementService managementService) throws MalformedUR

@Override
public List<RemoteProject> getRemoteProjects() throws ConnectionException, APIException, OidException {
logger.info("Fecthing remote projects");
logger.info("Fetching remote projects");
List<RemoteProject> remoteProjects = new ArrayList<RemoteProject>();
IAssetType scopeType = services.getMeta().getAssetType("Scope");
IAttributeDefinition nameAttributeDefinition = scopeType.getAttributeDefinition("Name");
Expand All @@ -92,7 +92,7 @@ public List<RemoteProject> getRemoteProjects() throws ConnectionException, APIEx

@Override
public RemoteProject getRemoteProjectByScopeId(final String scopeId) throws ConnectionException, APIException, OidException {
logger.info("Fecthing remote project by scope id " + scopeId);
logger.info("Fetching remote project by scope id " + scopeId);
Oid oid = services.getOid("Scope:" + scopeId);
IAssetType scopeType = services.getMeta().getAssetType("Scope");
IAttributeDefinition nameAttributeDefinition = scopeType.getAttributeDefinition("Name");
Expand Down Expand Up @@ -128,7 +128,7 @@ public int getPrimaryWorkItemCount(final String type, final String scopeId) thro

@Override
public List<Sprint> getActiveSprintsByProjectId(final String projectScopeId) throws ConnectionException, APIException, OidException, IOException {
logger.info("Fecthing active sprints for project with scope id " + projectScopeId);
logger.info("Fetching active sprints for project with scope id " + projectScopeId);
List<Sprint> activeSprints = new ArrayList<Sprint>();
IAssetType timeboxType = services.getMeta().getAssetType("Timebox");
IAttributeDefinition nameAttributeDefinition = timeboxType.getAttributeDefinition("Name");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package edu.tamu.app.service.registry;

import java.net.MalformedURLException;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
Expand All @@ -13,6 +13,7 @@
import com.versionone.apiclient.exceptions.V1Exception;

import edu.tamu.app.model.ManagementService;
import edu.tamu.app.service.manager.GitHubService;
import edu.tamu.app.service.manager.VersionOneService;

@Service
Expand All @@ -38,11 +39,13 @@ public void register(ManagementService managementService) {
case VERSION_ONE:
service = Optional.of((ManagementBean) new VersionOneService(managementService));
break;
case GITHUB:
service = Optional.of((ManagementBean) new GitHubService(managementService));
default:
break;
}

} catch (MalformedURLException | V1Exception e) {
} catch (IOException | V1Exception e) {
e.printStackTrace();
}

Expand Down

0 comments on commit 173114e

Please sign in to comment.