Adaptive Framework  0.9.0
All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
afw_request_handler_adaptor.c
Go to the documentation of this file.
1 // See the 'COPYING' file in the project root for licensing information.
2 /*
3  * Implementation of afw_request_handler interface for adaptor
4  *
5  * Copyright (c) 2010-2023 Clemson University
6  *
7  */
8 
9 
19 #include "afw_internal.h"
20 
21 
22 
23 /* Declares and rti/inf defines for interface afw_request_handler */
24 #define AFW_IMPLEMENTATION_ID "adaptor"
26 
27 
28 typedef struct {
29  const afw_request_t *request;
30  const afw_object_t *journal_entry;
31  const afw_content_type_t *content_type;
33  const afw_object_options_t *options;
34  afw_boolean_t called_once;
36 
37 
40  const afw_object_t *properties,
41  const afw_pool_t *p, afw_xctx_t *xctx)
42 {
44  const afw_object_t *options_object;
45 
47 
48  self->pub.inf = &impl_afw_request_handler_inf;
49  self->xctx = xctx;
50  self->p = xctx->p;
51  self->properties = properties;
52 
53  options_object = afw_object_old_get_property_as_object(self->properties,
54  &afw_s_defaultOptions, xctx);
55  if (options_object) {
56  self->default_options = afw_object_options_set_from_object(
57  NULL, options_object, p, xctx);
58  }
59 
60  /* Return self */
61  return (const afw_request_handler_t *)self;
62 }
63 
64 /* Callback function for retrieve. */
65 static afw_boolean_t impl_retrieve_cb(const afw_object_t *object,
66  void *context, afw_xctx_t *xctx)
67 {
68  impl_retrieve_cb_context_t *ctx = context;
69 
70  /* If first call, write begin object list. */
71  if (!ctx->content_type) {
72  ctx->content_type =
73  afw_request_prepare_response_content_type(ctx->request, xctx);
75  ctx->content_type, ctx->options,
76  (void *)ctx->request, ctx->request->write_content_cb,
77  xctx->p, xctx);
78  }
79 
80  /* If there is an object, write it. */
81  if (object) {
83  xctx->p, xctx);
85  /*afw_request_flush_response(ctx->request, xctx);*/
86  }
87 
88  /* If there are not more objects, release writer. */
89  else {
91  }
92 
93  /* Return indicating not to short circuit */
94  return false;
95 }
96 
97 /*
98  * Implementation of method release of interface afw_request_handler.
99  */
100 void
101 impl_afw_request_handler_release(
102  const afw_request_handler_t * instance,
103  afw_xctx_t *xctx)
104 {
105  /* Resources will be released when execution context (xctx) is released. */
106 }
107 
108 
109 /*
110  * Implementation of method process of interface afw_request_handler.
111  */
112 void
114  const afw_request_handler_t * instance,
115  const afw_request_t * request,
116  afw_xctx_t *xctx)
117 {
118  const afw_value_t *value;
119  const afw_query_criteria_t * query_criteria;
120  const afw_object_t *obj;
121  const afw_object_path_parsed_t *parsed_path;
122  const afw_object_t *journal_entry;
123  const afw_content_type_t *response_content_type;
124  const afw_stream_t *stream;
125  impl_retrieve_cb_context_t retrieve_cb_context;
126  afw_boolean_t temp_recurse = false;
129 
130  /* Parse the path. */
131  parsed_path = afw_object_path_parse(request->uri, NULL, self->default_options,
132  xctx->p, xctx);
133  if (!parsed_path) {
134  AFW_THROW_ERROR_Z(not_found, "Path format error.", xctx);
135  }
136 
137  /* Create journal entry memory object. */
138  journal_entry = afw_object_create_managed(xctx->p, xctx);
139 
140  /* Allow journal entry to be used as additional error info. */
141  afw_request_set_error_info(request, journal_entry, xctx);
142 
143 
144  /*
145  * Process based on HTTP method.
146  *
147  * Notes for using dojo/store/JsonRest:
148  *
149  * The Dojo documentation recommends using /{Table}/{id} as the URL
150  * structure for resources. This whole string is considered URI of
151  * the adaptive object. {Table} is the adaptor id. {id} is the object id.
152  *
153  * FIXME fix comment
154  *
155  * /{Table} is the request->uri up to, but not including, the second
156  * slash. The /{id} is the second slash and remainder of request->uri.
157  *
158  */
159 
160  AFW_TRY {
161 
162  /* If retrieve_objects or get objects: */
163  if (afw_utf8_equal_utf8_z(request->method, AFW_REQUEST_Q_METHOD_GET))
164  {
165  /* Object type required. */
166  if (parsed_path->object_type_id.len == 0) {
168  AFW_THROW_ERROR_Z(general, "URI missing Object type", xctx);
169  }
170 
171  /* If query_objects(), which is a GET without an object_id. */
172  else if (parsed_path->entity_object_id.len == 0)
173  {
174 
175  /* If query string specified, create query criteria. */
176  query_criteria = NULL;
177  if (request->query_string && request->query_string->len > 0) {
178  query_criteria =
180  request->query_string,
181  &parsed_path->adaptor_id,
182  &parsed_path->object_type_id,
183  journal_entry,
184  xctx->p, xctx);
185  }
186 
187  /* Prepare retrieve callback context. */
188  memset(&retrieve_cb_context, 0, sizeof(retrieve_cb_context));
189  retrieve_cb_context.request = request;
190  retrieve_cb_context.journal_entry = journal_entry;
191  retrieve_cb_context.options = parsed_path->options;
192 
193  /* Call adaptor to query for objects. */
195  &parsed_path->adaptor_id, &parsed_path->object_type_id,
196  parsed_path->options, query_criteria, journal_entry,
197  &retrieve_cb_context, impl_retrieve_cb,
198  NULL, xctx->p, xctx);
199  }
200 
201  /* If get_object(), which is a GET where path does not end with '/'. */
202  else {
203 
204  /* If query string specified, create query criteria. */
205  query_criteria = NULL;
206  if (request->query_string && request->query_string->len > 0) {
207  query_criteria =
209  request->query_string,
210  &parsed_path->adaptor_id,
211  &parsed_path->object_type_id,
212  journal_entry, xctx->p, xctx);
213  }
214 
215  /* Get object cache. */
217  &parsed_path->adaptor_id, &parsed_path->object_type_id,
218  &parsed_path->entity_object_id,
219  parsed_path->options, query_criteria,
220  journal_entry, NULL, xctx->p, xctx);
221 
222  /* If object not found, indicate. */
223  if (!obj) {
224  value = NULL;
225  }
226 
227  /* If there is a property name in path, get it. */
228  else if (parsed_path->first_property_name) {
230  parsed_path->first_property_name, xctx);
231  }
232 
233  /* If no property name in path, obj is value. */
234  else {
235  value = afw_value_create_object(obj, xctx->p, xctx);
236  }
237 
238  /* If value not found, throw error. */
239  if (!value) {
240  AFW_THROW_ERROR_FZ(not_found, xctx,
241  "%" AFW_UTF8_FMT " Not found.",
242  AFW_UTF8_FMT_ARG(request->uri));
243  }
244 
245  /* Write value to response body. */
247  parsed_path->options, xctx);
248  }
249  }
250 
251  else if (afw_utf8_equal_utf8_z(request->method,
253  {
254  /*
255  * Adaptive method Replace.
256  *
257  * The PUT method is used to completely replace an existing object
258  * whose object id is know.
259  *
260  * The body of the request must contain a JSON object containing the
261  * complete object.
262  *
263  * For now, If_None_Match and If_Match are ignore. If_None_Match:*
264  * is checked in the future, it will produce an error. If _Match:*
265  * is checked, it will basically be ignore since in the adaptive
266  * rest api, PUT is always a complete replacement or an existing
267  * object.
268  *
269  * In DOJO, specify an id, incremental false, and overwrite true
270  * to produce a Replace request.
271  */
272  value = afw_request_body_to_value(request, xctx->p, xctx);
273  value = afw_value_undecorate(value);
274  if (!value || !afw_value_is_object(value)) {
275  AFW_THROW_ERROR_Z(general, "Invalid post data", xctx);
276  }
277  obj = ((const afw_value_object_t *)value)->internal;
278 
279  if (parsed_path->entity_object_id.len == 0) {
280  AFW_THROW_ERROR_Z(general, "Object id missing in URI for replace.", xctx);
281  }
282  if (parsed_path->first_property_name) {
283  AFW_THROW_ERROR_Z(general,
284  "Object id can not have dotted property name for replace.", xctx);
287  &parsed_path->normalized_path, xctx);
288  afw_adaptor_replace_object(&parsed_path->adaptor_id,
289  &parsed_path->object_type_id, &parsed_path->entity_object_id,
290  obj, journal_entry, NULL, xctx);
291  afw_request_write_success_response(request, journal_entry, xctx);
292  }
293 
294  else if (afw_utf8_equal_utf8_z(request->method,
296  {
297  /*
298  * Adaptive methods Add, Update, Modify and Publish.
299  *
300  * The POST method is used to add a new object or update/modify an
301  * existing object.
302  *
303  * Perform actions request:
304  *
305  * POST /afw
306  *
307  * A request to perform actions.
308  * See /afw/_AdaptiveObjectType_/_AdaptiveActions_ for more
309  * information.
310  *
311  *
312  * Just adaptor_id specified.
313  *
314  * POST /<adaptor_id>
315  *
316  * Reserved. An error for now.
317  *
318  *
319  * Method Modify:
320  *
321  * Modify an object using action tuples.
322  *
323  * POST /<adaptor_id>/<object_type>/<object_id>
324  *
325  * Request data is a list of action tuples.
326  *
327  *
328  * Method Add:
329  *
330  * Add adds a new object.
331  *
332  * POST /<adaptor_id>/<object_type>
333  *
334  * An Add request does not specify an object id. The body of the
335  * request must be an object containing the complete object.
336  *
337  * In DOJO, do not specify id, specify incremental false and overwrite
338  * false.
339  *
340  *
341  * Method Update:
342  *
343  * POST /<adaptor_id>/<object_type>/<object_id>
344  *
345  * Request data is an object.
346  *
347  * Update replaces properties of an existing object with new values.
348  * The body of the request must be a JSON object containing properties
349  * to replace in the existing object. Any properties in the existing
350  * object that are not specified in the Update remain unchanged. If
351  * a property is specified with no value, it is removed from the
352  * existing object.
353  *
354  * In DOJO, specify id, incremental true and overwrite true.
355  *
356  * For now, If_None_Match and If_Match are ignore.
357  *
358  * If If_None_Match: * header is present, the object will not be
359  * overwritten.
360  *
361  * If If_Match: * header is present, the object must already exist
362  * and will be overwritten.
363  *
364  */
365  value = afw_request_body_to_value(request, xctx->p, xctx);
366  if (!value) {
367  AFW_THROW_ERROR_Z(general, "Post data missing", xctx);
368  }
369  value = afw_value_undecorate(value);
370 
371  /* If Modify ... */
372  if (afw_value_is_list(value)) {
373  if (parsed_path->object_type_id.len == 0 ||
374  parsed_path->entity_object_id.len == 0)
375  {
376  AFW_THROW_ERROR_Z(general,
377  "Modify requires object type and object id", xctx);
378  }
379  if (parsed_path->first_property_name) {
380  AFW_THROW_ERROR_Z(general,
381  "Object id can not have dotted property name for modify.", xctx);
382  }
383  afw_adaptor_modify_object(&parsed_path->adaptor_id,
384  &parsed_path->object_type_id, &parsed_path->entity_object_id,
385  ((const afw_value_list_t *)value)->internal, journal_entry,
386  NULL, xctx);
387  }
388 
389  else if (afw_value_is_object(value)) {
390  obj = ((const afw_value_object_t *)value)->internal;
391 
392  /* Perform actions if just /afw and no object type. */
393  if (parsed_path->object_type_id.len == 0 &&
394  afw_utf8_equal(&parsed_path->adaptor_id, &afw_s_afw))
395  {
396  response_content_type = afw_request_prepare_response_content_type(
397  request, xctx);
398  journal_entry = afw_action_perform(
399  obj, response_content_type, journal_entry, xctx->p, xctx);
400  }
401 
402  /* An error if just adaptor_id other than afw. */
403  else if (parsed_path->object_type_id.len == 0) {
404  AFW_THROW_ERROR_Z(general, "Invalid post path", xctx);
405  }
406 
407  /* If Add ... */
408  else if (parsed_path->entity_object_id.len == 0) {
409  afw_adaptor_add_object(&parsed_path->adaptor_id,
410  &parsed_path->object_type_id, NULL, obj, journal_entry,
411  /*FIXME */ NULL, xctx);
412  }
413 
414  /* If Update ... */
415  else {
416  if (parsed_path->first_property_name) {
417  AFW_THROW_ERROR_Z(general,
418  "Object id can not have dotted property name for update.",
419  xctx);
422  &parsed_path->normalized_path, xctx);
424  &parsed_path->adaptor_id,
425  &parsed_path->object_type_id,
426  &parsed_path->entity_object_id,
427  ((const afw_value_object_t *)value)->internal,
428  journal_entry, NULL, xctx);
429  }
430  }
431 
432  /* Not single_list or single_object, error. */
433  else {
434  AFW_THROW_ERROR_Z(general, "Invalid post data", xctx);
435  }
436 
437  /* Write success response. */
438  afw_request_write_success_response(request, journal_entry, xctx);
439  }
440 
441  /* If delete_object() */
442  else if (afw_utf8_equal_utf8_z(request->method,
444  {
445  /* Call adaptor to delete object. */
446  if (parsed_path->first_property_name) {
447  AFW_THROW_ERROR_Z(general,
448  "Object id can not have dotted property name for delete.",
449  xctx);
450  }
452  &parsed_path->adaptor_id, &parsed_path->object_type_id,
453  &parsed_path->entity_object_id, journal_entry, /*FIXME */ NULL,
454  xctx);
455  afw_request_write_success_response(request, journal_entry, xctx);
456  }
457 
458  /* Else not supported method. */
459  else {
460  AFW_THROW_ERROR_Z(general, "Method not supported.", xctx);
461  }
462 
463  }
464 
466 
467  if (!temp_recurse) {
468  temp_recurse = true;
469  /* Abort changes and release cached sessions and objects. */
471  }
472 
474  }
475 
476  AFW_ENDTRY;
477 
478  /* Commit changes and release cached sessions and objects. */
480 
481  /* If response_content_type application/x-afw set, write end frame. */
482  if (afw_content_type_is_application_afw(request->response_content_type)) {
483  stream = afw_stream_standard(raw_response_body, xctx);
484  afw_stream_write_integer(stream, ++xctx->write_sequence, xctx);
485  afw_stream_write_z(stream, " 0 end\n", xctx);
486  }
487 
488  /* Finish response. */
489  afw_request_finish_response(request, xctx);
490 }
AFW_DEFINE(const afw_object_t *)
Adaptive Framework Core Internal.
Interface afw_interface implementation declares.
afw_action_perform(const afw_object_t *request, const afw_content_type_t *response_content_type, const afw_object_t *response, const afw_pool_t *p, afw_xctx_t *xctx)
Perform actions(s) specified in AdaptiveActions object.
Definition: afw_action.c:228
afw_adaptor_modify_using_update_object(const afw_utf8_t *adaptor_id, const afw_utf8_t *object_type_id, const afw_utf8_t *object_id, const afw_object_t *update_object, const afw_object_t *journal_entry, const afw_object_t *adaptor_type_specific, afw_xctx_t *xctx)
Modify using update object and remove from cache.
afw_adaptor_retrieve_objects(const afw_utf8_t *adaptor_id, const afw_utf8_t *object_type_id, const afw_object_options_t *options, const afw_query_criteria_t *criteria, const afw_object_t *journal_entry, void *context, afw_object_cb_t callback, const afw_object_t *adaptor_type_specific, const afw_pool_t *p, afw_xctx_t *xctx)
Retrieve objects.
afw_adaptor_replace_object(const afw_utf8_t *adaptor_id, const afw_utf8_t *object_type_id, const afw_utf8_t *object_id, const afw_object_t *replacement_object, const afw_object_t *journal_entry, const afw_object_t *adaptor_type_specific, afw_xctx_t *xctx)
Replace object and remove from cache.
afw_adaptor_add_object(const afw_utf8_t *adaptor_id, const afw_utf8_t *object_type_id, const afw_utf8_t *suggested_object_id, const afw_object_t *object, const afw_object_t *journal_entry, const afw_object_t *adaptor_type_specific, afw_xctx_t *xctx)
Call adaptor to add object and remove from cache.
afw_adaptor_session_commit_and_release_cache(afw_boolean_t abort, afw_xctx_t *xctx)
Commit/Abort changes and release cached sessions and objects.
Definition: afw_adaptor.c:391
afw_adaptor_modify_object(const afw_utf8_t *adaptor_id, const afw_utf8_t *object_type_id, const afw_utf8_t *object_id, const afw_list_t *entries, const afw_object_t *journal_entry, const afw_object_t *adaptor_type_specific, afw_xctx_t *xctx)
Modify object and remove from cache.
afw_adaptor_get_object(const afw_utf8_t *adaptor_id, const afw_utf8_t *object_type_id, const afw_utf8_t *object_id, const afw_object_options_t *options, const afw_query_criteria_t *criteria, const afw_object_t *journal_entry, const afw_object_t *adaptor_type_specific, const afw_pool_t *p, afw_xctx_t *xctx)
Get and cache object.
afw_adaptor_delete_object(const afw_utf8_t *adaptor_id, const afw_utf8_t *object_type_id, const afw_utf8_t *object_id, const afw_object_t *journal_entry, const afw_object_t *adaptor_type_specific, afw_xctx_t *xctx)
Delete object from cache and via adaptor.
afw_adaptor_query_criteria_parse_url_encoded_rql_string(const afw_utf8_t *url_encoded_rql_string, const afw_utf8_t *adaptor_id, const afw_utf8_t *object_type_id, const afw_object_t *journal_entry, const afw_pool_t *p, afw_xctx_t *xctx)
Parse URL encoded RQL query string appropriate for an adaptor.
Definition: afw_adaptor.c:197
#define afw_value_is_list(A_VALUE)
Macro to determine if value is evaluated list.
#define afw_value_is_object(A_VALUE)
Macro to determine if value is evaluated object.
#define afw_object_old_get_property_as_object(object, property_name, xctx)
Get property function for data type object value.
afw_value_create_object(const afw_object_t *internal, const afw_pool_t *p, afw_xctx_t *xctx)
Create function for unmanaged data type object value.
#define AFW_UTF8_FMT_ARG(A_STRING)
Convenience Macro for use with AFW_UTF8_FMT to specify arg.
Definition: afw_common.h:605
_Bool afw_boolean_t
Definition: afw_common.h:373
#define AFW_UTF8_FMT
Format string specifier used for afw_utf8_t.
Definition: afw_common.h:588
#define afw_content_type_create_object_list_writer(instance, options, context, callback, p, xctx)
Call method create_object_list_writer of interface afw_content_type.
#define afw_content_type_object_list_writer_write_object(instance, object, p, xctx)
Call method write_object of interface afw_content_type_object_list_writer.
#define afw_content_type_object_list_writer_release(instance, xctx)
Call method release of interface afw_content_type_object_list_writer.
#define afw_content_type_is_application_afw(instance)
Determine if content type it application/x-afw.
#define AFW_CATCH_UNHANDLED
Catch an unhandled error that occurs in a AFW_TRY block.
Definition: afw_error.h:684
#define AFW_ENDTRY
Ends an AFW try block.
Definition: afw_error.h:727
#define AFW_TRY
Begin an AFW TRY block.
Definition: afw_error.h:634
#define AFW_THROW_ERROR_FZ(code, xctx, format_z,...)
Macro used to set error and 0 rv in xctx and throw it.
Definition: afw_error.h:319
#define AFW_MARK_UNHANDLED
Use in an AFW_CATCH or AFW_CATCH_UNHANDLED block to mark error as unhandled and break.
Definition: afw_error.h:750
#define AFW_THROW_ERROR_Z(code, message_z, xctx)
Macro used to set error and 0 rv in xctx and throw it.
Definition: afw_error.h:283
afw_object_meta_set_ids_using_path(const afw_object_t *instance, const afw_utf8_t *path, afw_xctx_t *xctx)
Set object's ids using path.
afw_object_options_set_from_object(const afw_object_options_t *initial_options, const afw_object_t *options_object, const afw_pool_t *p, afw_xctx_t *xctx)
Set object processing options from options object.
afw_object_path_property_name_list_get_property(const afw_object_t *object, const afw_object_path_property_name_entry_t *first_property_name, afw_xctx_t *xctx)
Get object property value using property names.
afw_object_path_parse(const afw_utf8_t *path, const afw_utf8_t *current_path, const afw_object_options_t *default_options, const afw_pool_t *p, afw_xctx_t *xctx)
Parse an object value path in specific pool.
#define afw_object_create_managed(p, xctx)
Create an empty entity object in its own pool.
Definition: afw_object.h:913
#define afw_pool_calloc_type(instance, type, xctx)
Macro to allocate cleared memory to hold type in pool.
Definition: afw_pool.h:167
void impl_afw_request_handler_process(const afw_request_handler_t *instance, const afw_request_t *request, afw_xctx_t *xctx)
afw_request_handler_adaptor_create_cede_p(const afw_object_t *properties, const afw_pool_t *p, afw_xctx_t *xctx)
Create an adaptor request handler.
#define afw_request_set_error_info(instance, error_info, xctx)
Call method set_error_info of interface afw_request.
#define afw_request_finish_response(instance, xctx)
Call method finish_response of interface afw_request.
#define AFW_REQUEST_Q_METHOD_GET
Request method GET quoted string.
Definition: afw_request.h:123
afw_request_body_to_value(const afw_request_t *instance, const afw_pool_t *p, afw_xctx_t *xctx)
Read a request body to value in a specifed pool.
Definition: afw_request.c:149
#define AFW_REQUEST_Q_METHOD_DELETE
Definition: afw_request.h:147
#define AFW_REQUEST_Q_METHOD_PUT
Definition: afw_request.h:135
void afw_request_write_value_to_response_body(const afw_request_t *instance, const afw_value_t *value, const afw_object_options_t *options, afw_xctx_t *xctx)
Write value to response body.
Definition: afw_request.c:187
#define AFW_REQUEST_Q_METHOD_POST
Definition: afw_request.h:129
afw_request_write_success_response(const afw_request_t *instance, const afw_object_t *response, afw_xctx_t *xctx)
Write simple success to response body.
Definition: afw_request.c:248
#define afw_stream_write_z(writer, s_z, xctx)
Call afw_stream_write() with zero terminated string.
Definition: afw_stream.h:178
#define afw_stream_standard(enum_suffix, xctx)
Get xctx stream instance.
Definition: afw_stream.h:122
afw_stream_write_integer(const afw_stream_t *writer, afw_integer_t integer, afw_xctx_t *xctx)
Call afw_stream_write() with an integer.
Definition: afw_stream.c:341
afw_boolean_t afw_utf8_equal_utf8_z(const afw_utf8_t *s1, const afw_utf8_z_t *s2_z)
Check to see if a string equals a utf8_z string.
afw_boolean_t afw_utf8_equal(const afw_utf8_t *s1, const afw_utf8_t *s2)
Check to see if a string equals another string.
afw_value_undecorate(const afw_value_t *value)
Return undecorated value.
Definition: afw_value.c:186
Interface afw_content_type_object_list_writer public struct.
Interface afw_content_type public struct.
Struct for object processing options.
Typedef for parsed object path.
Interface afw_object public struct.
Interface afw_pool public struct.
Parsed query criteria.
Interface afw_request_handler public struct.
Interface afw_request public struct.
Interface afw_stream public struct.
struct for data type list values.
struct for data type object values.
Interface afw_value public struct.
Interface afw_xctx public struct.