Uvod

Ciljna grupa: Srednji Gradle programeri koji žele da upravljaju složenim konfiguracijama.

U razvoju Gradle pluginova, upravljanje složenim konfiguracijama je čežnji zadatak. Umesto da preopterećite DSL stotinama osobina, često je čistije i lakše održivati da se konfiguracija definiše u vanjskom fajlu, kao što je YAML. Ovaj članak vas vodí kroz integraciju moćne Jackson biblioteke za parsiranje YAML fajlova u Kotlin objekte, primenjujući strogu TDD pristup kako bi se osigurala jakost našeg plugina.site-baker.

1. Контекст: Наш плагин `site-baker

Naš plugin`site-baker`je namenjen da automatizuje generisanje i implementaciju statičkog sajta. Treba da pročita datoteku konfiguracije YAML (managed-jbake-context.yml) za dobijanje informacija kao što su putanje izvora, odredišta deploy-a i Git identifikatori.

YAML konfiguracija koju želimo da parsiramo glasi ovako:

bake:
  srcPath: "./site/jbake"
  destDirPath: "bake"
  cname: "cheroliv.com"
pushPage:
  from: "bake"
  to: "cvs"
  repo:
    name: "trainings"
    repository: "https://github.com/pages-content/pages-content.github.io.git"
    credentials:
      username: "USERNAME"
      password: "SECRET_TOKEN"
  branch: "main"
  message: "cheroliv.com"
# ... autres configurations (pushMaquette, supabase, etc.)

2. Modelovanje Konfiguracije u Kotlinu

Пре него што парсирамо, морамо да дефинишемо структуру наших података у Kotlin. Jackson ће ови класе користити за аутоматичко мапирање садржаја YAML.

// plugin/src/main/kotlin/com/cheroliv/site/baker/data/SiteConfiguration.kt
package com.cheroliv.site.baker.data

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
import com.fasterxml.jackson.module.kotlin.readValue
import com.fasterxml.jackson.module.kotlin.registerKotlinModule

fun parseSiteConfiguration(yaml: String): SiteConfiguration {
    val mapper = ObjectMapper(YAMLFactory()).registerKotlinModule()
    return mapper.readValue(yaml)
}

data class GitPushConfiguration(
    val from: String = "",
    val to: String = "",
    val repo: RepositoryConfiguration = RepositoryConfiguration(),
    val branch: String = "",
    val message: String = "",
)

data class RepositoryConfiguration(
    val name: String = "",
    val repository: String = "",
    val credentials: RepositoryCredentials = RepositoryCredentials(),
) {
    companion object {
        const val ORIGIN = "origin"
        const val CNAME = "CNAME"
        const val REMOTE = "remote"
    }
}

data class RepositoryCredentials(val username: String = "", val password: String = "")

data class SiteConfiguration(
    val bake: BakeConfiguration = BakeConfiguration(),
    val pushPage: GitPushConfiguration = GitPushConfiguration(),
    val pushMaquette: GitPushConfiguration = GitPushConfiguration(),
    val pushSource: GitPushConfiguration? = null,
    val pushTemplate: GitPushConfiguration? = null,
    val supabase: SupabaseContactFormConfig? = null
)

data class BakeConfiguration(
    val srcPath: String = "",
    val destDirPath: String = "",
    val cname: String? = null,
)

// ... (autres data classes pour Supabase, si nécessaire)

Funkcija`parseSiteConfiguration`je naša tačka ulaza za deserijalizaciju. Ona koristi`ObjectMapper`de Jackson, konfigurisan sa`YAMLFactory`за YAML format i`registerKotlinModule()`за подршку специфичности Kotlin-а (kao što su podrazumevane vrednosti svojstava)

3. Интеграција Jackson-a у `build.gradle.kts

Da bismo koristili Jackson, moramo da dodamo neophodne zavisnosti u`build.gradle.kts`našeg modula`plugin`:

// plugin/build.gradle.kts
dependencies {
    // Jackson for YAML parsing
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.18.3")
    implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.18.3")

    // ... autres dépendances
}

Koristimo`jackson-module-kotlin`за подршку Kotlin и`jackson-dataformat-yaml`за управљање форматом YAML.

4. TDD pristup: Testiranje YAML parsiranja

Sada primenjujemo TDD da bismo validirali našu logiku parsiranja.

4.1. Jedinični test : Mapirati YAML u Kotlin objekat

Počinjemo sa jediničnim testom u`SiteBakerPluginTest.kt`за проверу да функција`parseSiteConfiguration`може правилно претворити YAML низ у објекат`SiteConfiguration`.

// plugin/src/test/kotlin/com/cheroliv/site/baker/SiteBakerPluginTest.kt
@Test
fun `can map configuration text to SiteConfiguration object`() {
    val yamlString = "../../managed-jbake-context.yml"
        .run(::File)
        .readText()
        .trimIndent()

    val config: SiteConfiguration = parseSiteConfiguration(yamlString)

    assertEquals("cheroliv.com", config.bake.cname)
    assertEquals("main", config.pushPage.branch)
    assertEquals("https://github.com/pages-content/pages-content.github.io.git", config.pushPage.repo.repository)
}

Ovaj test čita sadržaj fajla`managed-jbake-context.yml`(koji mora da postoji da bi test bio važeći) i izvršava tvrdnje na objektu`SiteConfiguration`резултант. Он проверава да ли кључне вредности YAML су правилно пресликане.

4.2. Funkcionišni test : Provera čitanja konfiguracionog fajla

Da bismo se uverili da plugin može da pročita fajl konfiguracije kroz svoj DSL i da parsiranje radi u stvarnom Gradle okruženju, dodajemo funkcionalni test u`SiteBakerPluginFunctionalTest.kt`.

Оvaj тест је веома битан јер симулира извршавање додатка у stvarном пројекту. Мора да будеhermetički, to jest da mora sam da kreira fajl`managed-jbake-context.yml`sa kontrolisanim sadržajem, da bi se osigurala ponovljivost i izolacija testa

// plugin/src/functionalTest/kotlin/com/cheroliv/site/baker/SiteBakerPluginFunctionalTest.kt
@Test
fun `config file contains good data`(){
    val configContent = configFile.readText(UTF_8)
    // Vérifications comme dans vos commentaires
    assertTrue(configContent.contains("bake"))
    assertTrue(configContent.contains("pushPage"))
    assertTrue(configContent.contains("repository"))
}

Ovaj funkcionalni test proverava da li fajl konfiguracije kopiran u privremeni direktorijum testa sadrži ocekivane podatke. Iako ovaj test ne parsira direktno YAML u Kotlin objekat, validira prisustvo fajla i njegovog sadržaja, što je neophodan prethodan korak za parsiranje od strane plugin‑a.

5. Vizualizacija toka parsiranja

Sljedeći dijagram ilustruje tok podataka i interakcije tokom parsiranja YAML konfiguracije:

@startuml
actor "Корисник Gradle" as User
participant "build.gradle.kts" as BuildScript
participant "SiteBakerPlugin" as Plugin
participant "Proširenje sajta" as Extension
participant "managed-bake-context.yml" as ConfigFile
participant "parsirajKonfiguracijuSajta()" as Parser
participant "SiteConfiguration (Kotlin objekat)" as KotlinObject

User -> BuildScript : Applique le plugin et configure le DSL
BuildScript -> Plugin : apply()
Plugin -> Extension : Crée et enregistre l'extension 'site'
User -> BuildScript : site { configPath = "..." }
BuildScript -> Extension : Définit configPath

Plugin -> ConfigFile : Lit le fichier (via configPath)
ConfigFile --> Plugin : Contenu YAML
Plugin -> Parser : Appelle parseSiteConfiguration(yamlString)
Parser -> KotlinObject : Mappe le YAML en objet SiteConfiguration
KotlinObject --> Plugin : Retourne l'objet configuré

@enduml

zaključak

Sledeći TDD pristup, uspešno smo integrirali biblioteku Jackson za parsiranje YAML konfiguracionih fajlova u Kotlin objekte u našem Gradle plug-inu. Ova nam metoda je omogućila:

  • Modelovati jasnonaša konfiguracija sa Kotlin data klasama.

  • Потврдити логику парсингаsa ciljnim jediničnim testovima.

  • Osigurati integracijuu realnom Gradle okruženju zahvaljujući hermetičkim funkcionalnim testovima

Ova jaka baza nam daje veliko pouzdanje da proširimo naš plugin sa funkcionalnostima koje se baziraju na ovoj konfiguraciji, uz osiguravanje održivosti i jakosti koda. Parsiranje YAML sada je pouzdana i testirana funkcionalnost našeg plugina.site-baker.

Повезани чланци