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

Working on combined merge of most open PRs #150

Merged
merged 18 commits into from
Oct 18, 2024
Merged
Show file tree
Hide file tree
Changes from 17 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
Expand Up @@ -47,4 +47,10 @@ fragment IssueData on Issue {
}
query CurrentMetaData {
...MetaData
}
query FindUser($login: String!) {
...MetaData
user(login: $login) {
...UserData
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,32 @@ mutation MutateCreateIssue($id: ID!, $title: String!, $body: String!) {
...IssueDataExtensive
}
}
}
}
mutation MutateAssignUser($id: ID!, $user: ID!) {
addAssigneesToAssignable(input:{assignableId: $id, assigneeIds: [$user]}){
assignable {
...on Issue {
timelineItems(itemTypes: [ASSIGNED_EVENT], last: 1) {
nodes {
...TimelineItemData
}
}
}
}
clientMutationId
}
}
mutation MutateUnassignUser($id: ID!, $user: ID!) {
removeAssigneesFromAssignable(input:{assignableId: $id, assigneeIds: [$user]}){
assignable {
...on Issue {
timelineItems(itemTypes: [UNASSIGNED_EVENT], last: 1) {
nodes {
...TimelineItemData
}
}
}
}
clientMutationId
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import gropius.sync.github.config.IMSConfig
import gropius.sync.github.config.IMSProjectConfig
import gropius.sync.github.generated.fragment.LabelData
import gropius.sync.github.generated.fragment.UserData
import gropius.sync.github.generated.fragment.UserData.Companion.asNode
import gropius.sync.github.generated.fragment.UserData.Companion.asUser
import gropius.sync.model.LabelInfo
import gropius.sync.repository.LabelInfoRepository
Expand Down Expand Up @@ -115,6 +116,12 @@ class GithubDataService(
val databaseId = userData?.asUser()?.databaseId
val encodedAccountId =
jsonNodeMapper.jsonNodeToDeterministicString(objectMapper.valueToTree<JsonNode>(databaseId ?: 0))
val encodedUserId =
jsonNodeMapper.jsonNodeToDeterministicString(
objectMapper.valueToTree<JsonNode>(
userData?.asNode()?.id ?: ""
)
)
val username = userData?.login ?: FALLBACK_USER_NAME
val ims = neoOperations.findById<IMS>(imsProject.ims().value.rawId!!)!!
if (databaseId != null) {
Expand All @@ -133,7 +140,7 @@ class GithubDataService(
userData?.asUser()?.email,
null,
username,
mutableMapOf("github_id" to encodedAccountId)
mutableMapOf("github_id" to encodedAccountId, "github_node_id" to encodedUserId)
)
imsUser.ims().value = imsProject.ims().value
imsUser.template().value = imsUser.ims().value.template().value.imsUserTemplate().value
Expand Down
78 changes: 78 additions & 0 deletions sync-github/src/main/kotlin/gropius/sync/github/GithubSync.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,23 @@ package gropius.sync.github
import gropius.model.architecture.IMSProject
import gropius.model.issue.Issue
import gropius.model.issue.Label
import gropius.model.issue.timeline.Assignment
import gropius.model.issue.timeline.IssueComment
import gropius.model.issue.timeline.TimelineItem
import gropius.model.template.IMSTemplate
import gropius.model.template.IssueState
import gropius.model.user.GropiusUser
import gropius.model.user.IMSUser
import gropius.model.user.User
import gropius.sync.*
import gropius.sync.github.config.IMSConfigManager
import gropius.sync.github.config.IMSProjectConfig
import gropius.sync.github.generated.*
import gropius.sync.github.generated.MutateAddLabelMutation.Data.AddLabelsToLabelable.Labelable.Companion.asIssue
import gropius.sync.github.generated.MutateAssignUserMutation.Data.AddAssigneesToAssignable.Assignable.Companion.asIssue
import gropius.sync.github.generated.MutateCreateCommentMutation.Data.AddComment.CommentEdge.Node.Companion.asIssueTimelineItems
import gropius.sync.github.generated.MutateRemoveLabelMutation.Data.RemoveLabelsFromLabelable.Labelable.Companion.asIssue
import gropius.sync.github.generated.MutateUnassignUserMutation.Data.RemoveAssigneesFromAssignable.Assignable.Companion.asIssue
import gropius.sync.github.generated.fragment.TimelineItemData.Companion.asNode
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Component
Expand Down Expand Up @@ -80,6 +86,10 @@ final class GithubSync(
return imsProjectConfig.enableOutgoingComments
}

override suspend fun isOutgoingTemplatedFieldsEnabled(imsProject: IMSProject): Boolean {
return false
}

override suspend fun isOutgoingTitleChangedEnabled(imsProject: IMSProject): Boolean {
val imsProjectConfig = IMSProjectConfig(helper, imsProject)
return imsProjectConfig.enableOutgoingTitleChanges
Expand Down Expand Up @@ -172,6 +182,74 @@ final class GithubSync(
return null
}

override suspend fun syncSingleAssigned(
imsProject: IMSProject, issueId: String, assignment: Assignment, users: List<User>
): TimelineItemConversionInformation? {
val assignedUser = assignment.user().value
val imsUsers =
if (assignedUser as? IMSUser != null) listOf(assignedUser) else if (assignedUser as? GropiusUser != null) assignedUser.imsUsers()
.filter { it.ims().value == imsProject.ims().value } else emptyList()
val ids = imsUsers.map {
githubDataService.objectMapper.readTree(it.templatedFields["github_node_id"]!!).textValue()
}
if (ids.isEmpty()) {
return null
}
val response = githubDataService.mutation(
imsProject, users, MutateAssignUserMutation(issueId, ids.first()), gropiusUserList(users)
).second
val item =
response.data?.addAssigneesToAssignable?.assignable?.asIssue()?.timelineItems?.nodes?.lastOrNull()?.asNode()
if (item != null) {
return TODOTimelineItemConversionInformation(imsProject.rawId!!, item.id)
}
logger.error("${response.data} ${response.errors}")
//TODO("ERROR HANDLING")
return null
}

override suspend fun syncSingleUnassigned(
imsProject: IMSProject, issueId: String, assignment: Assignment, users: List<User>
): TimelineItemConversionInformation? {
val assignedUser = assignment.user().value
val imsUsers =
if (assignedUser as? IMSUser != null) listOf(assignedUser) else if (assignedUser as? GropiusUser != null) assignedUser.imsUsers()
.filter { it.ims().value == imsProject.ims().value } else emptyList()
val ids = imsUsers.map {
githubDataService.objectMapper.readTree(it.templatedFields["github_node_id"]!!).textValue()
}
if (ids.isEmpty()) {
return null
}
val response = githubDataService.mutation(
imsProject, users, MutateUnassignUserMutation(issueId, ids.first()), gropiusUserList(users)
).second
val item =
response.data?.removeAssigneesFromAssignable?.assignable?.asIssue()?.timelineItems?.nodes?.lastOrNull()
?.asNode()
if (item != null) {
return TODOTimelineItemConversionInformation(imsProject.rawId!!, item.id)
}
logger.error("${response.data} ${response.errors}")
//TODO("ERROR HANDLING")
return null
}

override suspend fun syncFallbackComment(
imsProject: IMSProject, issueId: String, comment: String, original: TimelineItem?, users: List<User>
): TimelineItemConversionInformation? {
val response = githubDataService.mutation(
imsProject, users, MutateCreateCommentMutation(issueId, comment), gropiusUserList(users)
).second
val item = response.data?.addComment?.commentEdge?.node?.asIssueTimelineItems()
if (item != null) {
return TODOTimelineItemConversionInformation(imsProject.rawId!!, item.id)
}
logger.error("${response.data} ${response.errors}")
//TODO("ERROR HANDLING")
return null
}

override suspend fun syncAddedLabel(
imsProject: IMSProject, issueId: String, label: Label, users: List<User>
): TimelineItemConversionInformation? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -540,7 +540,7 @@ class UnassignedTimelineItem(
gropiusId
) else RemovedAssignmentEvent(createdAt, createdAt)
val opposite = issue.timelineItems().filterIsInstance<Assignment>().sortedBy { it.createdAt }
.lastOrNull { it.user().value.username == user }
.lastOrNull { it.user().value.username == user } // TODO: Catch multiple
if ((event == null) || (opposite == null)) {
return listOf<TimelineItem>() to convInfo;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ class IMSConfigManager(
"github_id" to obj {
"nullable" to true
"type" to "int32"
}.toString(),
"github_node_id" to obj {
"nullable" to true
"type" to "string"
}.toString()
)
}
Expand Down
28 changes: 20 additions & 8 deletions sync-jira/src/main/kotlin/gropius/sync/jira/JiraDataService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import gropius.sync.JsonHelper
import gropius.sync.SyncDataService
import gropius.sync.jira.config.IMSConfig
import gropius.sync.jira.config.IMSProjectConfig
import gropius.sync.jira.model.IssueDataRepository
import gropius.util.JsonNodeMapper
import io.ktor.client.*
import io.ktor.client.call.*
Expand Down Expand Up @@ -46,6 +47,7 @@ import java.util.*
* @param objectMapper Reference for the spring instance of ObjectMapper
* @param jsonNodeMapper Reference for the spring instance of JsonNodeMapper
* @param gropiusUserRepository Reference for the spring instance of GropiusUserRepository
* @param issueDataRepository Reference for the spring instance of IssueDataRepository
*/
@Component
class JiraDataService(
Expand All @@ -55,7 +57,8 @@ class JiraDataService(
val helper: JsonHelper,
val objectMapper: ObjectMapper,
val jsonNodeMapper: JsonNodeMapper,
val gropiusUserRepository: GropiusUserRepository
val gropiusUserRepository: GropiusUserRepository,
val issueDataRepository: IssueDataRepository
) : SyncDataService {

companion object {
Expand Down Expand Up @@ -108,11 +111,16 @@ class JiraDataService(
/**
* Find and ensure the IMSIssueTemplate in the database
* @param imsProject The IMSProject to work with
* @param name name of the type to look for, if known
* @return the IssueType
*/
suspend fun issueType(imsProject: IMSProject): IssueType {
suspend fun issueType(imsProject: IMSProject, name: String): IssueType {
val template = issueTemplate(imsProject)
val imsProjectConfig = IMSProjectConfig(helper, imsProject)
val namedType = template.issueTypes().firstOrNull { it.name == name }
if (namedType != null) {
return namedType
}
if (imsProjectConfig.defaultType != null) {
val type = neoOperations.findById<IssueType>(imsProjectConfig.defaultType)
if ((type != null) && (type.partOf().contains(template))) {
Expand Down Expand Up @@ -269,8 +277,7 @@ class JiraDataService(
): Optional<HttpResponse> {
val imsConfig = IMSConfig(helper, imsProject.ims().value, imsProject.ims().value.template().value)
val cloudId =
token.cloudIds?.filter { URI(it.url + "/rest/api/2") == URI(imsConfig.rootUrl.toString()) }?.map { it.id }
?.firstOrNull()
token.cloudIds?.filter { URI(it.url) == URI(imsConfig.rootUrl.toString()) }?.map { it.id }?.firstOrNull()
if (token.type == "PAT") {
try {
val res = client.request(imsConfig.rootUrl.toString()) {
Expand All @@ -289,15 +296,15 @@ class JiraDataService(
setBody(body)
}
}
logger.info("Response Code for request with token token is ${res.status}(${res.status.isSuccess()}): $body is ${res.bodyAsText()}")
return if (res.status.isSuccess()) {
logger.debug("Response for {} {}", res.request.url, res.bodyAsText())
Optional.of(res)
} else {
logger.info("Response Code for request with token token is ${res.status}(${res.status.isSuccess()}): $body is ${res.bodyAsText()}")
Optional.empty()
}
} catch (e: ClientRequestException) {
e.printStackTrace()
logger.warn("Request failed with token $token", e)
return Optional.empty()
}
} else if (cloudId != null) {
Expand All @@ -319,18 +326,23 @@ class JiraDataService(
setBody(body)
}
}
logger.info("Response Code for request with token token is ${res.status}(${res.status.isSuccess()}): $body is ${res.bodyAsText()}")
return if (res.status.isSuccess()) {
logger.debug("Response for {} {}", res.request.url, res.bodyAsText())
Optional.of(res)
} else {
logger.info("Response Code for request with token token is ${res.status}(${res.status.isSuccess()}): $body is ${res.bodyAsText()}")
Optional.empty()
}
} catch (e: ClientRequestException) {
e.printStackTrace()
logger.warn("Request failed with token $token", e)
return Optional.empty()
}
} else {
logger.error("Invalid value for token type: ${token.type} and cloudId: ${token.cloudIds} with ${
token.cloudIds?.map {
URI(it.url) to URI(imsConfig.rootUrl.toString())
}
}")
return Optional.empty()
}
}
Expand Down
Loading