diff --git a/sync-github/src/main/kotlin/gropius/sync/github/IssuePile.kt b/sync-github/src/main/kotlin/gropius/sync/github/IssuePile.kt index 5dc08288..56494c47 100644 --- a/sync-github/src/main/kotlin/gropius/sync/github/IssuePile.kt +++ b/sync-github/src/main/kotlin/gropius/sync/github/IssuePile.kt @@ -2,7 +2,6 @@ package gropius.sync.github import com.fasterxml.jackson.databind.JsonNode import gropius.model.architecture.IMSProject -import gropius.model.architecture.Project import gropius.model.issue.Issue import gropius.model.issue.timeline.* import gropius.sync.* @@ -13,7 +12,6 @@ import gropius.sync.github.generated.fragment.AssignedEventTimelineItemData.Assi import gropius.sync.github.generated.fragment.TimelineItemData.Companion.asNode import gropius.sync.github.generated.fragment.UnassignedEventTimelineItemData.Assignee.Companion.userData import jakarta.transaction.Transactional -import kotlinx.coroutines.reactive.awaitFirst import kotlinx.coroutines.reactor.awaitSingle import org.bson.types.ObjectId import org.springframework.data.annotation.Id @@ -536,18 +534,19 @@ class UnassignedTimelineItem( val convInfo = timelineItemConversionInformation ?: TODOTimelineItemConversionInformation(imsProject.rawId!!, githubId); val githubService = service as GithubDataService - // TODO if ((createdBy != null)) { val gropiusId = convInfo.gropiusId val event = if (gropiusId != null) githubService.neoOperations.findById( gropiusId ) else RemovedAssignmentEvent(createdAt, createdAt) - if (event == null) { + val opposite = issue.timelineItems().filterIsInstance().sortedBy { it.createdAt } + .lastOrNull { it.user().value.username == user } + if ((event == null) || (opposite == null)) { return listOf() to convInfo; } event.createdBy().value = githubService.mapUser(imsProject, createdBy) event.lastModifiedBy().value = githubService.mapUser(imsProject, createdBy) - event.removedAssignment().value = TODO() + event.removedAssignment().value = opposite return listOf(event) to convInfo; } return listOf() to convInfo; diff --git a/sync-jira/src/main/kotlin/gropius/sync/jira/JiraDataService.kt b/sync-jira/src/main/kotlin/gropius/sync/jira/JiraDataService.kt index e701ddab..4d9d5aad 100644 --- a/sync-jira/src/main/kotlin/gropius/sync/jira/JiraDataService.kt +++ b/sync-jira/src/main/kotlin/gropius/sync/jira/JiraDataService.kt @@ -3,6 +3,7 @@ package gropius.sync.jira import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.ObjectMapper import gropius.model.architecture.IMSProject +import gropius.model.issue.Issue import gropius.model.issue.Label import gropius.model.template.* import gropius.model.user.GropiusUser @@ -135,14 +136,28 @@ class JiraDataService( /** * Get the default issue state + * @param imsProject the ims project to work on + * @param issue the issue to work on (sometimes not yet saved or complete) * @param isOpen whether the issue state is open or closed * @return the default issue state */ - suspend fun issueState(imsProject: IMSProject, isOpen: Boolean): IssueState { + suspend fun issueState(imsProject: IMSProject, issue: Issue?, isOpen: Boolean): IssueState { val newIssueState = IssueState(if (isOpen) "open" else "closed", "", isOpen) newIssueState.partOf() += issueTemplate(imsProject) - return neoOperations.findAll(IssueState::class.java).filter { it.isOpen == isOpen }.awaitFirstOrNull() - ?: neoOperations.save(newIssueState).awaitSingle() + return (issue?.template?.invoke()?.value ?: issueTemplate(imsProject)).issueStates() + .firstOrNull { it.isOpen == isOpen } ?: neoOperations.save(newIssueState).awaitSingle() + } + + /** + * Get the named issue state + * @param imsProject the ims project to work on + * @param issue the issue to work on (sometimes not yet saved or complete) + * @param isOpen whether the issue state is open or closed + * @return the default issue state + */ + suspend fun issueState(imsProject: IMSProject, issue: Issue?, name: String): IssueState? { + return (issue?.template?.invoke()?.value ?: issueTemplate(imsProject)).issueStates() + .firstOrNull { it.name == name } } /** diff --git a/sync-jira/src/main/kotlin/gropius/sync/jira/JiraSync.kt b/sync-jira/src/main/kotlin/gropius/sync/jira/JiraSync.kt index a1e51905..1229acf5 100644 --- a/sync-jira/src/main/kotlin/gropius/sync/jira/JiraSync.kt +++ b/sync-jira/src/main/kotlin/gropius/sync/jira/JiraSync.kt @@ -100,8 +100,8 @@ final class JiraSync( for (imsProject in imsProjects) { jiraDataService.issueTemplate(imsProject) jiraDataService.issueType(imsProject) - jiraDataService.issueState(imsProject, true) - jiraDataService.issueState(imsProject, false) + jiraDataService.issueState(imsProject, null, true) + jiraDataService.issueState(imsProject, null, false) } for (imsProject in imsProjects) { diff --git a/sync-jira/src/main/kotlin/gropius/sync/jira/model/IssueData.kt b/sync-jira/src/main/kotlin/gropius/sync/jira/model/IssueData.kt index 9feb4e84..609898af 100644 --- a/sync-jira/src/main/kotlin/gropius/sync/jira/model/IssueData.kt +++ b/sync-jira/src/main/kotlin/gropius/sync/jira/model/IssueData.kt @@ -67,7 +67,11 @@ class JiraTimelineItem(val id: String, val created: String, val author: JsonObje return gropiusSummary(timelineItemConversionInformation, imsProject, service, jiraService) } else if (fieldId == "resolution") { return gropiusState( - timelineItemConversionInformation, imsProject, service, jiraService + timelineItemConversionInformation, imsProject, service, jiraService, issue + ) + } else if (fieldId == "state") { + return gropiusNamedState( + timelineItemConversionInformation, imsProject, service, jiraService, issue ) } else if (fieldId == "labels") { return gropiusLabels( @@ -122,9 +126,7 @@ class JiraTimelineItem(val id: String, val created: String, val author: JsonObje ) templateEvent.createdBy().value = jiraService.mapUser(imsProject, author) templateEvent.lastModifiedBy().value = jiraService.mapUser(imsProject, author) - return listOf( - templateEvent - ) to convInfo; + return listOf(templateEvent) to convInfo; } /** @@ -179,9 +181,7 @@ class JiraTimelineItem(val id: String, val created: String, val author: JsonObje removedLabelEvent.createdBy().value = jiraService.mapUser(imsProject, author) removedLabelEvent.lastModifiedBy().value = jiraService.mapUser(imsProject, author) removedLabelEvent.removedLabel().value = jiraService.mapLabel(imsProject, removedLabel) - return listOf( - removedLabelEvent - ) to convInfo; + return listOf(removedLabelEvent) to convInfo; } return listOf() to convInfo; } @@ -192,13 +192,15 @@ class JiraTimelineItem(val id: String, val created: String, val author: JsonObje * @param imsProject the ims project * @param service the service * @param jiraService the jira service + * @param issue the issue to work on (sometimes not yet saved or complete) * @return the pair of timeline items and conversion information */ private suspend fun gropiusState( timelineItemConversionInformation: TimelineItemConversionInformation?, imsProject: IMSProject, service: JiraDataService, - jiraService: JiraDataService + jiraService: JiraDataService, + issue: Issue ): Pair, TimelineItemConversionInformation> { val convInfo = timelineItemConversionInformation ?: JiraTimelineItemConversionInformation(imsProject.rawId!!, id); @@ -209,17 +211,54 @@ class JiraTimelineItem(val id: String, val created: String, val author: JsonObje ) else null) ?: StateChangedEvent( OffsetDateTime.parse( created, IssueData.formatter - ), OffsetDateTime.parse( + ).minusNanos(1), OffsetDateTime.parse( created, IssueData.formatter - ) + ).minusNanos(1) ) titleChangedEvent.createdBy().value = jiraService.mapUser(imsProject, author) titleChangedEvent.lastModifiedBy().value = jiraService.mapUser(imsProject, author) - titleChangedEvent.oldState().value = jiraService.issueState(imsProject, data.fromString == null) - titleChangedEvent.newState().value = jiraService.issueState(imsProject, data.toString == null) - return listOf( - titleChangedEvent - ) to convInfo; + titleChangedEvent.oldState().value = jiraService.issueState(imsProject, issue, data.fromString == null) + titleChangedEvent.newState().value = jiraService.issueState(imsProject, issue, data.toString == null) + return listOf(titleChangedEvent) to convInfo; + } + + /** + * Convert a single state change to a Gropius StateChangedEvent + * @param timelineItemConversionInformation the timeline item conversion information + * @param imsProject the ims project + * @param service the service + * @param jiraService the jira service + * @param issue the issue to work on (sometimes not yet saved or complete) + * @return the pair of timeline items and conversion information + */ + private suspend fun gropiusNamedState( + timelineItemConversionInformation: TimelineItemConversionInformation?, + imsProject: IMSProject, + service: JiraDataService, + jiraService: JiraDataService, + issue: Issue + ): Pair, TimelineItemConversionInformation> { + val newState = jiraService.issueState(imsProject, issue, data.toString!!) + ?: return listOf() to JiraTimelineItemConversionInformation(imsProject.rawId!!, id) + val convInfo = + timelineItemConversionInformation ?: JiraTimelineItemConversionInformation(imsProject.rawId!!, id); + val timelineId = timelineItemConversionInformation?.gropiusId + val stateChangedEvent: StateChangedEvent = + (if (timelineId != null) service.neoOperations.findById( + timelineId + ) else null) ?: StateChangedEvent( + OffsetDateTime.parse( + created, IssueData.formatter + ), OffsetDateTime.parse( + created, IssueData.formatter + ) + ) + stateChangedEvent.createdBy().value = jiraService.mapUser(imsProject, author) + stateChangedEvent.lastModifiedBy().value = jiraService.mapUser(imsProject, author) + stateChangedEvent.oldState().value = + jiraService.issueState(imsProject, issue, data.fromString!!) ?: issue.state().value + stateChangedEvent.newState().value = newState + return listOf(stateChangedEvent) to convInfo; } /** @@ -251,9 +290,7 @@ class JiraTimelineItem(val id: String, val created: String, val author: JsonObje ) titleChangedEvent.createdBy().value = jiraService.mapUser(imsProject, author) titleChangedEvent.lastModifiedBy().value = jiraService.mapUser(imsProject, author) - return listOf( - titleChangedEvent - ) to convInfo; + return listOf(titleChangedEvent) to convInfo; } } @@ -394,7 +431,7 @@ data class IssueData( issue.createdBy().value = jiraService.mapUser(imsProject, fields["creator"]!!) issue.lastModifiedBy().value = jiraService.mapUser(imsProject, fields["creator"]!!) issue.body().value.issue().value = issue - issue.state().value = jiraService.issueState(imsProject, true) + issue.state().value = jiraService.issueState(imsProject, null, true) issue.template().value = jiraService.issueTemplate(imsProject) issue.trackables() += jiraService.neoOperations.findAll(Project::class.java).awaitFirst() issue.type().value = jiraService.issueType(imsProject) diff --git a/sync/src/main/kotlin/gropius/sync/AbstractSync.kt b/sync/src/main/kotlin/gropius/sync/AbstractSync.kt index 2847c651..42d5f7e5 100644 --- a/sync/src/main/kotlin/gropius/sync/AbstractSync.kt +++ b/sync/src/main/kotlin/gropius/sync/AbstractSync.kt @@ -423,16 +423,20 @@ abstract class AbstractSync( mappedStates: List ) { val labelStateMap = this.labelStateMap(imsProject) - timelineItems.remove(timelineItem) - val newStateChange = StateChangedEvent( - timelineItem.createdAt, timelineItem.lastModifiedAt - ) - newStateChange.oldState().value = lastState - newStateChange.newState().value = mappedStates.firstOrNull() ?: lastState//TODO("Restore State out of nothing") - newStateChange.createdBy().value = timelineItem.createdBy().value - newStateChange.lastModifiedBy().value = timelineItem.lastModifiedBy().value - timelineItems.add(newStateChange) - issue.timelineItems().add(newStateChange) + val mappedState = lookupState(labelStateMap[timelineItem.removedLabel().value?.name]) + if ((mappedState != null) && !mappedStates.contains(mappedState)) { + timelineItems.remove(timelineItem) + val newStateChange = StateChangedEvent( + timelineItem.createdAt, timelineItem.lastModifiedAt + ) + newStateChange.oldState().value = lastState + newStateChange.newState().value = + mappedStates.firstOrNull() ?: lastState//TODO("Restore State out of nothing") + newStateChange.createdBy().value = timelineItem.createdBy().value + newStateChange.lastModifiedBy().value = timelineItem.lastModifiedBy().value + timelineItems.add(newStateChange) + issue.timelineItems().add(newStateChange) + } } /**