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

FIX JENKINS-63048 #25

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package org.jenkinsci.plugin.gitea.client.http;

import java.net.HttpURLConnection;

/**
* @see <a href="https://tools.ietf.org/html/rfc8288">RFC8288</a>
*/
public class PageLinkHeader {

public static final String HEADER = "Link";
private static final String PARAMETER_SEPARATOR = ",";
private static final String ATTRIBUTE_SEPARATOR = ";";

private String first;
private String last;
private String next;
private String prev;

private PageLinkHeader(HttpURLConnection connection) {
String headerField = connection.getHeaderField(HEADER);
if (headerField != null) {
for (String rawLink : headerField.split(PARAMETER_SEPARATOR)) {
String[] attributes = rawLink.split(ATTRIBUTE_SEPARATOR);
if (attributes.length < 2) {
continue;
}

String link = attributes[0].trim();
if (!link.startsWith("<") || !link.endsWith(">")) {
continue;
}

String parsedLink = link.substring(1, link.length() - 1);
for (int i = 1; i < attributes.length; i++) {
String[] parameterAttributes = attributes[i].split("=");
if (parameterAttributes.length < 2 || !parameterAttributes[0].trim().equals("rel")) {
continue;
}

String parameterAttributeValue = parameterAttributes[1].replace("\"", "").toLowerCase();
if ("first".equals(parameterAttributeValue)) {
first = parsedLink;
} else if ("last".equals(parameterAttributeValue)) {
last = parsedLink;
} else if ("next".equals(parameterAttributeValue)) {
next = parsedLink;
} else if ("prev".equals(parameterAttributeValue)) {
prev = parsedLink;
}
}
}
}
}

public static PageLinkHeader from(HttpURLConnection connection) {
return new PageLinkHeader(connection);
}

public String getFirst() {
return first;
}

public String getLast() {
return last;
}

public String getNext() {
return next;
}

public String getPrev() {
return prev;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,11 @@
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import javax.net.ssl.HttpsURLConnection;
import jenkins.model.Jenkins;
Expand All @@ -70,6 +71,7 @@
import org.jenkinsci.plugin.gitea.client.api.GiteaTag;
import org.jenkinsci.plugin.gitea.client.api.GiteaUser;
import org.jenkinsci.plugin.gitea.client.api.GiteaVersion;
import org.jenkinsci.plugin.gitea.client.http.PageLinkHeader;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;

Expand Down Expand Up @@ -237,15 +239,16 @@ public List<GiteaRepository> fetchRepositories(String username) throws IOExcepti

@Override
public List<GiteaRepository> fetchRepositories(GiteaOwner owner) throws IOException, InterruptedException {
if(owner instanceof GiteaOrganization) {
if (owner instanceof GiteaOrganization) {
return fetchOrganizationRepositories(owner);
}
return fetchRepositories(owner.getUsername());

}

@Override
public List<GiteaRepository> fetchOrganizationRepositories(GiteaOwner owner) throws IOException, InterruptedException {
public List<GiteaRepository> fetchOrganizationRepositories(GiteaOwner owner)
throws IOException, InterruptedException {
return getList(
api()
.literal("/orgs")
Expand Down Expand Up @@ -327,7 +330,8 @@ public GiteaAnnotatedTag fetchAnnotatedTag(String username, String repository, S
}

@Override
public GiteaAnnotatedTag fetchAnnotatedTag(GiteaRepository repository, GiteaTag tag) throws IOException, InterruptedException {
public GiteaAnnotatedTag fetchAnnotatedTag(GiteaRepository repository, GiteaTag tag)
throws IOException, InterruptedException {
return fetchAnnotatedTag(repository.getOwner().getUsername(), repository.getName(), tag.getId());
}

Expand Down Expand Up @@ -961,34 +965,45 @@ private <T> T patch(UriTemplate template, Object body, final Class<T> modelClass

private <T> List<T> getList(UriTemplate template, final Class<T> modelClass)
throws IOException, InterruptedException {
HttpURLConnection connection = openConnection(template);
withAuthentication(connection);
try {
connection.connect();
int status = connection.getResponseCode();
if (status / 100 == 2) {
try (InputStream is = connection.getInputStream()) {
List<T> list = mapper.readerFor(mapper.getTypeFactory()
.constructCollectionType(List.class, modelClass))
.readValue(is);
// strip null values from the list
for (Iterator<T> iterator = list.iterator(); iterator.hasNext(); ) {
if (iterator.next() == null) {
iterator.remove();
}

String uri = template.expand();

List<T> result = new ArrayList<>();
while (uri != null) {
HttpURLConnection connection = openConnection(uri);
withAuthentication(connection);
try {
connection.connect();
int status = connection.getResponseCode();
if (status / 100 == 2) {
uri = PageLinkHeader.from(connection).getNext();
try (InputStream is = connection.getInputStream()) {
result.addAll(mapper.readerFor(mapper.getTypeFactory()
.constructCollectionType(List.class, modelClass))
.readValue(is));

// strip null values from the list
result.removeIf(Objects::isNull);
}
return list;
} else {
throw new GiteaHttpStatusException(status, connection.getResponseMessage());
}
} finally {
connection.disconnect();
}
throw new GiteaHttpStatusException(status, connection.getResponseMessage());
} finally {
connection.disconnect();
}

return result;
}

@Restricted(NoExternalUse.class)
protected HttpURLConnection openConnection(UriTemplate template) throws IOException {
URL url = new URL(template.expand());
return openConnection(template.expand());
}

@Restricted(NoExternalUse.class)
protected HttpURLConnection openConnection(String uri) throws IOException {
URL url = new URL(uri);
Jenkins jenkins = Jenkins.get();
if (jenkins.proxy == null) {
return (HttpURLConnection) url.openConnection();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package org.jenkinsci.plugin.gitea.client.http;

import java.net.HttpURLConnection;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;

public class PageLinkHeaderTest {

@Test
public void given__pageable_connection__when__fetch__then__parse() {
StringBuilder headerBuilder = new StringBuilder();
headerBuilder.append("<http://try.gitea.io/api/v1/orgs/test_org/repos?page=3>; rel=\"next\",");
headerBuilder.append("<http://try.gitea.io/api/v1/orgs/test_org/repos?page=3>; rel=\"last\",");
headerBuilder.append("<http://try.gitea.io/api/v1/orgs/test_org/repos?page=1>; rel=\"first\",");
headerBuilder.append("<http://try.gitea.io/api/v1/orgs/test_org/repos?page=1>; rel=\"prev\"");

HttpURLConnection connect = Mockito.mock(HttpURLConnection.class);
Mockito.when(connect.getHeaderField(PageLinkHeader.HEADER)).thenReturn(headerBuilder.toString());


PageLinkHeader linkHeader = PageLinkHeader.from(connect);

Assert.assertNotNull(linkHeader.getFirst());
Assert.assertEquals("http://try.gitea.io/api/v1/orgs/test_org/repos?page=1", linkHeader.getFirst());

Assert.assertNotNull(linkHeader.getLast());
Assert.assertEquals("http://try.gitea.io/api/v1/orgs/test_org/repos?page=3", linkHeader.getLast());

Assert.assertNotNull(linkHeader.getPrev());
Assert.assertEquals("http://try.gitea.io/api/v1/orgs/test_org/repos?page=1", linkHeader.getPrev());

Assert.assertNotNull(linkHeader.getNext());
Assert.assertEquals("http://try.gitea.io/api/v1/orgs/test_org/repos?page=3", linkHeader.getNext());
}

@Test
public void given__pageable_rel_not_first__when__fetch__then__parse() {
StringBuilder headerBuilder = new StringBuilder();
headerBuilder.append("<http://try.gitea.io/api/v1/orgs/test_org/repos?page=2>; attr=value; rel=\"next\",");
headerBuilder.append("<http://try.gitea.io/api/v1/orgs/test_org/repos?page=2>; rel=\"last\",");

HttpURLConnection connect = Mockito.mock(HttpURLConnection.class);
Mockito.when(connect.getHeaderField(PageLinkHeader.HEADER)).thenReturn(headerBuilder.toString());


PageLinkHeader linkHeader = PageLinkHeader.from(connect);

Assert.assertNotNull(linkHeader.getNext());
Assert.assertEquals("http://try.gitea.io/api/v1/orgs/test_org/repos?page=2", linkHeader.getNext());

Assert.assertNotNull(linkHeader.getLast());
Assert.assertEquals("http://try.gitea.io/api/v1/orgs/test_org/repos?page=2", linkHeader.getLast());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public class GiteaConnection_DisabledPR_Issues extends DefaultGiteaConnection {
}

@Override
protected HttpURLConnection openConnection(UriTemplate template) throws IOException {
protected HttpURLConnection openConnection(String uri) throws IOException {
throw new GiteaHttpStatusException(404, "TEST Case");
}
}