diff --git a/bigbone-rx/src/main/kotlin/social/bigbone/rx/admin/RxAdminDomainAllowMethods.kt b/bigbone-rx/src/main/kotlin/social/bigbone/rx/admin/RxAdminDomainAllowMethods.kt
new file mode 100644
index 000000000..74af79211
--- /dev/null
+++ b/bigbone-rx/src/main/kotlin/social/bigbone/rx/admin/RxAdminDomainAllowMethods.kt
@@ -0,0 +1,57 @@
+package social.bigbone.rx.admin
+
+import io.reactivex.rxjava3.core.Completable
+import io.reactivex.rxjava3.core.Single
+import social.bigbone.MastodonClient
+import social.bigbone.api.Pageable
+import social.bigbone.api.Range
+import social.bigbone.api.entity.admin.AdminDomainAllow
+import social.bigbone.api.method.admin.AdminDomainAllowMethods
+
+/**
+ * Reactive implementation of [AdminDomainAllowMethods].
+ *
+ * Allow certain domains to federate.
+ * @see Mastodon admin/domain_allows API methods
+ */
+class RxAdminDomainAllowMethods(client: MastodonClient) {
+
+ private val adminAllowMethods = AdminDomainAllowMethods(client)
+
+ /**
+ * Show information about all allowed domains.
+ * @param range optional Range for the pageable return value
+ * @see Mastodon API documentation: methods/domain_allows/#get
+ */
+ @JvmOverloads
+ fun getAllAllowedDomains(range: Range = Range()): Single> = Single.fromCallable {
+ adminAllowMethods.getAllAllowedDomains(range = range).execute()
+ }
+
+ /**
+ * Show information about a single allowed domain.
+ * @param id The ID of the DomainAllow in the database.
+ * @see Mastodon API documentation: methods/domain_allows/#get-one
+ */
+ fun getAllowedDomain(id: String): Single = Single.fromCallable {
+ adminAllowMethods.getAllowedDomain(id = id).execute()
+ }
+
+ /**
+ * Add a domain to the list of domains allowed to federate, to be used when the instance is in allow-list federation mode.
+ * @param domain The domain to allow federation with.
+ * @see Mastodon API documentation: admin/domain_allows/#create
+ */
+ fun allowDomainToFederate(domain: String): Single = Single.fromCallable {
+ adminAllowMethods.allowDomainToFederate(domain = domain).execute()
+ }
+
+ /**
+ * Delete a domain from the allowed domains list.
+ * @param id The ID of the DomainAllow in the database.
+ * @see Mastodon API documentation: admin/domain_allows/#delete
+ */
+ fun removeAllowedDomain(id: String): Completable = Completable.fromAction {
+ adminAllowMethods.removeAllowedDomain(id = id)
+ }
+}
diff --git a/bigbone/src/main/kotlin/social/bigbone/MastodonClient.kt b/bigbone/src/main/kotlin/social/bigbone/MastodonClient.kt
index 641f141f9..af62f96c1 100644
--- a/bigbone/src/main/kotlin/social/bigbone/MastodonClient.kt
+++ b/bigbone/src/main/kotlin/social/bigbone/MastodonClient.kt
@@ -68,6 +68,7 @@ import social.bigbone.api.method.TrendMethods
import social.bigbone.api.method.admin.AdminAccountMethods
import social.bigbone.api.method.admin.AdminCanonicalEmailBlockMethods
import social.bigbone.api.method.admin.AdminDimensionMethods
+import social.bigbone.api.method.admin.AdminDomainAllowMethods
import social.bigbone.api.method.admin.AdminDomainBlockMethods
import social.bigbone.api.method.admin.AdminEmailDomainBlockMethods
import social.bigbone.api.method.admin.AdminIpBlockMethods
@@ -130,6 +131,13 @@ private constructor(
@get:JvmName("adminDimensions")
val adminDimensions: AdminDimensionMethods by lazy { AdminDimensionMethods(this) }
+ /**
+ * Access API methods under the "admin/domain_allows" endpoint.
+ */
+ @Suppress("unused") // public API
+ @get:JvmName("adminDomainAllow")
+ val adminDomainAllow: AdminDomainAllowMethods by lazy { AdminDomainAllowMethods(this) }
+
/**
* Access API methods under the "admin/domain_blocks" endpoint.
*/
diff --git a/bigbone/src/main/kotlin/social/bigbone/api/entity/admin/AdminDomainAllow.kt b/bigbone/src/main/kotlin/social/bigbone/api/entity/admin/AdminDomainAllow.kt
new file mode 100644
index 000000000..1dbde083c
--- /dev/null
+++ b/bigbone/src/main/kotlin/social/bigbone/api/entity/admin/AdminDomainAllow.kt
@@ -0,0 +1,32 @@
+package social.bigbone.api.entity.admin
+
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+import social.bigbone.DateTimeSerializer
+import social.bigbone.PrecisionDateTime
+
+/**
+ * Represents a domain allowed to federate.
+ * @see Mastodon API Admin::DomainAllow/
+ */
+@Serializable
+data class AdminDomainAllow(
+ /**
+ * The ID of the DomainAllow in the database.
+ */
+ @SerialName("id")
+ val id: String = "0",
+
+ /**
+ * The domain that is allowed to federate.
+ */
+ @SerialName("domain")
+ val domain: String = "",
+
+ /**
+ * When the domain was allowed to federate.
+ */
+ @SerialName("created_at")
+ @Serializable(with = DateTimeSerializer::class)
+ val createdAt: PrecisionDateTime = PrecisionDateTime.InvalidPrecisionDateTime.Unavailable
+)
diff --git a/bigbone/src/main/kotlin/social/bigbone/api/method/admin/AdminDomainAllowMethods.kt b/bigbone/src/main/kotlin/social/bigbone/api/method/admin/AdminDomainAllowMethods.kt
new file mode 100644
index 000000000..956747241
--- /dev/null
+++ b/bigbone/src/main/kotlin/social/bigbone/api/method/admin/AdminDomainAllowMethods.kt
@@ -0,0 +1,70 @@
+package social.bigbone.api.method.admin
+
+import social.bigbone.MastodonClient
+import social.bigbone.MastodonRequest
+import social.bigbone.Parameters
+import social.bigbone.api.Pageable
+import social.bigbone.api.Range
+import social.bigbone.api.entity.admin.AdminDomainAllow
+
+/**
+ * Allow certain domains to federate.
+ * @see Mastodon admin/domain_allows API methods
+ */
+class AdminDomainAllowMethods(private val client: MastodonClient) {
+
+ private val adminDomainAllowEndpoint = "api/v1/admin/domain_allows"
+
+ /**
+ * Show information about all allowed domains.
+ * @param range optional Range for the pageable return value
+ * @see Mastodon API documentation: methods/domain_allows/#get
+ */
+ @JvmOverloads
+ fun getAllAllowedDomains(range: Range = Range()): MastodonRequest> {
+ return client.getPageableMastodonRequest(
+ endpoint = adminDomainAllowEndpoint,
+ method = MastodonClient.Method.GET,
+ parameters = range.toParameters()
+ )
+ }
+
+ /**
+ * Show information about a single allowed domain.
+ * @param id The ID of the DomainAllow in the database.
+ * @see Mastodon API documentation: methods/domain_allows/#get-one
+ */
+ fun getAllowedDomain(id: String): MastodonRequest {
+ return client.getMastodonRequest(
+ endpoint = "$adminDomainAllowEndpoint/$id",
+ method = MastodonClient.Method.GET
+ )
+ }
+
+ /**
+ * Add a domain to the list of domains allowed to federate, to be used when the instance is in allow-list federation mode.
+ * @param domain The domain to allow federation with.
+ * @see Mastodon API documentation: admin/domain_allows/#create
+ */
+ fun allowDomainToFederate(domain: String): MastodonRequest {
+ return client.getMastodonRequest(
+ endpoint = adminDomainAllowEndpoint,
+ method = MastodonClient.Method.POST,
+ parameters = Parameters().apply {
+ append("domain", domain)
+ }
+ )
+ }
+
+ /**
+ * Delete a domain from the allowed domains list.
+ * @param id The ID of the DomainAllow in the database.
+ * @see Mastodon API documentation: admin/domain_allows/#delete
+ */
+ fun removeAllowedDomain(id: String) {
+ client.performAction(
+ endpoint = "$adminDomainAllowEndpoint/$id",
+ method = MastodonClient.Method.DELETE
+ )
+ }
+}
diff --git a/bigbone/src/main/kotlin/social/bigbone/api/method/admin/AdminDomainBlockMethods.kt b/bigbone/src/main/kotlin/social/bigbone/api/method/admin/AdminDomainBlockMethods.kt
index 1dc8f10ac..aa554a092 100644
--- a/bigbone/src/main/kotlin/social/bigbone/api/method/admin/AdminDomainBlockMethods.kt
+++ b/bigbone/src/main/kotlin/social/bigbone/api/method/admin/AdminDomainBlockMethods.kt
@@ -15,7 +15,7 @@ import social.bigbone.api.entity.admin.AdminDomainBlock.Severity
*/
class AdminDomainBlockMethods(private val client: MastodonClient) {
- private val adminDomainBlocksEndpoint = "api/v1/admin/domain_blocks"
+ private val endpoint = "api/v1/admin/domain_blocks"
/**
* Show information about all blocked domains.
@@ -27,7 +27,7 @@ class AdminDomainBlockMethods(private val client: MastodonClient) {
@JvmOverloads
fun getAllBlockedDomains(range: Range = Range()): MastodonRequest> {
return client.getPageableMastodonRequest(
- endpoint = adminDomainBlocksEndpoint,
+ endpoint = endpoint,
method = MastodonClient.Method.GET,
parameters = range.toParameters()
)
@@ -42,7 +42,7 @@ class AdminDomainBlockMethods(private val client: MastodonClient) {
*/
fun getBlockedDomain(id: String): MastodonRequest {
return client.getMastodonRequest(
- endpoint = "$adminDomainBlocksEndpoint/$id",
+ endpoint = "$endpoint/$id",
method = MastodonClient.Method.GET
)
}
@@ -71,7 +71,7 @@ class AdminDomainBlockMethods(private val client: MastodonClient) {
publicComment: String? = null
): MastodonRequest {
return client.getMastodonRequest(
- endpoint = adminDomainBlocksEndpoint,
+ endpoint = endpoint,
method = MastodonClient.Method.POST,
parameters = Parameters().apply {
append("domain", domain)
@@ -113,7 +113,7 @@ class AdminDomainBlockMethods(private val client: MastodonClient) {
publicComment: String? = null
): MastodonRequest {
return client.getMastodonRequest(
- endpoint = "$adminDomainBlocksEndpoint/$id",
+ endpoint = "$endpoint/$id",
method = MastodonClient.Method.PUT,
parameters = Parameters().apply {
severity?.let { append("severity", severity.apiName) }
@@ -138,7 +138,7 @@ class AdminDomainBlockMethods(private val client: MastodonClient) {
*/
fun removeBlockedDomain(id: String) {
client.performAction(
- endpoint = "$adminDomainBlocksEndpoint/$id",
+ endpoint = "$endpoint/$id",
method = MastodonClient.Method.DELETE
)
}
diff --git a/bigbone/src/test/assets/admin_domain_allow.json b/bigbone/src/test/assets/admin_domain_allow.json
new file mode 100644
index 000000000..94fd8ae3a
--- /dev/null
+++ b/bigbone/src/test/assets/admin_domain_allow.json
@@ -0,0 +1,5 @@
+{
+ "id": "1",
+ "domain": "mastodon.social",
+ "created_at": "2022-09-14T21:23:02.755Z"
+}
\ No newline at end of file
diff --git a/bigbone/src/test/assets/admin_domain_allow_list.json b/bigbone/src/test/assets/admin_domain_allow_list.json
new file mode 100644
index 000000000..f70c0faee
--- /dev/null
+++ b/bigbone/src/test/assets/admin_domain_allow_list.json
@@ -0,0 +1,12 @@
+[
+ {
+ "id": "2",
+ "domain": "mastodon",
+ "created_at": "2022-09-14T21:24:15.360Z"
+ },
+ {
+ "id": "1",
+ "domain": "mastodon.social",
+ "created_at": "2022-09-14T21:23:02.755Z"
+ }
+]
\ No newline at end of file
diff --git a/bigbone/src/test/kotlin/social/bigbone/api/method/admin/AdminDomainAllowMethodsTest.kt b/bigbone/src/test/kotlin/social/bigbone/api/method/admin/AdminDomainAllowMethodsTest.kt
new file mode 100644
index 000000000..6deb1356f
--- /dev/null
+++ b/bigbone/src/test/kotlin/social/bigbone/api/method/admin/AdminDomainAllowMethodsTest.kt
@@ -0,0 +1,116 @@
+package social.bigbone.api.method.admin
+
+import io.mockk.slot
+import io.mockk.verify
+import org.amshove.kluent.AnyException
+import org.amshove.kluent.invoking
+import org.amshove.kluent.shouldBeEqualTo
+import org.amshove.kluent.shouldNotThrow
+import org.amshove.kluent.shouldThrow
+import org.amshove.kluent.withMessage
+import org.junit.jupiter.api.Test
+import social.bigbone.MastodonClient
+import social.bigbone.Parameters
+import social.bigbone.api.Range
+import social.bigbone.api.exception.BigBoneRequestException
+import social.bigbone.testtool.MockClient
+
+class AdminDomainAllowMethodsTest {
+
+ private val adminDomainAllowEndpoint = "api/v1/admin/domain_allows"
+
+ @Test
+ fun `Given client returning success, when getting all allowed domains, then return list of allowed domains`() {
+ val client = MockClient.mock("admin_domain_allow_list.json")
+ val adminDomainAllowMethods = AdminDomainAllowMethods(client)
+ val range = Range("1", "10", "1", 10)
+ val allowedDomains = adminDomainAllowMethods.getAllAllowedDomains(range).execute()
+ allowedDomains.part.size shouldBeEqualTo 2
+ with(allowedDomains.part[1]) {
+ id shouldBeEqualTo "1"
+ domain shouldBeEqualTo "mastodon.social"
+ }
+
+ val parametersCapturingSlot = slot()
+ verify {
+ client.get(
+ path = adminDomainAllowEndpoint,
+ query = capture(parametersCapturingSlot)
+ )
+ }
+ with(parametersCapturingSlot.captured) {
+ toQuery() shouldBeEqualTo "max_id=${range.maxId}&min_id=${range.minId}&since_id=${range.sinceId}&limit=${range.limit}"
+ }
+ }
+
+ @Test
+ fun `Given client returning success, when getting specific allowed domain, then return expected data for single domain`() {
+ val client = MockClient.mock("admin_domain_allow.json")
+ val adminDomainAllowMethods = AdminDomainAllowMethods(client)
+ val pathVariable = "1"
+ val adminDomainAllowed = adminDomainAllowMethods.getAllowedDomain(pathVariable).execute()
+ with(adminDomainAllowed) {
+ id shouldBeEqualTo pathVariable
+ domain shouldBeEqualTo "mastodon.social"
+ }
+
+ verify {
+ client.get(
+ path = "$adminDomainAllowEndpoint/$pathVariable"
+ )
+ }
+ }
+
+ @Test
+ fun `Given client returning success, when posting a domain to be allowed to federate, then return expected data about the domain`() {
+ val client = MockClient.mock("admin_domain_allow.json")
+ val adminDomainAllowMethods = AdminDomainAllowMethods(client)
+
+ invoking {
+ adminDomainAllowMethods.allowDomainToFederate(domain = "mastodon.social").execute()
+ } shouldNotThrow AnyException
+
+ val parametersCapturingSlot = slot()
+ verify {
+ client.post(
+ path = adminDomainAllowEndpoint,
+ body = capture(parametersCapturingSlot),
+ addIdempotencyKey = false
+ )
+ }
+ with(parametersCapturingSlot.captured) {
+ toQuery() shouldBeEqualTo "domain=mastodon.social"
+ }
+ }
+
+ @Test
+ fun `Given client returning success, when removing specific domain from being allowd, then check data for single ip`() {
+ val client = MockClient.mock("admin_domain_allow.json")
+ val adminDomainAllowMethods = AdminDomainAllowMethods(client)
+ val id = "1"
+
+ invoking {
+ adminDomainAllowMethods.removeAllowedDomain(id = id)
+ } shouldNotThrow AnyException
+
+ verify {
+ client.performAction(
+ endpoint = "$adminDomainAllowEndpoint/$id",
+ method = MastodonClient.Method.DELETE
+ )
+ }
+ }
+
+ @Test
+ fun `Given a client returning forbidden, when getting specific allowed domain, then propagate error`() {
+ val client = MockClient.failWithResponse(
+ responseJsonAssetPath = "error_403_forbidden.json",
+ responseCode = 403,
+ message = "Forbidden"
+ )
+
+ invoking {
+ AdminDomainAllowMethods(client).getAllowedDomain(id = "1").execute()
+ } shouldThrow BigBoneRequestException::class withMessage "Forbidden"
+ }
+}
diff --git a/docs/api-coverage/admin/domain-allows.md b/docs/api-coverage/admin/domain-allows.md
index 431e4586f..f093c16d7 100644
--- a/docs/api-coverage/admin/domain-allows.md
+++ b/docs/api-coverage/admin/domain-allows.md
@@ -11,8 +11,8 @@ nav_order: 7
https://docs.joinmastodon.org/methods/admin/domain_allows/
| Method | Description | Status | Comments |
-|------------------------------------------|-----------------------------|--------|----------|
-| `GET /api/v1/admin/domain_allows` | List all allowed domains | 🔴 | |
-| `GET /api/v1/admin/domain_allows/:id` | Get a single allowed domain | 🔴 | |
-| `POST /api/v1/admin/domain_allows` | Allow a domain to federate | 🔴 | |
-| `DELETE /api/v1/admin/domain_allows/:id` | Delete an allowed domain | 🔴 | |
+|------------------------------------------|-----------------------------|----|--|
+| `GET /api/v1/admin/domain_allows` | List all allowed domains |
| Fully supported. |
+| `GET /api/v1/admin/domain_allows/:id` | Get a single allowed domain |
| Fully supported. |
+| `POST /api/v1/admin/domain_allows` | Allow a domain to federate |
| Fully supported. |
+| `DELETE /api/v1/admin/domain_allows/:id` | Delete an allowed domain |
| Fully supported. |