Skip to content

Commit

Permalink
Include non-public repositories whose name ends with -commercial
Browse files Browse the repository at this point in the history
  • Loading branch information
wilkinsona committed Jun 4, 2024
1 parent f4059d7 commit f82ab34
Show file tree
Hide file tree
Showing 8 changed files with 2,738 additions and 1,892 deletions.
6 changes: 3 additions & 3 deletions src/main/java/io/spring/calendar/github/GitHubOperations.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 the original author or authors.
* Copyright 2016-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -33,12 +33,12 @@ interface GitHubOperations {
Page<Milestone> getMilestones(Repository repository, Page<Milestone> earlierResponse);

/**
* Returns the public repositories of the given {@code organization}.
* Returns the repositories of the given {@code organization}.
* @param organization the name of the organization
* @param earlierResponse the first page of an earlier response that can be used to
* perform conditional requests, or {@code null}.
* @return the page of repositories
*/
Page<Repository> getPublicRepositories(String organization, Page<Repository> earlierResponse);
Page<Repository> getRepositories(String organization, Page<Repository> earlierResponse);

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2023 the original author or authors.
* Copyright 2016-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -27,6 +27,7 @@
import java.util.stream.Stream;

import io.spring.calendar.github.Milestone.State;
import io.spring.calendar.github.Repository.Visibility;
import io.spring.calendar.release.Release;
import io.spring.calendar.release.Release.Status;
import io.spring.calendar.release.ReleaseSchedule;
Expand Down Expand Up @@ -54,16 +55,23 @@ class GitHubReleaseScheduleSource implements ReleaseScheduleSource {

@Override
public List<ReleaseSchedule> get() {
return this.organizations.stream().flatMap(this::getRepositories).map(this::createReleaseSchedule).toList();
return this.organizations.stream()
.flatMap(this::getRepositories)
.filter(this::include)
.map(this::createReleaseSchedule)
.toList();
}

private Stream<Repository> getRepositories(String organization) {
Page<Repository> page = this.gitHub.getPublicRepositories(organization,
this.earlierRepositories.get(organization));
Page<Repository> page = this.gitHub.getRepositories(organization, this.earlierRepositories.get(organization));
this.earlierRepositories.put(organization, this.earlierRepositories.get(organization));
return collectContent(page).stream();
}

private boolean include(Repository repository) {
return (repository.getVisibility() == Visibility.PUBLIC) || repository.getName().endsWith("-commercial");
}

private ReleaseSchedule createReleaseSchedule(Repository repository) {
Page<Milestone> page = this.gitHub.getMilestones(repository,
this.earlierMilestones.get(repository.getFullName()));
Expand Down Expand Up @@ -98,8 +106,7 @@ private Release createRelease(Repository project, Milestone milestone) {
milestone.getDueOn()
.withZoneSameInstant(ZoneId.of("Europe/London"))
.format(DateTimeFormatter.ISO_LOCAL_DATE),
getStatus(milestone),
new URL(project.getHtmlUrl().toString() + "/milestone/" + milestone.getNumber()));
getStatus(milestone), getUrl(project, milestone));
}
catch (MalformedURLException ex) {
throw new RuntimeException(ex);
Expand All @@ -110,4 +117,9 @@ private Status getStatus(Milestone milestone) {
return (milestone.getState() == State.OPEN) ? Status.OPEN : Status.CLOSED;
}

private URL getUrl(Repository project, Milestone milestone) throws MalformedURLException {
return (project.getVisibility() == Repository.Visibility.PUBLIC)
? new URL(project.getHtmlUrl().toString() + "/milestone/" + milestone.getNumber()) : null;
}

}
4 changes: 2 additions & 2 deletions src/main/java/io/spring/calendar/github/GitHubTemplate.java
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,9 @@ public Page<Milestone> getMilestones(Repository repository, Page<Milestone> earl
}

@Override
public Page<Repository> getPublicRepositories(String organization, Page<Repository> earlierResponse) {
public Page<Repository> getRepositories(String organization, Page<Repository> earlierResponse) {
String url = (earlierResponse != null) ? earlierResponse.getUrl()
: "https://api.github.com/orgs/" + organization + "/repos?type=public&per_page=100";
: "https://api.github.com/orgs/" + organization + "/repos?per_page=100";
return new PageSupplier<>(url, earlierResponse, Repository[].class).get();
}

Expand Down
31 changes: 29 additions & 2 deletions src/main/java/io/spring/calendar/github/Repository.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 the original author or authors.
* Copyright 2016-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -41,14 +41,18 @@ class Repository {

private final URL htmlUrl;

private final Visibility visibility;

@JsonCreator
Repository(@JsonProperty("name") String name, @JsonProperty("full_name") String fullName,
@JsonProperty("milestones_url") String milestonesUrl, @JsonProperty("html_url") String htmlUrl) {
@JsonProperty("milestones_url") String milestonesUrl, @JsonProperty("html_url") String htmlUrl,
@JsonProperty("visibility") Visibility visibility) {
this.name = name;
this.fullName = name;
this.displayName = capitalize(name.replace('-', ' '));
this.milestonesUrl = sanitizeUrl(milestonesUrl);
this.htmlUrl = sanitizeUrl(htmlUrl);
this.visibility = visibility;
}

String getName() {
Expand All @@ -71,6 +75,10 @@ URL getHtmlUrl() {
return this.htmlUrl;
}

Visibility getVisibility() {
return this.visibility;
}

private static String capitalize(String input) {
StringWriter output = new StringWriter();
for (int i = 0; i < input.length(); i++) {
Expand All @@ -97,4 +105,23 @@ private static URL sanitizeUrl(String url) {
}
}

enum Visibility {

/**
* A private repository.
*/
PRIVATE,

/**
* A public repository.
*/
PUBLIC,

/**
* An internal repository.
*/
INTERNAL;

}

}
4 changes: 4 additions & 0 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
spring:
jackson:
mapper:
accept-case-insensitive-enums: true
calendar:
github:
token: ${calendar-github-token}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/*
* Copyright 2016-2024 the original author or authors.
*
* Licensed 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
*
* https://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 io.spring.calendar.github;

import java.time.ZonedDateTime;
import java.util.List;

import io.spring.calendar.github.Milestone.State;
import io.spring.calendar.github.Repository.Visibility;
import io.spring.calendar.release.ReleaseSchedule;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

/**
* Tests for {@link GitHubReleaseScheduleSource}.
*
* @author Andy Wilkinson
*/
class GitHubReleaseScheduleSourceTests {

private final GitHubOperations gitHub = mock(GitHubOperations.class);

private final GitHubReleaseScheduleSource source = new GitHubReleaseScheduleSource(this.gitHub,
List.of("spring-projects", "spring-cloud"));

@Test
void whenThereAreNoRepositoriesThenReleaseSchedulesIsEmpty() {
List<ReleaseSchedule> releases = this.source.get();
assertThat(releases).isEmpty();
verify(this.gitHub).getRepositories("spring-projects", null);
verify(this.gitHub).getRepositories("spring-cloud", null);
}

@Test
void whenThereAreNoMilestonesThenReleaseSchedulesHaveNoReleases() {
Repository springBoot = repository("spring-projects", "spring-boot");
given(this.gitHub.getRepositories("spring-projects", null)).willReturn(page(springBoot));
Repository springCloudCommons = repository("spring-cloud", "spring-cloud-commons");
given(this.gitHub.getRepositories("spring-cloud", null)).willReturn(page(springCloudCommons));
List<ReleaseSchedule> releaseSchedules = this.source.get();
assertThat(releaseSchedules).hasSize(2);
assertThat(releaseSchedules).first().satisfies((schedule) -> {
assertThat(schedule.getProject()).isEqualTo("Spring Boot");
assertThat(schedule.getReleases()).isEmpty();
});
assertThat(releaseSchedules).element(1).satisfies((schedule) -> {
assertThat(schedule.getProject()).isEqualTo("Spring Cloud Commons");
assertThat(schedule.getReleases()).isEmpty();
});
}

@Test
void whenMilestonesAreScheduledThenThereIsOneReleasePerMilestone() {
Repository springBoot = repository("spring-projects", "spring-boot");
given(this.gitHub.getRepositories("spring-projects", null)).willReturn(page(springBoot));
Repository springCloudCommons = repository("spring-cloud", "spring-cloud-commons");
given(this.gitHub.getRepositories("spring-cloud", null)).willReturn(page(springCloudCommons));
given(this.gitHub.getMilestones(springBoot, null))
.willReturn(page(new Milestone("3.3.1", ZonedDateTime.now(), State.OPEN, 1)));
given(this.gitHub.getMilestones(springCloudCommons, null))
.willReturn(page(new Milestone("1.2.3", ZonedDateTime.now(), State.OPEN, 1)));
List<ReleaseSchedule> releaseSchedules = this.source.get();
assertThat(releaseSchedules).hasSize(2);
assertThat(releaseSchedules).first().satisfies((schedule) -> {
assertThat(schedule.getProject()).isEqualTo("Spring Boot");
assertThat(schedule.getReleases()).hasSize(1);
});
assertThat(releaseSchedules).element(1).satisfies((schedule) -> {
assertThat(schedule.getProject()).isEqualTo("Spring Cloud Commons");
assertThat(schedule.getReleases()).hasSize(1);
});
}

@Test
void whenARepositoryIsInternalItIsIgnoredIfItsNameDoesNotEndWithDashCommercial() {
Repository hidden = repository("spring-projects", "hidden-repository", Visibility.INTERNAL);
Repository springBootCommercial = repository("spring-projects", "spring-boot-commercial", Visibility.INTERNAL);
given(this.gitHub.getRepositories("spring-projects", null)).willReturn(page(hidden, springBootCommercial));
given(this.gitHub.getMilestones(springBootCommercial, null))
.willReturn(page(new Milestone("2.7.21", ZonedDateTime.now(), State.OPEN, 1)));
List<ReleaseSchedule> releaseSchedules = this.source.get();
assertThat(releaseSchedules).singleElement().satisfies((schedule) -> {
assertThat(schedule.getProject()).isEqualTo("Spring Boot Commercial");
assertThat(schedule.getReleases()).hasSize(1);
});
}

@Test
void whenARepositoryIsPrivateItIsIgnoredIfItsNameDoesNotEndWithDashCommercial() {
Repository hidden = repository("spring-projects", "hidden-repository", Visibility.PRIVATE);
Repository springBootCommercial = repository("spring-projects", "spring-boot-commercial", Visibility.PRIVATE);
given(this.gitHub.getRepositories("spring-projects", null)).willReturn(page(hidden, springBootCommercial));
given(this.gitHub.getMilestones(springBootCommercial, null))
.willReturn(page(new Milestone("2.7.21", ZonedDateTime.now(), State.OPEN, 1)));
List<ReleaseSchedule> releaseSchedules = this.source.get();
assertThat(releaseSchedules).singleElement().satisfies((schedule) -> {
assertThat(schedule.getProject()).isEqualTo("Spring Boot Commercial");
assertThat(schedule.getReleases()).hasSize(1);
});
}

private Repository repository(String organization, String name) {
return this.repository(organization, name, Visibility.PUBLIC);
}

private Repository repository(String organization, String name, Visibility visibility) {
return new Repository(name, organization + "/" + name,
"https://api.github.com/repos/%s/%s/milestones{/number}".formatted(organization, name),
"https://github.com/%s/%s".formatted(organization, name), visibility);
}

@SafeVarargs
private <T> Page<T> page(T... content) {
return new StandardPage<>(List.of(content), null, null, () -> null);
}

}
12 changes: 6 additions & 6 deletions src/test/java/io/spring/calendar/github/GitHubTemplateTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class GitHubTemplateTests {

private final Repository repository = new Repository("spring-boot", "spring-projects/spring-boot",
"https://api.github.com/repos/spring-projects/spring-boot/milestones",
"https://github.com/spring-projects/spring-boot");
"https://github.com/spring-projects/spring-boot", Repository.Visibility.PUBLIC);

@Autowired
private GitHubTemplate gitHub;
Expand Down Expand Up @@ -95,15 +95,15 @@ void getMilestones() {
}

@Test
void getPublicRepositories() throws MalformedURLException {
this.server.expect(requestTo("https://api.github.com/orgs/spring-projects/repos?type=public&per_page=100"))
void getRepositories() throws MalformedURLException {
this.server.expect(requestTo("https://api.github.com/orgs/spring-projects/repos?per_page=100"))
.andRespond(this.testMethodResponse);
Page<Repository> page = this.gitHub.getPublicRepositories("spring-projects", null);
Page<Repository> page = this.gitHub.getRepositories("spring-projects", null);
assertThat(page.getContent()).hasSize(30);
assertThat(page.getContent().get(0).getMilestonesUrl())
.isEqualTo(new URL("https://api.github.com/repos/spring-projects/Spring-Integration-in-Action/milestones"));
.isEqualTo(new URL("https://api.github.com/repos/spring-projects/spring-data-commons/milestones"));
assertThat(page.getContent().get(0).getHtmlUrl())
.isEqualTo(new URL("https://github.com/spring-projects/Spring-Integration-in-Action"));
.isEqualTo(new URL("https://github.com/spring-projects/spring-data-commons"));
this.server.verify();
}

Expand Down
Loading

0 comments on commit f82ab34

Please sign in to comment.