ARI - implement allowMultiple for parameters
[asterisk/asterisk.git] / rest-api-templates / res_ari_resource.c.mustache
1 {{#api_declaration}}
2 /*
3  * Asterisk -- An open source telephony toolkit.
4  *
5  * {{{copyright}}}
6  *
7  * {{{author}}}
8 {{! Template Copyright
9  * Copyright (C) 2013, Digium, Inc.
10  *
11  * David M. Lee, II <dlee@digium.com>
12 }}
13  *
14  * See http://www.asterisk.org for more information about
15  * the Asterisk project. Please do not directly contact
16  * any of the maintainers of this project for assistance;
17  * the project provides a web site, mailing lists and IRC
18  * channels for your use.
19  *
20  * This program is free software, distributed under the terms of
21  * the GNU General Public License Version 2. See the LICENSE file
22  * at the top of the source tree.
23  */
24
25 {{! Template for rendering the res_ module for an HTTP resource. }}
26 /*
27 {{> do-not-edit}}
28  * This file is generated by a mustache template. Please see the original
29  * template in rest-api-templates/res_ari_resource.c.mustache
30  */
31
32 /*! \file
33  *
34  * \brief {{{description}}}
35  *
36  * \author {{{author}}}
37  */
38
39 /*** MODULEINFO
40         <depend type="module">res_ari</depend>
41         <depend type="module">res_stasis</depend>
42         <support_level>core</support_level>
43  ***/
44
45 #include "asterisk.h"
46
47 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
48
49 #include "asterisk/app.h"
50 #include "asterisk/module.h"
51 #include "asterisk/stasis_app.h"
52 #include "ari/resource_{{name}}.h"
53 #if defined(AST_DEVMODE)
54 #include "ari/ari_model_validators.h"
55 #endif
56
57 #define MAX_VALS 128
58
59 {{#apis}}
60 {{#operations}}
61 {{#is_req}}
62 /*!
63  * \brief Parameter parsing callback for {{path}}.
64  * \param get_params GET parameters in the HTTP request.
65  * \param path_vars Path variables extracted from the request.
66  * \param headers HTTP headers.
67  * \param[out] response Response to the HTTP request.
68  */
69 static void ast_ari_{{c_nickname}}_cb(
70         struct ast_variable *get_params, struct ast_variable *path_vars,
71         struct ast_variable *headers, struct ast_ari_response *response)
72 {
73         struct ast_{{c_nickname}}_args args = {};
74 {{#has_parameters}}
75         struct ast_variable *i;
76 {{/has_parameters}}
77 #if defined(AST_DEVMODE)
78         int is_valid;
79         int code;
80 #endif /* AST_DEVMODE */
81
82 {{> param_parsing}}
83         ast_ari_{{c_nickname}}(headers, &args, response);
84 #if defined(AST_DEVMODE)
85         code = response->response_code;
86
87         switch (code) {
88         case 0: /* Implementation is still a stub, or the code wasn't set */
89                 is_valid = response->message == NULL;
90                 break;
91         case 500: /* Internal Server Error */
92         case 501: /* Not Implemented */
93 {{#error_responses}}
94         case {{code}}: /* {{{reason}}} */
95 {{/error_responses}}
96                 is_valid = 1;
97                 break;
98         default:
99                 if (200 <= code && code <= 299) {
100 {{#response_class}}
101 {{#is_list}}
102                         is_valid = ast_ari_validate_list(response->message,
103                                 ast_ari_validate_{{c_singular_name}}_fn());
104 {{/is_list}}
105 {{^is_list}}
106                         is_valid = ast_ari_validate_{{c_name}}(
107                                 response->message);
108 {{/is_list}}
109 {{/response_class}}
110                 } else {
111                         ast_log(LOG_ERROR, "Invalid error response %d for {{path}}\n", code);
112                         is_valid = 0;
113                 }
114         }
115
116         if (!is_valid) {
117                 ast_log(LOG_ERROR, "Response validation failed for {{path}}\n");
118                 ast_ari_response_error(response, 500,
119                         "Internal Server Error", "Response validation failed");
120         }
121 #endif /* AST_DEVMODE */
122
123 fin: __attribute__((unused))
124 {{> param_cleanup}}
125         return;
126 }
127 {{/is_req}}
128 {{#is_websocket}}
129 static void ast_ari_{{c_nickname}}_ws_cb(struct ast_websocket *ws_session,
130         struct ast_variable *get_params, struct ast_variable *headers)
131 {
132         struct ast_{{c_nickname}}_args args = {};
133 {{#has_parameters}}
134         RAII_VAR(struct ast_ari_response *, response, NULL, ast_free);
135         struct ast_variable *i;
136 {{/has_parameters}}
137         RAII_VAR(struct ast_websocket *, s, ws_session, ast_websocket_unref);
138         RAII_VAR(struct ast_ari_websocket_session *, session, NULL, ao2_cleanup);
139 {{#has_path_parameters}}
140         /* TODO: It's not immediately obvious how to pass path params through
141          * the websocket code to this callback. Not needed right now, so we'll
142          * just punt. */
143         struct ast_variable *path_vars = NULL;
144 {{/has_path_parameters}}
145
146 {{#has_parameters}}
147         response = ast_calloc(1, sizeof(*response));
148         if (!response) {
149                 ast_log(LOG_ERROR, "Failed to create response.\n");
150                 goto fin;
151         }
152 {{/has_parameters}}
153
154 #if defined(AST_DEVMODE)
155         session = ast_ari_websocket_session_create(ws_session,
156                 ast_ari_validate_{{response_class.c_name}}_fn());
157 #else
158         session = ast_ari_websocket_session_create(ws_session, NULL);
159 #endif
160         if (!session) {
161                 ast_log(LOG_ERROR, "Failed to create ARI session\n");
162                 goto fin;
163         }
164
165 {{> param_parsing}}
166
167         ast_ari_websocket_{{c_nickname}}(session, headers, &args);
168
169 fin: __attribute__((unused))
170         if (response && response->response_code != 0) {
171                 /* Param parsing failure */
172                 /* TODO - ideally, this would return the error code to the
173                  * HTTP client; but we've already done the WebSocket
174                  * negotiation. Param parsing should happen earlier, but we
175                  * need a way to pass it through the WebSocket code to the
176                  * callback */
177                 RAII_VAR(char *, msg, NULL, ast_free);
178                 if (response->message) {
179                         msg = ast_json_dump_string(response->message);
180                 } else {
181                         msg = ast_strdup("?");
182                 }
183                 ast_websocket_write(ws_session, AST_WEBSOCKET_OPCODE_TEXT, msg,
184                         strlen(msg));
185         }
186 {{> param_cleanup}}
187 }
188 {{/is_websocket}}
189 {{/operations}}
190 {{/apis}}
191
192 {{! The rest_handler partial expands to the tree of stasis_rest_handlers }}
193 {{#root_path}}
194 {{> rest_handler}}
195 {{/root_path}}
196
197 static int load_module(void)
198 {
199         int res = 0;
200 {{#apis}}
201 {{#has_websocket}}
202         {{full_name}}.ws_server = ast_websocket_server_create();
203         if (!{{full_name}}.ws_server) {
204                 return AST_MODULE_LOAD_FAILURE;
205         }
206 {{/has_websocket}}
207 {{#operations}}
208 {{#is_websocket}}
209         res |= ast_websocket_server_add_protocol({{full_name}}.ws_server,
210                 "{{websocket_protocol}}", ast_ari_{{c_nickname}}_ws_cb);
211 {{/is_websocket}}
212 {{/operations}}
213 {{/apis}}
214         stasis_app_ref();
215         res |= ast_ari_add_handler(&{{root_full_name}});
216         return res;
217 }
218
219 static int unload_module(void)
220 {
221         ast_ari_remove_handler(&{{root_full_name}});
222 {{#apis}}
223 {{#has_websocket}}
224         ao2_cleanup({{full_name}}.ws_server);
225         {{full_name}}.ws_server = NULL;
226 {{/has_websocket}}
227 {{/apis}}
228         stasis_app_unref();
229         return 0;
230 }
231
232 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "RESTful API module - {{{description}}}",
233         .load = load_module,
234         .unload = unload_module,
235         .nonoptreq = "res_ari,res_stasis",
236         );
237 {{/api_declaration}}