Skip to content

Commit 140008e

Browse files
Merge branch '3.0.x' into 3.1.x
Closes gh-2344
2 parents 50fa05a + f154bf5 commit 140008e

File tree

12 files changed

+854
-0
lines changed

12 files changed

+854
-0
lines changed

build.gradle

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ buildscript {
2525
}
2626

2727
apply plugin: 'io.spring.convention.root'
28+
apply plugin: 'org.springframework.security.update-dependencies'
2829

2930
group = 'org.springframework.session'
3031
description = 'Spring Session'
@@ -46,3 +47,21 @@ nohttp {
4647
source.exclude "spring-session-docs/.gradle/nodejs/**"
4748
source.exclude "spring-session-docs/modules/ROOT/examples/**/build/**"
4849
}
50+
51+
updateDependenciesSettings {
52+
gitHub {
53+
organization = "spring-projects"
54+
repository = "spring-session"
55+
}
56+
dependencyExcludes {
57+
majorVersionBump()
58+
minorVersionBump()
59+
alphaBetaVersions()
60+
snapshotVersions()
61+
}
62+
addFiles({
63+
return [
64+
project.file("gradle/dependency-management.gradle")
65+
]
66+
})
67+
}

buildSrc/build.gradle

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ plugins {
22
id "java-gradle-plugin"
33
id "java"
44
id "groovy"
5+
id 'com.apollographql.apollo' version '2.4.5'
56
}
67

78
sourceCompatibility = JavaVersion.VERSION_11
@@ -46,6 +47,10 @@ gradlePlugin {
4647
id = "org.springframework.propdeps"
4748
implementationClass = "org.springframework.gradle.propdeps.PropDepsPlugin"
4849
}
50+
updateDependencies {
51+
id = "org.springframework.security.update-dependencies"
52+
implementationClass = "org.springframework.security.convention.versions.UpdateDependenciesPlugin"
53+
}
4954
}
5055
}
5156

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
mutation CreateIssueInput($assigneeId: ID!, $labelIds: [ID!], $milestoneId: ID!, $repositoryId: ID!, $title: String!) {
2+
createIssue(input: {assigneeIds: [$assigneeId], labelIds: $labelIds, milestoneId: $milestoneId, projectIds: [], repositoryId: $repositoryId, title: $title}) {
3+
issue {
4+
number
5+
}
6+
}
7+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
query FindCreateIssueInput($owner: String!, $name: String!, $labelQuery: String, $milestoneName: String) {
2+
repository(owner: $owner, name: $name) {
3+
id
4+
labels(query: $labelQuery, first: 1) {
5+
nodes {
6+
id
7+
name
8+
}
9+
}
10+
milestones(query: $milestoneName, states: [OPEN], first: 1) {
11+
nodes {
12+
id
13+
title
14+
}
15+
}
16+
}
17+
viewer {
18+
id
19+
}
20+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
query RateLimit {
2+
rateLimit {
3+
limit
4+
cost
5+
remaining
6+
resetAt
7+
}
8+
}

buildSrc/src/main/graphql/org/springframework/security/convention/versions/schema.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright 2019-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.convention.versions;
18+
19+
import java.io.File;
20+
import java.io.IOException;
21+
import java.io.InputStream;
22+
import java.io.PrintStream;
23+
import java.util.Arrays;
24+
import java.util.Scanner;
25+
26+
class CommandLineUtils {
27+
static void runCommand(File dir, String... args) {
28+
try {
29+
Process process = new ProcessBuilder()
30+
.directory(dir)
31+
.command(args)
32+
.start();
33+
writeLinesTo(process.getInputStream(), System.out);
34+
writeLinesTo(process.getErrorStream(), System.out);
35+
if (process.waitFor() != 0) {
36+
new RuntimeException("Failed to run " + Arrays.toString(args));
37+
}
38+
} catch (IOException | InterruptedException e) {
39+
throw new RuntimeException("Failed to run " + Arrays.toString(args), e);
40+
}
41+
}
42+
43+
private static void writeLinesTo(InputStream input, PrintStream out) {
44+
Scanner scanner = new Scanner(input);
45+
while(scanner.hasNextLine()) {
46+
out.println(scanner.nextLine());
47+
}
48+
}
49+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright 2019-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.convention.versions;
18+
19+
import java.io.File;
20+
import java.io.IOException;
21+
import java.nio.file.Files;
22+
import java.util.function.Function;
23+
24+
class FileUtils {
25+
static void replaceFileText(File file, Function<String, String> replaceText) {
26+
String buildFileText = readString(file);
27+
String updatedBuildFileText = replaceText.apply(buildFileText);
28+
writeString(file, updatedBuildFileText);
29+
}
30+
31+
static String readString(File file) {
32+
try {
33+
byte[] bytes = Files.readAllBytes(file.toPath());
34+
return new String(bytes);
35+
}
36+
catch (IOException e) {
37+
throw new RuntimeException(e);
38+
}
39+
}
40+
41+
private static void writeString(File file, String text) {
42+
try {
43+
Files.write(file.toPath(), text.getBytes());
44+
}
45+
catch (IOException e) {
46+
throw new RuntimeException(e);
47+
}
48+
}
49+
}
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
package org.springframework.security.convention.versions;
2+
3+
import java.io.IOException;
4+
import java.time.Duration;
5+
import java.util.List;
6+
import java.util.Optional;
7+
import java.util.stream.Collectors;
8+
9+
import com.apollographql.apollo.ApolloCall;
10+
import com.apollographql.apollo.ApolloClient;
11+
import com.apollographql.apollo.api.Input;
12+
import com.apollographql.apollo.api.Response;
13+
import com.apollographql.apollo.exception.ApolloException;
14+
import okhttp3.Interceptor;
15+
import okhttp3.OkHttpClient;
16+
import okhttp3.Request;
17+
import org.jetbrains.annotations.NotNull;
18+
import reactor.core.publisher.Mono;
19+
import reactor.util.retry.RetrySpec;
20+
21+
public class GitHubApi {
22+
23+
private final ApolloClient apolloClient;
24+
25+
public GitHubApi(String githubToken) {
26+
if (githubToken == null) {
27+
throw new IllegalArgumentException("githubToken is required. You can set it using -PgitHubAccessToken=");
28+
}
29+
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
30+
clientBuilder.addInterceptor(new AuthorizationInterceptor(githubToken));
31+
this.apolloClient = ApolloClient.builder()
32+
.serverUrl("https://api.github.com/graphql")
33+
.okHttpClient(clientBuilder.build())
34+
.build();
35+
}
36+
37+
public Mono<FindCreateIssueResult> findCreateIssueInput(String owner, String name, String milestone) {
38+
String label = "\"type: dependency-upgrade\"";
39+
FindCreateIssueInputQuery findCreateIssueInputQuery = new FindCreateIssueInputQuery(owner, name, Input.optional(label), Input.optional(milestone));
40+
return Mono.create( sink -> this.apolloClient.query(findCreateIssueInputQuery)
41+
.enqueue(new ApolloCall.Callback<>() {
42+
@Override
43+
public void onResponse(@NotNull Response<FindCreateIssueInputQuery.Data> response) {
44+
if (response.hasErrors()) {
45+
sink.error(new RuntimeException(response.getErrors().stream().map(e -> e.getMessage()).collect(Collectors.joining(" "))));
46+
} else {
47+
FindCreateIssueInputQuery.Data data = response.getData();
48+
FindCreateIssueInputQuery.Repository repository = data.repository();
49+
List<String> labels = repository.labels().nodes().stream().map(FindCreateIssueInputQuery.Node::id).collect(Collectors.toList());
50+
if (labels.isEmpty()) {
51+
sink.error(new IllegalArgumentException("Could not find label for " + label));
52+
return;
53+
}
54+
Optional<String> firstMilestoneId = repository.milestones().nodes().stream().map(FindCreateIssueInputQuery.Node1::id).findFirst();
55+
if (!firstMilestoneId.isPresent()) {
56+
sink.error(new IllegalArgumentException("Could not find OPEN milestone id for " + milestone));
57+
return;
58+
}
59+
String milestoneId = firstMilestoneId.get();
60+
String repositoryId = repository.id();
61+
String assigneeId = data.viewer().id();
62+
sink.success(new FindCreateIssueResult(repositoryId, labels, milestoneId, assigneeId));
63+
}
64+
}
65+
66+
@Override
67+
public void onFailure(@NotNull ApolloException e) {
68+
sink.error(e);
69+
}
70+
}));
71+
}
72+
73+
public static class FindCreateIssueResult {
74+
private final String repositoryId;
75+
private final List<String> labelIds;
76+
private final String milestoneId;
77+
private final String assigneeId;
78+
79+
public FindCreateIssueResult(String repositoryId, List<String> labelIds, String milestoneId, String assigneeId) {
80+
this.repositoryId = repositoryId;
81+
this.labelIds = labelIds;
82+
this.milestoneId = milestoneId;
83+
this.assigneeId = assigneeId;
84+
}
85+
86+
public String getRepositoryId() {
87+
return repositoryId;
88+
}
89+
90+
public List<String> getLabelIds() {
91+
return labelIds;
92+
}
93+
94+
public String getMilestoneId() {
95+
return milestoneId;
96+
}
97+
98+
public String getAssigneeId() {
99+
return assigneeId;
100+
}
101+
}
102+
103+
public Mono<RateLimitQuery.RateLimit> findRateLimit() {
104+
return Mono.create( sink -> this.apolloClient.query(new RateLimitQuery())
105+
.enqueue(new ApolloCall.Callback<>() {
106+
@Override
107+
public void onResponse(@NotNull Response<RateLimitQuery.Data> response) {
108+
if (response.hasErrors()) {
109+
sink.error(new RuntimeException(response.getErrors().stream().map(e -> e.getMessage()).collect(Collectors.joining(" "))));
110+
} else {
111+
sink.success(response.getData().rateLimit());
112+
}
113+
}
114+
115+
@Override
116+
public void onFailure(@NotNull ApolloException e) {
117+
sink.error(e);
118+
}
119+
}));
120+
}
121+
122+
public Mono<Integer> createIssue(String repositoryId, String title, List<String> labelIds, String milestoneId, String assigneeId) {
123+
CreateIssueInputMutation createIssue = new CreateIssueInputMutation.Builder()
124+
.repositoryId(repositoryId)
125+
.title(title)
126+
.labelIds(labelIds)
127+
.milestoneId(milestoneId)
128+
.assigneeId(assigneeId)
129+
.build();
130+
return Mono.create( sink -> this.apolloClient.mutate(createIssue)
131+
.enqueue(new ApolloCall.Callback<>() {
132+
@Override
133+
public void onResponse(@NotNull Response<CreateIssueInputMutation.Data> response) {
134+
if (response.hasErrors()) {
135+
String message = response.getErrors().stream().map(e -> e.getMessage() + " " + e.getCustomAttributes() + " " + e.getLocations()).collect(Collectors.joining(" "));
136+
if (message.contains("was submitted too quickly")) {
137+
sink.error(new SubmittedTooQuick(message));
138+
} else {
139+
sink.error(new RuntimeException(message));
140+
}
141+
} else {
142+
sink.success(response.getData().createIssue().issue().number());
143+
}
144+
}
145+
146+
@Override
147+
public void onFailure(@NotNull ApolloException e) {
148+
sink.error(e);
149+
}
150+
}))
151+
.retryWhen(
152+
RetrySpec.fixedDelay(3, Duration.ofMinutes(1))
153+
.filter(SubmittedTooQuick.class::isInstance)
154+
.doBeforeRetry(r -> System.out.println("Pausing for 1 minute and then retrying due to receiving \"submitted too quickly\" error from GitHub API"))
155+
)
156+
.cast(Integer.class);
157+
}
158+
159+
public static class SubmittedTooQuick extends RuntimeException {
160+
public SubmittedTooQuick(String message) {
161+
super(message);
162+
}
163+
}
164+
165+
private static class AuthorizationInterceptor implements Interceptor {
166+
167+
private final String token;
168+
169+
public AuthorizationInterceptor(String token) {
170+
this.token = token;
171+
}
172+
173+
@Override
174+
public okhttp3.Response intercept(Chain chain) throws IOException {
175+
Request request = chain.request().newBuilder()
176+
.addHeader("Authorization", "Bearer " + this.token).build();
177+
return chain.proceed(request);
178+
}
179+
}
180+
}

0 commit comments

Comments
 (0)