Repository: getmoto/moto

Issue:
Support NextToken for SSM's GetParametersByPath
Currently SSM implementation doesn't return NextToken yet. It would be nice if this is added into feature backlog. For now we have to add `pragma: no branch` to ignore 'NextToken' branch.

I have seen a similar request for SNS. Wonder if this could be quickly implement using the same way. https://github.com/spulec/moto/issues/144


Discussion:
Thanks; setting as a feature request.

Resolving patch:
diff --git a/moto/ssm/models.py b/moto/ssm/models.py
--- a/moto/ssm/models.py
+++ b/moto/ssm/models.py
@@ -510,7 +510,15 @@ def get_parameters(self, names, with_decryption):
                 result.append(self.get_parameter(name, with_decryption))
         return result
 
-    def get_parameters_by_path(self, path, with_decryption, recursive, filters=None):
+    def get_parameters_by_path(
+        self,
+        path,
+        with_decryption,
+        recursive,
+        filters=None,
+        next_token=None,
+        max_results=10,
+    ):
         """Implement the get-parameters-by-path-API in the backend."""
         result = []
         # path could be with or without a trailing /. we handle this
@@ -527,7 +535,19 @@ def get_parameters_by_path(self, path, with_decryption, recursive, filters=None)
                 continue
             result.append(self.get_parameter(param_name, with_decryption))
 
-        return result
+        return self._get_values_nexttoken(result, max_results, next_token)
+
+    def _get_values_nexttoken(self, values_list, max_results, next_token=None):
+        if next_token is None:
+            next_token = 0
+        next_token = int(next_token)
+        max_results = int(max_results)
+        values = values_list[next_token : next_token + max_results]
+        if len(values) == max_results:
+            next_token = str(next_token + max_results)
+        else:
+            next_token = None
+        return values, next_token
 
     def get_parameter_history(self, name, with_decryption):
         if name in self._parameters:
diff --git a/moto/ssm/responses.py b/moto/ssm/responses.py
--- a/moto/ssm/responses.py
+++ b/moto/ssm/responses.py
@@ -77,12 +77,19 @@ def get_parameters_by_path(self):
         with_decryption = self._get_param("WithDecryption")
         recursive = self._get_param("Recursive", False)
         filters = self._get_param("ParameterFilters")
-
-        result = self.ssm_backend.get_parameters_by_path(
-            path, with_decryption, recursive, filters
+        token = self._get_param("NextToken")
+        max_results = self._get_param("MaxResults", 10)
+
+        result, next_token = self.ssm_backend.get_parameters_by_path(
+            path,
+            with_decryption,
+            recursive,
+            filters,
+            next_token=token,
+            max_results=max_results,
         )
 
-        response = {"Parameters": []}
+        response = {"Parameters": [], "NextToken": next_token}
 
         for parameter in result:
             param_data = parameter.response_object(with_decryption)


Test patch:
diff --git a/tests/test_ssm/test_ssm_boto3.py b/tests/test_ssm/test_ssm_boto3.py
--- a/tests/test_ssm/test_ssm_boto3.py
+++ b/tests/test_ssm/test_ssm_boto3.py
@@ -158,6 +158,20 @@ def test_get_parameters_by_path():
     len(response["Parameters"]).should.equal(1)
     {p["Name"] for p in response["Parameters"]}.should.equal(set(["/baz/pwd"]))
 
+    response = client.get_parameters_by_path(Path="/", Recursive=True, MaxResults=4)
+    len(response["Parameters"]).should.equal(4)
+    response["NextToken"].should.equal("4")
+    response = client.get_parameters_by_path(
+        Path="/", Recursive=True, MaxResults=4, NextToken=response["NextToken"]
+    )
+    len(response["Parameters"]).should.equal(4)
+    response["NextToken"].should.equal("8")
+    response = client.get_parameters_by_path(
+        Path="/", Recursive=True, MaxResults=4, NextToken=response["NextToken"]
+    )
+    len(response["Parameters"]).should.equal(1)
+    response.should_not.have.key("NextToken")
+
 
 @mock_ssm
 def test_put_parameter():