Skip to content

Commit 8f80b96

Browse files
committed
Misc updates
1 parent 77e11b5 commit 8f80b96

15 files changed

+135
-38
lines changed

src/main/java/org/hibernate/infra/sync/jira/JiraConfig.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ interface Instance {
7979

8080
URI apiUri();
8181

82-
@WithDefault("true")
82+
@WithDefault("false")
8383
boolean logRequests();
8484
}
8585

src/main/java/org/hibernate/infra/sync/jira/ProcessingConfig.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ public interface ProcessingConfig {
88
@WithDefault("10000")
99
int queueSize();
1010

11-
@WithDefault("5")
11+
@WithDefault("2")
1212
int threads();
1313
}

src/main/java/org/hibernate/infra/sync/jira/service/jira/HandlerProjectContext.java

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ public final class HandlerProjectContext implements AutoCloseable {
3131
private final AtomicLong currentIssueKeyNumber;
3232
private final JiraIssueBulk bulk;
3333

34+
private final String projectKeyWithDash;
35+
3436
public HandlerProjectContext(String projectName, String projectGroupName, JiraRestClient sourceJiraClient,
3537
JiraRestClient destinationJiraClient, HandlerProjectGroupContext projectGroupContext) {
3638
this.projectName = projectName;
@@ -41,6 +43,7 @@ public HandlerProjectContext(String projectName, String projectGroupName, JiraRe
4143
this.project = projectGroup().projects().get(projectName());
4244
this.currentIssueKeyNumber = new AtomicLong(getCurrentLatestJiraIssueKeyNumber());
4345
this.bulk = new JiraIssueBulk(createIssuePlaceholder(), ISSUES_PER_REQUEST);
46+
this.projectKeyWithDash = "%s-".formatted(project.projectKey());
4447
}
4548

4649
public JiraConfig.JiraProject project() {
@@ -82,8 +85,15 @@ public Long getLargestSyncedJiraIssueKeyNumber() {
8285
}
8386

8487
public Optional<JiraIssue> getNextIssueToSync(Long latestSyncedJiraIssueKeyNumber) {
85-
JiraIssues issues = destinationJiraClient.find("project = %s and key > %s-%s ORDER BY key ASC".formatted(
86-
project.originalProjectKey(), project.originalProjectKey(), latestSyncedJiraIssueKeyNumber), 0, 1);
88+
String query;
89+
if (latestSyncedJiraIssueKeyNumber > 0) {
90+
query = "project = %s and key > %s-%s ORDER BY key ASC".formatted(project.originalProjectKey(),
91+
project.originalProjectKey(), latestSyncedJiraIssueKeyNumber);
92+
} else {
93+
query = "project = %s ORDER BY key ASC".formatted(project.originalProjectKey(),
94+
project.originalProjectKey());
95+
}
96+
JiraIssues issues = sourceJiraClient.find(query, 0, 1);
8797
if (issues.issues.isEmpty()) {
8898
return Optional.empty();
8999
} else {
@@ -107,7 +117,12 @@ private Long getCurrentLatestJiraIssueKeyNumber() {
107117
}
108118

109119
public void createNextPlaceholderBatch(String upToKey) {
110-
createNextPlaceholderBatch(JiraIssue.keyToLong(upToKey));
120+
// it only makes sense to do the bulk-create if we are requesting it for the
121+
// same project we are actually in.
122+
if (upToKey.startsWith(projectKeyWithDash)) {
123+
Long upToKeyNumber = JiraIssue.keyToLong(upToKey);
124+
createNextPlaceholderBatch(upToKeyNumber);
125+
}
111126
}
112127

113128
public void createNextPlaceholderBatch(Long upToKeyNumber) {

src/main/java/org/hibernate/infra/sync/jira/service/jira/JiraService.java

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import java.util.concurrent.ThreadPoolExecutor;
1313
import java.util.concurrent.TimeUnit;
1414
import java.util.concurrent.atomic.AtomicLong;
15+
import java.util.function.Supplier;
1516

1617
import org.hibernate.infra.sync.jira.JiraConfig;
1718
import org.hibernate.infra.sync.jira.ProcessingConfig;
@@ -23,6 +24,7 @@
2324
import org.hibernate.infra.sync.jira.service.jira.model.hook.JiraWebHookObject;
2425
import org.hibernate.infra.sync.jira.service.jira.model.hook.JiraWebhookEventType;
2526
import org.hibernate.infra.sync.jira.service.jira.model.rest.JiraComment;
27+
import org.hibernate.infra.sync.jira.service.jira.model.rest.JiraComments;
2628
import org.hibernate.infra.sync.jira.service.jira.model.rest.JiraIssue;
2729
import org.hibernate.infra.sync.jira.service.jira.model.rest.JiraIssueLink;
2830
import org.hibernate.infra.sync.jira.service.jira.model.rest.JiraIssues;
@@ -46,15 +48,18 @@ public class JiraService {
4648

4749
private final ReportingConfig reportingConfig;
4850
private final ExecutorService executor;
51+
private final Supplier<Integer> workQueueSize;
4952
private final Map<String, HandlerProjectContext> contextPerProject;
5053
private final JiraConfig jiraConfig;
5154
private final Scheduler scheduler;
5255

5356
@Inject
5457
public JiraService(ProcessingConfig processingConfig, JiraConfig jiraConfig, ReportingConfig reportingConfig,
5558
Scheduler scheduler) {
59+
LinkedBlockingDeque<Runnable> workQueue = new LinkedBlockingDeque<>(processingConfig.queueSize());
60+
workQueueSize = workQueue::size;
5661
executor = new ThreadPoolExecutor(processingConfig.threads(), processingConfig.threads(), 0L,
57-
TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>(processingConfig.queueSize()));
62+
TimeUnit.MILLISECONDS, workQueue);
5863

5964
Map<String, HandlerProjectContext> contextMap = new HashMap<>();
6065
for (var entry : jiraConfig.projectGroup().entrySet()) {
@@ -107,7 +112,33 @@ public void registerManagementRoutes(@Observes ManagementInterface mi) {
107112
if (issueToSync.isEmpty()) {
108113
scheduler.unscheduleJob(identity);
109114
} else {
110-
triggerSyncEvent(issueToSync.get());
115+
triggerSyncEvent(issueToSync.get(), context);
116+
largestSyncedJiraIssueKeyNumber.set(JiraIssue.keyToLong(issueToSync.get().key));
117+
}
118+
}).schedule();
119+
rc.end();
120+
});
121+
mi.router().get("/sync/issues/init/:project").consumes(MediaType.APPLICATION_JSON).blockingHandler(rc -> {
122+
String project = rc.pathParam("project");
123+
124+
HandlerProjectContext context = contextPerProject.get(project);
125+
126+
if (context == null) {
127+
throw new IllegalArgumentException("Unknown project '%s'".formatted(project));
128+
}
129+
130+
AtomicLong largestSyncedJiraIssueKeyNumber = new AtomicLong(context.getLargestSyncedJiraIssueKeyNumber());
131+
132+
String identity = "Init Sync for project %s".formatted(project);
133+
scheduler.newJob(identity).setConcurrentExecution(Scheduled.ConcurrentExecution.SKIP)
134+
// every 10 seconds:
135+
.setCron("0/10 * * * * ?").setTask(executionContext -> {
136+
Optional<JiraIssue> issueToSync = context
137+
.getNextIssueToSync(largestSyncedJiraIssueKeyNumber.get());
138+
if (issueToSync.isEmpty()) {
139+
scheduler.unscheduleJob(identity);
140+
} else {
141+
triggerSyncEvent(issueToSync.get(), context);
111142
largestSyncedJiraIssueKeyNumber.set(JiraIssue.keyToLong(issueToSync.get().key));
112143
}
113144
}).schedule();
@@ -127,7 +158,7 @@ public void registerManagementRoutes(@Observes ManagementInterface mi) {
127158

128159
executor.submit(() -> {
129160
for (String issueKey : issueKeys) {
130-
triggerSyncEvent(context.sourceJiraClient().getIssue(issueKey));
161+
triggerSyncEvent(context.sourceJiraClient().getIssue(issueKey), context);
131162
}
132163
});
133164
rc.end();
@@ -158,8 +189,9 @@ public void registerManagementRoutes(@Observes ManagementInterface mi) {
158189
throw new IllegalArgumentException("Unknown project '%s'".formatted(project));
159190
}
160191

192+
// TODO: needs an issue key
161193
// can trigger directly as we do not make any REST requests here:
162-
triggerCommentSyncEvents(project, comments);
194+
triggerCommentSyncEvents(project, null, comments);
163195
rc.end();
164196
});
165197
}
@@ -243,25 +275,34 @@ private void syncByQuery(String query, HandlerProjectContext context) {
243275
int max = 100;
244276
do {
245277
issues = context.sourceJiraClient().find(query, start, max);
246-
issues.issues.forEach(this::triggerSyncEvent);
278+
issues.issues.forEach(jiraIssue -> triggerSyncEvent(jiraIssue, context));
247279

248280
start += max;
249281
} while (!issues.issues.isEmpty());
250282
}
251283

252-
private void triggerSyncEvent(JiraIssue jiraIssue) {
284+
private void triggerSyncEvent(JiraIssue jiraIssue, HandlerProjectContext context) {
285+
Log.infof("Adding sync events for a jira issue: %s; Already queued events: %s", jiraIssue.key,
286+
workQueueSize.get());
287+
JiraWebHookIssue issue = new JiraWebHookIssue();
288+
issue.id = jiraIssue.id;
289+
issue.key = jiraIssue.key;
290+
253291
JiraWebHookEvent event = new JiraWebHookEvent();
254292
event.webhookEvent = JiraWebhookEventType.ISSUE_UPDATED.getName();
255-
event.issue = new JiraWebHookIssue();
256-
event.issue.id = jiraIssue.id;
257-
event.issue.key = jiraIssue.key;
293+
event.issue = issue;
258294

259295
String projectKey = Objects.toString(jiraIssue.fields.project.properties().get("key"));
260296
acknowledge(projectKey, event);
261297

262298
// now sync comments:
263299
if (jiraIssue.fields.comment != null && jiraIssue.fields.comment.comments != null) {
264-
triggerCommentSyncEvents(projectKey, jiraIssue.fields.comment.comments);
300+
triggerCommentSyncEvents(projectKey, issue, jiraIssue.fields.comment.comments);
301+
} else {
302+
// comments not always come in the jira request... so if we didn't get any, just
303+
// in case we will query for them:
304+
JiraComments comments = context.sourceJiraClient().getComments(jiraIssue.id, 0, 500);
305+
triggerCommentSyncEvents(projectKey, issue, comments.comments);
265306
}
266307

267308
// and links:
@@ -277,11 +318,12 @@ private void triggerSyncEvent(JiraIssue jiraIssue) {
277318
}
278319
}
279320

280-
private void triggerCommentSyncEvents(String projectKey, List<JiraComment> comments) {
321+
private void triggerCommentSyncEvents(String projectKey, JiraWebHookIssue issue, List<JiraComment> comments) {
281322
for (JiraComment comment : comments) {
282323
var event = new JiraWebHookEvent();
283324
event.comment = new JiraWebHookObject();
284325
event.comment.id = Long.parseLong(comment.id);
326+
event.issue = issue;
285327
event.webhookEvent = JiraWebhookEventType.COMMENT_UPDATED.getName();
286328
acknowledge(projectKey, event);
287329
}

src/main/java/org/hibernate/infra/sync/jira/service/jira/client/JiraRestClient.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.fasterxml.jackson.annotation.JsonInclude;
2121
import com.fasterxml.jackson.databind.ObjectMapper;
2222
import io.quarkus.rest.client.reactive.ClientExceptionMapper;
23+
import io.quarkus.rest.client.reactive.ClientQueryParam;
2324
import io.quarkus.rest.client.reactive.jackson.ClientObjectMapper;
2425
import jakarta.ws.rs.Consumes;
2526
import jakarta.ws.rs.DELETE;
@@ -37,6 +38,8 @@
3738
* Note: the client assumes that the base URI for it is defined up to the API
3839
* version (included), e.g.:
3940
*/
41+
// so that we do not spam with all notifications ...
42+
@ClientQueryParam(name = "notifyUsers", value = "false")
4043
public interface JiraRestClient {
4144

4245
@GET

src/main/java/org/hibernate/infra/sync/jira/service/jira/handler/JiraCommentDeleteEventHandler.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,10 @@ protected void doRun() {
3636
}
3737
}
3838
}
39+
40+
@Override
41+
public String toString() {
42+
return "JiraCommentDeleteEventHandler[" + "issueId=" + issueId + ", objectId=" + objectId + ", project="
43+
+ context.projectName() + ']';
44+
}
3945
}

src/main/java/org/hibernate/infra/sync/jira/service/jira/handler/JiraCommentUpsertEventHandler.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,10 @@ private String prepareCommentQuote(JiraIssue issue, JiraComment comment) {
6464
6565
""".formatted(jiraCommentUri, JiraTextContent.userIdPart(comment.author), jiraUserUri);
6666
}
67+
68+
@Override
69+
public String toString() {
70+
return "JiraCommentUpsertEventHandler[" + "issueId=" + issueId + ", objectId=" + objectId + ", project="
71+
+ context.projectName() + ']';
72+
}
6773
}

src/main/java/org/hibernate/infra/sync/jira/service/jira/handler/JiraEventHandler.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ protected Optional<String> statusToTransition(String sourceId) {
109109
protected Optional<String> linkType(String sourceId) {
110110
JiraConfig.ValueMapping mappedValues = context.projectGroup().issueLinkTypes();
111111
return Optional.ofNullable(JiraStaticFieldMappingCache.linkType(context.projectGroupName(), sourceId, pk -> {
112-
Map<String, String> mapping = context.projectGroup().issueTypes().mapping();
112+
Map<String, String> mapping = context.projectGroup().issueLinkTypes().mapping();
113113
if (!mapping.isEmpty()) {
114114
return mapping;
115115
}
@@ -151,7 +151,7 @@ public final void run() {
151151
context.startProcessingEvent();
152152
doRun();
153153
} catch (RuntimeException e) {
154-
failureCollector.critical("Failed to handled the event", e);
154+
failureCollector.critical("Failed to handled the event: %s".formatted(this), e);
155155
} catch (InterruptedException e) {
156156
failureCollector.critical("Interrupted while waiting in the queue", e);
157157
Thread.currentThread().interrupt();
@@ -168,4 +168,6 @@ protected String toDestinationKey(String key) {
168168
}
169169
return key;
170170
}
171+
172+
public abstract String toString();
171173
}

src/main/java/org/hibernate/infra/sync/jira/service/jira/handler/JiraIssueDeleteEventHandler.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,10 @@ protected void doRun() {
5959
}
6060
}
6161
}
62+
63+
@Override
64+
public String toString() {
65+
return "JiraIssueDeleteEventHandler[" + "key='" + key + '\'' + ", objectId=" + objectId + ", project="
66+
+ context.projectName() + ']';
67+
}
6268
}

src/main/java/org/hibernate/infra/sync/jira/service/jira/handler/JiraIssueLinkDeleteEventHandler.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,10 @@
77
import org.hibernate.infra.sync.jira.service.reporting.ReportingConfig;
88

99
public class JiraIssueLinkDeleteEventHandler extends JiraEventHandler {
10-
1110
private final Long sourceIssueId;
11+
1212
private final Long destinationIssueId;
1313
private final String issueLinkTypeId;
14-
1514
public JiraIssueLinkDeleteEventHandler(ReportingConfig reportingConfig, HandlerProjectContext context, Long id,
1615
Long sourceIssueId, Long destinationIssueId, String issueLinkTypeId) {
1716
super(reportingConfig, context, id);
@@ -65,4 +64,11 @@ protected void doRun() {
6564
}
6665
}
6766
}
67+
68+
@Override
69+
public String toString() {
70+
return "JiraIssueLinkDeleteEventHandler[" + "sourceIssueId=" + sourceIssueId + ", destinationIssueId="
71+
+ destinationIssueId + ", issueLinkTypeId='" + issueLinkTypeId + '\'' + ", project="
72+
+ context.projectName() + ']';
73+
}
6874
}

0 commit comments

Comments
 (0)