1 // Written in D programming language
2 /**
3 * JSON-RPC 2.0 Protocol<br>
4 * 
5 * $(B This module contain JSON-RPC 2.0 response)
6 *
7 * See_Also:
8 *    $(LINK http://www.jsonrpc.org/specification)
9 *
10 * Copyright: © 2014 DSoftOut
11 * License: Subject to the terms of the MIT license, as written in the included LICENSE file.
12 * Authors: Zaramzan <shamyan.roman@gmail.com>
13 *
14 */
15 module json_rpc.response;
16 
17 import vibe.data.json;
18 import vibe.data.bson;
19 
20 import util;
21 
22 import json_rpc.error;
23 
24 /**
25 * Struct desctibes JSON-RPC 2.0 response
26 *
27 * Example
28 * ------
29 *  auto res1 = RpcResponse(1, error);
30 *  auto res2 = RpcResponse(Json(null), result);
31 *  aut0 res3 = RpcResponse("mycustomidsystem", result);
32 * ------
33 * 
34 * Authors: Zaramzan <shamyan.roman@gmail.com>
35 */
36 struct RpcResponse
37 {	
38 	private string jsonrpc = RPC_VERSION;
39 	
40 	mixin t_field!(RpcResult, "result");
41 	
42 	mixin t_field!(RpcError, "error");
43 	
44 	Json id = Json(null);
45 	
46 	this(Json id)
47 	{		
48 		this.id = id;
49 	}
50 	
51 	this(Json id, RpcError error)
52 	{
53 		this(id);
54 		
55 		this.error = error;
56 	}
57 	
58 	this(Json id, RpcResult result)
59 	{
60 		this(id);
61 		
62 		this.result = result;
63 	}
64 	
65 	Json toJson()
66 	{
67 		if (!isValid)
68 		{
69 			throw new RpcInternalError();
70 		}
71 		
72 		Json ret = Json.emptyObject;
73 		
74 		ret.jsonrpc = jsonrpc;
75 		
76 		if (f_result)
77 		{
78 			ret.result = result.toJson();
79 		}
80 		else if (f_error)
81 		{
82 			ret.error = error.toJson();
83 		}
84 		
85 		ret.id = id;
86 		
87 		return ret;
88 	}
89 	
90 	bool isValid() @property
91 	{
92 		return f_result || f_error;
93 	}
94 	
95 	shared void opAssign(shared RpcResponse res)
96 	{
97 		this = res;
98 	}
99 	
100 	shared(RpcResponse) toShared()
101 	{
102 		auto res = this;
103 		
104 		return cast(shared RpcResponse) res;
105 	}
106 }
107 
108 
109 /**
110 * Struct describes JSON-RPC 2.0 result which used in RpcRequest
111 *
112 * Example
113 * ------
114 *  auto res = RpcResult(res);
115 * ------ 
116 * 
117 * Authors: Zaramzan <shamyan.roman@gmail.com>
118 */
119 struct RpcResult
120 {
121 	mixin t_field!(Bson, "bson");
122 	
123 	///Supported only ctor from $(B Bson) yet
124 	this(in Bson bson)
125 	{
126 		this.bson = bson;
127 	}
128 	
129 	Json toJson() @property
130 	{
131 	    import std.array;
132 	    
133 		if (f_bson)
134 		{
135 		    auto json = bson.toJson;
136 		    assert(json.type == Json.Type.array);
137 		    if(json.length == 1) return json[0];
138 			else return json;
139 		}
140 		
141 		return Json.emptyObject;
142 	}
143 }
144 
145 version(unittest)
146 {
147 	import vibe.data.bson;
148 	import vibe.data.json;
149 	
150 	__gshared RpcResponse normalRes;
151 	__gshared RpcResponse  notificationRes;
152 	__gshared RpcResponse mnfRes;
153 	__gshared RpcResponse  invalidParasmRes;
154 	
155 	void initResponses()
156 	{		
157 		normalRes = RpcResponse(Json(1), 
158 			RpcResult(Bson([Bson(19)])));
159 
160 		notificationRes = RpcResponse(Json(null),
161 			RpcResult(Bson([Bson(966)])));
162 		
163 		mnfRes = RpcResponse(Json(null),
164 			RpcError(new RpcMethodNotFound()));
165 		
166 		invalidParasmRes = RpcResponse(Json(null),
167 			RpcError(new RpcInvalidParams()));
168 	
169 	}
170 }
171 
172 unittest
173 {
174 	import std.stdio;
175 	import vibe.data.bson;
176 	import vibe.data.json;
177 	
178 	//Testing normal response
179 	auto arr = new Bson[0];
180 	arr ~= Bson("1");
181 	arr ~= Bson(2);
182 	arr ~= Bson(null);
183 	
184 	auto result = RpcResult(Bson(arr));
185 	
186 	auto id = Json(null);
187 	
188 	auto res1 = RpcResponse(id, result).toJson();
189 	
190 	auto res2 = Json.emptyObject;
191 	res2.id = id;
192 	res2.jsonrpc = RPC_VERSION;
193 	res2.result = result.toJson();
194 	
195 	assert(res1 == res2, "RpcResponse unittest failed");
196 	
197 	
198 	//Testing error response
199 	auto code = cast(int) RPC_ERROR_CODE.METHOD_NOT_FOUND;
200 	auto message = "METHOD NOT FOUND";
201 	
202 	auto error = RpcError(cast(RPC_ERROR_CODE)code, message);
203 	
204 	auto res3 = RpcResponse(id, error).toJson();
205 	
206 	auto res4 = Json.emptyObject;
207 	res4.id = id;
208 	res4.jsonrpc = RPC_VERSION;
209 	res4.error = error.toJson();
210 	
211 	assert(res3 == res4, "RpcResponse unittest failed");
212 }