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

Implement Jira authentication #232

Closed
wants to merge 3 commits into from
Closed
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
@@ -22,12 +22,13 @@
*/
package org.openjdk.skara.issuetracker.jira;

import org.openjdk.skara.host.*;
import org.openjdk.skara.host.HostUser;
import org.openjdk.skara.issuetracker.*;
import org.openjdk.skara.json.*;
import org.openjdk.skara.network.*;
import org.openjdk.skara.json.JSON;

import java.net.URI;
import java.util.Arrays;

public class JiraHost implements IssueTracker {
private final URI uri;
@@ -42,6 +43,14 @@ public class JiraHost implements IssueTracker {
request = new RestRequest(baseApi);
}

JiraHost(URI uri, JiraVault jiraVault) {
this.uri = uri;
var baseApi = URIBuilder.base(uri)
.setPath("/rest/api/2/")
.build();
request = new RestRequest(baseApi, () -> Arrays.asList("Cookie", jiraVault.getCookie()));
}

URI getUri() {
return uri;
}
@@ -59,18 +68,44 @@ public IssueProject project(String name) {
return new JiraProject(this, request, name);
}

private JSONObject userData(String name) {
var data = request.get("user")
.param("username", name)
.execute();
return data.asObject();
}

@Override
public HostUser user(String username) {
throw new RuntimeException("needs authentication; not implemented yet");
var data = request.get("user")
.param("username", username)
.execute();
var user = new HostUser(data.get("name").asString(),
data.get("name").asString(),
data.get("displayName").asString());
return user;
}

@Override
public HostUser currentUser() {
throw new RuntimeException("needs authentication; not implemented yet");
var data = request.get("myself").execute();
var user = new HostUser(data.get("name").asString(),
data.get("name").asString(),
data.get("displayName").asString());
return user;
}

@Override
public boolean isMemberOf(String groupId, HostUser user) {
throw new RuntimeException("not implemented yet");
var data = request.get("user")
.param("username", user.id())
.param("expand", "groups")
.execute();
for (var group : data.get("groups").get("items").asArray()) {
if (group.get("name").asString().equals(groupId)) {
return true;
}
}
return false;
}
}
@@ -24,18 +24,22 @@

import org.openjdk.skara.host.*;
import org.openjdk.skara.issuetracker.*;
import org.openjdk.skara.json.*;
import org.openjdk.skara.network.*;
import org.openjdk.skara.json.JSONValue;

import java.net.URI;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;

public class JiraIssue implements Issue {
private final JiraProject jiraProject;
private final RestRequest request;
private final JSONValue json;

private static final DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ");

JiraIssue(JiraProject jiraProject, RestRequest request, JSONValue json) {
this.jiraProject = jiraProject;
this.request = request;
@@ -66,7 +70,10 @@ public String title() {

@Override
public void setTitle(String title) {
throw new RuntimeException("not implemented yet");
var query = JSON.object()
.put("fields", JSON.object()
.put("summary", title));
request.put("").body(query).execute();
}

@Override
@@ -80,52 +87,98 @@ public String body() {

@Override
public void setBody(String body) {
throw new RuntimeException("not implemented yet");
var query = JSON.object()
.put("fields", JSON.object()
.put("description", body));
request.put("").body(query).execute();
}

private Comment parseComment(JSONValue json) {
return new Comment(json.get("id").asString(),
json.get("body").asString(),
new HostUser(json.get("author").get("name").asString(),
json.get("author").get("name").asString(),
json.get("author").get("displayName").asString()),
ZonedDateTime.parse(json.get("created").asString(), dateFormat),
ZonedDateTime.parse(json.get("updated").asString(), dateFormat));
}

@Override
public List<Comment> comments() {
throw new RuntimeException("not implemented yet");
var comments = request.get("/comment")
.param("maxResults", "1000")
.execute();
return comments.get("comments").stream()
.map(this::parseComment)
.collect(Collectors.toList());
}

@Override
public Comment addComment(String body) {
throw new RuntimeException("not implemented yet");
var json = request.post("/comment")
.body("body", body)
.execute();
return parseComment(json);
}

@Override
public Comment updateComment(String id, String body) {
throw new RuntimeException("not implemented yet");
var json = request.put("/comment/" + id)
.body("body", body)
.execute();
return parseComment(json);
}

@Override
public ZonedDateTime createdAt() {
return ZonedDateTime.parse(json.get("fields").get("created").asString());
return ZonedDateTime.parse(json.get("fields").get("created").asString(), dateFormat);
}

@Override
public ZonedDateTime updatedAt() {
return ZonedDateTime.parse(json.get("fields").get("updated").asString());
return ZonedDateTime.parse(json.get("fields").get("updated").asString(), dateFormat);
}

@Override
public void setState(State state) {
throw new RuntimeException("not implemented yet");
var transitions = request.get("/transitions").execute();
var wantedStateName = state == State.CLOSED ? "Closed" : "Open";
for (var transition : transitions.get("transitions").asArray()) {
if (transition.get("to").get("name").asString().equals(wantedStateName)) {
var query = JSON.object()
.put("transition", JSON.object()
.put("id", transition.get("id").asString()));
request.post("/transitions")
.body(query)
.execute();
return;
}
}
}

@Override
public void addLabel(String label) {
throw new RuntimeException("not implemented yet");
var query = JSON.object()
.put("update", JSON.object()
.put("labels", JSON.array().add(JSON.object()
.put("add", label))));
request.put("").body(query).execute();
}

@Override
public void removeLabel(String label) {
throw new RuntimeException("not implemented yet");
var query = JSON.object()
.put("update", JSON.object()
.put("labels", JSON.array().add(JSON.object()
.put("remove", label))));
request.put("").body(query).execute();
}

@Override
public List<String> labels() {
throw new RuntimeException("not implemented yet");
return json.get("fields").get("labels").stream()
.map(JSONValue::asString)
.collect(Collectors.toList());
}

@Override
@@ -137,11 +190,32 @@ public URI webUrl() {

@Override
public List<HostUser> assignees() {
throw new RuntimeException("not implemented yet");
var assignee = json.get("fields").get("assignee");
if (assignee.isNull()) {
return List.of();
}

var user = new HostUser(assignee.get("name").asString(),
assignee.get("name").asString(),
assignee.get("displayName").asString());
return List.of(user);
}

@Override
public void setAssignees(List<HostUser> assignees) {
throw new RuntimeException("not implemented yet");
String assignee;
switch (assignees.size()) {
case 0:
assignee = null;
break;
case 1:
assignee = assignees.get(0).id();
break;
default:
throw new RuntimeException("multiple assignees not supported");
}
request.put("/assignee")
.body("name", assignee)
.execute();
}
}
@@ -25,6 +25,7 @@
import org.openjdk.skara.host.Credential;
import org.openjdk.skara.issuetracker.*;
import org.openjdk.skara.json.JSONObject;
import org.openjdk.skara.network.URIBuilder;

import java.net.URI;

@@ -39,7 +40,13 @@ public IssueTracker create(URI uri, Credential credential, JSONObject configurat
if (credential == null) {
return new JiraHost(uri);
} else {
throw new RuntimeException("authentication not implemented yet");
if (credential.username().startsWith("https://")) {
var vaultUrl = URIBuilder.base(credential.username()).build();
var jiraVault = new JiraVault(vaultUrl, credential.password());
return new JiraHost(uri, jiraVault);
} else {
throw new RuntimeException("basic authentication not implemented yet");
}
}
}
}
@@ -23,7 +23,7 @@
package org.openjdk.skara.issuetracker.jira;

import org.openjdk.skara.issuetracker.*;
import org.openjdk.skara.json.JSON;
import org.openjdk.skara.json.*;
import org.openjdk.skara.network.*;

import java.net.URI;
@@ -34,12 +34,51 @@ public class JiraProject implements IssueProject {
private final String projectName;
private final RestRequest request;

private JSONObject projectMetadataCache = null;

JiraProject(JiraHost host, RestRequest request, String projectName) {
this.jiraHost = host;
this.projectName = projectName;
this.request = request;
}

private JSONObject project() {
if (projectMetadataCache == null) {
projectMetadataCache = request.get("project/" + projectName).execute().asObject();
}
return projectMetadataCache;
}

private Map<String, String> issueTypes() {
var ret = new HashMap<String, String>();
for (var type : project().get("issueTypes").asArray()) {
ret.put(type.get("name").asString(), type.get("id").asString());
}
return ret;
}

private Map<String, String> components() {
var ret = new HashMap<String, String>();
for (var type : project().get("components").asArray()) {
ret.put(type.get("name").asString(), type.get("id").asString());
}
return ret;
}

private String projectId() {
return project().get("id").asString();
}

private String defaultIssueType() {
return issueTypes().values().stream()
.min(Comparator.naturalOrder()).orElseThrow();
}

private String defaultComponent() {
return components().values().stream()
.min(Comparator.naturalOrder()).orElseThrow();
}

@Override
public IssueTracker issueTracker() {
return jiraHost;
@@ -52,19 +91,35 @@ public URI webUrl() {

@Override
public Issue createIssue(String title, List<String> body) {
throw new RuntimeException("needs authentication; not implemented yet");
var query = JSON.object()
.put("fields", JSON.object()
.put("project", JSON.object()
.put("id", projectId()))
.put("issuetype", JSON.object()
.put("id", defaultIssueType()))
.put("components", JSON.array()
.add(JSON.object().put("id", defaultComponent())))
.put("summary", title)
.put("description", String.join("\n", body)));

var data = request.post("issue")
.body(query)
.execute();

return issue(data.get("key").asString()).orElseThrow();
}

@Override
public Optional<Issue> issue(String id) {
if (id.indexOf('-') < 0) {
id = projectName.toUpperCase() + "-" + id;
}
var issue = request.get("issue/" + id)
var issueRequest = request.restrict("issue/" + id);
var issue = issueRequest.get("")
.onError(r -> r.statusCode() == 404 ? JSON.object().put("NOT_FOUND", true) : null)
.execute();
if (!issue.contains("NOT_FOUND")) {
return Optional.of(new JiraIssue(this, request, issue));
return Optional.of(new JiraIssue(this, issueRequest, issue));
} else {
return Optional.empty();
}