2121#include < iostream>
2222#include < string>
2323#include < unordered_set>
24+ #include < unordered_map>
2425
2526// namespaces
2627using std::cerr;
2728using std::cout;
2829using std::endl;
2930using std::string;
3031using std::unordered_set;
32+ using std::unordered_map;
3133namespace rj = rapidjson;
3234
3335
34- // /////////////////////////////////////////////////////////////////////////////
35- // Routines
36- // /////////////////////////////////////////////////////////////////////////////
37-
3836namespace jsonutils
3937{
40- // Helper class resolving remote schema for schema $ref operator
38+
39+ // //////////////////////////////////////////////////////////////////////////
40+ // Classes
41+ // //////////////////////////////////////////////////////////////////////////
42+
43+ // / Helper class resolving remote schema for schema $ref operator
4144 class MyRemoteSchemaDocumentProvider : public rj ::IRemoteSchemaDocumentProvider
4245 {
4346 public:
@@ -50,27 +53,129 @@ namespace jsonutils
5053 rj::Document d;
5154 d.ParseStream (fReadStream );
5255 fclose (pFileIn);
53- remoteSchemaDoc = new rj::SchemaDocument (d);
56+ remoteSchemaDoc_ = new rj::SchemaDocument (d);
5457 };
5558
56- ~MyRemoteSchemaDocumentProvider ()
57- {
58- delete remoteSchemaDoc ;
59- };
59+ // ~MyRemoteSchemaDocumentProvider()
60+ // {
61+ // delete remoteSchemaDoc_ ;
62+ // };
6063
6164 private:
6265
63- rj::SchemaDocument *remoteSchemaDoc ;
66+ rj::SchemaDocument *remoteSchemaDoc_ ;
6467
6568 virtual const rj::SchemaDocument*
6669 GetRemoteDocument (const char * uri, rj::SizeType length)
6770 {
6871 // Resolve the URI and return a pointer to that schema
69- return remoteSchemaDoc ;
72+ return remoteSchemaDoc_ ;
7073 }
7174 };
7275
73- // SAX events helper struct for jsonStrToUset() routine
76+ class BfxSchemaValidator
77+ {
78+ public:
79+
80+ BfxSchemaValidator () {}
81+ BfxSchemaValidator (unordered_set<string> &symbols,
82+ unordered_set<string> ¤cies)
83+ {
84+ // Mapping is needed because rapidjson implementation of $ref
85+ // keyword in json schema doesn't support json keys which contain
86+ // special characters.
87+ // See https://github.com/Tencent/rapidjson/issues/1311
88+
89+ // Create map for endpoints which use symbols in URL
90+ for (const auto &symbol : symbols)
91+ {
92+ // Map /pubticker/[symbol] endpoints to "pubticker" schema
93+ apiEndPointToSchemaMap_.emplace (" /pubticker/" + symbol,
94+ " pubticker" );
95+ // Map /stats/[symbol] endpoints to "stats" schema
96+ apiEndPointToSchemaMap_.emplace (" /stats/" + symbol,
97+ " stats" );
98+ // Map /book/[symbol] endpoints to "book" schema
99+ apiEndPointToSchemaMap_.emplace (" /book/" + symbol,
100+ " book" );
101+ // Map /trades/[symbol] endpoints to "trades" schema
102+ apiEndPointToSchemaMap_.emplace (" /trades/" + symbol,
103+ " trades" );
104+ }
105+
106+ // Create map for endpoints which use currencies in URL
107+ for (const auto ¤cy : currencies)
108+ {
109+ // Map /lendbook/[currency] endpoints to "lendbook" schema
110+ apiEndPointToSchemaMap_.emplace (" /lendbook/" + currency,
111+ " lendbook" );
112+ // Map /lends/[currency] endpoints to "lends" schema
113+ apiEndPointToSchemaMap_.emplace (" /lends/" + currency,
114+ " lends" );
115+ }
116+
117+ // Create map for static endpoints
118+ apiEndPointToSchemaMap_.emplace (" /symbols/" , " symbols" );
119+ apiEndPointToSchemaMap_.emplace (" /symbols_details/" ,
120+ " symbols_details" );
121+
122+ }
123+
124+ auto validateSchema (const string &apiEndPoint, const string &inputJson)
125+ {
126+ const auto schemaName = getApiEndPointSchemaName (apiEndPoint);
127+
128+ // Create rapidjson schema document
129+ rj::Document sd;
130+ string schema =
131+ " { \" $ref\" : \" definitions.json#/" + schemaName + " \" }" ;
132+ sd.Parse (schema.c_str ());
133+ rj::SchemaDocument schemaDocument (sd, 0 , 0 , &provider_);
134+
135+ // Create rapidjson document and check for parse errors
136+ rj::Document d;
137+ if (d.Parse (inputJson.c_str ()).HasParseError ())
138+ {
139+ cerr << " Invalid json - response:" << endl;
140+ cerr << inputJson << endl;
141+ cerr << " API endpoint: " << apiEndPoint << endl;
142+ return bfxERR::responseParseError;
143+ }
144+
145+ // Create rapidjson validator and check for schema errors
146+ rj::SchemaValidator validator (schemaDocument);
147+ if (!d.Accept (validator))
148+ {
149+ // Input JSON is invalid according to the schema
150+ // Output diagnostic information
151+ rj::StringBuffer sb;
152+ validator.GetInvalidSchemaPointer ().StringifyUriFragment (sb);
153+ cerr << " Invalid schema: " << sb.GetString () << endl;
154+ cerr << " Invalid keyword: " << validator.GetInvalidSchemaKeyword () << endl;
155+ sb.Clear ();
156+ validator.GetInvalidDocumentPointer ().StringifyUriFragment (sb);
157+ cerr << " Invalid document: " << sb.GetString () << endl;
158+ cerr << " Invalid response: " << inputJson << endl;
159+ cerr << " Invalid API endpoint: " << apiEndPoint << endl;
160+ return bfxERR::responseSchemaError;
161+ }
162+
163+ return bfxERR::noError;
164+ }
165+
166+ private:
167+
168+ MyRemoteSchemaDocumentProvider provider_;
169+ unordered_map<string, string> apiEndPointToSchemaMap_;
170+
171+ const string& getApiEndPointSchemaName (const string& apiEndpoint) noexcept
172+ {
173+ return apiEndPointToSchemaMap_[apiEndpoint];
174+ }
175+
176+ };
177+
178+ // / SAX events helper struct for jsonStrToUset() routine
74179 struct jsonStrToUsetHandler :
75180 public rj::BaseReaderHandler<rj::UTF8<>, jsonStrToUsetHandler>
76181 {
@@ -118,7 +223,11 @@ namespace jsonutils
118223 } state_;
119224 };
120225
121- bfxERR jsonStrToUset (unordered_set<string> &uSet, const string &jsonStr)
226+ // //////////////////////////////////////////////////////////////////////////
227+ // Routines
228+ // //////////////////////////////////////////////////////////////////////////
229+
230+ bfxERR jsonStrToUset (unordered_set<string> &uSet, const string &inputJson)
122231 {
123232 // Create schema $ref resolver
124233 rj::Document sd;
@@ -132,7 +241,8 @@ namespace jsonutils
132241 jsonStrToUsetHandler handler;
133242
134243 // Create schema validator
135- rj::GenericSchemaValidator<rj::SchemaDocument, jsonStrToUsetHandler> validator (schemaDoc, handler);
244+ rj::GenericSchemaValidator<rj::SchemaDocument, jsonStrToUsetHandler>
245+ validator (schemaDoc, handler);
136246
137247 // Create reader
138248 rj::Reader reader;
@@ -142,7 +252,7 @@ namespace jsonutils
142252 // / DEBUG
143253
144254 // Create input JSON StringStream
145- rj::StringStream ss (jsonStr .c_str ());
255+ rj::StringStream ss (inputJson .c_str ());
146256// rj::StringStream ss(mockJson.c_str()); // DEBUG
147257
148258 // Parse and validate
@@ -162,6 +272,7 @@ namespace jsonutils
162272 sb.Clear ();
163273 validator.GetInvalidDocumentPointer ().StringifyUriFragment (sb);
164274 cerr << " Invalid document: " << sb.GetString () << endl;
275+ cerr << " Invalid response: " << inputJson << endl;
165276 }
166277 return bfxERR::jsonStrToUSetError;
167278 }
0 commit comments