Repository: TheFriendlyCoder/friendlypins

Issue:
Add code to create new boards
The next logical progression in the API development is to add code for creating new boards for a particular authenticated user.

Discussion:

Patch:
diff --git a/src/friendlypins/api.py b/src/friendlypins/api.py
index f8b7255..4b014c9 100644
--- a/src/friendlypins/api.py
+++ b/src/friendlypins/api.py
@@ -25,7 +25,18 @@ class API(object):  # pylint: disable=too-few-public-methods
         """
         self._log.debug("Getting authenticated user details...")
 
-        fields = "id,username,first_name,last_name,bio,created_at,counts,image"
+        fields = ",".join([
+            "id",
+            "username",
+            "first_name",
+            "last_name",
+            "bio",
+            "created_at",
+            "counts",
+            "image",
+            "account_type",
+            "url"
+        ])
         result = self._io.get("me", {"fields": fields})
         assert 'data' in result
 
diff --git a/src/friendlypins/board.py b/src/friendlypins/board.py
index d8626f6..4118157 100644
--- a/src/friendlypins/board.py
+++ b/src/friendlypins/board.py
@@ -47,6 +47,14 @@ class Board(object):
         """
         return self._data['name']
 
+    @property
+    def description(self):
+        """Gets the descriptive text associated with this board
+
+        :rtype: :class:`str`
+        """
+        return self._data['description']
+
     @property
     def url(self):
         """Web address for the UI associated with the dashboard
diff --git a/src/friendlypins/user.py b/src/friendlypins/user.py
index 42367b6..2230b65 100644
--- a/src/friendlypins/user.py
+++ b/src/friendlypins/user.py
@@ -109,7 +109,9 @@ class User(object):
                 "creator",
                 "created_at",
                 "counts",
-                "image"
+                "image",
+                "reason",
+                "privacy"
             ])
         }
 
@@ -119,6 +121,35 @@ class User(object):
             for cur_item in cur_page['data']:
                 yield Board(cur_item, self._io)
 
+    def create_board(self, name, description=None):
+        """Creates a new board for the currently authenticated user
+
+        :param str name: name for the new board
+        :param str description: optional descriptive text for the board
+        :returns: reference to the newly created board
+        :rtype: :class:`friendlypins.board.Board`
+        """
+        properties = {
+            "fields": ','.join([
+                "id",
+                "name",
+                "url",
+                "description",
+                "creator",
+                "created_at",
+                "counts",
+                "image",
+                "reason",
+                "privacy"
+            ])
+        }
+
+        data = {"name": name}
+        if description:
+            data["description"] = description
+
+        result = self._io.post("boards", data, properties)
+        return Board(result['data'], self._io)
 
 if __name__ == "__main__":
     pass
diff --git a/src/friendlypins/utils/rest_io.py b/src/friendlypins/utils/rest_io.py
index 20456a5..ed7a77e 100644
--- a/src/friendlypins/utils/rest_io.py
+++ b/src/friendlypins/utils/rest_io.py
@@ -59,12 +59,44 @@ class RestIO(object):
         properties["access_token"] = self._token
 
         response = requests.get(temp_url, params=properties)
+
+        self._log.debug("Get response text is %s", response.text)
         self._latest_header = Headers(response.headers)
         self._log.debug("%s query header: %s", path, self._latest_header)
         response.raise_for_status()
 
         return response.json()
 
+    def post(self, path, data, properties=None):
+        """Posts API data to a given sub-path
+
+        :param str path: sub-path with in the REST API to send data to
+        :param dict data: form data to be posted to the API endpoint
+        :param dict properties:
+            optional set of request properties to append to the API call
+        :returns: json data returned from the API endpoint
+        :rtype: :class:`dict`
+        """
+        self._log.debug(
+            "Posting data from %s with options %s",
+            path,
+            properties
+        )
+        temp_url = "{0}/{1}/".format(self._root_url, path)
+
+        if properties is None:
+            properties = dict()
+        properties["access_token"] = self._token
+
+        response = requests.post(temp_url, data=data, params=properties)
+        self._latest_header = Headers(response.headers)
+        self._log.debug("%s query header: %s", path, self._latest_header)
+        self._log.debug("Post response text is %s", response.text)
+
+        response.raise_for_status()
+
+        return response.json()
+
     def get_pages(self, path, properties=None):
         """Generator for iterating over paged results returned from API
 


Test patch:
diff --git a/unit_tests/test_rest_io.py b/unit_tests/test_rest_io.py
index b8b4b92..4be8eab 100644
--- a/unit_tests/test_rest_io.py
+++ b/unit_tests/test_rest_io.py
@@ -43,5 +43,30 @@ def test_get_headers(mock_requests):
     assert tmp.bytes == expected_bytes
 
 
+@mock.patch("friendlypins.utils.rest_io.requests")
+def test_post(mock_requests):
+    obj = RestIO("1234abcd")
+    expected_path = "me/boards"
+    expected_data = {
+        "name": "My New Board",
+        "description": "Here is my cool description"
+    }
+
+    expected_results = {
+        "testing": "123"
+    }
+    mock_response = mock.MagicMock()
+    mock_requests.post.return_value = mock_response
+    mock_response.json.return_value = expected_results
+
+    res = obj.post(expected_path, expected_data)
+
+    mock_response.json.assert_called_once()
+    mock_requests.post.assert_called_once()
+
+    assert expected_path in mock_requests.post.call_args[0][0]
+    assert "data" in mock_requests.post.call_args[1]
+    assert mock_requests.post.call_args[1]["data"] == expected_data
+
 if __name__ == "__main__":
     pytest.main([__file__, "-v", "-s"])
diff --git a/unit_tests/test_user.py b/unit_tests/test_user.py
index 6ce221e..75d051e 100644
--- a/unit_tests/test_user.py
+++ b/unit_tests/test_user.py
@@ -63,6 +63,29 @@ def test_get_boards():
     assert expected_name == result[0].name
     assert expected_id == result[0].unique_id
 
+def test_create_board():
+    expected_name = "My Board"
+    expected_desc = "My new board is about this stuff..."
+    data = {
+        "id": "1234",
+        "first_name": "Jonh",
+        "last_name": "Doe"
+    }
+    mock_io = mock.MagicMock()
+    mock_io.post.return_value = {
+        "data": {
+            "name": expected_name,
+            "description": expected_desc,
+            "id": "12345"
+        }
+    }
+    obj = User(data, mock_io)
+
+    board = obj.create_board(expected_name, expected_desc)
+    mock_io.post.assert_called_once()
+    assert board is not None
+    assert board.name == expected_name
+    assert board.description == expected_desc
 
 if __name__ == "__main__":
     pytest.main([__file__, "-v", "-s"])