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

Fixed absence of detection for duplicate titles #134

Merged
merged 1 commit into from
Dec 30, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased

### Fixed

- Detection of duplicate titles in scanned files (#131)

### Changed

- dependency updates:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import com.github.ajalt.clikt.parameters.options.RawOption
import com.github.ajalt.clikt.parameters.options.nullableFlag
import com.github.ajalt.mordant.terminal.ConfirmationPrompt
import com.github.ajalt.mordant.terminal.StringPrompt
import com.github.zeldigas.text2confl.convert.ConversionException
import com.github.zeldigas.text2confl.convert.ConversionFailedException
import com.github.zeldigas.text2confl.convert.FileDoesNotExistException
import com.github.zeldigas.text2confl.core.ContentValidationFailedException
import com.github.zeldigas.text2confl.core.upload.ContentUploadException
import com.github.zeldigas.text2confl.core.upload.InvalidTenantException
Expand All @@ -33,7 +33,7 @@ fun RawOption.optionalFlag(vararg secondaryNames: String): NullableOption<Boolea
fun tryHandleException(ex: Exception): Nothing {
when (ex) {
is InvalidTenantException -> error(ex.message!!)
is FileDoesNotExistException -> error(ex.message!!)
is ConversionException -> error(ex.message!!)
is ContentUploadException -> error(ex.message!!)
is ConversionFailedException -> {
val reason = buildString {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@ import com.github.zeldigas.text2confl.convert.markdown.MarkdownFileConverter
import java.io.File
import java.nio.file.Files
import java.nio.file.Path
import kotlin.io.path.exists
import kotlin.io.path.extension
import kotlin.io.path.listDirectoryEntries
import kotlin.io.path.nameWithoutExtension
import kotlin.io.path.*

interface Converter {

Expand All @@ -22,7 +19,10 @@ interface Converter {

}

class FileDoesNotExistException(val file: Path) : RuntimeException("File does not exist: $file")
open class ConversionException(message: String) : RuntimeException(message)

class FileDoesNotExistException(val file: Path) : ConversionException("File does not exist: $file")
class DuplicateTitlesException(val duplicates: List<String>, message: String) : ConversionException(message)

const val DEFAULT_AUTOGEN_BANNER =
"Edit <a href=\"__doc-root____file__\">source file</a> instead of changing page in Confluence. " +
Expand Down Expand Up @@ -77,12 +77,32 @@ internal class UniversalConverter(
override fun convertDir(dir: Path): List<Page> {
val documents = scanDocuments(dir)

checkForDuplicates(dir, documents)

return convertFilesInDirectory(
dir,
ConvertingContext(ReferenceProvider.fromDocuments(dir, documents), conversionParameters, space)
)
}

private fun checkForDuplicates(base: Path, documents: Map<Path, PageHeader>) {
val duplicates = documents.entries
.groupBy { (_, v) -> v.title }
.entries.asSequence()
.filter { (_, v) -> v.size > 1 }
.map { (title, v) -> title to v.map { it.key.relativeTo(base) }.sorted() }
.map { (title, paths) -> "\"$title\": ${paths.joinToString(", ")}" }
.toList()
if (duplicates.isNotEmpty()) {
throw DuplicateTitlesException(
duplicates,
"Files with duplicate titles detected. Confluence has flat structure and every published page must have unique title.\n${
duplicates.joinToString("\n")
}"
)
}
}

private fun scanDocuments(dir: Path) =
dir.toFile().walk().filter { it.supported() }
.map {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ import io.mockk.junit5.MockKExtension
import io.mockk.mockkStatic
import io.mockk.verify
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.extension.ExtendWith
import org.junit.jupiter.api.io.TempDir
import java.io.File
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
Expand Down Expand Up @@ -84,6 +86,34 @@ internal class UniversalConverterTest(

@Test
internal fun `Directory conversion`(@TempDir dir: Path) {
createFileStructure(
dir,
"one.t",
"_two.t",
"three.t",
"three/foo.t",
"three/one.t",
"three/bar.md",
"four.md",
"five.adoc",
"another/hello.t",
"another/foo.t",
)
every { fileConverter.readHeader(any(), any()) } answers {
val file = it.invocation.args[0] as Path
PageHeader(file.fileName.toString(), emptyMap())
}
val exception = assertThrows<DuplicateTitlesException> { converter.convertDir(dir) }
assertThat(exception).all {
prop(DuplicateTitlesException::duplicates).hasSize(2)
messageContains("Files with duplicate titles detected. Confluence has flat structure and every published page must have unique title")
messageContains(""""one.t": one.t, three${File.separator}one.t""")
messageContains(""""foo.t": another${File.separator}foo.t, three${File.separator}foo.t""")
}
}

@Test
internal fun `Detection of documents with duplicate titles`(@TempDir dir: Path) {
createFileStructure(
dir,
"one.t",
Expand Down