Skip to content

Commit

Permalink
Update migration guide and add Locator.fromJSON(withLegacyHref)
Browse files Browse the repository at this point in the history
  • Loading branch information
mickael-menu committed Sep 29, 2023
1 parent 744793f commit 12e3e64
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 4 deletions.
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ All notable changes to this project will be documented in this file. Take a look
### Changed

* Readium resources are now prefixed with `readium_`. Take care of updating any overridden resource by following [the migration guide](docs/migration-guide.md#300).
* `Link` and `Locator`'s `href` do not start with a `/` for packaged publications anymore.
* To ensure backward-compatibility, `href` starting with a `/` are still supported. But you may want to update the locators persisted in your database to drop the `/` prefix for packaged publications.
* `Link` and `Locator`'s `href` are normalized as valid URLs to improve interoperability with the Readium Web toolkits.
* **You MUST migrate your database if you were persisting HREF and Locators**. Take a look at [the migration guide](docs/migration-guide.md) for guidance.

#### Shared

Expand Down
56 changes: 56 additions & 0 deletions docs/migration-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,62 @@ dependencies {
}
```

### Migration of HREFs (bookmarks, annotations, etc.)

:warning: This requires a database migration in your application, if you were persisting `Locator` objects.

In Readium v2.x, a `Link` or `Locator`'s `href` could be either:

* a full valid URL for a streamed publication, e.g. `https://domain.com/isbn/dir/my%20chapter.html`,
* a percent-decoded path for a local archive such as an EPUB, e.g. `/dir/my chapter.html`.
* Note that it was relative to the root of the archive (`/`).

To improve the interoperability with other Readium toolkits – in particular the Readium Web Toolkits, which only work in a streaming context – **Readium v3 now generates and expects valid URLs** for `Locator` and `Link`'s `href`.

* `https://domain.com/isbn/dir/my%20chapter.html` is left unchanged, as it already a valid URL.
* `/dir/my chapter.html` becomes the relative URL path `dir/my%20chapter.html`
* We dropped the `/` prefix to avoid issues with resolving to a base URL.
* Special characters are percent-encoded.

**You must migrate the HREFs or Locators stored in your database** when upgrading to Readium 3.

To assist you in migrating your data, two helpers are provided: `Url.fromLegacyHref()` and `Locator.fromJSON(withLegacyHref)`:

```kotlin
val href = Url.fromLegacyHref(legacyHref)?.toString()
val locator = Locator.fromJSON(legacyJSON, withLegacyHref = true)
```

Here's a sample Jetpack Room migration that you can use as inspiration:

```kotlin
val MIGRATION_HREF = object : Migration(1, 2) {

override fun migrate(db: SupportSQLiteDatabase) {
val normalizedHrefs: Map<Long, String> = buildMap {
db.query("SELECT id, href FROM bookmarks").use { cursor ->
while (cursor.moveToNext()) {
val id = cursor.getLong(0)
val href = cursor.getString(1)

val normalizedHref = Url.fromLegacyHref(href)?.toString()
if (normalizedHref != null) {
put(id, normalizedHref)
}
}
}
}

val stmt = db.compileStatement("UPDATE bookmarks SET href = ? WHERE id = ?")
for ((id, href) in normalizedHrefs) {
stmt.bindString(1, href)
stmt.bindLong(2, id)
stmt.executeUpdateDelete()
}
}
}
```

### All resources now have the prefix `readium_`.

To avoid conflicts when merging your app resources, all resources declared in the Readium toolkit now have the prefix `readium_`. This means that you must rename any layouts or strings you have overridden. Here is a comprehensive list of the changes.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,8 @@ public data class Locator(
public fun fromJSON(
json: JSONObject?,
mediaTypeRetriever: MediaTypeRetriever = MediaTypeRetriever(),
warnings: WarningLogger? = null
warnings: WarningLogger? = null,
withLegacyHref: Boolean = false
): Locator? {
val href = json?.optNullableString("href")
val type = json?.optNullableString("type")
Expand All @@ -209,7 +210,10 @@ public data class Locator(
return null
}

val url = Url(href) ?: run {
val url = (
if (withLegacyHref) Url.fromLegacyHref(href)
else Url(href)
) ?: run {
warnings?.log(Locator::class.java, "[href] is not a valid URL", json)
return null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,23 @@ class LocatorTest {
assertNull(Locator.fromJSON(JSONObject("{ 'invalid': 'object' }")))
}

@Test fun `parse {Locator} with legacy HREF`() {
val json = JSONObject("""
{
"href": "legacy href",
"type": "text/html"
}
""")

assertNull(Locator.fromJSON(json))
assertNull(Locator.fromJSON(json, withLegacyHref = false))

assertEquals(
Locator(href = Url("legacy%20href")!!, mediaType = MediaType.HTML),
Locator.fromJSON(json, withLegacyHref = true)
)
}

@Test fun `get {Locator} minimal JSON`() {
assertJSONEquals(
JSONObject(
Expand Down

0 comments on commit 12e3e64

Please sign in to comment.