Repository: pseudonym117/Riot-Watcher

Issue:
Positional Ranking Deprecation
Riot's API team [just announced](https://www.riotgames.com/en/DevRel/riot-api-update-190417) that positional ranking endpoints have been deprecated and will be removed on Monday, June 17th, meaning there are changes to the `league-v4` API.

These endpoints are being removed:
- `/lol/league/v4/positional-rank-queues`
- `/lol/league/v4/positions/by-summoner/{encryptedSummonerId}`
- `/lol/league/v4/positions/{positionalQueue}/{tier}/{division}/{position}/{page}`

And these are being added:
- `/lol/league/v4/entries/by-summoner/{encryptedSummonerId}`
- `/lol/league/v4/entries/{queue}/{tier}/{division}`

Unfortunately, I don't currently have the bandwidth to make a PR for this myself, but I hope that by posting this early on, someone will be able to pick up this issue! 😓 

Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/pseudonym117/Riot-Watcher/pull/118?src=pr&el=h1) Report
> Merging [#118](https://codecov.io/gh/pseudonym117/Riot-Watcher/pull/118?src=pr&el=desc) into [master](https://codecov.io/gh/pseudonym117/Riot-Watcher/commit/4ce2587aad465e93ab920aaac0c60b17e70b433a?src=pr&el=desc) will **increase** coverage by `0.12%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/pseudonym117/Riot-Watcher/pull/118/graphs/tree.svg?width=650&token=a5BzeJ4fPu&height=150&src=pr)](https://codecov.io/gh/pseudonym117/Riot-Watcher/pull/118?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #118      +/-   ##
==========================================
+ Coverage   90.75%   90.87%   +0.12%     
==========================================
  Files          39       39              
  Lines         573      581       +8     
  Branches       61       61              
==========================================
+ Hits          520      528       +8     
  Misses         31       31              
  Partials       22       22
```


| [Impacted Files](https://codecov.io/gh/pseudonym117/Riot-Watcher/pull/118?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/riotwatcher/\_apis/urls/LeagueApiUrls.py](https://codecov.io/gh/pseudonym117/Riot-Watcher/pull/118/diff?src=pr&el=tree#diff-c3JjL3Jpb3R3YXRjaGVyL19hcGlzL3VybHMvTGVhZ3VlQXBpVXJscy5weQ==) | `100% <100%> (ø)` | :arrow_up: |
| [src/riotwatcher/\_apis/LeagueApiV4.py](https://codecov.io/gh/pseudonym117/Riot-Watcher/pull/118/diff?src=pr&el=tree#diff-c3JjL3Jpb3R3YXRjaGVyL19hcGlzL0xlYWd1ZUFwaVY0LnB5) | `100% <100%> (ø)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/pseudonym117/Riot-Watcher/pull/118?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/pseudonym117/Riot-Watcher/pull/118?src=pr&el=footer). Last update [4ce2587...db4c9a4](https://codecov.io/gh/pseudonym117/Riot-Watcher/pull/118?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/.gitignore b/.gitignore
index 17341c6..f3a28e9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -63,3 +63,4 @@ target/
 .vscode/
 
 api_key
+.venv
diff --git a/README.rst b/README.rst
index b7d555e..d0482ad 100644
--- a/README.rst
+++ b/README.rst
@@ -7,7 +7,7 @@ Check for full (read: slightly better) documentation `here <http://riot-watcher.
 
 RiotWatcher is a thin wrapper on top of the `Riot Games API for League
 of Legends <https://developer.riotgames.com/>`__. All public methods as
-of 1/7/2019 are supported in full.
+of 4/19/2019 are supported in full.
 
 RiotWatcher by default supports a naive rate limiter. This rate limiter will
 try to stop you from making too many requests, and in a single threaded test
@@ -114,6 +114,8 @@ vNext - Unlreleased
 
 Removed deprecated v3 endpoints
 
+Add support for league v4 entry/by-summoner and entry/queue/tier/division endpoints
+
 v2.5.0 - 1/7/2019
 ~~~~~~~~~~~~~~~~~
 
diff --git a/src/riotwatcher/_apis/LeagueApiV4.py b/src/riotwatcher/_apis/LeagueApiV4.py
index 945fcaa..a0cd40d 100644
--- a/src/riotwatcher/_apis/LeagueApiV4.py
+++ b/src/riotwatcher/_apis/LeagueApiV4.py
@@ -65,8 +65,40 @@ class LeagueApiV4(NamedEndpoint):
         url, query = LeagueApiV4Urls.by_id(region=region, league_id=league_id)
         return self._raw_request(self.by_id.__name__, region, url, query)
 
+    def by_summoner(self, region, encrypted_summoner_id):
+        """
+        Get league entries in all queues for a given summoner ID
+
+        :param string region:                   the region to execute this request on
+        :param string encrypted_summoner_id:    the summoner ID to query
+
+        :returns: Set[LeagueEntryDTO]
+        """
+        url, query = LeagueApiV4Urls.by_summoner(
+            region=region, encrypted_summoner_id=encrypted_summoner_id
+        )
+        return self._raw_request(self.by_summoner.__name__, region, url, query)
+
+    def entries(self, region, queue, tier, division):
+        """
+        Get all the league entries
+
+        :param string region:   the region to execute this request on
+        :param string queue:    the queue to query, i.e. RANKED_SOLO_5x5
+        :param string tier:     the tier to query, i.e. DIAMOND
+        :param string division: the division to query, i.e. III
+
+        :returns: Set[LeagueEntryDTO]
+        """
+        url, query = LeagueApiV4Urls.entries(
+            region=region, queue=queue, tier=tier, division=division
+        )
+        return self._raw_request(self.entries.__name__, region, url, query)
+
     def positions_by_summoner(self, region, encrypted_summoner_id):
         """
+        DEPRECATED
+
         Get league positions in all queues for a given summoner ID
 
         :param string region:                   the region to execute this request on
diff --git a/src/riotwatcher/_apis/urls/LeagueApiUrls.py b/src/riotwatcher/_apis/urls/LeagueApiUrls.py
index a45f1c2..0e0ffb2 100644
--- a/src/riotwatcher/_apis/urls/LeagueApiUrls.py
+++ b/src/riotwatcher/_apis/urls/LeagueApiUrls.py
@@ -12,6 +12,10 @@ class LeagueApiV4Urls(object):
     grandmaster_by_queue = LeagueV4Endpoint("/grandmasterleagues/by-queue/{queue}")
     by_id = LeagueV4Endpoint("/leagues/{league_id}")
     master_by_queue = LeagueV4Endpoint("/masterleagues/by-queue/{queue}")
+    by_summoner = LeagueV4Endpoint("/entries/by-summoner/{encrypted_summoner_id}")
+    entries = LeagueV4Endpoint("/entries/{queue}/{tier}/{division}")
+
+    # deprecated
     positions_by_summoner = LeagueV4Endpoint(
         "/positions/by-summoner/{encrypted_summoner_id}"
     )


Test patch:
diff --git a/tests/_apis/test_LeagueApiV4.py b/tests/_apis/test_LeagueApiV4.py
index 02120b6..5acaaa0 100644
--- a/tests/_apis/test_LeagueApiV4.py
+++ b/tests/_apis/test_LeagueApiV4.py
@@ -104,6 +104,54 @@ class TestLeagueApiV4(object):
 
         assert ret is expected_return
 
+    def test_by_summoner(self):
+        mock_base_api = MagicMock()
+        expected_return = object()
+        mock_base_api.raw_request.return_value = expected_return
+
+        league = LeagueApiV4(mock_base_api)
+        region = "fdsga"
+        encrypted_summoner_id = "enc_summoner_1"
+
+        ret = league.by_summoner(region, encrypted_summoner_id)
+
+        mock_base_api.raw_request.assert_called_once_with(
+            LeagueApiV4.__name__,
+            league.by_summoner.__name__,
+            region,
+            "https://{region}.api.riotgames.com/lol/league/v4/entries/by-summoner/{encrypted_summonerI_id}".format(
+                region=region, encrypted_summonerI_id=encrypted_summoner_id
+            ),
+            {},
+        )
+
+        assert ret is expected_return
+
+    def test_entries(self):
+        mock_base_api = MagicMock()
+        expected_return = object()
+        mock_base_api.raw_request.return_value = expected_return
+
+        league = LeagueApiV4(mock_base_api)
+        region = "hfhafg"
+        queue = "yolo_q"
+        tier = "wood"
+        division = "VI"
+
+        ret = league.entries(region, queue, tier, division)
+
+        mock_base_api.raw_request.assert_called_once_with(
+            LeagueApiV4.__name__,
+            league.entries.__name__,
+            region,
+            "https://{region}.api.riotgames.com/lol/league/v4/entries/{queue}/{tier}/{division}".format(
+                region=region, queue=queue, tier=tier, division=division
+            ),
+            {},
+        )
+
+        assert ret is expected_return
+
     def test_positions_by_summoner(self):
         mock_base_api = MagicMock()
         expected_return = object()
diff --git a/tests/integration/test_LeagueApiV4.py b/tests/integration/test_LeagueApiV4.py
index f3a8f30..bc903ee 100644
--- a/tests/integration/test_LeagueApiV4.py
+++ b/tests/integration/test_LeagueApiV4.py
@@ -90,6 +90,43 @@ class TestLeagueApiV4(object):
             headers={"X-Riot-Token": mock_context_v4.api_key},
         )
 
+    @pytest.mark.parametrize(
+        "encrypted_summoner_id",
+        ["50", "424299938281", "9999999999999999999999", "rtbf12345"],
+    )
+    def test_by_summoner(self, mock_context_v4, region, encrypted_summoner_id):
+        actual_response = mock_context_v4.watcher.league.by_summoner(
+            region, encrypted_summoner_id
+        )
+
+        assert mock_context_v4.expected_response == actual_response
+        mock_context_v4.get.assert_called_once_with(
+            "https://{region}.api.riotgames.com/lol/league/v4/entries/by-summoner/{encrypted_summoner_id}".format(
+                region=region, encrypted_summoner_id=encrypted_summoner_id
+            ),
+            params={},
+            headers={"X-Riot-Token": mock_context_v4.api_key},
+        )
+
+    @pytest.mark.parametrize(
+        "queue", ["RANKED_SOLO_5x5", "RANKED_FLEX_SR", "RANKED_FLEX_TT"]
+    )
+    @pytest.mark.parametrize("tier", ["IRON", "SILVER", "DIAMOND"])
+    @pytest.mark.parametrize("division", ["I", "IV"])
+    def test_entries(self, mock_context_v4, region, queue, tier, division):
+        actual_response = mock_context_v4.watcher.league.entries(
+            region, queue, tier, division
+        )
+
+        assert mock_context_v4.expected_response == actual_response
+        mock_context_v4.get.assert_called_once_with(
+            "https://{region}.api.riotgames.com/lol/league/v4/entries/{queue}/{tier}/{division}".format(
+                region=region, queue=queue, tier=tier, division=division
+            ),
+            params={},
+            headers={"X-Riot-Token": mock_context_v4.api_key},
+        )
+
     @pytest.mark.parametrize(
         "encrypted_summoner_id",
         ["50", "424299938281", "9999999999999999999999", "rtbf12345"],