- refactor jsonpi utils, add test
[oweals/gnunet.git] / src / jsonapi / jsonapi_document.c
1
2 #include "platform.h"
3 #include "gnunet_util_lib.h"
4 #include "gnunet_json_lib.h"
5 #include "jsonapi_objects.h"
6
7 /**
8  * Get a JSON API object resource count
9  *
10  * @param resp the JSON API object
11  * @return the number of resources
12  */
13 int
14 GNUNET_JSONAPI_document_resource_count (struct GNUNET_JSONAPI_Document *doc)
15 {
16   return doc->res_count;
17 }
18
19 /**
20  * Get a JSON API object resource by index
21  *
22  * @param resp the JSON API object
23  * @param idx index of the resource
24  * @return the resource
25  */
26 struct GNUNET_JSONAPI_Resource*
27 GNUNET_JSONAPI_document_get_resource (struct GNUNET_JSONAPI_Document *doc,
28                                       int idx)
29 {
30   struct GNUNET_JSONAPI_Resource *res;
31   int i;
32
33   if ((0 == doc->res_count) ||
34       (idx >= doc->res_count))
35     return NULL;
36   res = doc->res_list_head;
37   for (i = 0; i < idx; i++)
38   {
39     res = res->next;
40   }
41   return res;
42 }
43
44 /**
45  * Delete a JSON API primary data
46  *
47  * @param type the JSON API resource type
48  * @param id the JSON API resource id
49  * @return a new JSON API resource or NULL on error.
50  */
51 void
52 GNUNET_JSONAPI_document_delete (struct GNUNET_JSONAPI_Document *doc)
53 {
54   struct GNUNET_JSONAPI_Resource *res;
55   struct GNUNET_JSONAPI_Resource *res_next;
56   
57
58   for (res = doc->res_list_head;
59        res != NULL;)
60   {
61     res_next = res->next;
62     GNUNET_CONTAINER_DLL_remove (doc->res_list_head,
63                                  doc->res_list_tail,
64                                  res);
65     GNUNET_JSONAPI_resource_delete (res);
66     res = res_next;
67   }
68   GNUNET_free (doc);
69   doc = NULL;
70 }
71
72 /**
73  * Create a JSON API primary data
74  *
75  * @return a new JSON API resource or NULL on error.
76  */
77 struct GNUNET_JSONAPI_Document*
78 GNUNET_JSONAPI_document_new ()
79 {
80   struct GNUNET_JSONAPI_Document *result;
81
82   result = GNUNET_new (struct GNUNET_JSONAPI_Document);
83   result->res_count = 0;
84   result->err_count = 0;
85   result->meta = 0;
86   return result;
87 }
88
89 /**
90  * Add a JSON API error to document
91  *
92  * @param data The JSON API document to add to
93  * @param res the JSON API error to add
94  * @return the new number of resources
95  */
96 void
97 GNUNET_JSONAPI_document_error_add (struct GNUNET_JSONAPI_Document *doc,
98                                       struct GNUNET_JSONAPI_Error *err)
99 {
100   GNUNET_CONTAINER_DLL_insert (doc->err_list_head,
101                                doc->err_list_tail,
102                                err);
103
104   doc->err_count++;
105 }
106
107 /**
108  * Add a JSON API resource to primary data
109  *
110  * @param data The JSON API data to add to
111  * @param res the JSON API resource to add
112  * @return the new number of resources
113  */
114 void
115 GNUNET_JSONAPI_document_resource_add (struct GNUNET_JSONAPI_Document *doc,
116                                          struct GNUNET_JSONAPI_Resource *res)
117 {
118   GNUNET_CONTAINER_DLL_insert (doc->res_list_head,
119                                doc->res_list_tail,
120                                res);
121
122   doc->res_count++;
123 }
124
125
126 /**
127  * Parse given JSON object to jsonapi document.
128  *
129  * @param cls closure, NULL
130  * @param root the json object representing data
131  * @param[out] spec where to write the data
132  * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
133  */
134 static int
135 parse_jsonapiobject (void *cls,
136                      json_t *root,
137                      struct GNUNET_JSON_Specification *spec)
138 {
139   struct GNUNET_JSONAPI_Document *result;
140   struct GNUNET_JSONAPI_Error *error;
141   struct GNUNET_JSONAPI_Resource *resource;
142   json_t *meta_json;
143   json_t *resource_json;
144   json_t *errors_json;
145   json_t *value;
146   size_t index;
147
148   struct GNUNET_JSON_Specification jsonapispecerrors[] = {
149     GNUNET_JSON_spec_json (GNUNET_JSONAPI_KEY_ERRORS, &errors_json),
150     GNUNET_JSON_spec_end()
151   };
152   if (GNUNET_OK !=
153       GNUNET_JSON_parse (root, jsonapispecerrors,
154                          NULL, NULL))
155   {
156     GNUNET_log (GNUNET_ERROR_TYPE_INFO, 
157                 "JSONAPI document does not contain error objects\n");
158   } else if (!json_is_array (errors_json))
159   {
160     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
161                 "Error object is not array!\n");
162     GNUNET_JSON_parse_free (jsonapispecerrors);
163     return GNUNET_SYSERR;
164   }
165   struct GNUNET_JSON_Specification jsonapispecmeta[] = {
166     GNUNET_JSON_spec_json (GNUNET_JSONAPI_KEY_META, &meta_json),
167     GNUNET_JSON_spec_end()
168   };
169   if (GNUNET_OK !=
170       GNUNET_JSON_parse (root, jsonapispecmeta,
171                          NULL, NULL))
172   {
173     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
174                 "JSONAPI document does not contain error objects\n");
175   }
176   struct GNUNET_JSON_Specification jsonapispecresource[] = {
177     GNUNET_JSON_spec_json (GNUNET_JSONAPI_KEY_DATA, &resource_json),
178     GNUNET_JSON_spec_end()
179   };
180   if (GNUNET_OK !=
181       GNUNET_JSON_parse (root, jsonapispecresource,
182                          NULL, NULL))
183   {
184     if (NULL == errors_json)
185     {
186       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
187                   "JSONAPI document contains neither error nor data!\n");
188       GNUNET_JSON_parse_free (jsonapispecerrors);
189       GNUNET_JSON_parse_free (jsonapispecmeta);
190       return GNUNET_SYSERR;
191     }
192   } else {
193     if (NULL != errors_json)
194     {
195       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
196                   "JSONAPI document contains both error and data!\n");
197       GNUNET_JSON_parse_free (jsonapispecerrors);
198       GNUNET_JSON_parse_free (jsonapispecmeta);
199       GNUNET_JSON_parse_free (jsonapispecresource);
200       return GNUNET_SYSERR;
201     }
202   }
203
204   result = GNUNET_new (struct GNUNET_JSONAPI_Document);
205   result->res_count = 0;
206   result->err_count = 0;
207   if (NULL != meta_json)
208     result->meta = json_deep_copy (meta_json);
209   if (NULL != errors_json) {
210     json_array_foreach(errors_json, index, value) {
211       GNUNET_assert (GNUNET_OK == 
212                      GNUNET_JSONAPI_json_to_error (value,
213                                                    &error));
214       GNUNET_JSONAPI_document_error_add (result, error);
215     }
216   }
217   if (NULL != resource_json) {
218     if (0 != json_is_array (resource_json))
219     {
220       json_array_foreach(resource_json, index, value) {
221         GNUNET_assert (GNUNET_OK == 
222                        GNUNET_JSONAPI_json_to_resource (value,
223                                                         &resource));
224         GNUNET_JSONAPI_document_resource_add (result, resource);
225       }
226     } else {
227       GNUNET_assert (GNUNET_OK == 
228                      GNUNET_JSONAPI_json_to_resource (resource_json,
229                                                       &resource));
230       GNUNET_JSONAPI_document_resource_add (result, resource);
231     }
232   }
233   if (NULL != errors_json)
234     GNUNET_JSON_parse_free (jsonapispecerrors);
235   if (NULL != resource)
236     GNUNET_JSON_parse_free (jsonapispecresource);
237   if (NULL != meta_json)
238     GNUNET_JSON_parse_free (jsonapispecmeta);
239   *(struct GNUNET_JSONAPI_Document **) spec->ptr = result;
240   return GNUNET_OK;
241 }
242
243
244 /**
245  * Cleanup data left from parsing RSA public key.
246  *
247  * @param cls closure, NULL
248  * @param[out] spec where to free the data
249  */
250 static void
251 clean_jsonapiobject (void *cls,
252                      struct GNUNET_JSON_Specification *spec)
253 {
254   struct GNUNET_JSONAPI_Document **jsonapi_obj;
255   jsonapi_obj = (struct GNUNET_JSONAPI_Document **) spec->ptr;
256   if (NULL != *jsonapi_obj)
257   {
258     GNUNET_JSONAPI_document_delete (*jsonapi_obj);
259     *jsonapi_obj = NULL;
260   }
261 }
262
263 /**
264  * Add a JSON API resource to primary data
265  *
266  * @param data The JSON API data to add to
267  * @param res the JSON API resource to add
268  * @return the new number of resources
269  */
270 void
271 GNUNET_JSONAPI_document_resource_remove (struct GNUNET_JSONAPI_Document *resp,
272                                          struct GNUNET_JSONAPI_Resource *res)
273 {
274   GNUNET_CONTAINER_DLL_remove (resp->res_list_head,
275                                resp->res_list_tail,
276                                res);
277   resp->res_count--;
278 }
279
280
281 /**
282  * String serialze jsonapi primary data
283  *
284  * @param data the JSON API primary data
285  * @param result where to store the result
286  * @return GNUNET_SYSERR on error else GNUNET_OK
287  */
288 int
289 GNUNET_JSONAPI_document_to_json (const struct GNUNET_JSONAPI_Document *doc,
290                                  json_t **root_json)
291 {
292   struct GNUNET_JSONAPI_Resource *res;
293   struct GNUNET_JSONAPI_Error *error;
294   json_t *res_json;
295   json_t *res_json_tmp;
296
297   if ((NULL == doc))
298     return GNUNET_SYSERR;
299
300   *root_json = json_object ();
301
302   //Check for errors first 
303   if (doc->err_count != 0)
304   {
305     res_json = json_array ();
306     for (error = doc->err_list_head;
307          error != NULL;
308          error = error->next)
309     {
310       GNUNET_assert (GNUNET_OK ==
311                      GNUNET_JSONAPI_error_to_json (error,
312                                                    &res_json_tmp));
313       json_array_append (res_json, res_json_tmp);
314     }
315     json_object_set_new (*root_json,
316                          GNUNET_JSONAPI_KEY_ERRORS,
317                          res_json);
318   } else {
319     switch (doc->res_count)
320     {
321       case 0:
322         res_json = json_null();
323         break;
324       case 1:
325         GNUNET_assert (GNUNET_OK ==
326                        GNUNET_JSONAPI_resource_to_json (doc->res_list_head,
327                                                         &res_json));
328         break;
329       default:
330         res_json = json_array ();
331         for (res = doc->res_list_head;
332              res != NULL;
333              res = res->next)
334         {
335           GNUNET_assert (GNUNET_OK ==
336                          GNUNET_JSONAPI_resource_to_json (res,
337                                                           &res_json_tmp));
338           json_array_append (res_json, res_json_tmp);
339         }
340         break;
341     }
342     json_object_set_new (*root_json,
343                          GNUNET_JSONAPI_KEY_DATA,
344                          res_json);
345   }
346   json_object_set (*root_json,
347                    GNUNET_JSONAPI_KEY_META,
348                    doc->meta);
349   return GNUNET_OK;
350 }
351
352 /**
353  * String serialze jsonapi primary data
354  *
355  * @param data the JSON API primary data
356  * @param result where to store the result
357  * @return GNUNET_SYSERR on error else GNUNET_OK
358  */
359 int
360 GNUNET_JSONAPI_document_serialize (const struct GNUNET_JSONAPI_Document *doc,
361                                    char **result)
362 {
363   json_t *json_doc;
364   if (GNUNET_OK != GNUNET_JSONAPI_document_to_json (doc,
365                                                     &json_doc))
366     return GNUNET_SYSERR;
367
368   *result = json_dumps (json_doc, JSON_INDENT(2));
369   json_decref (json_doc);
370   return GNUNET_OK;
371 }
372
373 /**
374  * JSON object.
375  *
376  * @param name name of the JSON field
377  * @param[out] jsonp where to store the JSON found under @a name
378  */
379 struct GNUNET_JSON_Specification
380 GNUNET_JSON_spec_jsonapi_document (struct GNUNET_JSONAPI_Document **jsonapi_object)
381 {
382   struct GNUNET_JSON_Specification ret = {
383     .parser = &parse_jsonapiobject,
384     .cleaner = &clean_jsonapiobject,
385     .cls = NULL,
386     .field = NULL,
387     .ptr = jsonapi_object,
388     .ptr_size = 0,
389     .size_ptr = NULL
390   };
391   *jsonapi_object = NULL;
392   return ret;
393 }
394
395