Repository: openlawlibrary/pygls

Issue:
Response object should contain result OR error field
**Current behavior:**

Both _result_ and _error_ fields are serialized and returned as part of response object to the client.

**Expected behavior:**

From json RPC [specification](https://www.jsonrpc.org/specification):

```
jsonrpc
A String specifying the version of the JSON-RPC protocol. MUST be exactly "2.0".

result
This member is REQUIRED on success.
This member MUST NOT exist if there was an error invoking the method.
The value of this member is determined by the method invoked on the Server.

error
This member is REQUIRED on error.
This member MUST NOT exist if there was no error triggered during invocation.
The value for this member MUST be an Object as defined in section 5.1.

id
This member is REQUIRED.
It MUST be the same as the value of the id member in the Request Object.
If there was an error in detecting the id in the Request object (e.g. Parse error/Invalid Request), it MUST be Null.
```

Discussion:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9a31d44..4667dd6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,10 +13,12 @@ and this project adheres to [Semantic Versioning][semver].
 
 ### Changed
 
+- Response object should contain result OR error field ([#64])
 - Fix handling parameters whose names are reserved by Python ([#56])
 
-[#56]: https://github.com/openlawlibrary/pygls/pull/56
+[#64]: https://github.com/openlawlibrary/pygls/pull/64
 [#61]: https://github.com/openlawlibrary/pygls/pull/61
+[#56]: https://github.com/openlawlibrary/pygls/pull/56
 
 ## [0.7.4] - 03/21/2019
 
diff --git a/pygls/protocol.py b/pygls/protocol.py
index 181132f..d035c8f 100644
--- a/pygls/protocol.py
+++ b/pygls/protocol.py
@@ -156,6 +156,13 @@ class JsonRPCResponseMessage:
         self.result = result
         self.error = error
 
+    def without_none_fields(self):
+        if self.result:
+            del self.error
+        else:
+            del self.result
+        return self
+
 
 class JsonRPCProtocol(asyncio.Protocol):
     """Json RPC protocol implementation using on top of `asyncio.Protocol`.
@@ -386,7 +393,7 @@ class JsonRPCProtocol(asyncio.Protocol):
                                           JsonRPCProtocol.VERSION,
                                           result,
                                           error)
-        self._send_data(response)
+        self._send_data(response.without_none_fields())
 
     def connection_made(self, transport: asyncio.Transport):
         """Method from base class, called when connection is established"""


Test patch:
diff --git a/tests/test_protocol.py b/tests/test_protocol.py
index 1b5ed79..3e9cf6f 100644
--- a/tests/test_protocol.py
+++ b/tests/test_protocol.py
@@ -71,7 +71,12 @@ def test_deserialize_message_without_jsonrpc_field__should_return_object():
 
     assert type(result).__name__ == 'Object'
     assert result.random == "data"
-    assert result._1 == "def"
+
+    # namedtuple does not guarantee field order
+    try:
+        assert result._0 == "def"
+    except AttributeError:
+        assert result._1 == "def"
 
 
 def test_deserialize_message_should_return_response_message():
@@ -152,6 +157,24 @@ def test_initialize_should_return_server_capabilities(client_server):
     assert isinstance(server_capabilities, InitializeResult)
 
 
+def test_response_object_fields():
+    # Result field set
+    response = JsonRPCResponseMessage(0, '2.0', 'result', None).without_none_fields()
+
+    assert hasattr(response, 'id')
+    assert hasattr(response, 'jsonrpc')
+    assert hasattr(response, 'result')
+    assert not hasattr(response, 'error')
+
+    # Error field set
+    response = JsonRPCResponseMessage(0, '2.0', None, 'error').without_none_fields()
+
+    assert hasattr(response, 'id')
+    assert hasattr(response, 'jsonrpc')
+    assert hasattr(response, 'error')
+    assert not hasattr(response, 'result')
+
+
 def test_to_lsp_name():
     f_name = 'text_document__did_open'
     name = 'textDocument/didOpen'