Repository: hikari-py/hikari
Pull request #2020 (2024-08-16 10:59:08)

Add fetch_role function
### Summary
[Added this](https://discord.com/developers/docs/change-log#get-guild-role-endpoint)

### Checklist
<!-- Make sure to tick all the following boxes by putting an `x` in between (like this `[x]`) -->
- [x] I have run `nox` and all the pipelines have passed.
- [x] I have made unittests according to the code I have added/modified/deleted.

### Related issues
<!--
To mention an issue use `#issue-id` and to mention a merge request use `!merge-request-id`
To close/fix an issue use `Close #issue-id` or `Fix #issue-id` (depending on the merge request)
-->


syncblaze: anything left needing to change?

Patch:
diff --git a/changes/2020.feature.md b/changes/2020.feature.md
new file mode 100644
index 00000000..e87de0ac
--- /dev/null
+++ b/changes/2020.feature.md
@@ -0,0 +1,1 @@
+Add `fetch_role` method to `RESTClient`
diff --git a/hikari/api/rest.py b/hikari/api/rest.py
index 8ad797eb..537f5ab4 100644
--- a/hikari/api/rest.py
+++ b/hikari/api/rest.py
@@ -5926,6 +5926,39 @@ def fetch_bans(
             If an internal error occurs on Discord while handling the request.
         """
 
+    @abc.abstractmethod
+    async def fetch_role(
+        self, guild: snowflakes.SnowflakeishOr[guilds.PartialGuild], role: snowflakes.SnowflakeishOr[guilds.PartialRole]
+    ) -> guilds.Role:
+        """Fetch a single role of a guild.
+
+        Parameters
+        ----------
+        guild
+            The guild to fetch the role from. This may be the
+            object or the ID of an existing guild.
+        role
+            The role to fetch. This may be the object or the
+            ID of an existing role.
+
+        Returns
+        -------
+        hikari.guilds.Role
+            The requested role.
+
+        Raises
+        ------
+        hikari.errors.UnauthorizedError
+            If you are unauthorized to make the request (invalid/missing token).
+        hikari.errors.NotFoundError
+            If the guild or the role is not found.
+        hikari.errors.RateLimitTooLongError
+            Raised in the event that a rate limit occurs that is
+            longer than `max_rate_limit` when making a request.
+        hikari.errors.InternalServerError
+            If an internal error occurs on Discord while handling the request.
+        """
+
     @abc.abstractmethod
     async def fetch_roles(self, guild: snowflakes.SnowflakeishOr[guilds.PartialGuild]) -> typing.Sequence[guilds.Role]:
         """Fetch the roles of a guild.
diff --git a/hikari/impl/rest.py b/hikari/impl/rest.py
index f76f4c58..fbceb44c 100644
--- a/hikari/impl/rest.py
+++ b/hikari/impl/rest.py
@@ -3433,6 +3433,15 @@ def fetch_bans(
             self._entity_factory, self._request, guild, newest_first, str(start_at)
         )
 
+    async def fetch_role(
+        self, guild: snowflakes.SnowflakeishOr[guilds.PartialGuild], role: snowflakes.SnowflakeishOr[guilds.PartialRole]
+    ) -> guilds.Role:
+        route = routes.GET_GUILD_ROLE.compile(guild=guild, role=role)
+        response = await self._request(route)
+        assert isinstance(response, dict)
+        guild_id = snowflakes.Snowflake(guild)
+        return self._entity_factory.deserialize_role(response, guild_id=guild_id)
+
     async def fetch_roles(self, guild: snowflakes.SnowflakeishOr[guilds.PartialGuild]) -> typing.Sequence[guilds.Role]:
         route = routes.GET_GUILD_ROLES.compile(guild=guild)
         response = await self._request(route)
diff --git a/hikari/internal/routes.py b/hikari/internal/routes.py
index 26ebc20d..484c41fb 100644
--- a/hikari/internal/routes.py
+++ b/hikari/internal/routes.py
@@ -434,6 +434,7 @@ def compile_to_file(
 GET_GUILD_PRUNE: typing.Final[Route] = Route(GET, "/guilds/{guild}/prune")
 POST_GUILD_PRUNE: typing.Final[Route] = Route(POST, "/guilds/{guild}/prune")
 
+GET_GUILD_ROLE: typing.Final[Route] = Route(GET, "/guilds/{guild}/roles/{role}")
 PATCH_GUILD_ROLE: typing.Final[Route] = Route(PATCH, "/guilds/{guild}/roles/{role}")
 DELETE_GUILD_ROLE: typing.Final[Route] = Route(DELETE, "/guilds/{guild}/roles/{role}")
 


Test patch:
diff --git a/tests/hikari/impl/test_rest.py b/tests/hikari/impl/test_rest.py
index 0c068783..b69d752a 100644
--- a/tests/hikari/impl/test_rest.py
+++ b/tests/hikari/impl/test_rest.py
@@ -5130,6 +5130,17 @@ async def test_fetch_ban(self, rest_client):
         rest_client._request.assert_awaited_once_with(expected_route)
         rest_client._entity_factory.deserialize_guild_member_ban.assert_called_once_with({"id": "789"})
 
+    async def test_fetch_role(self, rest_client):
+        role = StubModel(456)
+        expected_route = routes.GET_GUILD_ROLE.compile(guild=123, role=456)
+        rest_client._request = mock.AsyncMock(return_value={"id": "456"})
+        rest_client._entity_factory.deserialize_role = mock.Mock(return_value=role)
+
+        assert await rest_client.fetch_role(StubModel(123), StubModel(456)) is role
+
+        rest_client._request.assert_awaited_once_with(expected_route)
+        rest_client._entity_factory.deserialize_role.assert_called_once_with({"id": "456"}, guild_id=123)
+
     async def test_fetch_roles(self, rest_client):
         role1 = StubModel(456)
         role2 = StubModel(789)