- refactor jsonpi utils, add test
[oweals/gnunet.git] / src / jsonapi / jsonapi_resource.c
1 #include "platform.h"
2 #include "gnunet_jsonapi_lib.h"
3 #include "jsonapi_objects.h"
4
5 /**
6  * String serialze jsonapi resources
7  *
8  * @param data the JSON API resource
9  * @param result where to store the result
10  * @return GNUNET_SYSERR on error else GNUNET_OK
11  */
12 int
13 GNUNET_JSONAPI_resource_to_json (const struct GNUNET_JSONAPI_Resource *res,
14                                  json_t **result)
15 {
16   struct GNUNET_JSONAPI_Resource *rel_res;
17   json_t *relationship;
18   json_t *res_json_tmp;
19   *result = json_object ();
20
21   if (0 != json_object_set_new (*result,
22                                 GNUNET_JSONAPI_KEY_ID,
23                                 json_string (res->id)))
24     return GNUNET_SYSERR;
25   if (0 != json_object_set_new (*result,
26                                 GNUNET_JSONAPI_KEY_TYPE,
27                                 json_string (res->type)))
28     return GNUNET_SYSERR;
29   if ((NULL != res->attr_obj) &&
30       (0 != json_object_set (*result,
31                              GNUNET_JSONAPI_KEY_ATTRIBUTES,
32                              res->attr_obj)))
33     return GNUNET_SYSERR;
34
35   //Relationships
36   if (NULL != res->relationship)
37   {
38     relationship = json_object ();
39     if (0 != res->relationship->res_count)
40     {
41       json_t *res_json;
42       switch (res->relationship->res_count)
43       {
44         case 0:
45           res_json = json_null();
46           break;
47         case 1:
48           GNUNET_assert (GNUNET_OK ==
49                          GNUNET_JSONAPI_resource_to_json (res->relationship->res_list_head,
50                                                           &res_json));
51           break;
52         default:
53           res_json = json_array ();
54           rel_res = NULL;
55           for (rel_res = rel_res->relationship->res_list_head;
56                rel_res != NULL;
57                rel_res = rel_res->next)
58           {
59             GNUNET_assert (GNUNET_OK ==
60                            GNUNET_JSONAPI_resource_to_json (rel_res,
61                                                             &res_json_tmp));
62             json_array_append_new (res_json, res_json_tmp);
63           }
64           break;
65       }
66       json_object_set_new (relationship,
67                            GNUNET_JSONAPI_KEY_DATA,
68                            res_json);
69     }
70     if ((NULL != res->relationship->meta) &&
71         (0 != json_object_set_new (relationship,
72                                    GNUNET_JSONAPI_KEY_META,
73                                    res->relationship->meta)))
74       return GNUNET_SYSERR;
75     //TODO link
76   }
77
78
79   return GNUNET_OK;
80 }
81
82
83 /**
84  * Create a JSON API resource
85  *
86  * @param type the JSON API resource type
87  * @param id the JSON API resource id
88  * @return a new JSON API resource or NULL on error.
89  */
90 struct GNUNET_JSONAPI_Resource*
91 GNUNET_JSONAPI_resource_new (const char *type, const char *id)
92 {
93   struct GNUNET_JSONAPI_Resource *res;
94
95   if ( (NULL == type) || (0 == strlen (type)) )
96     return NULL;
97   if ( (NULL == id) || (0 == strlen (id)) )
98     return NULL;
99
100   res = GNUNET_new (struct GNUNET_JSONAPI_Resource);
101   res->prev = NULL;
102   res->next = NULL;
103   res->attr_obj = NULL;
104   res->relationship = NULL;
105   res->id = GNUNET_strdup (id);
106   res->type = GNUNET_strdup (type);
107   return res;
108 }
109
110 /**
111  * Add a jsonapi relationship
112  * @param res the resource to add to
113  * @param rel the relationship to add
114  * @return #GNUNETOK if added successfully
115  */
116 int
117 GNUNET_JSONAPI_resource_set_relationship (struct GNUNET_JSONAPI_Resource *res,
118                                           struct GNUNET_JSONAPI_Relationship *rel)
119 {
120   GNUNET_assert (NULL != res);
121   GNUNET_assert (NULL != rel);
122   if (NULL != res->relationship)
123     return GNUNET_SYSERR;
124   res->relationship = rel;
125   return GNUNET_OK;
126 }
127
128 /**
129  * Add a JSON API attribute
130  *
131  * @param res the JSON resource
132  * @param key the key for the attribute
133  * @param json the json_t attribute to add
134  * @return #GNUNET_OK if added successfully
135  *         #GNUNET_SYSERR if not
136  */
137 int
138 GNUNET_JSONAPI_resource_add_attr (struct GNUNET_JSONAPI_Resource *resource,
139                                   const char* key,
140                                   json_t *json)
141 {
142   if ( (NULL == resource) ||
143        (NULL == key) ||
144        (NULL == json) )
145     return GNUNET_SYSERR;
146   if (NULL == resource->attr_obj)
147     resource->attr_obj = json_object ();
148   json_object_set_new (resource->attr_obj, key, json);
149   return GNUNET_OK;
150 }
151
152 /**
153  * Read a JSON API attribute
154  *
155  * @param res the JSON resource
156  * @param key the key for the attribute
157  * @return the json_t object
158  */
159 json_t*
160 GNUNET_JSONAPI_resource_read_attr (const struct GNUNET_JSONAPI_Resource *resource,
161                                    const char* key)
162 {
163   if ( (NULL == resource) ||
164        (NULL == key) ||
165        (NULL == resource->attr_obj))
166     return NULL;
167   return json_object_get (resource->attr_obj, key);
168 }
169
170 int
171 check_resource_attr_str (const struct GNUNET_JSONAPI_Resource *resource,
172                          const char* key,
173                          const char* attr)
174 {
175   json_t *value;
176   if ( (NULL == resource) ||
177        (NULL == key) ||
178        (NULL == attr) ||
179        (NULL == resource->attr_obj))
180     return GNUNET_NO;
181   value = json_object_get (resource->attr_obj, key);
182   if (NULL == value)
183     return GNUNET_NO;
184   if (!json_is_string (value) ||
185       (0 != strcmp (attr, json_string_value(value))))
186   {
187     return GNUNET_NO;
188   }
189   return GNUNET_YES;
190 }
191
192 /**
193  * Check a JSON API resource type
194  *
195  * @param res the JSON resource
196  * @param type the expected type
197  * @return GNUNET_YES if id matches
198  */
199 int
200 GNUNET_JSONAPI_resource_check_type (const struct GNUNET_JSONAPI_Resource *resource,
201                                     const char* type)
202 {
203   return (0 == memcmp (type, resource->type,
204                        strlen (resource->type))) ? GNUNET_YES : GNUNET_NO;
205 }
206
207
208 /**
209  * Delete a JSON API resource
210  *
211  * @param res the JSON resource
212  * @param result Pointer where the resource should be stored
213  */
214 void
215 GNUNET_JSONAPI_resource_delete (struct GNUNET_JSONAPI_Resource *resource)
216 {
217   GNUNET_free (resource->id);
218   GNUNET_free (resource->type);
219   if (NULL != resource->attr_obj)
220     json_decref (resource->attr_obj);
221   if (NULL != resource->relationship)
222     GNUNET_JSONAPI_relationship_delete (resource->relationship);
223   GNUNET_free (resource);
224   resource = NULL;
225 }
226
227
228 /**
229  * Check a JSON API resource id
230  *
231  * @param res the JSON resource
232  * @param id the expected id
233  * @return GNUNET_YES if id matches
234  */
235 int
236 GNUNET_JSONAPI_resource_check_id (const struct GNUNET_JSONAPI_Resource *resource,
237                                   const char* id)
238 {
239   return (0 == memcmp (resource->id, id, strlen (id))) ? GNUNET_YES : GNUNET_NO;
240 }
241
242 /**
243  * Check a JSON API resource id
244  *
245  * @param res the JSON resource
246  * @return the resource id
247  */
248 char*
249 GNUNET_JSONAPI_resource_get_id (const struct GNUNET_JSONAPI_Resource *resource)
250 {
251   return resource->id;
252 }
253
254 /**
255  * Parse json to resource object
256  *
257  * @param res_json JSON object
258  * @param[out] res resource object
259  * @return GNUNET_OK on success
260  */
261 int
262 GNUNET_JSONAPI_json_to_resource (json_t *res_json,
263                                  struct GNUNET_JSONAPI_Resource **res)
264 {
265   struct GNUNET_JSON_Specification jsonapispecresource[] = {
266     GNUNET_JSON_spec_jsonapi_resource (res),
267     GNUNET_JSON_spec_end()
268   };
269   return GNUNET_JSON_parse (res_json, jsonapispecresource,
270                             NULL, NULL);
271 }
272
273 /**
274  * Parse given JSON object to jsonapi document.
275  *
276  * @param cls closure, NULL
277  * @param root the json object representing data
278  * @param[out] spec where to write the data
279  * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
280  */
281 static int
282 parse_jsonapiresource (void *cls,
283                        json_t *root,
284                        struct GNUNET_JSON_Specification *spec)
285 {
286   struct GNUNET_JSONAPI_Resource *res;
287   const char *type;
288   const char *id;
289   json_t *attrs;
290
291   struct GNUNET_JSON_Specification dspec[] = {
292     GNUNET_JSON_spec_string (GNUNET_JSONAPI_KEY_TYPE, &type),
293     GNUNET_JSON_spec_string (GNUNET_JSONAPI_KEY_ID, &id),
294     GNUNET_JSON_spec_end()
295   };
296
297   if (GNUNET_OK !=
298       GNUNET_JSON_parse (root, dspec,
299                          NULL, NULL))
300   {
301     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unable to parse resource\n");
302     return GNUNET_SYSERR;
303   }
304   res = GNUNET_JSONAPI_resource_new (type, id);
305   GNUNET_JSON_parse_free (dspec);
306
307   struct GNUNET_JSON_Specification attrspec[] = {
308     GNUNET_JSON_spec_json (GNUNET_JSONAPI_KEY_ATTRIBUTES, &attrs),
309     GNUNET_JSON_spec_end()
310   };
311   if (GNUNET_OK !=
312       GNUNET_JSON_parse (root, attrspec,
313                          NULL, NULL))
314     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Resource does not contain attributes\n");
315   if (NULL != attrs)
316     res->attr_obj = json_deep_copy (attrs);
317
318   //TODO relationship
319   GNUNET_JSON_parse_free (attrspec);
320   *(struct GNUNET_JSONAPI_Resource **) spec->ptr = res;
321   return GNUNET_OK;
322 }
323
324
325 /**
326  * Cleanup data left from parsing resource.
327  *
328  * @param cls closure, NULL
329  * @param[out] spec where to free the data
330  */
331 static void
332 clean_jsonapiresource (void *cls,
333                        struct GNUNET_JSON_Specification *spec)
334 {
335   struct GNUNET_JSONAPI_Resource **jsonapi_obj;
336   jsonapi_obj = (struct GNUNET_JSONAPI_Resource **) spec->ptr;
337   if (NULL != *jsonapi_obj)
338   {
339     GNUNET_JSONAPI_resource_delete (*jsonapi_obj);
340     *jsonapi_obj = NULL;
341   }
342 }
343
344
345 /**
346  * JSON object.
347  *
348  * @param name name of the JSON field
349  * @param[out] jsonp where to store the JSON found under @a name
350  */
351 struct GNUNET_JSON_Specification
352 GNUNET_JSON_spec_jsonapi_resource (struct GNUNET_JSONAPI_Resource **jsonapi_object)
353 {
354   struct GNUNET_JSON_Specification ret = {
355     .parser = &parse_jsonapiresource,
356     .cleaner = &clean_jsonapiresource,
357     .cls = NULL,
358     .field = NULL,
359     .ptr = jsonapi_object,
360     .ptr_size = 0,
361     .size_ptr = NULL
362   };
363   *jsonapi_object = NULL;
364   return ret;
365 }
366
367