Merge pull request #9 from jlengrand/feat/invisible-jsoup

Feat/invisible jsoup

Fixes #8
This commit is contained in:
julien Lengrand-Lambert
2025-05-16 23:58:00 +02:00
committed by GitHub
13 changed files with 193 additions and 95 deletions

View File

@@ -17,7 +17,7 @@ jobs:
distribution: 'zulu'
java-version: 21
- name: Publish to MavenCentral
run: ./gradlew publishToMavenCentral --no-configuration-cache
run: ./gradlew publishAndReleaseToMavenCentral --no-configuration-cache
env:
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}

1
.idea/gradle.xml generated
View File

@@ -10,6 +10,7 @@
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/demo" />
<option value="$PROJECT_DIR$/demo-remote" />
<option value="$PROJECT_DIR$/opengraphkt" />
</set>
</option>

View File

@@ -1,5 +1,8 @@
# OpenGraphKt
![Maven Central Version](https://img.shields.io/maven-central/v/fr.lengrand/opengraphkt)
[OpenGraphKt](https://github.com/jlengrand/OpenGraphKt) is a minimalist Kotlin library to work with the [Open Graph tags](https://ogp.me/) protocol.
OpenGraphKt is a tiny wrapper on top of JSoup.

View File

@@ -0,0 +1,28 @@
plugins {
kotlin("jvm")
application
}
group = "fr.lengrand"
repositories {
mavenCentral()
}
dependencies {
implementation("org.jsoup:jsoup:1.20.1")
implementation("fr.lengrand:opengraphkt:0.0.1")
testImplementation(kotlin("test"))
}
tasks.test {
useJUnitPlatform()
}
kotlin {
jvmToolchain(23)
}
application {
mainClass = "fr.lengrand.opengraphkt.MainKt"
}

View File

@@ -0,0 +1,38 @@
package fr.lengrand.opengraphkt
import org.jsoup.Jsoup
/**
* This module is only here to verify that the latest Maven Central release can be imported and used as intended.
*/
fun main() {
val parser = OpenGraphParser()
val html = """
<!DOCTYPE html>
<html>
<head>
<title>Open Graph Example</title>
<meta property="og:title" content="The Rock" />
<meta property="og:type" content="video.movie" />
<meta property="og:url" content="https://example.com/the-rock" />
<meta property="og:image" content="https://example.com/rock.jpg" />
<meta property="og:image:width" content="300" />
<meta property="og:image:height" content="200" />
<meta property="og:description" content="An action movie about a rock" />
<meta property="og:site_name" content="Example Movies" />
</head>
<body>
<h1>Example Page</h1>
</body>
</html>
""".trimIndent()
println("Parsing from JSoup Document")
val doc = Jsoup.parse(html)
val openGraphDataDoc = parser.parse(doc)
println("Title: ${openGraphDataDoc.title}")
println("Is valid: ${openGraphDataDoc.isValid()}")
}

View File

@@ -1,12 +1,11 @@
import org.gradle.kotlin.dsl.implementation
plugins {
kotlin("jvm") version "2.1.21"
kotlin("jvm")
application
}
group = "nl.lengrand"
version = "0.1-SNAPSHOT"
group = "fr.lengrand"
repositories {
mavenCentral()
@@ -18,22 +17,10 @@ dependencies {
testImplementation(kotlin("test"))
}
java {
withSourcesJar()
}
tasks.test {
useJUnitPlatform()
}
tasks.jar {
manifest {
attributes(mapOf("Implementation-Title" to project.name,
"Implementation-Version" to project.version))
}
}
kotlin {
jvmToolchain(23)
}

View File

@@ -1,19 +1,19 @@
package fr.lengrand.opengraphkt
import org.jsoup.Jsoup
import java.io.File
import java.net.URI
/**
* Example demonstrating how to use the OpenGraphParser to extract Open Graph data from HTML.
*/
fun main() {
val parser = OpenGraphParser()
val fetcher = DocumentFetcher()
// Example 1: Parse Open Graph data from a URL
println("Example 1: Parsing from URL")
try {
val document = fetcher.fromUrl("https://www.imdb.com/title/tt0068646/")
val openGraphData = parser.parse(document)
val openGraphData = parser.parse(URI("https://www.imdb.com/title/tt0068646/").toURL())
println("Title: ${openGraphData.title}")
println("Is valid: ${openGraphData.isValid()}")
@@ -28,8 +28,7 @@ fun main() {
val resourceFile = File(resourceUrl.toURI())
// Parse the file
val document = fetcher.fromFile(resourceFile)
val openGraphData = parser.parse(document)
val openGraphData = parser.parse(resourceFile)
println("Title: ${openGraphData.title}")
println("Is valid: ${openGraphData.isValid()}")
@@ -38,7 +37,7 @@ fun main() {
}
// Example 3: Parse Open Graph data from an HTML string
println("\nExample 2: Parsing from HTML string")
println("\nExample 3: Parsing from HTML string")
val html = """
<!DOCTYPE html>
<html>
@@ -59,9 +58,17 @@ fun main() {
</html>
""".trimIndent()
val document = fetcher.fromString(html)
val openGraphData = parser.parse(document)
val openGraphData = parser.parse(html)
println("Title: ${openGraphData.title}")
println("Is valid: ${openGraphData.isValid()}")
// Example 4: Parse Open Graph data from a Jsoup Document
println("\nExample 4: Parsing from JSoup Document")
val doc = Jsoup.parse(html)
val openGraphDataDoc = parser.parse(doc)
println("Title: ${openGraphDataDoc.title}")
println("Is valid: ${openGraphDataDoc.isValid()}")
}

View File

@@ -1,7 +1,7 @@
import com.vanniktech.maven.publish.SonatypeHost
plugins {
kotlin("jvm") version "2.1.21"
kotlin("jvm")
id("com.vanniktech.maven.publish") version "0.30.0"
}

View File

@@ -1,29 +0,0 @@
package fr.lengrand.opengraphkt
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import java.io.File
/**
* DocumentFetcher's job is to take any type of input and transform it into a JSoup document for the Parser to then do its job
*/
class DocumentFetcher {
fun fromUrl(url: String): Document {
return Jsoup.connect(url).get()
}
fun fromString(html: String): Document {
return Jsoup.parse(html)
}
/**
* Parses HTML from a file and returns a JSoup Document
* @param file The file to parse
* @param charsetName The charset to use for parsing (default is UTF-8)
* @return A JSoup Document representing the parsed HTML
*/
fun fromFile(file: File, charsetName: String = "UTF-8") : Document {
return Jsoup.parse(file, charsetName)
}
}

View File

@@ -1,7 +1,10 @@
package fr.lengrand.opengraphkt
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.jsoup.select.Elements
import java.io.File
import java.net.URL
data class OpenGraphTag(
val property: String,
@@ -149,6 +152,40 @@ class OpenGraphParser {
return buildOpenGraphData(openGraphTags)
}
/**
* Extracts all Open Graph tags from a URL and returns a structured OpenGraphData object.
*
* @param url The URL to be parsed for Open Graph information.
* @return An OpenGraphData object containing all extracted Open Graph data.
*/
fun parse(url: URL) : OpenGraphData {
val doc = Jsoup.connect(url.toString()).get()
return parse(doc)
}
/**
* Extracts all Open Graph tags from a raw HTML String and returns a structured OpenGraphData object.
*
* @param html The raw HTML String to be parsed for Open Graph information.
* @return An OpenGraphData object containing all extracted Open Graph data.
*/
fun parse(html: String) : OpenGraphData {
val doc = Jsoup.parse(html)
return parse(doc)
}
/**
* Extracts all Open Graph tags from a raw HTML String and returns a structured OpenGraphData object.
*
* @param file The file to parse
* @param charset The charset to use for parsing (default is UTF-8)
* @return An OpenGraphData object containing all extracted Open Graph data.
*/
fun parse(file: File, charset: String = "UTF-8") : OpenGraphData {
val doc = Jsoup.parse(file, charset)
return parse(doc)
}
/**
* Extracts Open Graph tags from JSoup Elements and converts them to OpenGraphTag objects.
*

View File

@@ -1,6 +1,8 @@
package fr.lengrand.opengraphkt
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.io.TempDir
import java.io.File
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
@@ -8,7 +10,6 @@ import kotlin.test.assertTrue
class OpenGraphParserTest {
private val parser = OpenGraphParser()
private val fetcher = DocumentFetcher()
// Sample HTML with all required OpenGraph tags and some structured properties
private val completeHtml = """
@@ -139,14 +140,13 @@ class OpenGraphParserTest {
@Test
fun `test parse with complete OpenGraph tags`() {
val document = fetcher.fromString(completeHtml)
val openGraphData = parser.parse(document)
val openGraphData = parser.parse(completeHtml)
// Verify that all required properties are extracted correctly
assertEquals("The Rock", openGraphData.title)
assertEquals("video.movie", openGraphData.type)
assertEquals("https://example.com/the-rock", openGraphData.url)
// Verify that the OpenGraphData object is valid
assertTrue(openGraphData.isValid())
@@ -184,96 +184,116 @@ class OpenGraphParserTest {
@Test
fun `test parse with article-specific tags`() {
val document = fetcher.fromString(articleHtml)
val openGraphData = parser.parse(document)
val openGraphData = parser.parse(articleHtml)
// Verify basic properties
assertEquals("Breaking News", openGraphData.title)
assertEquals("article", openGraphData.type)
assertEquals("https://example.com/news/breaking", openGraphData.url)
assertEquals("Latest breaking news", openGraphData.description)
// Verify article-specific properties
assertNotNull(openGraphData.article)
assertEquals("2023-01-01T00:00:00Z", openGraphData.article?.publishedTime)
assertEquals("2023-01-02T12:00:00Z", openGraphData.article?.modifiedTime)
assertEquals("News", openGraphData.article?.section)
assertEquals(2, openGraphData.article?.authors?.size)
assertTrue(openGraphData.article?.authors?.contains("John Doe") ?: false)
assertTrue(openGraphData.article?.authors?.contains("Jane Smith") ?: false)
assertEquals(2, openGraphData.article?.tags?.size)
assertTrue(openGraphData.article?.tags?.contains("breaking") ?: false)
assertTrue(openGraphData.article?.tags?.contains("news") ?: false)
assertEquals("2023-01-01T00:00:00Z", openGraphData.article.publishedTime)
assertEquals("2023-01-02T12:00:00Z", openGraphData.article.modifiedTime)
assertEquals("News", openGraphData.article.section)
assertEquals(2, openGraphData.article.authors.size)
assertTrue(openGraphData.article.authors.contains("John Doe"))
assertTrue(openGraphData.article.authors.contains("Jane Smith"))
assertEquals(2, openGraphData.article.tags.size)
assertTrue(openGraphData.article.tags.contains("breaking"))
assertTrue(openGraphData.article.tags.contains("news"))
}
@Test
fun `test parse with profile-specific tags`() {
val document = fetcher.fromString(profileHtml)
val openGraphData = parser.parse(document)
val openGraphData = parser.parse(profileHtml)
// Verify basic properties
assertEquals("John Doe", openGraphData.title)
assertEquals("profile", openGraphData.type)
assertEquals("https://example.com/profile/johndoe", openGraphData.url)
assertEquals("John Doe's profile", openGraphData.description)
// Verify profile-specific properties
assertNotNull(openGraphData.profile)
assertEquals("John", openGraphData.profile?.firstName)
assertEquals("Doe", openGraphData.profile?.lastName)
assertEquals("johndoe", openGraphData.profile?.username)
assertEquals("male", openGraphData.profile?.gender)
assertEquals("John", openGraphData.profile.firstName)
assertEquals("Doe", openGraphData.profile.lastName)
assertEquals("johndoe", openGraphData.profile.username)
assertEquals("male", openGraphData.profile.gender)
}
@Test
fun `test parse with book-specific tags`() {
val document = fetcher.fromString(bookHtml)
val openGraphData = parser.parse(document)
val openGraphData = parser.parse(bookHtml)
// Verify basic properties
assertEquals("The Great Novel", openGraphData.title)
assertEquals("book", openGraphData.type)
assertEquals("https://example.com/books/great-novel", openGraphData.url)
assertEquals("A great novel", openGraphData.description)
// Verify book-specific properties
assertNotNull(openGraphData.book)
assertEquals(1, openGraphData.book?.authors?.size)
assertEquals("Famous Author", openGraphData.book?.authors?.get(0))
assertEquals("1234567890123", openGraphData.book?.isbn)
assertEquals("2023-01-01", openGraphData.book?.releaseDate)
assertEquals(2, openGraphData.book?.tags?.size)
assertTrue(openGraphData.book?.tags?.contains("fiction") ?: false)
assertTrue(openGraphData.book?.tags?.contains("novel") ?: false)
assertEquals(1, openGraphData.book.authors.size)
assertEquals("Famous Author", openGraphData.book.authors.get(0))
assertEquals("1234567890123", openGraphData.book.isbn)
assertEquals("2023-01-01", openGraphData.book.releaseDate)
assertEquals(2, openGraphData.book.tags.size)
assertTrue(openGraphData.book.tags.contains("fiction"))
assertTrue(openGraphData.book.tags.contains("novel"))
}
@Test
fun `test parse with multiple images`() {
val document = fetcher.fromString(multipleImagesHtml)
val openGraphData = parser.parse(document)
val openGraphData = parser.parse(multipleImagesHtml)
// Verify basic properties
assertEquals("Photo Gallery", openGraphData.title)
assertEquals("website", openGraphData.type)
assertEquals("https://example.com/gallery", openGraphData.url)
assertEquals("A gallery of images", openGraphData.description)
// Verify multiple images
assertEquals(3, openGraphData.images.size)
// First image
assertEquals("https://example.com/image1.jpg", openGraphData.images[0].url)
assertEquals(800, openGraphData.images[0].width)
assertEquals(600, openGraphData.images[0].height)
// Second image
assertEquals("https://example.com/image2.jpg", openGraphData.images[1].url)
assertEquals(1024, openGraphData.images[1].width)
assertEquals(768, openGraphData.images[1].height)
// Third image
assertEquals("https://example.com/image3.jpg", openGraphData.images[2].url)
assertEquals(1200, openGraphData.images[2].width)
assertEquals(900, openGraphData.images[2].height)
}
}
@Test
fun `test parse with File`(@TempDir tempDir: File) {
// Create a temporary HTML file
val htmlFile = File(tempDir, "test.html")
htmlFile.writeText(articleHtml)
val openGraphData = parser.parse(htmlFile)
// Verify basic properties
assertEquals("Breaking News", openGraphData.title)
assertEquals("article", openGraphData.type)
assertEquals("https://example.com/news/breaking", openGraphData.url)
assertEquals("Latest breaking news", openGraphData.description)
// Verify article-specific properties
assertNotNull(openGraphData.article)
assertEquals("2023-01-01T00:00:00Z", openGraphData.article.publishedTime)
assertEquals("2023-01-02T12:00:00Z", openGraphData.article.modifiedTime)
assertEquals("News", openGraphData.article.section)
assertEquals(2, openGraphData.article.authors.size)
assertTrue(openGraphData.article.authors.contains("John Doe"))
assertTrue(openGraphData.article.authors.contains("Jane Smith"))
}
}

View File

@@ -1,6 +1,12 @@
pluginManagement {
plugins {
kotlin("jvm") version "2.1.21"
}
}
plugins {
id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"
}
rootProject.name = "OpenGraphKt"
include("opengraphkt")
include("demo")
include("demo")
include("demo-remote")