Skip to content

Commit

Permalink
添加版本控制 Service
Browse files Browse the repository at this point in the history
  • Loading branch information
JensenQi committed Dec 14, 2021
1 parent ecc3886 commit f9d637a
Show file tree
Hide file tree
Showing 7 changed files with 466 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
/* * 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
*
* http://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 tech.cuda.woden.common.service

import me.liuwj.ktorm.database.Database
import me.liuwj.ktorm.dsl.*
import me.liuwj.ktorm.global.addEntity
import me.liuwj.ktorm.global.global
import me.liuwj.ktorm.global.select
import me.liuwj.ktorm.global.update
import tech.cuda.woden.common.service.dao.CommitmentDAO
import tech.cuda.woden.common.service.dao.WorkingTreeDAO
import tech.cuda.woden.common.service.dto.CommitmentDTO
import tech.cuda.woden.common.service.dto.WorkingTreeDTO
import tech.cuda.woden.common.service.dto.toCommitmentDTO
import tech.cuda.woden.common.service.dto.toWorkingTreeDTO
import tech.cuda.woden.common.service.exception.NotFoundException
import tech.cuda.woden.common.service.po.CommitmentPO
import tech.cuda.woden.common.service.po.WorkingTreePO
import java.time.LocalDateTime

/**
* @author Jensen Qi <[email protected]>
* @since 0.1.0
*/
object VersionControlService {

private fun requireDirectory(node: WorkingTreePO) = require(node.name.split(".").size == 1) {
"Tree Node ${node.name} must be a directory"
}


private fun requireNonDirectory(node: WorkingTreePO) = require(node.name.split(".").size > 1) {
"Tree Node ${node.name} can not be a directory"
}

internal fun selectWorkingTreeById(nodeId: Int) = WorkingTreeDAO.select()
.where { (WorkingTreeDAO.isRemove eq false) and (WorkingTreeDAO.id eq nodeId) }
.map { WorkingTreeDAO.createEntity(it) }
.firstOrNull()


internal fun selectCommitmentById(commitId: Int) = CommitmentDAO.select()
.where { (CommitmentDAO.isRemove eq false) and (CommitmentDAO.id eq commitId) }
.map { CommitmentDAO.createEntity(it) }
.firstOrNull()

/**
* 创建一个 workingTree 的根节点并返回
*/
fun createWorkingTree(): WorkingTreeDTO {
Database.global.useTransaction {
val now = LocalDateTime.now()
val workingTree = WorkingTreePO {
this.name = "root"
this.parentId = null
this.stage = null
this.commitId = null
this.isRemove = false
this.createTime = now
this.updateTime = now
}.also { WorkingTreeDAO.addEntity(it) }
return workingTree.toWorkingTreeDTO()
}
}

/**
* 创建节点名称为[name],父节点为[parentId]的 Working Tree 节点
* 如果[parentId]对应的节点不存在或已被删除,则抛出[NotFoundException]
* 如果[parentId]不为目录节点,则抛出[IllegalArgumentException]
*/
fun createWorkingNode(name: String, parentId: Int): WorkingTreeDTO = Database.global.useTransaction {
val parent = selectWorkingTreeById(parentId) ?: throw NotFoundException()
requireDirectory(parent)
val now = LocalDateTime.now()
val workingTree = WorkingTreePO {
this.name = name
this.parentId = parent.id
this.stage = null
this.commitId = null
this.isRemove = false
this.createTime = now
this.updateTime = now
}.also { WorkingTreeDAO.addEntity(it) }
return workingTree.toWorkingTreeDTO()
}

/**
* 查找 working Tree 中节点 ID 为 [nodeId] 的发布历史
* 如果[nodeId]节点不存在或已被删除,则抛出[NotFoundException]
* 如果[nodeId]节点为目录节点,则抛出[IllegalArgumentException]
*/
fun listingCommitHistory(nodeId: Int): List<CommitmentDTO> {
val node = selectWorkingTreeById(nodeId) ?: throw NotFoundException()
requireNonDirectory(node)
return CommitmentDAO.select()
.where { (CommitmentDAO.nodeId eq nodeId) and (CommitmentDAO.isRemove eq false) }
.orderBy(CommitmentDAO.createTime.desc())
.map { CommitmentDAO.createEntity(it).toCommitmentDTO() }
}

/**
* 列出 WorkingTree 中节点 ID 为 [nodeId] 的所有子节点
* 如果[nodeId]节点不存在或已被删除,则抛出[NotFoundException]
* 如果[nodeId]节点不为目录节点,则抛出[IllegalArgumentException]
*/
fun listingChildren(nodeId: Int): List<WorkingTreeDTO> {
val node = selectWorkingTreeById(nodeId) ?: throw NotFoundException()
requireDirectory(node)
return WorkingTreeDAO.select()
.where { (WorkingTreeDAO.isRemove eq false) and (WorkingTreeDAO.parentId eq node.id) }
.map { WorkingTreeDAO.createEntity(it).toWorkingTreeDTO() }
}


/**
* 删除 workingTree 中的节点 ID 为 [nodeId] 的节点
* 如果[nodeId]节点不存在或已被删除,则抛出[NotFoundException]
* 如果[nodeId]节点为根节点,则抛出[IllegalArgumentException]
*/
fun remove(nodeId: Int) = Database.global.useTransaction {
val node = selectWorkingTreeById(nodeId) ?: throw NotFoundException()
require(node.parentId != null) { "can not remove root dir /" }
node.isRemove = true
node.updateTime = LocalDateTime.now()
node.flushChanges()
}

/**
* 获取节点 ID 为 [nodeId] 的当前 commit
* 如果[nodeId]节点不存在或已被删除,则抛出[NotFoundException]
* 如果[nodeId]节点为目录节点,则抛出[IllegalArgumentException]
* 如果[nodeId]不存在已提交的 commit,则抛出[IllegalArgumentException]
*/
fun getCurrentCommit(nodeId: Int): CommitmentDTO {
val node = selectWorkingTreeById(nodeId) ?: throw NotFoundException()
requireNonDirectory(node)
require(node.commitId != null) { "current has no committed" }
val commitment = selectCommitmentById(node.commitId!!) ?: throw NotFoundException()
return commitment.toCommitmentDTO()
}

/**
* 将 [content] 写入 ID 为 [nodeId] 的节点缓存区
* 如果[nodeId]节点不存在或已被删除,则抛出[NotFoundException]
* 如果[nodeId]节点为目录节点,则抛出[IllegalArgumentException]
*/
fun writeStage(nodeId: Int, content: String) = Database.global.useTransaction {
val node = selectWorkingTreeById(nodeId) ?: throw NotFoundException()
requireNonDirectory(node)
node.stage = content
node.updateTime = LocalDateTime.now()
node.flushChanges()
}

/**
* 发布 ID 为 [nodeId] 的节点中的缓存区
* 如果[nodeId]节点不存在或已被删除,则抛出[NotFoundException]
* 如果[nodeId]为目录节点,则抛出[IllegalArgumentException]
* 如果[nodeId]的缓存区为 null,则抛出[IllegalArgumentException]
*/
fun commit(nodeId: Int, message: String): CommitmentDTO = Database.global.useTransaction {
val node = selectWorkingTreeById(nodeId) ?: throw NotFoundException()
requireNonDirectory(node)
require(node.stage != null) { "Stage of tree node ${node.name} is null" }
val now = LocalDateTime.now()
val commitment = CommitmentPO {
this.nodeId = node.id
this.content = node.stage!!
this.message = message
this.isRemove = false
this.createTime = now
this.updateTime = now
}.also { CommitmentDAO.addEntity(it) }
WorkingTreeDAO.update {
set(it.commitId, commitment.id)
set(it.updateTime, now)
where { it.id eq node.id }
}
return commitment.toCommitmentDTO()
}

/**
* 将 ID 为 [nodeId] 的节点回滚为 ID 为 [commitID] 的 commitment
* 如果[nodeId]节点或已被删除,则抛出[NotFoundException]
* 如果[commitID]提交不存在或已被删除,则抛出[NotFoundException]
* 如果[nodeId]为目录节点,则抛出[IllegalArgumentException]
* 如果[commitID]不归属与 [nodeId],则抛出[IllegalStateException]
*/
fun rollback(nodeId: Int, commitID: Int) = Database.global.useTransaction {
val node = selectWorkingTreeById(nodeId) ?: throw NotFoundException()
val commitment = selectCommitmentById(commitID) ?: throw NotFoundException()
requireNonDirectory(node)
check(commitment.nodeId == node.id) { "commitment ${commitment.id} do not belong to node ${node.id}" }
node.stage = commitment.content
node.commitId = commitment.id
node.updateTime = LocalDateTime.now()
node.flushChanges()
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* 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
*
* http://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 tech.cuda.woden.common.service.dao

import me.liuwj.ktorm.schema.*
import tech.cuda.woden.annotation.mysql.*
import tech.cuda.woden.common.service.mysql.type.longtext
import tech.cuda.woden.common.service.po.CommitmentPO

/**
* @author Jensen Qi <[email protected]>
* @since 0.1.0
*/
@STORE_IN_MYSQL
internal object CommitmentDAO : Table<CommitmentPO>("commitment") {
@BIGINT
@UNSIGNED
@AUTO_INCREMENT
@PRIMARY_KEY
@COMMENT("commit ID")
val id = int("id").primaryKey().bindTo { it.id }

@BIGINT
@UNSIGNED
@COMMENT("归属的 Working Tree 节点 ID")
val nodeId = int("node_id").bindTo { it.nodeId }

@LONGTEXT
@COMMENT("commit 内容")
val content = longtext("content").bindTo { it.content }

@VARCHAR(256)
@COMMENT("commit 注释")
val message = varchar("message").bindTo { it.message }

@BOOL
@COMMENT("逻辑删除")
val isRemove = boolean("is_remove").bindTo { it.isRemove }

@DATETIME
@COMMENT("创建时间")
val createTime = datetime("create_time").bindTo { it.createTime }

@DATETIME
@COMMENT("更新时间")
val updateTime = datetime("update_time").bindTo { it.updateTime }

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* 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
*
* http://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 tech.cuda.woden.common.service.dao

import me.liuwj.ktorm.schema.*
import tech.cuda.woden.annotation.mysql.*
import tech.cuda.woden.common.service.mysql.type.longtext
import tech.cuda.woden.common.service.po.WorkingTreePO

/**
* @author Jensen Qi <[email protected]>
* @since 0.1.0
*/
@STORE_IN_MYSQL
internal object WorkingTreeDAO : Table<WorkingTreePO>("working_tree") {
@BIGINT
@UNSIGNED
@AUTO_INCREMENT
@PRIMARY_KEY
@COMMENT("Working Tree 节点 ID")
val id = int("id").primaryKey().bindTo { it.id }


@VARCHAR(256)
@COMMENT("节点名,有后缀的为叶子节点,无后缀的为非叶子节点(即目录节点)")
val name = varchar("name").bindTo { it.name }

@BIGINT
@UNSIGNED
@COMMENT("父节点ID")
val parentId = int("parent_id").bindTo { it.parentId }

@LONGTEXT
@COMMENT("当前 stage 区内容")
val stage = longtext("stage").bindTo { it.stage }

@BIGINT
@UNSIGNED
@COMMENT("当天已发布的 commit ID")
val commitId = int("commit_id").bindTo { it.commitId }

@BOOL
@COMMENT("逻辑删除")
val isRemove = boolean("is_remove").bindTo { it.isRemove }

@DATETIME
@COMMENT("创建时间")
val createTime = datetime("create_time").bindTo { it.createTime }

@DATETIME
@COMMENT("更新时间")
val updateTime = datetime("update_time").bindTo { it.updateTime }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* 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
*
* http://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 tech.cuda.woden.common.service.dto

import tech.cuda.woden.annotation.pojo.DTO
import tech.cuda.woden.common.service.po.CommitmentPO
import tech.cuda.woden.common.service.po.ContainerPO
import java.time.LocalDateTime

/**
* @author Jensen Qi <[email protected]>
* @since 0.1.0
*/
@DTO(CommitmentPO::class)
data class CommitmentDTO(
val id: Int,
val nodeId: Int,
val content: String,
val message: String,
val createTime: LocalDateTime,
val updateTime: LocalDateTime
)
Loading

0 comments on commit f9d637a

Please sign in to comment.