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 }