diff --git a/bigbone-rx/src/main/kotlin/social/bigbone/rx/RxOEmbedMethods.kt b/bigbone-rx/src/main/kotlin/social/bigbone/rx/RxOEmbedMethods.kt
new file mode 100644
index 000000000..9b2b5dc55
--- /dev/null
+++ b/bigbone-rx/src/main/kotlin/social/bigbone/rx/RxOEmbedMethods.kt
@@ -0,0 +1,35 @@
+package social.bigbone.rx
+
+import io.reactivex.rxjava3.core.Single
+import social.bigbone.MastodonClient
+import social.bigbone.api.entity.OEmbedMetadata
+import social.bigbone.api.method.OEmbedMethods
+
+/**
+ * Reactive implementation of [OEmbedMethods]. For generating OEmbed previews.
+ * @see Mastodon oembed API methods
+ */
+class RxOEmbedMethods(private val mastodonClient: MastodonClient) {
+
+ private val oEmbedMethods = OEmbedMethods(mastodonClient)
+
+ /**
+ * Get oEmbed info as JSON.
+ * @see Mastodon API documentation: methods/oembed/#get
+ * @param urlOfStatus URL of a status for which to return oEmbed info.
+ * @param maxWidth Width of the iframe. Defaults to 400.
+ * @param maxHeight Height of the iframe. Defaults to null.
+ */
+ @JvmOverloads
+ fun getOEmbedInfoAsJson(
+ urlOfStatus: String,
+ maxWidth: Int = 400,
+ maxHeight: Int? = null
+ ): Single = Single.fromCallable {
+ oEmbedMethods.getOEmbedInfoAsJson(
+ urlOfStatus = urlOfStatus,
+ maxWidth = maxWidth,
+ maxHeight = maxHeight
+ ).execute()
+ }
+}
diff --git a/bigbone/src/main/kotlin/social/bigbone/MastodonClient.kt b/bigbone/src/main/kotlin/social/bigbone/MastodonClient.kt
index 8cb542d9c..78db60986 100644
--- a/bigbone/src/main/kotlin/social/bigbone/MastodonClient.kt
+++ b/bigbone/src/main/kotlin/social/bigbone/MastodonClient.kt
@@ -32,6 +32,7 @@ import social.bigbone.api.method.MediaMethods
import social.bigbone.api.method.MuteMethods
import social.bigbone.api.method.NotificationMethods
import social.bigbone.api.method.OAuthMethods
+import social.bigbone.api.method.OEmbedMethods
import social.bigbone.api.method.PollMethods
import social.bigbone.api.method.PreferenceMethods
import social.bigbone.api.method.ReportMethods
@@ -67,214 +68,221 @@ private constructor(
private var port: Int = 443
/**
- * Access API methods under "api/vX/accounts" endpoint.
+ * Access API methods under the "accounts" endpoint.
*/
@Suppress("unused") // public API
@get:JvmName("accounts")
val accounts: AccountMethods by lazy { AccountMethods(this) }
/**
- * Access API methods under "api/vX/announcements" endpoint.
+ * Access API methods under the "announcements" endpoint.
*/
@Suppress("unused") // public API
@get:JvmName("announcements")
val announcements: AnnouncementMethods by lazy { AnnouncementMethods(this) }
/**
- * Access API methods under "api/vX/apps" endpoint.
+ * Access API methods under the "apps" endpoint.
*/
@Suppress("unused") // public API
@get:JvmName("apps")
val apps: AppMethods by lazy { AppMethods(this) }
/**
- * Access API methods under "api/vX/blocks" endpoint.
+ * Access API methods under the "blocks" endpoint.
*/
@Suppress("unused") // public API
@get:JvmName("blocks")
val blocks: BlockMethods by lazy { BlockMethods(this) }
/**
- * Access API methods under "api/vX/bookmarks" endpoint.
+ * Access API methods under the "bookmarks" endpoint.
*/
@Suppress("unused") // public API
@get:JvmName("bookmarks")
val bookmarks: BookmarkMethods by lazy { BookmarkMethods(this) }
/**
- * Access API methods under "api/vX/conversations" endpoint.
+ * Access API methods under the "conversations" endpoint.
*/
@Suppress("unused") // public API
@get:JvmName("conversations")
val conversations: ConversationMethods by lazy { ConversationMethods(this) }
/**
- * Access API methods under "api/vX/custom_emojis" endpoint.
+ * Access API methods under the "custom_emojis" endpoint.
*/
@Suppress("unused") // public API
@get:JvmName("customEmojis")
val customEmojis: CustomEmojiMethods by lazy { CustomEmojiMethods(this) }
/**
- * Access API methods under "api/vX/directory" endpoint.
+ * Access API methods under the "directory" endpoint.
*/
@Suppress("unused") // public API
@get:JvmName("directories")
val directories: DirectoryMethods by lazy { DirectoryMethods(this) }
/**
- * Access API methods under "api/vX/domain_blocks" endpoint.
+ * Access API methods under the "domain_blocks" endpoint.
*/
@Suppress("unused") // public API
@get:JvmName("domainBlocks")
val domainBlocks: DomainBlockMethods by lazy { DomainBlockMethods(this) }
/**
- * Access API methods under "api/vX/endorsements" endpoint.
+ * Access API methods under the "endorsements" endpoint.
*/
@Suppress("unused") // public API
@get:JvmName("endorsements")
val endorsements: EndorsementMethods by lazy { EndorsementMethods(this) }
/**
- * Access API methods under "api/vX/favourites" endpoint.
+ * Access API methods under the "favourites" endpoint.
*/
@Suppress("unused") // public API
@get:JvmName("favourites")
val favourites: FavouriteMethods by lazy { FavouriteMethods(this) }
/**
- * Access API methods under "api/vX/featured_tags" endpoint.
+ * Access API methods under the "featured_tags" endpoint.
*/
@Suppress("unused") // public API
@get:JvmName("featuredTags")
val featuredTags: FeaturedTagMethods by lazy { FeaturedTagMethods(this) }
/**
- * Access API methods under "api/vX/filters" endpoint.
+ * Access API methods under the "filters" endpoint.
*/
@Suppress("unused") // public API
@get:JvmName("filters")
val filters: FilterMethods by lazy { FilterMethods(this) }
/**
- * Access API methods under "api/vX/follow_requests" endpoint.
+ * Access API methods under the "follow_requests" endpoint.
*/
@Suppress("unused") // public API
@get:JvmName("followRequests")
val followRequests: FollowRequestMethods by lazy { FollowRequestMethods(this) }
/**
- * Access API methods under "api/vX/instance" endpoint.
+ * Access API methods under the "instance" endpoint.
*/
@Suppress("unused") // public API
@get:JvmName("instances")
val instances: InstanceMethods by lazy { InstanceMethods(this) }
/**
- * Access API methods under "api/vX/lists" endpoint.
+ * Access API methods under the "lists" endpoint.
*/
@Suppress("unused") // public API
@get:JvmName("lists")
val lists: ListMethods by lazy { ListMethods(this) }
/**
- * Save and restore your position in timelines.
+ * Access API methods under the "markers" endpoint.
*/
@Suppress("unused") // public API
@get:JvmName("markers")
val markers: MarkerMethods by lazy { MarkerMethods(this) }
/**
- * Access API methods under "api/vX/media" endpoint.
+ * Access API methods under the "media" endpoint.
*/
@Suppress("unused") // public API
@get:JvmName("media")
val media: MediaMethods by lazy { MediaMethods(this) }
/**
- * Access API methods under "api/vX/mutes" endpoint.
+ * Access API methods under the "mutes" endpoint.
*/
@Suppress("unused") // public API
@get:JvmName("mutes")
val mutes: MuteMethods by lazy { MuteMethods(this) }
/**
- * Access API methods under "api/vX/notification[s]" endpoint.
+ * Access API methods under the "notification" endpoint.
*/
@Suppress("unused") // public API
@get:JvmName("notifications")
val notifications: NotificationMethods by lazy { NotificationMethods(this) }
/**
- * Access API methods under "oauth" endpoint.
+ * Access API methods under the "oauth" endpoint.
*/
@Suppress("unused") // public API
@get:JvmName("oauth")
val oauth: OAuthMethods by lazy { OAuthMethods(this) }
/**
- * Access API methods under "polls" endpoint.
+ * Access API methods under the "oembed" endpoint.
+ */
+ @Suppress("unused") // public API
+ @get:JvmName("oembed")
+ val oembed: OEmbedMethods by lazy { OEmbedMethods(this) }
+
+ /**
+ * Access API methods under the "polls" endpoint.
*/
@Suppress("unused") // public API
@get:JvmName("polls")
val polls: PollMethods by lazy { PollMethods(this) }
/**
- * Access API methods under "api/vX/reports" endpoint.
+ * Access API methods under the "preferences" endpoint.
+ */
+ @Suppress("unused") // public API
+ @get:JvmName("preferences")
+ val preferences: PreferenceMethods by lazy { PreferenceMethods(this) }
+
+ /**
+ * Access API methods under the "reports" endpoint.
*/
@Suppress("unused") // public API
@get:JvmName("reports")
val reports: ReportMethods by lazy { ReportMethods(this) }
/**
- * Access API methods under "api/vX/search" endpoint.
+ * Access API methods under the "search" endpoint.
*/
@Suppress("unused") // public API
@get:JvmName("search")
val search: SearchMethods by lazy { SearchMethods(this) }
/**
- * Access API methods under "api/vX/statuses" endpoint.
+ * Access API methods under the "statuses" endpoint.
*/
@Suppress("unused") // public API
@get:JvmName("statuses")
val statuses: StatusMethods by lazy { StatusMethods(this) }
/**
- * Access API methods under "api/vX/streaming" endpoint.
+ * Access API methods under the "streaming" endpoint.
*/
@Suppress("unused") // public API
@get:JvmName("streaming")
val streaming: StreamingMethods by lazy { StreamingMethods(this) }
/**
- * Access API methods under "api/vX/tags" endpoint.
- */
- @Suppress("unused") // public API
- @get:JvmName("tags")
- val tags: TagMethods by lazy { TagMethods(this) }
-
- /**
- * Access API methods under "api/vX/timelines" endpoint.
+ * Access API methods under the "suggestions" endpoint.
*/
@Suppress("unused") // public API
- @get:JvmName("timelines")
- val timelines: TimelineMethods by lazy { TimelineMethods(this) }
+ @get:JvmName("suggestions")
+ val suggestions: SuggestionMethods by lazy { SuggestionMethods(this) }
/**
- * Access API methods under "preferences" endpoint.
+ * Access API methods under the "tags" endpoint.
*/
@Suppress("unused") // public API
- @get:JvmName("preferences")
- val preferences: PreferenceMethods by lazy { PreferenceMethods(this) }
+ @get:JvmName("tags")
+ val tags: TagMethods by lazy { TagMethods(this) }
/**
- * Access API methods under "api/vX/suggestions" endpoint.
+ * Access API methods under the "timelines" endpoint.
*/
@Suppress("unused") // public API
- @get:JvmName("suggestions")
- val suggestions: SuggestionMethods by lazy { SuggestionMethods(this) }
+ @get:JvmName("timelines")
+ val timelines: TimelineMethods by lazy { TimelineMethods(this) }
/**
* Specifies the HTTP methods / HTTP verb that can be used by this class.
diff --git a/bigbone/src/main/kotlin/social/bigbone/api/entity/OEmbedMetadata.kt b/bigbone/src/main/kotlin/social/bigbone/api/entity/OEmbedMetadata.kt
new file mode 100644
index 000000000..7c2238b31
--- /dev/null
+++ b/bigbone/src/main/kotlin/social/bigbone/api/entity/OEmbedMetadata.kt
@@ -0,0 +1,83 @@
+package social.bigbone.api.entity
+
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+import social.bigbone.api.method.OEmbedMethods
+
+/**
+ * oEmbed metadata returned by [OEmbedMethods].
+ * @see OEmbed response parameters documentation
+ */
+@Serializable
+data class OEmbedMetadata(
+ /**
+ * The resource type.
+ * For now, this will always be "rich".
+ */
+ @SerialName("type")
+ val type: String = "rich",
+
+ /**
+ * The oEmbed version number. Must be 1.0.
+ */
+ @SerialName("version")
+ val version: String = "1.0",
+
+ /**
+ * A text title, describing this resource.
+ */
+ @SerialName("title")
+ val title: String? = "",
+
+ /**
+ * The name of the author/owner of the resource.
+ */
+ @SerialName("author_name")
+ val authorName: String? = "",
+
+ /**
+ * A URL for the other/owner of the resource.
+ */
+ @SerialName("author_url")
+ val authorUrl: String? = "",
+
+ /**
+ * The name of the resource provider.
+ */
+ @SerialName("provider_name")
+ val providerName: String? = "",
+
+ /**
+ * The URL of the resource provider.
+ */
+ @SerialName("provider_url")
+ val providerUrl: String? = "",
+
+ /**
+ * The suggested cache lifetime for this resource, in seconds.
+ * Consumers may choose to use this value or not.
+ */
+ @SerialName("cache_age")
+ val cacheAge: Int? = null,
+
+ /**
+ * The HTML required to display the resource.
+ * The HTML should have no padding or margins.
+ * Consumers may wish to load the HTML in an off-domain iframe to avoid XSS vulnerabilities.
+ * The markup should be valid XHTML 1.0 Basic.
+ */
+ @SerialName("html")
+ val html: String? = "",
+
+ /**
+ * The width in pixels required to display the HTML.
+ */
+ @SerialName("width")
+ val width: Int? = null,
+
+ /**
+ * The height in pixels required to display the HTML.
+ */
+ @SerialName("height")
+ val height: Int? = null
+)
diff --git a/bigbone/src/main/kotlin/social/bigbone/api/method/OEmbedMethods.kt b/bigbone/src/main/kotlin/social/bigbone/api/method/OEmbedMethods.kt
new file mode 100644
index 000000000..fb539b26d
--- /dev/null
+++ b/bigbone/src/main/kotlin/social/bigbone/api/method/OEmbedMethods.kt
@@ -0,0 +1,39 @@
+package social.bigbone.api.method
+
+import social.bigbone.MastodonClient
+import social.bigbone.MastodonRequest
+import social.bigbone.Parameters
+import social.bigbone.api.entity.OEmbedMetadata
+
+/**
+ * For generating OEmbed previews.
+ * @see Mastodon oembed API methods
+ */
+class OEmbedMethods(private val mastodonClient: MastodonClient) {
+
+ private val oEmbedEndpoint = "api/oembed"
+
+ /**
+ * Get oEmbed info as JSON.
+ * @see Mastodon API documentation: methods/oembed/#get
+ * @param urlOfStatus URL of a status for which to return oEmbed info.
+ * @param maxWidth Width of the iframe. Defaults to 400.
+ * @param maxHeight Height of the iframe. Defaults to null.
+ */
+ @JvmOverloads
+ fun getOEmbedInfoAsJson(
+ urlOfStatus: String,
+ maxWidth: Int = 400,
+ maxHeight: Int? = null
+ ): MastodonRequest {
+ return mastodonClient.getMastodonRequest(
+ endpoint = oEmbedEndpoint,
+ method = MastodonClient.Method.GET,
+ parameters = Parameters().apply {
+ append("url", urlOfStatus)
+ append("maxwidth", maxWidth)
+ maxHeight?.let { append("maxheight", it) }
+ }
+ )
+ }
+}
diff --git a/bigbone/src/test/assets/oembed_get_success.json b/bigbone/src/test/assets/oembed_get_success.json
new file mode 100644
index 000000000..9a0534877
--- /dev/null
+++ b/bigbone/src/test/assets/oembed_get_success.json
@@ -0,0 +1,13 @@
+{
+ "type": "rich",
+ "version": "1.0",
+ "title": "New status by trwnh",
+ "author_name": "infinite love ⴳ",
+ "author_url": "https://mastodon.social/@trwnh",
+ "provider_name": "mastodon.social",
+ "provider_url": "https://mastodon.social/",
+ "cache_age": 86400,
+ "html": "",
+ "width": 400,
+ "height": null
+}
diff --git a/bigbone/src/test/kotlin/social/bigbone/api/method/OEmbedMethodsTest.kt b/bigbone/src/test/kotlin/social/bigbone/api/method/OEmbedMethodsTest.kt
new file mode 100644
index 000000000..8e00faf86
--- /dev/null
+++ b/bigbone/src/test/kotlin/social/bigbone/api/method/OEmbedMethodsTest.kt
@@ -0,0 +1,51 @@
+package social.bigbone.api.method
+
+import io.mockk.verify
+import org.amshove.kluent.shouldBeEqualTo
+import org.amshove.kluent.shouldBeNull
+import org.junit.jupiter.api.Test
+import social.bigbone.Parameters
+import social.bigbone.api.entity.OEmbedMetadata
+import social.bigbone.testtool.MockClient
+
+class OEmbedMethodsTest {
+
+ @Test
+ fun `Given a client returning successfully, when getting oEmbed info, then ensure correct parsing and correct method was called`() {
+ val client = MockClient.mock("oembed_get_success.json")
+ val oEmbedMethods = OEmbedMethods(client)
+ val urlOfStatus: String = "https://mastodon.social/@trwnh/99664077509711321"
+ val maxWidth: Int = 640
+ val maxHeight: Int? = null
+
+ val meta: OEmbedMetadata = oEmbedMethods
+ .getOEmbedInfoAsJson(urlOfStatus = urlOfStatus, maxWidth = maxWidth, maxHeight = maxHeight)
+ .execute()
+
+ with(meta) {
+ type shouldBeEqualTo "rich"
+ version shouldBeEqualTo "1.0"
+ title shouldBeEqualTo "New status by trwnh"
+ authorName shouldBeEqualTo "infinite love ⴳ"
+ authorUrl shouldBeEqualTo "https://mastodon.social/@trwnh"
+ providerName shouldBeEqualTo "mastodon.social"
+ providerUrl shouldBeEqualTo "https://mastodon.social/"
+ cacheAge shouldBeEqualTo 86_400
+ html shouldBeEqualTo "" +
+ ""
+ width shouldBeEqualTo 400
+ height.shouldBeNull()
+ }
+
+ verify {
+ client.get(
+ path = "api/oembed",
+ query = any()
+ )
+ }
+ }
+}
diff --git a/sample-java/src/main/java/social/bigbone/sample/GetOEmbedMetadata.java b/sample-java/src/main/java/social/bigbone/sample/GetOEmbedMetadata.java
new file mode 100644
index 000000000..d18beab12
--- /dev/null
+++ b/sample-java/src/main/java/social/bigbone/sample/GetOEmbedMetadata.java
@@ -0,0 +1,20 @@
+package social.bigbone.sample;
+
+import social.bigbone.MastodonClient;
+import social.bigbone.api.entity.OEmbedMetadata;
+import social.bigbone.api.exception.BigBoneRequestException;
+
+public class GetOEmbedMetadata {
+
+ public static void main(final String[] args) throws BigBoneRequestException {
+ final String instance = args[0];
+ final String statusUrl = args[1];
+
+ // Instantiate client
+ final MastodonClient client = new MastodonClient.Builder(instance).build();
+
+ // Get oEmbed metadata for [statusUrl]
+ final OEmbedMetadata oEmbedMetadata = client.oembed().getOEmbedInfoAsJson(statusUrl).execute();
+ System.out.println(oEmbedMetadata);
+ }
+}
diff --git a/sample-kotlin/src/main/kotlin/social/bigbone/sample/GetOEmbedMetadata.kt b/sample-kotlin/src/main/kotlin/social/bigbone/sample/GetOEmbedMetadata.kt
new file mode 100644
index 000000000..903ad70dd
--- /dev/null
+++ b/sample-kotlin/src/main/kotlin/social/bigbone/sample/GetOEmbedMetadata.kt
@@ -0,0 +1,20 @@
+package social.bigbone.sample
+
+import social.bigbone.MastodonClient
+import social.bigbone.api.entity.OEmbedMetadata
+
+object GetOEmbedMetadata {
+
+ @JvmStatic
+ fun main(args: Array) {
+ val instance = args[0]
+ val statusUrl = args[1]
+
+ // Instantiate client
+ val client = MastodonClient.Builder(instance).build()
+
+ // Get oEmbed metadata for [statusUrl]
+ val oEmbedMetadata: OEmbedMetadata = client.oembed.getOEmbedInfoAsJson(urlOfStatus = statusUrl).execute()
+ println(oEmbedMetadata)
+ }
+}