From fb59ffe3d8c20463724175c7145add04bb987cb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Gasser?= Date: Tue, 6 Jun 2023 23:02:01 +0200 Subject: [PATCH] Added missing fields to Instance entity. (#217) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added missing fields to Instance entity. Added the missing fields to the InstanceV1 entity. Also moved some Instance-specific classes into the Instance class (as nested data class). Added code samples. * Code review fixes Moved Rule class into separate entity, renamed some data classes related to the Instance entity. * Fix code review findings * Added test for reading config section in instance entity --------- Co-authored-by: André Gasser --- .../social/bigbone/api/entity/Instance.kt | 194 +++++++++++++++++- .../bigbone/api/entity/InstanceStats.kt | 27 --- .../social/bigbone/api/entity/InstanceUrls.kt | 15 -- .../kotlin/social/bigbone/api/entity/Rule.kt | 21 ++ .../src/test/assets/instance_extended.json | 127 ++++++++++++ .../bigbone/api/method/InstanceMethodsTest.kt | 31 +++ .../bigbone/sample/GetInstanceInfo.java | 22 ++ .../social/bigbone/sample/GetInstanceInfo.kt | 20 ++ 8 files changed, 411 insertions(+), 46 deletions(-) delete mode 100644 bigbone/src/main/kotlin/social/bigbone/api/entity/InstanceStats.kt delete mode 100644 bigbone/src/main/kotlin/social/bigbone/api/entity/InstanceUrls.kt create mode 100644 bigbone/src/main/kotlin/social/bigbone/api/entity/Rule.kt create mode 100644 bigbone/src/test/assets/instance_extended.json create mode 100644 sample-java/src/main/java/social/bigbone/sample/GetInstanceInfo.java create mode 100644 sample-kotlin/src/main/kotlin/social/bigbone/sample/GetInstanceInfo.kt diff --git a/bigbone/src/main/kotlin/social/bigbone/api/entity/Instance.kt b/bigbone/src/main/kotlin/social/bigbone/api/entity/Instance.kt index 7eb978b60..594ec4177 100644 --- a/bigbone/src/main/kotlin/social/bigbone/api/entity/Instance.kt +++ b/bigbone/src/main/kotlin/social/bigbone/api/entity/Instance.kt @@ -47,13 +47,13 @@ data class Instance( * URLs of interest for clients apps. */ @SerializedName("urls") - val urls: InstanceUrls? = null, + val urls: Urls? = null, /** * Statistics about how much information the instance contains. */ @SerializedName("stats") - val stats: InstanceStats? = null, + val stats: Stats? = null, /** * Banner image for the website. @@ -85,9 +85,195 @@ data class Instance( @SerializedName("invites_enabled") val invitesEnabled: Boolean = false, + /** + * Configured values and limits for this website. + */ + @SerializedName("configuration") + val configuration: Configuration? = null, + /** * A user that can be contacted, as an alternative to email. */ @SerializedName("contact_account") - val contactAccount: Account? = null -) + val contactAccount: Account? = null, + + /** + * An itemized list of rules for this website. + */ + @SerializedName("rules") + val rules: List? = null +) { + /** + * URLs of interest for clients apps. + * @see Mastodon API V1::Instance + */ + data class Urls( + /** + * The Websockets URL for connecting to the streaming API. + */ + @SerializedName("streaming_api") + val streamingApi: String = "" + ) + + /** + * Statistics about how much information the instance contains. + * @see Mastodon API V1::Instance + */ + data class Stats( + /** + * Total users on this instance. + */ + @SerializedName("user_count") + val userCount: Long = 0, + + /** + * Total statuses on this instance. + */ + @SerializedName("status_count") + val statusCount: Long = 0, + + /** + * Total domains discovered by this instance. + */ + @SerializedName("domain_count") + val domainCount: Long = 0 + ) + + /** + * Configured values and limits for this website. + * @see Mastodon API V1::Instance + */ + data class Configuration( + /** + * Limits related to accounts. + */ + @SerializedName("accounts") + val accounts: Accounts? = null, + + /** + * Limits related to authoring statuses. + */ + @SerializedName("statuses") + val statuses: Statuses? = null, + + /** + * Hints for which attachments will be accepted. + */ + @SerializedName("media_attachments") + val mediaAttachments: MediaAttachments? = null, + + /** + * Limits related to polls. + */ + @SerializedName("polls") + val polls: Polls? = null + ) { + /** + * Limits related to accounts. + * @see Mastodon API V1::Instance + */ + data class Accounts( + /** + * The maximum number of featured tags allowed for each account. + */ + @SerializedName("max_featured_tags") + val maxFeaturedTags: Int = 0 + ) + + /** + * Limits related to authoring statuses. + * @see Mastodon API V1::Instance + */ + data class Statuses( + /** + * The maximum number of allowed characters per status. + */ + @SerializedName("max_characters") + val maxCharacters: Int = 0, + + /** + * The maximum number of media attachments that can be added to a status. + */ + @SerializedName("max_media_attachments") + val maxMediaAttachments: Int = 0, + + /** + * Each URL in a status will be assumed to be exactly this many characters. + */ + @SerializedName("characters_reserved_per_url") + val charactersReservedPerUrl: Int = 0 + ) + + /** + * Hints for which attachments will be accepted. + * @see Mastodon API V1::Instance + */ + data class MediaAttachments( + /** + * Contains MIME types that can be uploaded. + */ + @SerializedName("supported_mime_types") + val supportedMimeTypes: List = emptyList(), + + /** + * The maximum size of any uploaded image, in bytes. + */ + @SerializedName("image_size_limit") + val imageSizeLimit: Int = 0, + + /** + * The maximum number of pixels (width times height) for image uploads. + */ + @SerializedName("image_matrix_limit") + val imageMatrixLimit: Int = 0, + + /** + * The maximum size of any uploaded video, in bytes. + */ + @SerializedName("video_size_limit") + val videoSizeLimit: Int = 0, + + /** + * The maximum frame rate for any uploaded video. + */ + @SerializedName("video_frame_rate_limit") + val videoFrameRateLimit: Int = 0, + + /** + * The maximum number of pixels (width times height) for video uploads. + */ + @SerializedName("video_matrix_limit") + val videoMatrixLimit: Int = 0 + ) + + /** + * Limits related to polls. + * @see Mastodon API V1::Instance + */ + data class Polls( + /** + * Each poll is allowed to have up to this many options. + */ + @SerializedName("max_options") + val maxOptions: Int = 0, + + /** + * Each poll option is allowed to have this many characters. + */ + @SerializedName("max_characters_per_option") + val maxCharactersPerOption: Int = 0, + + /** + * The shortest allowed poll duration, in seconds. + */ + @SerializedName("min_expiration") + val minExpiration: Int = 0, + + /** + * The longest allowed poll duration, in seconds. + */ + @SerializedName("max_expiration") + val maxExpiration: Int = 0 + ) + } +} diff --git a/bigbone/src/main/kotlin/social/bigbone/api/entity/InstanceStats.kt b/bigbone/src/main/kotlin/social/bigbone/api/entity/InstanceStats.kt deleted file mode 100644 index 2fe19eb1d..000000000 --- a/bigbone/src/main/kotlin/social/bigbone/api/entity/InstanceStats.kt +++ /dev/null @@ -1,27 +0,0 @@ -package social.bigbone.api.entity - -import com.google.gson.annotations.SerializedName - -/** - * Statistics about how much information the instance contains. - * @see Mastodon API V1::Instance - */ -data class InstanceStats( - /** - * Total users on this instance. - */ - @SerializedName("user_count") - val userCount: Long = 0, - - /** - * Total statuses on this instance. - */ - @SerializedName("status_count") - val statusCount: Long = 0, - - /** - * Total domains discovered by this instance. - */ - @SerializedName("domain_count") - val domainCount: Long = 0 -) diff --git a/bigbone/src/main/kotlin/social/bigbone/api/entity/InstanceUrls.kt b/bigbone/src/main/kotlin/social/bigbone/api/entity/InstanceUrls.kt deleted file mode 100644 index 62ec770eb..000000000 --- a/bigbone/src/main/kotlin/social/bigbone/api/entity/InstanceUrls.kt +++ /dev/null @@ -1,15 +0,0 @@ -package social.bigbone.api.entity - -import com.google.gson.annotations.SerializedName - -/** - * URLs of interest for clients apps. - * @see Mastodon API V1::Instance - */ -data class InstanceUrls( - /** - * The Websockets URL for connecting to the streaming API. - */ - @SerializedName("streaming_api") - val streamingApi: String = "" -) diff --git a/bigbone/src/main/kotlin/social/bigbone/api/entity/Rule.kt b/bigbone/src/main/kotlin/social/bigbone/api/entity/Rule.kt new file mode 100644 index 000000000..da2414485 --- /dev/null +++ b/bigbone/src/main/kotlin/social/bigbone/api/entity/Rule.kt @@ -0,0 +1,21 @@ +package social.bigbone.api.entity + +import com.google.gson.annotations.SerializedName + +/** + * Represents a rule that server users should follow. + * @see Mastodon API Rule + */ +data class Rule( + /** + * An identifier for the rule. + */ + @SerializedName("id") + val id: String = "0", + + /** + * The rule to be followed. + */ + @SerializedName("text") + val text: String = "" +) diff --git a/bigbone/src/test/assets/instance_extended.json b/bigbone/src/test/assets/instance_extended.json new file mode 100644 index 000000000..d7786e53e --- /dev/null +++ b/bigbone/src/test/assets/instance_extended.json @@ -0,0 +1,127 @@ +{ + "uri":"mastodon.social", + "title":"Mastodon", + "short_description":"The original server operated by the Mastodon gGmbH non-profit", + "description":"", + "email":"staff@mastodon.social", + "version":"3.5.3", + "urls":{ + "streaming_api":"wss://mastodon.social" + }, + "stats":{ + "user_count":812303, + "status_count":38151616, + "domain_count":25255 + }, + "thumbnail":"https://files.mastodon.social/site_uploads/files/000/000/001/original/vlcsnap-2018-08-27-16h43m11s127.png", + "languages":[ + "en" + ], + "registrations":false, + "approval_required":false, + "invites_enabled":true, + "configuration":{ + "statuses":{ + "max_characters":500, + "max_media_attachments":4, + "characters_reserved_per_url":23 + }, + "media_attachments":{ + "supported_mime_types":[ + "image/jpeg", + "image/png", + "image/gif", + "image/webp", + "video/webm", + "video/mp4", + "video/quicktime", + "video/ogg", + "audio/wave", + "audio/wav", + "audio/x-wav", + "audio/x-pn-wave", + "audio/vnd.wave", + "audio/ogg", + "audio/vorbis", + "audio/mpeg", + "audio/mp3", + "audio/webm", + "audio/flac", + "audio/aac", + "audio/m4a", + "audio/x-m4a", + "audio/mp4", + "audio/3gpp", + "video/x-ms-asf" + ], + "image_size_limit":10485760, + "image_matrix_limit":16777216, + "video_size_limit":41943040, + "video_frame_rate_limit":60, + "video_matrix_limit":2304000 + }, + "polls":{ + "max_options":4, + "max_characters_per_option":50, + "min_expiration":300, + "max_expiration":2629746 + } + }, + "contact_account":{ + "id":"1", + "username":"Gargron", + "acct":"Gargron", + "display_name":"Eugen", + "locked":false, + "bot":false, + "discoverable":true, + "group":false, + "created_at":"2016-03-16T00:00:00.000Z", + "note":"\u003cp\u003eFounder, CEO and lead developer \u003cspan class=\"h-card\"\u003e\u003ca href=\"https://mastodon.social/@Mastodon\" class=\"u-url mention\"\u003e@\u003cspan\u003eMastodon\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e, Germany.\u003c/p\u003e", + "url":"https://mastodon.social/@Gargron", + "avatar":"https://files.mastodon.social/accounts/avatars/000/000/001/original/dc4286ceb8fab734.jpg", + "avatar_static":"https://files.mastodon.social/accounts/avatars/000/000/001/original/dc4286ceb8fab734.jpg", + "header":"https://files.mastodon.social/accounts/headers/000/000/001/original/3b91c9965d00888b.jpeg", + "header_static":"https://files.mastodon.social/accounts/headers/000/000/001/original/3b91c9965d00888b.jpeg", + "followers_count":118944, + "following_count":305, + "statuses_count":72309, + "last_status_at":"2022-08-24", + "emojis":[ + + ], + "fields":[ + { + "name":"Patreon", + "value":"\u003ca href=\"https://www.patreon.com/mastodon\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\"\u003e\u003cspan class=\"invisible\"\u003ehttps://www.\u003c/span\u003e\u003cspan class=\"\"\u003epatreon.com/mastodon\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e", + "verified_at":null + } + ] + }, + "rules":[ + { + "id":"1", + "text":"Sexually explicit or violent media must be marked as sensitive when posting" + }, + { + "id":"2", + "text":"No racism, sexism, homophobia, transphobia, xenophobia, or casteism" + }, + { + "id":"3", + "text":"No incitement of violence or promotion of violent ideologies" + }, + { + "id":"4", + "text":"No harassment, dogpiling or doxxing of other users" + }, + { + "id":"5", + "text":"No content illegal in Germany" + }, + { + "id":"7", + "text":"Do not share intentionally false or misleading information" + } + ] +} diff --git a/bigbone/src/test/kotlin/social/bigbone/api/method/InstanceMethodsTest.kt b/bigbone/src/test/kotlin/social/bigbone/api/method/InstanceMethodsTest.kt index 91e9548bc..a49c016c2 100644 --- a/bigbone/src/test/kotlin/social/bigbone/api/method/InstanceMethodsTest.kt +++ b/bigbone/src/test/kotlin/social/bigbone/api/method/InstanceMethodsTest.kt @@ -21,6 +21,37 @@ class InstanceMethodsTest { instance.version shouldBeEqualTo "1.3.2" } + @Test + fun getInstanceExtended() { + val client = MockClient.mock("instance_extended.json") + val instanceMethods = InstanceMethods(client) + + val instance = instanceMethods.getInstance().execute() + instance.uri shouldBeEqualTo "mastodon.social" + instance.title shouldBeEqualTo "Mastodon" + instance.description shouldBeEqualTo "" + instance.email shouldBeEqualTo "staff@mastodon.social" + instance.version shouldBeEqualTo "3.5.3" + val config = instance.configuration!! + config.statuses!!.maxCharacters shouldBeEqualTo 500 + config.statuses!!.maxMediaAttachments shouldBeEqualTo 4 + config.statuses!!.charactersReservedPerUrl shouldBeEqualTo 23 + config.mediaAttachments!!.supportedMimeTypes.size shouldBeEqualTo 25 + config.mediaAttachments!!.imageSizeLimit shouldBeEqualTo 10_485_760 + config.mediaAttachments!!.imageMatrixLimit shouldBeEqualTo 16_777_216 + config.mediaAttachments!!.videoSizeLimit shouldBeEqualTo 41_943_040 + config.mediaAttachments!!.videoFrameRateLimit shouldBeEqualTo 60 + config.mediaAttachments!!.videoMatrixLimit shouldBeEqualTo 2_304_000 + config.polls!!.maxOptions shouldBeEqualTo 4 + config.polls!!.maxCharactersPerOption shouldBeEqualTo 50 + config.polls!!.minExpiration shouldBeEqualTo 300 + config.polls!!.maxExpiration shouldBeEqualTo 2_629_746 + val rules = instance.rules!! + rules.size shouldBeEqualTo 6 + rules[0].id shouldBeEqualTo "1" + rules[0].text shouldBeEqualTo "Sexually explicit or violent media must be marked as sensitive when posting" + } + @Test fun getInstanceWithJson() { val client = MockClient.mock("instance.json") diff --git a/sample-java/src/main/java/social/bigbone/sample/GetInstanceInfo.java b/sample-java/src/main/java/social/bigbone/sample/GetInstanceInfo.java new file mode 100644 index 000000000..db8d84a1c --- /dev/null +++ b/sample-java/src/main/java/social/bigbone/sample/GetInstanceInfo.java @@ -0,0 +1,22 @@ +package social.bigbone.sample; + +import com.google.gson.Gson; +import social.bigbone.MastodonClient; +import social.bigbone.api.entity.Instance; +import social.bigbone.api.exception.BigBoneRequestException; + +@SuppressWarnings("PMD.SystemPrintln") +public class GetInstanceInfo { + public static void main(final String[] args) throws BigBoneRequestException { + final String instance = args[0]; + + // Instantiate client + final MastodonClient client = new MastodonClient.Builder(instance) + .build(); + + // Get instance info and dump it to the console as JSON + final Instance instanceInfo = client.instances().getInstance().execute(); + final Gson gson = new Gson(); + System.out.println(gson.toJson(instanceInfo)); + } +} diff --git a/sample-kotlin/src/main/kotlin/social/bigbone/sample/GetInstanceInfo.kt b/sample-kotlin/src/main/kotlin/social/bigbone/sample/GetInstanceInfo.kt new file mode 100644 index 000000000..6542c5d07 --- /dev/null +++ b/sample-kotlin/src/main/kotlin/social/bigbone/sample/GetInstanceInfo.kt @@ -0,0 +1,20 @@ +package social.bigbone.sample + +import com.google.gson.Gson +import social.bigbone.MastodonClient + +object GetInstanceInfo { + @JvmStatic + fun main(args: Array) { + val instance = args[0] + + // Instantiate client + val client = MastodonClient.Builder(instance) + .build() + + // Get instance info and dump it to the console as JSON + val instanceInfo = client.instances.getInstance().execute() + val gson = Gson() + println(gson.toJson(instanceInfo)) + } +}