This patch adds a RESTful HTTP interface to Asterisk.
The API itself is documented using Swagger, a lightweight mechanism for documenting RESTful API's using JSON. This allows us to use swagger-ui to provide executable documentation for the API, generate client bindings in different languages, and generate a lot of the boilerplate code for implementing the RESTful bindings. The API docs live in the rest-api/ directory. The RESTful bindings are generated from the Swagger API docs using a set of Mustache templates. The code generator is written in Python, and uses Pystache. Pystache has no dependencies, and be installed easily using pip. Code generation code lives in rest-api-templates/. The generated code reduces a lot of boilerplate when it comes to handling HTTP requests. It also helps us have greater consistency in the REST API. (closes issue ASTERISK-20891) Review: https://reviewboard.asterisk.org/r/2376/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@386232 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
parent
1871017cc6
commit
1c21b8575b
21
Makefile
21
Makefile
|
@ -453,6 +453,9 @@ datafiles: _all doc/core-en_US.xml
|
|||
$(INSTALL) -m 644 $$x "$(DESTDIR)$(ASTDATADIR)/images" ; \
|
||||
done
|
||||
$(MAKE) -C sounds install
|
||||
find rest-api -name "*.json" | while read x; do \
|
||||
$(INSTALL) -m 644 $$x "$(DESTDIR)$(ASTDATADIR)/rest-api" ; \
|
||||
done
|
||||
|
||||
ifneq ($(GREP),)
|
||||
XML_core_en_US = $(foreach dir,$(MOD_SUBDIRS),$(shell $(GREP) -l "language=\"en_US\"" $(dir)/*.c $(dir)/*.cc 2>/dev/null))
|
||||
|
@ -537,8 +540,8 @@ INSTALLDIRS="$(ASTLIBDIR)" "$(ASTMODDIR)" "$(ASTSBINDIR)" "$(ASTETCDIR)" "$(ASTV
|
|||
"$(ASTLOGDIR)/cel-custom" "$(ASTDATADIR)" "$(ASTDATADIR)/documentation" \
|
||||
"$(ASTDATADIR)/documentation/thirdparty" "$(ASTDATADIR)/firmware" \
|
||||
"$(ASTDATADIR)/firmware/iax" "$(ASTDATADIR)/images" "$(ASTDATADIR)/keys" \
|
||||
"$(ASTDATADIR)/phoneprov" "$(ASTDATADIR)/static-http" "$(ASTDATADIR)/sounds" \
|
||||
"$(ASTDATADIR)/moh" "$(ASTMANDIR)/man8" "$(AGI_DIR)" "$(ASTDBDIR)"
|
||||
"$(ASTDATADIR)/phoneprov" "$(ASTDATADIR)/rest-api" "$(ASTDATADIR)/static-http" \
|
||||
"$(ASTDATADIR)/sounds" "$(ASTDATADIR)/moh" "$(ASTMANDIR)/man8" "$(AGI_DIR)" "$(ASTDBDIR)"
|
||||
|
||||
installdirs:
|
||||
@for i in $(INSTALLDIRS); do \
|
||||
|
@ -958,6 +961,19 @@ menuselect-tree: $(foreach dir,$(filter-out main,$(MOD_SUBDIRS)),$(wildcard $(di
|
|||
@cat sounds/sounds.xml >> $@
|
||||
@echo "</menu>" >> $@
|
||||
|
||||
# We don't want to require Python or Pystache for every build, so this is its
|
||||
# own target.
|
||||
stasis-stubs:
|
||||
ifeq ($(PYTHON),:)
|
||||
@echo "--------------------------------------------------------------------------"
|
||||
@echo "--- Please install python to build Stasis HTTP stubs ---"
|
||||
@echo "--------------------------------------------------------------------------"
|
||||
@false
|
||||
else
|
||||
$(PYTHON) rest-api-templates/make_stasis_http_stubs.py \
|
||||
rest-api/resources.json res/
|
||||
endif
|
||||
|
||||
.PHONY: menuselect
|
||||
.PHONY: main
|
||||
.PHONY: sounds
|
||||
|
@ -977,6 +993,7 @@ menuselect-tree: $(foreach dir,$(filter-out main,$(MOD_SUBDIRS)),$(wildcard $(di
|
|||
.PHONY: installdirs
|
||||
.PHONY: validate-docs
|
||||
.PHONY: _clean
|
||||
.PHONY: stasis-stubs
|
||||
.PHONY: $(SUBDIRS_INSTALL)
|
||||
.PHONY: $(SUBDIRS_DIST_CLEAN)
|
||||
.PHONY: $(SUBDIRS_CLEAN)
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
[general]
|
||||
enabled = yes ; When set to no, stasis-http support is disabled
|
||||
;pretty = no ; When set to yes, responses from stasis-http are
|
||||
; ; formatted to be human readable
|
||||
;allowed_origins = ; Comma separated list of allowed origins, for
|
||||
; ; Cross-Origin Resource Sharing. May be set to * to allow
|
||||
; ; all origins.
|
||||
|
||||
;[user-username]
|
||||
;read_only = no ; When set to yes, user is only authorized for
|
||||
; ; read-only requests
|
||||
;
|
||||
; If a password is specified, user must authenticate using HTTP Basic
|
||||
; authentication. If no password is specified, then the user may authenticate
|
||||
; simply by adding ?api_key=username to their requests.
|
||||
;
|
||||
;password = ; Crypted or plaintext password (see crypt_password)
|
||||
;
|
||||
; crypt_password may be set to crypt (the default) or plain. When set to crypt,
|
||||
; crypt(3) is used to encrypt the password. A crypted password can be generated
|
||||
; using mkpasswd -m sha-512.
|
||||
;
|
||||
; When set to plain, the password is in plaintext
|
||||
;
|
||||
;crypt_password = plain
|
|
@ -58,7 +58,10 @@ enum ast_http_method {
|
|||
AST_HTTP_GET = 0,
|
||||
AST_HTTP_POST,
|
||||
AST_HTTP_HEAD,
|
||||
AST_HTTP_PUT, /*!< Not supported in Asterisk */
|
||||
AST_HTTP_PUT,
|
||||
AST_HTTP_DELETE,
|
||||
AST_HTTP_OPTIONS,
|
||||
AST_HTTP_MAX_METHOD, /*!< Last entry in ast_http_method enum */
|
||||
};
|
||||
|
||||
struct ast_http_uri;
|
||||
|
|
|
@ -585,17 +585,34 @@ int ast_json_object_iter_set(struct ast_json *object, struct ast_json_iter *iter
|
|||
|
||||
/*!@{*/
|
||||
|
||||
/*!
|
||||
* \brief Encoding format type.
|
||||
* \since 12.0.0
|
||||
*/
|
||||
enum ast_json_encoding_format
|
||||
{
|
||||
/*! Compact format, low human readability */
|
||||
AST_JSON_COMPACT,
|
||||
/*! Formatted for human readability */
|
||||
AST_JSON_PRETTY,
|
||||
};
|
||||
|
||||
#define ast_json_dump_string(root) ast_json_dump_string_format(root, AST_JSON_COMPACT)
|
||||
|
||||
/*!
|
||||
* \brief Encode a JSON value to a string.
|
||||
* \since 12.0.0
|
||||
*
|
||||
* Returned string must be freed by calling ast_free().
|
||||
*
|
||||
* \param JSON value.
|
||||
* \param root JSON value.
|
||||
* \param format encoding format type.
|
||||
* \return String encoding of \a root.
|
||||
* \return \c NULL on error.
|
||||
*/
|
||||
char *ast_json_dump_string(struct ast_json *root);
|
||||
char *ast_json_dump_string_format(struct ast_json *root, enum ast_json_encoding_format format);
|
||||
|
||||
#define ast_json_dump_str(root, dst) ast_json_dump_str_format(root, dst, AST_JSON_COMPACT)
|
||||
|
||||
/*!
|
||||
* \brief Encode a JSON value to an \ref ast_str.
|
||||
|
@ -605,10 +622,13 @@ char *ast_json_dump_string(struct ast_json *root);
|
|||
*
|
||||
* \param root JSON value.
|
||||
* \param dst \ref ast_str to store JSON encoding.
|
||||
* \param format encoding format type.
|
||||
* \return 0 on success.
|
||||
* \return -1 on error. The contents of \a dst are undefined.
|
||||
*/
|
||||
int ast_json_dump_str(struct ast_json *root, struct ast_str **dst);
|
||||
int ast_json_dump_str_format(struct ast_json *root, struct ast_str **dst, enum ast_json_encoding_format format);
|
||||
|
||||
#define ast_json_dump_file(root, output) ast_json_dump_file_format(root, output, AST_JSON_COMPACT)
|
||||
|
||||
/*!
|
||||
* \brief Encode a JSON value to a \c FILE.
|
||||
|
@ -616,10 +636,13 @@ int ast_json_dump_str(struct ast_json *root, struct ast_str **dst);
|
|||
*
|
||||
* \param root JSON value.
|
||||
* \param output File to write JSON encoding to.
|
||||
* \param format encoding format type.
|
||||
* \return 0 on success.
|
||||
* \return -1 on error. The contents of \a output are undefined.
|
||||
*/
|
||||
int ast_json_dump_file(struct ast_json *root, FILE *output);
|
||||
int ast_json_dump_file_format(struct ast_json *root, FILE *output, enum ast_json_encoding_format format);
|
||||
|
||||
#define ast_json_dump_new_file(root, path) ast_json_dump_new_file_format(root, path, AST_JSON_COMPACT)
|
||||
|
||||
/*!
|
||||
* \brief Encode a JSON value to a file at the given location.
|
||||
|
@ -627,10 +650,11 @@ int ast_json_dump_file(struct ast_json *root, FILE *output);
|
|||
*
|
||||
* \param root JSON value.
|
||||
* \param path Path to file to write JSON encoding to.
|
||||
* \param format encoding format type.
|
||||
* \return 0 on success.
|
||||
* \return -1 on error. The contents of \a output are undefined.
|
||||
*/
|
||||
int ast_json_dump_new_file(struct ast_json *root, const char *path);
|
||||
int ast_json_dump_new_file_format(struct ast_json *root, const char *path, enum ast_json_encoding_format format);
|
||||
|
||||
#define AST_JSON_ERROR_TEXT_LENGTH 160
|
||||
#define AST_JSON_ERROR_SOURCE_LENGTH 80
|
||||
|
|
|
@ -66,7 +66,7 @@ struct ast_channel_snapshot;
|
|||
* \param argv Arguments for the application.
|
||||
*/
|
||||
int stasis_app_exec(struct ast_channel *chan, const char *app_name, int argc,
|
||||
char *argv[]);
|
||||
char *argv[]);
|
||||
|
||||
/*! @} */
|
||||
|
||||
|
@ -126,22 +126,50 @@ int stasis_app_send(const char *app_name, struct ast_json *message);
|
|||
struct stasis_app_control;
|
||||
|
||||
/*!
|
||||
* \brief Returns the handler for the given channel
|
||||
* \brief Returns the handler for the given channel.
|
||||
* \param chan Channel to handle.
|
||||
* \return NULL channel not in Stasis application
|
||||
* \return Pointer to stasis handler.
|
||||
* \return NULL channel not in Stasis application.
|
||||
* \return Pointer to \c res_stasis handler.
|
||||
*/
|
||||
struct stasis_app_control *stasis_app_control_find_by_channel(
|
||||
const struct ast_channel *chan);
|
||||
|
||||
/*!
|
||||
* \brief Exit \c app_stasis and continue execution in the dialplan.
|
||||
*
|
||||
* If the channel is no longer in \c app_stasis, this function does nothing.
|
||||
*
|
||||
* \param handler Handler for \c app_stasis
|
||||
* \brief Returns the handler for the channel with the given id.
|
||||
* \param channel_id Uniqueid of the channel.
|
||||
* \return NULL channel not in Stasis application, or channel does not exist.
|
||||
* \return Pointer to \c res_stasis handler.
|
||||
*/
|
||||
void stasis_app_control_continue(struct stasis_app_control *handler);
|
||||
struct stasis_app_control *stasis_app_control_find_by_channel_id(
|
||||
const char *channel_id);
|
||||
|
||||
/*!
|
||||
* \brief Exit \c res_stasis and continue execution in the dialplan.
|
||||
*
|
||||
* If the channel is no longer in \c res_stasis, this function does nothing.
|
||||
*
|
||||
* \param control Control for \c res_stasis
|
||||
*/
|
||||
void stasis_app_control_continue(struct stasis_app_control *control);
|
||||
|
||||
/*!
|
||||
* \brief Answer the channel associated with this control.
|
||||
* \param control Control for \c res_stasis.
|
||||
* \return 0 for success.
|
||||
* \return -1 for error.
|
||||
*/
|
||||
int stasis_app_control_answer(struct stasis_app_control *control);
|
||||
|
||||
/*! @} */
|
||||
|
||||
/*! @{ */
|
||||
|
||||
/*!
|
||||
* \brief Build a JSON object from a \ref ast_channel_snapshot.
|
||||
* \return JSON object representing channel snapshot.
|
||||
* \return \c NULL on error
|
||||
*/
|
||||
struct ast_json *ast_channel_snapshot_to_json(const struct ast_channel_snapshot *snapshot);
|
||||
|
||||
/*! @} */
|
||||
|
||||
|
|
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2012 - 2013, Digium, Inc.
|
||||
*
|
||||
* David M. Lee, II <dlee@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef _ASTERISK_STASIS_HTTP_H
|
||||
#define _ASTERISK_STASIS_HTTP_H
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Stasis RESTful API hooks.
|
||||
*
|
||||
* This header file is used mostly as glue code between generated declarations
|
||||
* and res_stasis_http.c.
|
||||
*
|
||||
* \author David M. Lee, II <dlee@digium.com>
|
||||
*/
|
||||
|
||||
#include "asterisk/http.h"
|
||||
#include "asterisk/json.h"
|
||||
#include "asterisk/http_websocket.h"
|
||||
|
||||
struct stasis_http_response;
|
||||
|
||||
/*!
|
||||
* \brief Callback type for RESTful method handlers.
|
||||
* \param get_params GET parameters from the HTTP request.
|
||||
* \param path_vars Path variables from any wildcard path segments.
|
||||
* \param headers HTTP headers from the HTTP requiest.
|
||||
* \param[out] response The RESTful response.
|
||||
*/
|
||||
typedef void (*stasis_rest_callback)(struct ast_variable *get_params,
|
||||
struct ast_variable *path_vars,
|
||||
struct ast_variable *headers,
|
||||
struct stasis_http_response *response);
|
||||
|
||||
/*!
|
||||
* \brief Handler for a single RESTful path segment.
|
||||
*/
|
||||
struct stasis_rest_handlers {
|
||||
/*! Path segement to handle */
|
||||
const char *path_segment;
|
||||
/*! If true (non-zero), path_segment is a wildcard, and will match all values.
|
||||
*
|
||||
* Value of the segement will be passed into the \a path_vars parameter of the callback.
|
||||
*/
|
||||
int is_wildcard;
|
||||
/*! Callbacks for all handled HTTP methods. */
|
||||
stasis_rest_callback callbacks[AST_HTTP_MAX_METHOD];
|
||||
/*! Number of children in the children array */
|
||||
size_t num_children;
|
||||
/*! Handlers for sub-paths */
|
||||
struct stasis_rest_handlers *children[];
|
||||
};
|
||||
|
||||
/*!
|
||||
* Response type for RESTful requests
|
||||
*/
|
||||
struct stasis_http_response {
|
||||
/*! Response message */
|
||||
struct ast_json *message;
|
||||
/*! \r\n seperated response headers */
|
||||
struct ast_str *headers;
|
||||
/*! HTTP response code.
|
||||
* See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html */
|
||||
int response_code;
|
||||
/*! Corresponding text for the response code */
|
||||
const char *response_text; // Shouldn't http.c handle this?
|
||||
};
|
||||
|
||||
/*!
|
||||
* Add a resource for REST handling.
|
||||
* \param handler Handler to add.
|
||||
* \return 0 on success.
|
||||
* \return non-zero on failure.
|
||||
*/
|
||||
int stasis_http_add_handler(struct stasis_rest_handlers *handler);
|
||||
|
||||
/*!
|
||||
* Remove a resource for REST handling.
|
||||
* \param handler Handler to add.
|
||||
* \return 0 on success.
|
||||
* \return non-zero on failure.
|
||||
*/
|
||||
int stasis_http_remove_handler(struct stasis_rest_handlers *handler);
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Stasis RESTful invocation handler.
|
||||
*
|
||||
* Only call from res_stasis_http and test_stasis_http. Only public to allow
|
||||
* for unit testing.
|
||||
*
|
||||
* \param uri HTTP URI, relative to the API path.
|
||||
* \param method HTTP method.
|
||||
* \param get_params HTTP \c GET parameters.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response RESTful HTTP response.
|
||||
*/
|
||||
void stasis_http_invoke(const char *uri, enum ast_http_method method, struct ast_variable *get_params,
|
||||
struct ast_variable *headers, struct stasis_http_response *response);
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Service function for API declarations.
|
||||
*
|
||||
* Only call from res_stasis_http and test_stasis_http. Only public to allow
|
||||
* for unit testing.
|
||||
*
|
||||
* \param uri Requested URI, relative to the docs path.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response RESTful HTTP response.
|
||||
*/
|
||||
void stasis_http_get_docs(const char *uri, struct ast_variable *headers, struct stasis_http_response *response);
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Stasis WebSocket connection handler
|
||||
* \param session WebSocket session.
|
||||
* \param parameters HTTP \c GET parameters.
|
||||
* \param headers HTTP headers.
|
||||
*/
|
||||
void stasis_websocket_callback(struct ast_websocket *session, struct ast_variable *parameters, struct ast_variable *headers);
|
||||
|
||||
/*!
|
||||
* \brief Fill in an error \a stasis_http_response.
|
||||
* \param response Response to fill in.
|
||||
* \param response_code HTTP response code.
|
||||
* \param response_text Text corresponding to the HTTP response code.
|
||||
* \param message_fmt Error message format string.
|
||||
*/
|
||||
void stasis_http_response_error(struct stasis_http_response *response,
|
||||
int response_code,
|
||||
const char *response_text,
|
||||
const char *message_fmt, ...)
|
||||
__attribute__((format(printf, 4, 5)));
|
||||
|
||||
/*!
|
||||
* \brief Fill in an \c OK (200) \a stasis_http_response.
|
||||
* \param response Response to fill in.
|
||||
* \param message JSON response. This reference is stolen, so just \ref
|
||||
* ast_json_incref if you need to keep a reference to it.
|
||||
*/
|
||||
void stasis_http_response_ok(struct stasis_http_response *response,
|
||||
struct ast_json *message);
|
||||
|
||||
/*!
|
||||
* \brief Fill in a <tt>No Content</tt> (204) \a stasis_http_response.
|
||||
*/
|
||||
void stasis_http_response_no_content(struct stasis_http_response *response);
|
||||
|
||||
/*!
|
||||
* \brief Fill in \a response with a 500 message for allocation failures.
|
||||
* \param response Response to fill in.
|
||||
*/
|
||||
void stasis_http_response_alloc_failed(struct stasis_http_response *response);
|
||||
|
||||
#endif /* _ASTERISK_STASIS_HTTP_H */
|
|
@ -82,6 +82,48 @@ static force_inline int attribute_pure ast_strlen_zero(const char *s)
|
|||
*/
|
||||
#define S_COR(a, b, c) ({typeof(&((b)[0])) __x = (b); (a) && !ast_strlen_zero(__x) ? (__x) : (c);})
|
||||
|
||||
/*
|
||||
\brief Checks whether a string begins with another.
|
||||
\since 12.0.0
|
||||
\param str String to check.
|
||||
\param prefix Prefix to look for.
|
||||
\param 1 if \a str begins with \a prefix, 0 otherwise.
|
||||
*/
|
||||
static int force_inline attribute_pure ast_begins_with(const char *str, const char *prefix)
|
||||
{
|
||||
ast_assert(str != NULL);
|
||||
ast_assert(prefix != NULL);
|
||||
while (*str == *prefix && *prefix != '\0') {
|
||||
++str;
|
||||
++prefix;
|
||||
}
|
||||
return *prefix == '\0';
|
||||
}
|
||||
|
||||
/*
|
||||
\brief Checks whether a string ends with another.
|
||||
\since 12.0.0
|
||||
\param str String to check.
|
||||
\param suffix Suffix to look for.
|
||||
\param 1 if \a str ends with \a suffix, 0 otherwise.
|
||||
*/
|
||||
static int force_inline attribute_pure ast_ends_with(const char *str, const char *suffix)
|
||||
{
|
||||
size_t str_len;
|
||||
size_t suffix_len;
|
||||
|
||||
ast_assert(str != NULL);
|
||||
ast_assert(suffix != NULL);
|
||||
str_len = strlen(str);
|
||||
suffix_len = strlen(suffix);
|
||||
|
||||
if (suffix_len > str_len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return strcmp(str + str_len - suffix_len, suffix) == 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief return Yes or No depending on the argument.
|
||||
*
|
||||
|
|
|
@ -153,6 +153,8 @@ static const struct ast_cfhttp_methods_text {
|
|||
{ AST_HTTP_POST, "POST" },
|
||||
{ AST_HTTP_HEAD, "HEAD" },
|
||||
{ AST_HTTP_PUT, "PUT" },
|
||||
{ AST_HTTP_DELETE, "DELETE" },
|
||||
{ AST_HTTP_OPTIONS, "OPTIONS" },
|
||||
};
|
||||
|
||||
const char *ast_get_http_method(enum ast_http_method method)
|
||||
|
@ -897,6 +899,10 @@ static void *httpd_helper_thread(void *data)
|
|||
http_method = AST_HTTP_HEAD;
|
||||
} else if (!strcasecmp(method,"PUT")) {
|
||||
http_method = AST_HTTP_PUT;
|
||||
} else if (!strcasecmp(method,"DELETE")) {
|
||||
http_method = AST_HTTP_DELETE;
|
||||
} else if (!strcasecmp(method,"OPTIONS")) {
|
||||
http_method = AST_HTTP_OPTIONS;
|
||||
}
|
||||
|
||||
uri = ast_skip_blanks(uri); /* Skip white space */
|
||||
|
|
27
main/json.c
27
main/json.c
|
@ -338,20 +338,15 @@ int ast_json_object_iter_set(struct ast_json *object, struct ast_json_iter *iter
|
|||
/*!
|
||||
* \brief Default flags for JSON encoding.
|
||||
*/
|
||||
static size_t dump_flags(void)
|
||||
static size_t dump_flags(enum ast_json_encoding_format format)
|
||||
{
|
||||
/* There's a chance this could become a runtime flag */
|
||||
int flags = JSON_COMPACT;
|
||||
#ifdef AST_DEVMODE
|
||||
/* In dev mode, write readable JSON */
|
||||
flags = JSON_INDENT(2) | JSON_PRESERVE_ORDER;
|
||||
#endif
|
||||
return flags;
|
||||
return format == AST_JSON_PRETTY ?
|
||||
JSON_INDENT(2) | JSON_PRESERVE_ORDER : JSON_COMPACT;
|
||||
}
|
||||
|
||||
char *ast_json_dump_string(struct ast_json *root)
|
||||
char *ast_json_dump_string_format(struct ast_json *root, enum ast_json_encoding_format format)
|
||||
{
|
||||
return json_dumps((json_t *)root, dump_flags());
|
||||
return json_dumps((json_t *)root, dump_flags(format));
|
||||
}
|
||||
|
||||
static int write_to_ast_str(const char *buffer, size_t size, void *data)
|
||||
|
@ -385,25 +380,25 @@ static int write_to_ast_str(const char *buffer, size_t size, void *data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int ast_json_dump_str(struct ast_json *root, struct ast_str **dst)
|
||||
int ast_json_dump_str_format(struct ast_json *root, struct ast_str **dst, enum ast_json_encoding_format format)
|
||||
{
|
||||
return json_dump_callback((json_t *)root, write_to_ast_str, dst, dump_flags());
|
||||
return json_dump_callback((json_t *)root, write_to_ast_str, dst, dump_flags(format));
|
||||
}
|
||||
|
||||
|
||||
int ast_json_dump_file(struct ast_json *root, FILE *output)
|
||||
int ast_json_dump_file_format(struct ast_json *root, FILE *output, enum ast_json_encoding_format format)
|
||||
{
|
||||
if (!root || !output) {
|
||||
return -1;
|
||||
}
|
||||
return json_dumpf((json_t *)root, output, dump_flags());
|
||||
return json_dumpf((json_t *)root, output, dump_flags(format));
|
||||
}
|
||||
int ast_json_dump_new_file(struct ast_json *root, const char *path)
|
||||
int ast_json_dump_new_file_format(struct ast_json *root, const char *path, enum ast_json_encoding_format format)
|
||||
{
|
||||
if (!root || !path) {
|
||||
return -1;
|
||||
}
|
||||
return json_dump_file((json_t *)root, path, dump_flags());
|
||||
return json_dump_file((json_t *)root, path, dump_flags(format));
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
|
@ -67,4 +67,7 @@ endif
|
|||
ael/pval.o: ael/pval.c
|
||||
|
||||
clean::
|
||||
rm -f snmp/*.o snmp/*.i ael/*.o ael/*.i ais/*.o ais/*.i
|
||||
rm -f snmp/*.[oi] ael/*.[oi] ais/*.[oi] stasis_http/*.[oi]
|
||||
|
||||
# Dependencies for res_stasis_http_*.so are generated, so they're in this file
|
||||
include stasis_http.make
|
||||
|
|
170
res/res_stasis.c
170
res/res_stasis.c
|
@ -40,6 +40,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||
#include "asterisk/stasis_channels.h"
|
||||
#include "asterisk/strings.h"
|
||||
|
||||
/*! Time to wait for a frame in the application */
|
||||
#define MAX_WAIT_MS 200
|
||||
|
||||
/*!
|
||||
* \brief Number of buckets for the Stasis application hash table. Remember to
|
||||
* keep it a prime number!
|
||||
|
@ -147,7 +150,67 @@ static void app_send(struct app *app, struct ast_json *message)
|
|||
app->handler(app->data, app->name, message);
|
||||
}
|
||||
|
||||
typedef void* (*stasis_app_command_cb)(struct stasis_app_control *control,
|
||||
struct ast_channel *chan,
|
||||
void *data);
|
||||
|
||||
struct stasis_app_command {
|
||||
ast_mutex_t lock;
|
||||
ast_cond_t condition;
|
||||
stasis_app_command_cb callback;
|
||||
void *data;
|
||||
void *retval;
|
||||
int is_done:1;
|
||||
};
|
||||
|
||||
static void command_dtor(void *obj)
|
||||
{
|
||||
struct stasis_app_command *command = obj;
|
||||
ast_mutex_destroy(&command->lock);
|
||||
ast_cond_destroy(&command->condition);
|
||||
}
|
||||
|
||||
static struct stasis_app_command *command_create(stasis_app_command_cb callback,
|
||||
void *data)
|
||||
{
|
||||
RAII_VAR(struct stasis_app_command *, command, NULL, ao2_cleanup);
|
||||
|
||||
command = ao2_alloc(sizeof(*command), command_dtor);
|
||||
if (!command) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ast_mutex_init(&command->lock);
|
||||
ast_cond_init(&command->condition, 0);
|
||||
command->callback = callback;
|
||||
command->data = data;
|
||||
|
||||
ao2_ref(command, +1);
|
||||
return command;
|
||||
}
|
||||
|
||||
static void command_complete(struct stasis_app_command *command, void *retval)
|
||||
{
|
||||
SCOPED_MUTEX(lock, &command->lock);
|
||||
|
||||
command->is_done = 1;
|
||||
command->retval = retval;
|
||||
ast_cond_signal(&command->condition);
|
||||
}
|
||||
|
||||
static void *wait_for_command(struct stasis_app_command *command)
|
||||
{
|
||||
SCOPED_MUTEX(lock, &command->lock);
|
||||
while (!command->is_done) {
|
||||
ast_cond_wait(&command->condition, &command->lock);
|
||||
}
|
||||
|
||||
return command->retval;
|
||||
}
|
||||
|
||||
struct stasis_app_control {
|
||||
/*! Queue of commands to dispatch on the channel */
|
||||
struct ao2_container *command_queue;
|
||||
/*!
|
||||
* When set, /c app_stasis should exit and continue in the dialplan.
|
||||
*/
|
||||
|
@ -167,11 +230,24 @@ static struct stasis_app_control *control_create(const char *uniqueid)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
control->command_queue = ao2_container_alloc_list(0, 0, NULL, NULL);
|
||||
|
||||
strncpy(control->channel_id, uniqueid, size - sizeof(*control));
|
||||
|
||||
return control;
|
||||
}
|
||||
|
||||
static void *exec_command(struct stasis_app_control *control,
|
||||
struct stasis_app_command *command)
|
||||
{
|
||||
ao2_lock(control);
|
||||
ao2_ref(command, +1);
|
||||
ao2_link(control->command_queue, command);
|
||||
ao2_unlock(control);
|
||||
|
||||
return wait_for_command(command);
|
||||
}
|
||||
|
||||
/*! AO2 hash function for \ref stasis_app_control */
|
||||
static int control_hash(const void *obj, const int flags)
|
||||
{
|
||||
|
@ -199,13 +275,20 @@ static int control_compare(void *lhs, void *rhs, int flags)
|
|||
struct stasis_app_control *stasis_app_control_find_by_channel(
|
||||
const struct ast_channel *chan)
|
||||
{
|
||||
RAII_VAR(struct ao2_container *, controls, NULL, ao2_cleanup);
|
||||
if (chan == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return stasis_app_control_find_by_channel_id(
|
||||
ast_channel_uniqueid(chan));
|
||||
}
|
||||
|
||||
struct stasis_app_control *stasis_app_control_find_by_channel_id(
|
||||
const char *channel_id)
|
||||
{
|
||||
RAII_VAR(struct ao2_container *, controls, NULL, ao2_cleanup);
|
||||
controls = app_controls();
|
||||
return ao2_find(controls, ast_channel_uniqueid(chan), OBJ_KEY);
|
||||
return ao2_find(controls, channel_id, OBJ_KEY);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -233,6 +316,33 @@ void stasis_app_control_continue(struct stasis_app_control *control)
|
|||
control->continue_to_dialplan = 1;
|
||||
}
|
||||
|
||||
static int OK = 0;
|
||||
static int FAIL = -1;
|
||||
|
||||
static void *__app_control_answer(struct stasis_app_control *control,
|
||||
struct ast_channel *chan, void *data)
|
||||
{
|
||||
ast_debug(3, "%s: Answering", control->channel_id);
|
||||
return __ast_answer(chan, 0, 1) == 0 ? &OK : &FAIL;
|
||||
}
|
||||
|
||||
int stasis_app_control_answer(struct stasis_app_control *control)
|
||||
{
|
||||
RAII_VAR(struct stasis_app_command *, command, NULL, ao2_cleanup);
|
||||
int *retval;
|
||||
|
||||
ast_debug(3, "%s: Sending answer command\n", control->channel_id);
|
||||
|
||||
command = command_create(__app_control_answer, NULL);
|
||||
retval = exec_command(control, command);
|
||||
|
||||
if (*retval != 0) {
|
||||
ast_log(LOG_WARNING, "Failed to answer channel");
|
||||
}
|
||||
|
||||
return *retval;
|
||||
}
|
||||
|
||||
static struct ast_json *app_event_create(
|
||||
const char *event_name,
|
||||
const struct ast_channel_snapshot *snapshot,
|
||||
|
@ -410,6 +520,26 @@ static void control_unlink(struct stasis_app_control *control)
|
|||
ao2_cleanup(control);
|
||||
}
|
||||
|
||||
static void dispatch_commands(struct stasis_app_control *control,
|
||||
struct ast_channel *chan)
|
||||
{
|
||||
struct ao2_iterator i;
|
||||
void *obj;
|
||||
|
||||
SCOPED_AO2LOCK(lock, control);
|
||||
|
||||
i = ao2_iterator_init(control->command_queue, AO2_ITERATOR_UNLINK);
|
||||
|
||||
while ((obj = ao2_iterator_next(&i))) {
|
||||
RAII_VAR(struct stasis_app_command *, command, obj, ao2_cleanup);
|
||||
void *retval = command->callback(control, chan, command->data);
|
||||
command_complete(command, retval);
|
||||
}
|
||||
|
||||
ao2_iterator_destroy(&i);
|
||||
}
|
||||
|
||||
|
||||
/*! /brief Stasis dialplan application callback */
|
||||
int stasis_app_exec(struct ast_channel *chan, const char *app_name, int argc,
|
||||
char *argv[])
|
||||
|
@ -458,8 +588,38 @@ int stasis_app_exec(struct ast_channel *chan, const char *app_name, int argc,
|
|||
return res;
|
||||
}
|
||||
|
||||
while (!hungup && !control_continue_test_and_reset(control) && ast_waitfor(chan, -1) > -1) {
|
||||
RAII_VAR(struct ast_frame *, f, ast_read(chan), ast_frame_dtor);
|
||||
while (1) {
|
||||
RAII_VAR(struct ast_frame *, f, NULL, ast_frame_dtor);
|
||||
int r;
|
||||
|
||||
if (hungup) {
|
||||
ast_debug(3, "%s: Hangup\n",
|
||||
ast_channel_uniqueid(chan));
|
||||
break;
|
||||
}
|
||||
|
||||
if (control_continue_test_and_reset(control)) {
|
||||
ast_debug(3, "%s: Continue\n",
|
||||
ast_channel_uniqueid(chan));
|
||||
break;
|
||||
}
|
||||
|
||||
r = ast_waitfor(chan, MAX_WAIT_MS);
|
||||
|
||||
if (r < 0) {
|
||||
ast_debug(3, "%s: Poll error\n",
|
||||
ast_channel_uniqueid(chan));
|
||||
break;
|
||||
}
|
||||
|
||||
dispatch_commands(control, chan);
|
||||
|
||||
if (r == 0) {
|
||||
/* Timeout */
|
||||
continue;
|
||||
}
|
||||
|
||||
f = ast_read(chan);
|
||||
if (!f) {
|
||||
ast_debug(3, "%s: No more frames. Must be done, I guess.\n", ast_channel_uniqueid(chan));
|
||||
break;
|
||||
|
@ -468,8 +628,6 @@ int stasis_app_exec(struct ast_channel *chan, const char *app_name, int argc,
|
|||
switch (f->frametype) {
|
||||
case AST_FRAME_CONTROL:
|
||||
if (f->subclass.integer == AST_CONTROL_HANGUP) {
|
||||
ast_debug(3, "%s: Received hangup\n",
|
||||
ast_channel_uniqueid(chan));
|
||||
hungup = 1;
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -0,0 +1,932 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2012 - 2013, Digium, Inc.
|
||||
*
|
||||
* David M. Lee, II <dlee@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief HTTP binding for the Stasis API
|
||||
* \author David M. Lee, II <dlee@digium.com>
|
||||
*
|
||||
* The API itself is documented using <a
|
||||
* href="https://developers.helloreverb.com/swagger/">Swagger</a>, a lightweight
|
||||
* mechanism for documenting RESTful API's using JSON. This allows us to use <a
|
||||
* href="https://github.com/wordnik/swagger-ui">swagger-ui</a> to provide
|
||||
* executable documentation for the API, generate client bindings in different
|
||||
* <a href="https://github.com/asterisk/asterisk_rest_libraries">languages</a>,
|
||||
* and generate a lot of the boilerplate code for implementing the RESTful
|
||||
* bindings. The API docs live in the \c rest-api/ directory.
|
||||
*
|
||||
* The RESTful bindings are generated from the Swagger API docs using a set of
|
||||
* <a href="http://mustache.github.io/mustache.5.html">Mustache</a> templates.
|
||||
* The code generator is written in Python, and uses the Python implementation
|
||||
* <a href="https://github.com/defunkt/pystache">pystache</a>. Pystache has no
|
||||
* dependencies, and be installed easily using \c pip. Code generation code
|
||||
* lives in \c rest-api-templates/.
|
||||
*
|
||||
* The generated code reduces a lot of boilerplate when it comes to handling
|
||||
* HTTP requests. It also helps us have greater consistency in the REST API.
|
||||
*
|
||||
* The structure of the generated code is:
|
||||
*
|
||||
* - res/stasis_http/resource_{resource}.h
|
||||
* - For each operation in the resouce, a generated argument structure
|
||||
* (holding the parsed arguments from the request) and function
|
||||
* declarations (to implement in res/stasis_http/resource_{resource}.c)
|
||||
* - res_stasis_http_{resource}.c
|
||||
* - A set of \ref stasis_rest_callback functions, which glue the two
|
||||
* together. They parse out path variables and request parameters to
|
||||
* populate a specific \c *_args which is passed to the specific request
|
||||
* handler (in res/stasis_http/resource_{resource}.c)
|
||||
* - A tree of \ref stasis_rest_handlers for routing requests to its
|
||||
* \ref stasis_rest_callback
|
||||
*
|
||||
* The basic flow of an HTTP request is:
|
||||
*
|
||||
* - stasis_http_callback()
|
||||
* 1. Initial request validation
|
||||
* 2. Routes as either a doc request (stasis_http_get_docs) or API
|
||||
* request (stasis_http_invoke)
|
||||
* - stasis_http_invoke()
|
||||
* 1. Further request validation
|
||||
* 2. Routes the request through the tree of generated
|
||||
* \ref stasis_rest_handlers.
|
||||
* 3. Dispatch to the generated callback
|
||||
* - \c stasis_http_*_cb
|
||||
* 1. Populate \c *_args struct with path and get params
|
||||
* 2. Invoke the request handler
|
||||
* 3. Validates and sends response
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<depend type="module">app_stasis</depend>
|
||||
<support_level>core</support_level>
|
||||
***/
|
||||
|
||||
/*** DOCUMENTATION
|
||||
<configInfo name="res_stasis_http" language="en_US">
|
||||
<synopsis>HTTP binding for the Stasis API</synopsis>
|
||||
<configFile name="stasis_http.conf">
|
||||
<configObject name="global">
|
||||
<synopsis>Global configuration settings</synopsis>
|
||||
<configOption name="enabled">
|
||||
<synopsis>Enable/disable the stasis-http module</synopsis>
|
||||
</configOption>
|
||||
<configOption name="pretty">
|
||||
<synopsis>Responses from stasis-http are formatted to be human readable</synopsis>
|
||||
</configOption>
|
||||
</configObject>
|
||||
</configFile>
|
||||
</configInfo>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/paths.h"
|
||||
#include "asterisk/stasis_http.h"
|
||||
#include "asterisk/config_options.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/*! \brief Global configuration options for stasis http. */
|
||||
struct conf_global_options {
|
||||
/*! Enabled by default, disabled if false. */
|
||||
int enabled:1;
|
||||
/*! Encoding format used during output (default compact). */
|
||||
enum ast_json_encoding_format format;
|
||||
};
|
||||
|
||||
/*! \brief All configuration options for stasis http. */
|
||||
struct conf {
|
||||
/*! The general section configuration options. */
|
||||
struct conf_global_options *global;
|
||||
};
|
||||
|
||||
/*! \brief Locking container for safe configuration access. */
|
||||
static AO2_GLOBAL_OBJ_STATIC(confs);
|
||||
|
||||
/*! \brief Mapping of the stasis http conf struct's globals to the
|
||||
* general context in the config file. */
|
||||
static struct aco_type global_option = {
|
||||
.type = ACO_GLOBAL,
|
||||
.name = "global",
|
||||
.item_offset = offsetof(struct conf, global),
|
||||
.category = "^general$",
|
||||
.category_match = ACO_WHITELIST
|
||||
};
|
||||
|
||||
static struct aco_type *global_options[] = ACO_TYPES(&global_option);
|
||||
|
||||
/*! \brief Disposes of the stasis http conf object */
|
||||
static void conf_destructor(void *obj)
|
||||
{
|
||||
struct conf *cfg = obj;
|
||||
ao2_cleanup(cfg->global);
|
||||
}
|
||||
|
||||
/*! \brief Creates the statis http conf object. */
|
||||
static void *conf_alloc(void)
|
||||
{
|
||||
struct conf *cfg;
|
||||
|
||||
if (!(cfg = ao2_alloc(sizeof(*cfg), conf_destructor))) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(cfg->global = ao2_alloc(sizeof(*cfg->global), NULL))) {
|
||||
ao2_ref(cfg, -1);
|
||||
return NULL;
|
||||
}
|
||||
return cfg;
|
||||
}
|
||||
|
||||
/*! \brief The conf file that's processed for the module. */
|
||||
static struct aco_file conf_file = {
|
||||
/*! The config file name. */
|
||||
.filename = "stasis_http.conf",
|
||||
/*! The mapping object types to be processed. */
|
||||
.types = ACO_TYPES(&global_option),
|
||||
};
|
||||
|
||||
CONFIG_INFO_STANDARD(cfg_info, confs, conf_alloc,
|
||||
.files = ACO_FILES(&conf_file));
|
||||
|
||||
/*! \brief Bitfield handler since it is not possible to take address. */
|
||||
static int conf_bitfield_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
||||
{
|
||||
struct conf_global_options *global = obj;
|
||||
|
||||
if (!strcasecmp(var->name, "enabled")) {
|
||||
global->enabled = ast_true(var->value);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief Encoding format handler converts from boolean to enum. */
|
||||
static int encoding_format_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
||||
{
|
||||
struct conf_global_options *global = obj;
|
||||
|
||||
if (!strcasecmp(var->name, "pretty")) {
|
||||
global->format = ast_true(var->value) ? AST_JSON_PRETTY : AST_JSON_COMPACT;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief Helper function to check if module is enabled. */
|
||||
static char is_enabled(void)
|
||||
{
|
||||
RAII_VAR(struct conf *, cfg, ao2_global_obj_ref(confs), ao2_cleanup);
|
||||
|
||||
return cfg->global->enabled;
|
||||
}
|
||||
|
||||
/*! Lock for \ref root_handler */
|
||||
static ast_mutex_t root_handler_lock;
|
||||
|
||||
/*! Handler for root RESTful resource. */
|
||||
static struct stasis_rest_handlers *root_handler;
|
||||
|
||||
/*! Pre-defined message for allocation failures. */
|
||||
static struct ast_json *alloc_failed_message;
|
||||
|
||||
int stasis_http_add_handler(struct stasis_rest_handlers *handler)
|
||||
{
|
||||
RAII_VAR(struct stasis_rest_handlers *, new_handler, NULL, ao2_cleanup);
|
||||
size_t old_size, new_size;
|
||||
|
||||
SCOPED_MUTEX(lock, &root_handler_lock);
|
||||
|
||||
old_size = sizeof(*new_handler) +
|
||||
root_handler->num_children * sizeof(handler);
|
||||
new_size = old_size + sizeof(handler);
|
||||
|
||||
new_handler = ao2_alloc(new_size, NULL);
|
||||
if (!new_handler) {
|
||||
return -1;
|
||||
}
|
||||
memcpy(new_handler, root_handler, old_size);
|
||||
new_handler->children[new_handler->num_children++] = handler;
|
||||
|
||||
ao2_cleanup(root_handler);
|
||||
ao2_ref(new_handler, +1);
|
||||
root_handler = new_handler;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int stasis_http_remove_handler(struct stasis_rest_handlers *handler)
|
||||
{
|
||||
RAII_VAR(struct stasis_rest_handlers *, new_handler, NULL, ao2_cleanup);
|
||||
size_t old_size, new_size, i, j;
|
||||
|
||||
SCOPED_MUTEX(lock, &root_handler_lock);
|
||||
old_size = sizeof(*new_handler) +
|
||||
root_handler->num_children * sizeof(handler);
|
||||
new_size = old_size - sizeof(handler);
|
||||
|
||||
new_handler = ao2_alloc(new_size, NULL);
|
||||
if (!new_handler) {
|
||||
return -1;
|
||||
}
|
||||
memcpy(new_handler, root_handler, sizeof(*new_handler));
|
||||
|
||||
for (i = 0, j = 0; i < root_handler->num_children; ++i) {
|
||||
if (root_handler->children[i] == handler) {
|
||||
continue;
|
||||
}
|
||||
new_handler->children[j++] = root_handler->children[i];
|
||||
}
|
||||
new_handler->num_children = j;
|
||||
|
||||
ao2_cleanup(root_handler);
|
||||
ao2_ref(new_handler, +1);
|
||||
root_handler = new_handler;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct stasis_rest_handlers *get_root_handler(void)
|
||||
{
|
||||
SCOPED_MUTEX(lock, &root_handler_lock);
|
||||
ao2_ref(root_handler, +1);
|
||||
return root_handler;
|
||||
}
|
||||
|
||||
static struct stasis_rest_handlers *root_handler_create(void)
|
||||
{
|
||||
RAII_VAR(struct stasis_rest_handlers *, handler, NULL, ao2_cleanup);
|
||||
|
||||
handler = ao2_alloc(sizeof(*handler), NULL);
|
||||
if (!handler) {
|
||||
return NULL;
|
||||
}
|
||||
handler->path_segment = "stasis";
|
||||
|
||||
ao2_ref(handler, +1);
|
||||
return handler;
|
||||
}
|
||||
|
||||
void stasis_http_response_error(struct stasis_http_response *response,
|
||||
int response_code,
|
||||
const char *response_text,
|
||||
const char *message_fmt, ...)
|
||||
{
|
||||
RAII_VAR(struct ast_json *, message, NULL, ast_json_unref);
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, message_fmt);
|
||||
message = ast_json_vstringf(message_fmt, ap);
|
||||
response->message = ast_json_pack("{s: o}",
|
||||
"message", ast_json_ref(message));
|
||||
response->response_code = response_code;
|
||||
response->response_text = response_text;
|
||||
}
|
||||
|
||||
void stasis_http_response_ok(struct stasis_http_response *response,
|
||||
struct ast_json *message)
|
||||
{
|
||||
response->message = message;
|
||||
response->response_code = 200;
|
||||
response->response_text = "OK";
|
||||
}
|
||||
|
||||
void stasis_http_response_no_content(struct stasis_http_response *response)
|
||||
{
|
||||
response->message = NULL;
|
||||
response->response_code = 204;
|
||||
response->response_text = "No Content";
|
||||
}
|
||||
|
||||
void stasis_http_response_alloc_failed(struct stasis_http_response *response)
|
||||
{
|
||||
response->message = ast_json_ref(alloc_failed_message);
|
||||
response->response_code = 500;
|
||||
response->response_text = "Internal Server Error";
|
||||
}
|
||||
|
||||
static void add_allow_header(struct stasis_rest_handlers *handler,
|
||||
struct stasis_http_response *response)
|
||||
{
|
||||
enum ast_http_method m;
|
||||
ast_str_append(&response->headers, 0,
|
||||
"Allow: OPTIONS");
|
||||
for (m = 0; m < AST_HTTP_MAX_METHOD; ++m) {
|
||||
if (handler->callbacks[m] != NULL) {
|
||||
ast_str_append(&response->headers, 0,
|
||||
",%s", ast_get_http_method(m));
|
||||
}
|
||||
}
|
||||
ast_str_append(&response->headers, 0, "\r\n");
|
||||
}
|
||||
|
||||
#define ACR_METHOD "Access-Control-Request-Method"
|
||||
#define ACR_HEADERS "Access-Control-Request-Headers"
|
||||
#define ACA_METHODS "Access-Control-Allow-Methods"
|
||||
#define ACA_HEADERS "Access-Control-Allow-Headers"
|
||||
|
||||
/*!
|
||||
* \brief Handle OPTIONS request, mainly for CORS preflight requests.
|
||||
*
|
||||
* Some browsers will send this prior to non-simple methods (i.e. DELETE).
|
||||
* See http://www.w3.org/TR/cors/ for the spec. Especially section 6.2.
|
||||
*/
|
||||
static void handle_options(struct stasis_rest_handlers *handler,
|
||||
struct ast_variable *headers,
|
||||
struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_variable *header;
|
||||
char const *acr_method = NULL;
|
||||
char const *acr_headers = NULL;
|
||||
char const *origin = NULL;
|
||||
|
||||
RAII_VAR(struct ast_str *, allow, NULL, ast_free);
|
||||
enum ast_http_method m;
|
||||
int allowed = 0;
|
||||
|
||||
/* Regular OPTIONS response */
|
||||
add_allow_header(handler, response);
|
||||
response->response_code = 204;
|
||||
response->response_text = "No Content";
|
||||
response->message = NULL;
|
||||
|
||||
/* Parse CORS headers */
|
||||
for (header = headers; header != NULL; header = header->next) {
|
||||
if (strcmp(ACR_METHOD, header->name) == 0) {
|
||||
acr_method = header->value;
|
||||
} else if (strcmp(ACR_HEADERS, header->name) == 0) {
|
||||
acr_headers = header->value;
|
||||
} else if (strcmp("Origin", header->name) == 0) {
|
||||
origin = header->value;
|
||||
}
|
||||
}
|
||||
|
||||
/* CORS 6.2, #1 - "If the Origin header is not present terminate this
|
||||
* set of steps.
|
||||
*/
|
||||
if (origin == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* CORS 6.2, #2 - "If the value of the Origin header is not a
|
||||
* case-sensitive match for any of the values in list of origins do not
|
||||
* set any additional headers and terminate this set of steps.
|
||||
*
|
||||
* "Always matching is acceptable since the list of origins can be
|
||||
* unbounded.
|
||||
*
|
||||
* "The Origin header can only contain a single origin as the user agent
|
||||
* will not follow redirects.
|
||||
*
|
||||
* TODO - pull list of allowed origins from config
|
||||
*/
|
||||
|
||||
/* CORS 6.2, #3 - "If there is no Access-Control-Request-Method header
|
||||
* or if parsing failed, do not set any additional headers and terminate
|
||||
* this set of steps."
|
||||
*/
|
||||
if (acr_method == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* CORS 6.2, #4 - "If there are no Access-Control-Request-Headers
|
||||
* headers let header field-names be the empty list."
|
||||
*/
|
||||
if (acr_headers == NULL) {
|
||||
acr_headers = "";
|
||||
}
|
||||
|
||||
/* CORS 6.2, #5 - "If method is not a case-sensitive match for any of
|
||||
* the values in list of methods do not set any additional headers and
|
||||
* terminate this set of steps."
|
||||
*/
|
||||
allow = ast_str_create(20);
|
||||
|
||||
if (!allow) {
|
||||
stasis_http_response_alloc_failed(response);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Go ahead and build the ACA_METHODS header at the same time */
|
||||
for (m = 0; m < AST_HTTP_MAX_METHOD; ++m) {
|
||||
if (handler->callbacks[m] != NULL) {
|
||||
char const *m_str = ast_get_http_method(m);
|
||||
if (strcmp(m_str, acr_method) == 0) {
|
||||
allowed = 1;
|
||||
}
|
||||
ast_str_append(&allow, 0, ",%s", m_str);
|
||||
}
|
||||
}
|
||||
|
||||
if (!allowed) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* CORS 6.2 #6 - "If any of the header field-names is not a ASCII
|
||||
* case-insensitive match for any of the values in list of headers do
|
||||
* not set any additional headers and terminate this set of steps.
|
||||
*
|
||||
* "Note: Always matching is acceptable since the list of headers can be
|
||||
* unbounded."
|
||||
*/
|
||||
|
||||
/* CORS 6.2 #7 - "If the resource supports credentials add a single
|
||||
* Access-Control-Allow-Origin header, with the value of the Origin
|
||||
* header as value, and add a single Access-Control-Allow-Credentials
|
||||
* header with the case-sensitive string "true" as value."
|
||||
*
|
||||
* Added by process_cors_request() earlier in the request.
|
||||
*/
|
||||
|
||||
/* CORS 6.2 #8 - "Optionally add a single Access-Control-Max-Age
|
||||
* header..."
|
||||
*/
|
||||
|
||||
/* CORS 6.2 #9 - "Add one or more Access-Control-Allow-Methods headers
|
||||
* consisting of (a subset of) the list of methods."
|
||||
*/
|
||||
ast_str_append(&response->headers, 0, "%s: OPTIONS,%s\r\n",
|
||||
ACA_METHODS, ast_str_buffer(allow));
|
||||
|
||||
|
||||
/* CORS 6.2, #10 - "Add one or more Access-Control-Allow-Headers headers
|
||||
* consisting of (a subset of) the list of headers.
|
||||
*
|
||||
* "Since the list of headers can be unbounded simply returning headers
|
||||
* can be enough."
|
||||
*/
|
||||
if (!ast_strlen_zero(acr_headers)) {
|
||||
ast_str_append(&response->headers, 0, "%s: %s\r\n",
|
||||
ACA_HEADERS, acr_headers);
|
||||
}
|
||||
}
|
||||
|
||||
void stasis_http_invoke(const char *uri,
|
||||
enum ast_http_method method,
|
||||
struct ast_variable *get_params,
|
||||
struct ast_variable *headers,
|
||||
struct stasis_http_response *response)
|
||||
{
|
||||
RAII_VAR(char *, response_text, NULL, ast_free);
|
||||
RAII_VAR(struct stasis_rest_handlers *, root, NULL, ao2_cleanup);
|
||||
struct stasis_rest_handlers *handler;
|
||||
struct ast_variable *path_vars = NULL;
|
||||
char *path = ast_strdupa(uri);
|
||||
const char *path_segment;
|
||||
stasis_rest_callback callback;
|
||||
|
||||
root = handler = get_root_handler();
|
||||
ast_assert(root != NULL);
|
||||
|
||||
while ((path_segment = strsep(&path, "/")) && (strlen(path_segment) > 0)) {
|
||||
struct stasis_rest_handlers *found_handler = NULL;
|
||||
int i;
|
||||
ast_debug(3, "Finding handler for %s\n", path_segment);
|
||||
for (i = 0; found_handler == NULL && i < handler->num_children; ++i) {
|
||||
struct stasis_rest_handlers *child = handler->children[i];
|
||||
|
||||
ast_debug(3, " Checking %s\n", child->path_segment);
|
||||
if (child->is_wildcard) {
|
||||
/* Record the path variable */
|
||||
struct ast_variable *path_var = ast_variable_new(child->path_segment, path_segment, __FILE__);
|
||||
path_var->next = path_vars;
|
||||
path_vars = path_var;
|
||||
found_handler = child;
|
||||
} else if (strcmp(child->path_segment, path_segment) == 0) {
|
||||
found_handler = child;
|
||||
}
|
||||
}
|
||||
|
||||
if (found_handler == NULL) {
|
||||
/* resource not found */
|
||||
ast_debug(3, " Handler not found\n");
|
||||
stasis_http_response_error(
|
||||
response, 404, "Not Found",
|
||||
"Resource not found");
|
||||
return;
|
||||
} else {
|
||||
ast_debug(3, " Got it!\n");
|
||||
handler = found_handler;
|
||||
}
|
||||
}
|
||||
|
||||
ast_assert(handler != NULL);
|
||||
if (method == AST_HTTP_OPTIONS) {
|
||||
handle_options(handler, headers, response);
|
||||
return;
|
||||
}
|
||||
|
||||
if (method < 0 || method >= AST_HTTP_MAX_METHOD) {
|
||||
add_allow_header(handler, response);
|
||||
stasis_http_response_error(
|
||||
response, 405, "Method Not Allowed",
|
||||
"Invalid method");
|
||||
return;
|
||||
}
|
||||
|
||||
callback = handler->callbacks[method];
|
||||
if (callback == NULL) {
|
||||
add_allow_header(handler, response);
|
||||
stasis_http_response_error(
|
||||
response, 405, "Method Not Allowed",
|
||||
"Invalid method");
|
||||
return;
|
||||
}
|
||||
|
||||
callback(get_params, path_vars, headers, response);
|
||||
if (response->message == NULL && response->response_code == 0) {
|
||||
/* Really should not happen */
|
||||
ast_assert(0);
|
||||
stasis_http_response_error(
|
||||
response, 418, "I'm a teapot",
|
||||
"Method not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
void stasis_http_get_docs(const char *uri, struct ast_variable *headers,
|
||||
struct stasis_http_response *response)
|
||||
{
|
||||
RAII_VAR(struct ast_str *, absolute_path_builder, NULL, ast_free);
|
||||
RAII_VAR(char *, absolute_api_dirname, NULL, free);
|
||||
RAII_VAR(char *, absolute_filename, NULL, free);
|
||||
struct ast_json *obj = NULL;
|
||||
struct ast_variable *host = NULL;
|
||||
struct ast_json_error error = {};
|
||||
struct stat file_stat;
|
||||
|
||||
ast_debug(3, "%s(%s)\n", __func__, uri);
|
||||
|
||||
absolute_path_builder = ast_str_create(80);
|
||||
if (absolute_path_builder == NULL) {
|
||||
stasis_http_response_alloc_failed(response);
|
||||
return;
|
||||
}
|
||||
|
||||
/* absolute path to the rest-api directory */
|
||||
ast_str_append(&absolute_path_builder, 0, "%s", ast_config_AST_DATA_DIR);
|
||||
ast_str_append(&absolute_path_builder, 0, "/rest-api/");
|
||||
absolute_api_dirname = realpath(ast_str_buffer(absolute_path_builder), NULL);
|
||||
if (absolute_api_dirname == NULL) {
|
||||
ast_log(LOG_ERROR, "Error determining real directory for rest-api\n");
|
||||
stasis_http_response_error(
|
||||
response, 500, "Internal Server Error",
|
||||
"Cannot find rest-api directory");
|
||||
return;
|
||||
}
|
||||
|
||||
/* absolute path to the requested file */
|
||||
ast_str_append(&absolute_path_builder, 0, "%s", uri);
|
||||
absolute_filename = realpath(ast_str_buffer(absolute_path_builder), NULL);
|
||||
if (absolute_filename == NULL) {
|
||||
switch (errno) {
|
||||
case ENAMETOOLONG:
|
||||
case ENOENT:
|
||||
case ENOTDIR:
|
||||
stasis_http_response_error(
|
||||
response, 404, "Not Found",
|
||||
"Resource not found");
|
||||
break;
|
||||
case EACCES:
|
||||
stasis_http_response_error(
|
||||
response, 403, "Forbidden",
|
||||
"Permission denied");
|
||||
break;
|
||||
default:
|
||||
ast_log(LOG_ERROR,
|
||||
"Error determining real path for uri '%s': %s\n",
|
||||
uri, strerror(errno));
|
||||
stasis_http_response_error(
|
||||
response, 500, "Internal Server Error",
|
||||
"Cannot find file");
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ast_begins_with(absolute_filename, absolute_api_dirname)) {
|
||||
/* HACKERZ! */
|
||||
ast_log(LOG_ERROR,
|
||||
"Invalid attempt to access '%s' (not in %s)\n",
|
||||
absolute_filename, absolute_api_dirname);
|
||||
stasis_http_response_error(
|
||||
response, 404, "Not Found",
|
||||
"Resource not found");
|
||||
return;
|
||||
}
|
||||
|
||||
if (stat(absolute_filename, &file_stat) == 0) {
|
||||
if (!(file_stat.st_mode & S_IFREG)) {
|
||||
/* Not a file */
|
||||
stasis_http_response_error(
|
||||
response, 403, "Forbidden",
|
||||
"Invalid access");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
/* Does not exist */
|
||||
stasis_http_response_error(
|
||||
response, 404, "Not Found",
|
||||
"Resource not found");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Load resource object from file */
|
||||
obj = ast_json_load_new_file(absolute_filename, &error);
|
||||
if (obj == NULL) {
|
||||
ast_log(LOG_ERROR, "Error parsing resource file: %s:%d(%d) %s\n",
|
||||
error.source, error.line, error.column, error.text);
|
||||
stasis_http_response_error(
|
||||
response, 500, "Internal Server Error",
|
||||
"Yikes! Cannot parse resource");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Update the basePath properly */
|
||||
if (ast_json_object_get(obj, "basePath") != NULL) {
|
||||
for (host = headers; host; host = host->next) {
|
||||
if (strcasecmp(host->name, "Host") == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (host != NULL) {
|
||||
ast_json_object_set(
|
||||
obj, "basePath",
|
||||
ast_json_stringf("http://%s/stasis", host->value));
|
||||
} else {
|
||||
/* Without the host, we don't have the basePath */
|
||||
ast_json_object_del(obj, "basePath");
|
||||
}
|
||||
}
|
||||
|
||||
stasis_http_response_ok(response, obj);
|
||||
}
|
||||
|
||||
static void remove_trailing_slash(const char *uri,
|
||||
struct stasis_http_response *response)
|
||||
{
|
||||
char *slashless = ast_strdupa(uri);
|
||||
slashless[strlen(slashless) - 1] = '\0';
|
||||
|
||||
ast_str_append(&response->headers, 0,
|
||||
"Location: /stasis/%s\r\n", slashless);
|
||||
stasis_http_response_error(response, 302, "Found",
|
||||
"Redirecting to %s", slashless);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Handle CORS headers for simple requests.
|
||||
*
|
||||
* See http://www.w3.org/TR/cors/ for the spec. Especially section 6.1.
|
||||
*/
|
||||
static void process_cors_request(struct ast_variable *headers,
|
||||
struct stasis_http_response *response)
|
||||
{
|
||||
char const *origin = NULL;
|
||||
struct ast_variable *header;
|
||||
|
||||
/* Parse CORS headers */
|
||||
for (header = headers; header != NULL; header = header->next) {
|
||||
if (strcmp("Origin", header->name) == 0) {
|
||||
origin = header->value;
|
||||
}
|
||||
}
|
||||
|
||||
/* CORS 6.1, #1 - "If the Origin header is not present terminate this
|
||||
* set of steps."
|
||||
*/
|
||||
if (origin == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* CORS 6.1, #2 - "If the value of the Origin header is not a
|
||||
* case-sensitive match for any of the values in list of origins, do not
|
||||
* set any additional headers and terminate this set of steps.
|
||||
*
|
||||
* "Note: Always matching is acceptable since the list of origins can be
|
||||
* unbounded."
|
||||
*
|
||||
* TODO - pull list of allowed origins from config
|
||||
*/
|
||||
|
||||
/* CORS 6.1, #3 - "If the resource supports credentials add a single
|
||||
* Access-Control-Allow-Origin header, with the value of the Origin
|
||||
* header as value, and add a single Access-Control-Allow-Credentials
|
||||
* header with the case-sensitive string "true" as value.
|
||||
*
|
||||
* "Otherwise, add a single Access-Control-Allow-Origin header, with
|
||||
* either the value of the Origin header or the string "*" as value."
|
||||
*
|
||||
* TODO - when we add authentication, this will change to
|
||||
* Access-Control-Allow-Credentials.
|
||||
*/
|
||||
ast_str_append(&response->headers, 0,
|
||||
"Access-Control-Allow-Origin: %s\r\n", origin);
|
||||
|
||||
/* CORS 6.1, #4 - "If the list of exposed headers is not empty add one
|
||||
* or more Access-Control-Expose-Headers headers, with as values the
|
||||
* header field names given in the list of exposed headers."
|
||||
*
|
||||
* No exposed headers; skipping
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Stasis HTTP handler.
|
||||
*
|
||||
* This handler takes the HTTP request and turns it into the appropriate
|
||||
* RESTful request (conversion to JSON, routing, etc.)
|
||||
*
|
||||
* \param ser TCP session.
|
||||
* \param urih URI handler.
|
||||
* \param uri URI requested.
|
||||
* \param method HTTP method.
|
||||
* \param get_params HTTP \c GET params.
|
||||
* \param headers HTTP headers.
|
||||
*/
|
||||
static int stasis_http_callback(struct ast_tcptls_session_instance *ser,
|
||||
const struct ast_http_uri *urih,
|
||||
const char *uri,
|
||||
enum ast_http_method method,
|
||||
struct ast_variable *get_params,
|
||||
struct ast_variable *headers)
|
||||
{
|
||||
RAII_VAR(struct conf *, cfg, ao2_global_obj_ref(confs), ao2_cleanup);
|
||||
RAII_VAR(struct ast_str *, response_headers, ast_str_create(40), ast_free);
|
||||
RAII_VAR(struct ast_str *, response_body, ast_str_create(256), ast_free);
|
||||
struct stasis_http_response response = {};
|
||||
int ret = 0;
|
||||
|
||||
if (!response_headers || !response_body) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
response.headers = ast_str_create(40);
|
||||
|
||||
process_cors_request(headers, &response);
|
||||
|
||||
if (ast_ends_with(uri, "/")) {
|
||||
remove_trailing_slash(uri, &response);
|
||||
} else if (ast_begins_with(uri, "api-docs/")) {
|
||||
/* Serving up API docs */
|
||||
if (method != AST_HTTP_GET) {
|
||||
response.message =
|
||||
ast_json_pack("{s: s}",
|
||||
"message", "Unsupported method");
|
||||
response.response_code = 405;
|
||||
response.response_text = "Method Not Allowed";
|
||||
} else {
|
||||
/* Skip the api-docs prefix */
|
||||
stasis_http_get_docs(strchr(uri, '/') + 1, headers, &response);
|
||||
}
|
||||
} else {
|
||||
/* Other RESTful resources */
|
||||
stasis_http_invoke(uri, method, get_params, headers, &response);
|
||||
}
|
||||
|
||||
/* Leaving message unset is only allowed for 204 (No Content).
|
||||
* If you explicitly want to have no content for a different return
|
||||
* code, set message to ast_json_null().
|
||||
*/
|
||||
ast_assert(response.response_code == 204 || response.message != NULL);
|
||||
ast_assert(response.response_code > 0);
|
||||
|
||||
ast_str_append(&response_headers, 0, "%s", ast_str_buffer(response.headers));
|
||||
|
||||
/* response.message could be NULL, in which case the empty response_body
|
||||
* is correct
|
||||
*/
|
||||
if (response.message && !ast_json_is_null(response.message)) {
|
||||
ast_str_append(&response_headers, 0,
|
||||
"Content-type: application/json\r\n");
|
||||
if (ast_json_dump_str_format(response.message, &response_body, cfg->global->format) != 0) {
|
||||
/* Error encoding response */
|
||||
response.response_code = 500;
|
||||
response.response_text = "Internal Server Error";
|
||||
ast_str_set(&response_body, 0, "%s", "");
|
||||
ast_str_set(&response_headers, 0, "%s", "");
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
|
||||
ast_http_send(ser, method, response.response_code,
|
||||
response.response_text, response_headers, response_body,
|
||||
0, 0);
|
||||
/* ast_http_send takes ownership, so we don't have to free them */
|
||||
response_headers = NULL;
|
||||
response_body = NULL;
|
||||
|
||||
ast_json_unref(response.message);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct ast_http_uri http_uri = {
|
||||
.callback = stasis_http_callback,
|
||||
.description = "Asterisk RESTful API",
|
||||
.uri = "stasis",
|
||||
|
||||
.has_subtree = 1,
|
||||
.data = NULL,
|
||||
.key = __FILE__,
|
||||
};
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
ast_mutex_init(&root_handler_lock);
|
||||
root_handler = root_handler_create();
|
||||
if (!root_handler) {
|
||||
return AST_MODULE_LOAD_FAILURE;
|
||||
}
|
||||
|
||||
if (aco_info_init(&cfg_info)) {
|
||||
aco_info_destroy(&cfg_info);
|
||||
return AST_MODULE_LOAD_DECLINE;
|
||||
}
|
||||
|
||||
aco_option_register_custom(&cfg_info, "enabled", ACO_EXACT, global_options,
|
||||
"yes", conf_bitfield_handler, 0);
|
||||
aco_option_register_custom(&cfg_info, "pretty", ACO_EXACT, global_options,
|
||||
"no", encoding_format_handler, 0);
|
||||
|
||||
if (aco_process_config(&cfg_info, 0)) {
|
||||
aco_info_destroy(&cfg_info);
|
||||
return AST_MODULE_LOAD_DECLINE;
|
||||
}
|
||||
|
||||
alloc_failed_message = ast_json_pack(
|
||||
"{s: s}", "message", "Allocation failed");
|
||||
|
||||
if (is_enabled()) {
|
||||
ast_http_uri_link(&http_uri);
|
||||
}
|
||||
|
||||
return AST_MODULE_LOAD_SUCCESS;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
ast_json_unref(alloc_failed_message);
|
||||
alloc_failed_message = NULL;
|
||||
|
||||
if (is_enabled()) {
|
||||
ast_http_uri_unlink(&http_uri);
|
||||
}
|
||||
|
||||
aco_info_destroy(&cfg_info);
|
||||
ao2_global_obj_release(confs);
|
||||
|
||||
ao2_cleanup(root_handler);
|
||||
ast_mutex_destroy(&root_handler_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int reload_module(void)
|
||||
{
|
||||
char was_enabled = is_enabled();
|
||||
|
||||
if (aco_process_config(&cfg_info, 1)) {
|
||||
return AST_MODULE_LOAD_DECLINE;
|
||||
}
|
||||
|
||||
if (was_enabled && !is_enabled()) {
|
||||
ast_http_uri_unlink(&http_uri);
|
||||
} else if (!was_enabled && is_enabled()) {
|
||||
ast_http_uri_link(&http_uri);
|
||||
}
|
||||
|
||||
return AST_MODULE_LOAD_SUCCESS;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO(ASTERISK_GPL_KEY,
|
||||
AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER,
|
||||
"Stasis HTTP bindings",
|
||||
.load = load_module,
|
||||
.unload = unload_module,
|
||||
.reload = reload_module,
|
||||
.nonoptreq = "app_stasis",
|
||||
.load_pri = AST_MODPRI_APP_DEPEND,
|
||||
);
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
global:
|
||||
LINKER_SYMBOL_PREFIXstasis_http_*;
|
||||
local:
|
||||
*;
|
||||
};
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2012 - 2013, Digium, Inc.
|
||||
*
|
||||
* David M. Lee, II <dlee@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* !!!!! DO NOT EDIT !!!!!
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* This file is generated by a mustache template. Please see the original
|
||||
* template in rest-api-templates/res_stasis_http_resource.c.mustache
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Asterisk resources
|
||||
*
|
||||
* \author David M. Lee, II <dlee@digium.com>
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<depend type="module">res_stasis_http</depend>
|
||||
<support_level>core</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include "asterisk/module.h"
|
||||
#include "stasis_http/resource_asterisk.h"
|
||||
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /asterisk/info.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_get_asterisk_info_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_get_asterisk_info_args args = {};
|
||||
struct ast_variable *i;
|
||||
|
||||
for (i = get_params; i; i = i->next) {
|
||||
if (strcmp(i->name, "only") == 0) {
|
||||
args.only = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
stasis_http_get_asterisk_info(headers, &args, response);
|
||||
}
|
||||
|
||||
/*! \brief REST handler for /api-docs/asterisk.{format} */
|
||||
static struct stasis_rest_handlers asterisk_info = {
|
||||
.path_segment = "info",
|
||||
.callbacks = {
|
||||
[AST_HTTP_GET] = stasis_http_get_asterisk_info_cb,
|
||||
},
|
||||
.num_children = 0,
|
||||
.children = { }
|
||||
};
|
||||
/*! \brief REST handler for /api-docs/asterisk.{format} */
|
||||
static struct stasis_rest_handlers asterisk = {
|
||||
.path_segment = "asterisk",
|
||||
.callbacks = {
|
||||
},
|
||||
.num_children = 1,
|
||||
.children = { &asterisk_info, }
|
||||
};
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
return stasis_http_add_handler(&asterisk);
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
stasis_http_remove_handler(&asterisk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT,
|
||||
"RESTful API module - Asterisk resources",
|
||||
.load = load_module,
|
||||
.unload = unload_module,
|
||||
.nonoptreq = "res_stasis_http",
|
||||
);
|
|
@ -0,0 +1,291 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2012 - 2013, Digium, Inc.
|
||||
*
|
||||
* David M. Lee, II <dlee@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* !!!!! DO NOT EDIT !!!!!
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* This file is generated by a mustache template. Please see the original
|
||||
* template in rest-api-templates/res_stasis_http_resource.c.mustache
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Bridge resources
|
||||
*
|
||||
* \author David M. Lee, II <dlee@digium.com>
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<depend type="module">res_stasis_http</depend>
|
||||
<support_level>core</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include "asterisk/module.h"
|
||||
#include "stasis_http/resource_bridges.h"
|
||||
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /bridges.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_get_bridges_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_get_bridges_args args = {};
|
||||
stasis_http_get_bridges(headers, &args, response);
|
||||
}
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /bridges.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_new_bridge_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_new_bridge_args args = {};
|
||||
struct ast_variable *i;
|
||||
|
||||
for (i = get_params; i; i = i->next) {
|
||||
if (strcmp(i->name, "type") == 0) {
|
||||
args.type = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
stasis_http_new_bridge(headers, &args, response);
|
||||
}
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /bridges/{bridgeId}.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_get_bridge_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_get_bridge_args args = {};
|
||||
struct ast_variable *i;
|
||||
|
||||
for (i = path_vars; i; i = i->next) {
|
||||
if (strcmp(i->name, "bridgeId") == 0) {
|
||||
args.bridge_id = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
stasis_http_get_bridge(headers, &args, response);
|
||||
}
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /bridges/{bridgeId}.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_delete_bridge_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_delete_bridge_args args = {};
|
||||
struct ast_variable *i;
|
||||
|
||||
for (i = path_vars; i; i = i->next) {
|
||||
if (strcmp(i->name, "bridgeId") == 0) {
|
||||
args.bridge_id = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
stasis_http_delete_bridge(headers, &args, response);
|
||||
}
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /bridges/{bridgeId}/addChannel.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_add_channel_to_bridge_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_add_channel_to_bridge_args args = {};
|
||||
struct ast_variable *i;
|
||||
|
||||
for (i = get_params; i; i = i->next) {
|
||||
if (strcmp(i->name, "channel") == 0) {
|
||||
args.channel = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
for (i = path_vars; i; i = i->next) {
|
||||
if (strcmp(i->name, "bridgeId") == 0) {
|
||||
args.bridge_id = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
stasis_http_add_channel_to_bridge(headers, &args, response);
|
||||
}
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /bridges/{bridgeId}/removeChannel.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_remove_channel_from_bridge_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_remove_channel_from_bridge_args args = {};
|
||||
struct ast_variable *i;
|
||||
|
||||
for (i = get_params; i; i = i->next) {
|
||||
if (strcmp(i->name, "channel") == 0) {
|
||||
args.channel = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
for (i = path_vars; i; i = i->next) {
|
||||
if (strcmp(i->name, "bridgeId") == 0) {
|
||||
args.bridge_id = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
stasis_http_remove_channel_from_bridge(headers, &args, response);
|
||||
}
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /bridges/{bridgeId}/record.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_record_bridge_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_record_bridge_args args = {};
|
||||
struct ast_variable *i;
|
||||
|
||||
for (i = get_params; i; i = i->next) {
|
||||
if (strcmp(i->name, "name") == 0) {
|
||||
args.name = (i->value);
|
||||
} else
|
||||
if (strcmp(i->name, "maxDurationSeconds") == 0) {
|
||||
args.max_duration_seconds = atoi(i->value);
|
||||
} else
|
||||
if (strcmp(i->name, "maxSilenceSeconds") == 0) {
|
||||
args.max_silence_seconds = atoi(i->value);
|
||||
} else
|
||||
if (strcmp(i->name, "append") == 0) {
|
||||
args.append = atoi(i->value);
|
||||
} else
|
||||
if (strcmp(i->name, "beep") == 0) {
|
||||
args.beep = atoi(i->value);
|
||||
} else
|
||||
if (strcmp(i->name, "terminateOn") == 0) {
|
||||
args.terminate_on = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
for (i = path_vars; i; i = i->next) {
|
||||
if (strcmp(i->name, "bridgeId") == 0) {
|
||||
args.bridge_id = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
stasis_http_record_bridge(headers, &args, response);
|
||||
}
|
||||
|
||||
/*! \brief REST handler for /api-docs/bridges.{format} */
|
||||
static struct stasis_rest_handlers bridges_bridgeId_addChannel = {
|
||||
.path_segment = "addChannel",
|
||||
.callbacks = {
|
||||
[AST_HTTP_POST] = stasis_http_add_channel_to_bridge_cb,
|
||||
},
|
||||
.num_children = 0,
|
||||
.children = { }
|
||||
};
|
||||
/*! \brief REST handler for /api-docs/bridges.{format} */
|
||||
static struct stasis_rest_handlers bridges_bridgeId_removeChannel = {
|
||||
.path_segment = "removeChannel",
|
||||
.callbacks = {
|
||||
[AST_HTTP_POST] = stasis_http_remove_channel_from_bridge_cb,
|
||||
},
|
||||
.num_children = 0,
|
||||
.children = { }
|
||||
};
|
||||
/*! \brief REST handler for /api-docs/bridges.{format} */
|
||||
static struct stasis_rest_handlers bridges_bridgeId_record = {
|
||||
.path_segment = "record",
|
||||
.callbacks = {
|
||||
[AST_HTTP_POST] = stasis_http_record_bridge_cb,
|
||||
},
|
||||
.num_children = 0,
|
||||
.children = { }
|
||||
};
|
||||
/*! \brief REST handler for /api-docs/bridges.{format} */
|
||||
static struct stasis_rest_handlers bridges_bridgeId = {
|
||||
.path_segment = "bridgeId",
|
||||
.is_wildcard = 1,
|
||||
.callbacks = {
|
||||
[AST_HTTP_GET] = stasis_http_get_bridge_cb,
|
||||
[AST_HTTP_DELETE] = stasis_http_delete_bridge_cb,
|
||||
},
|
||||
.num_children = 3,
|
||||
.children = { &bridges_bridgeId_addChannel,&bridges_bridgeId_removeChannel,&bridges_bridgeId_record, }
|
||||
};
|
||||
/*! \brief REST handler for /api-docs/bridges.{format} */
|
||||
static struct stasis_rest_handlers bridges = {
|
||||
.path_segment = "bridges",
|
||||
.callbacks = {
|
||||
[AST_HTTP_GET] = stasis_http_get_bridges_cb,
|
||||
[AST_HTTP_POST] = stasis_http_new_bridge_cb,
|
||||
},
|
||||
.num_children = 1,
|
||||
.children = { &bridges_bridgeId, }
|
||||
};
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
return stasis_http_add_handler(&bridges);
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
stasis_http_remove_handler(&bridges);
|
||||
return 0;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT,
|
||||
"RESTful API module - Bridge resources",
|
||||
.load = load_module,
|
||||
.unload = unload_module,
|
||||
.nonoptreq = "res_stasis_http",
|
||||
);
|
|
@ -0,0 +1,504 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2012 - 2013, Digium, Inc.
|
||||
*
|
||||
* David M. Lee, II <dlee@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* !!!!! DO NOT EDIT !!!!!
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* This file is generated by a mustache template. Please see the original
|
||||
* template in rest-api-templates/res_stasis_http_resource.c.mustache
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Channel resources
|
||||
*
|
||||
* \author David M. Lee, II <dlee@digium.com>
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<depend type="module">res_stasis_http</depend>
|
||||
<support_level>core</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include "asterisk/module.h"
|
||||
#include "stasis_http/resource_channels.h"
|
||||
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /channels.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_get_channels_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_get_channels_args args = {};
|
||||
stasis_http_get_channels(headers, &args, response);
|
||||
}
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /channels.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_originate_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_originate_args args = {};
|
||||
struct ast_variable *i;
|
||||
|
||||
for (i = get_params; i; i = i->next) {
|
||||
if (strcmp(i->name, "endpoint") == 0) {
|
||||
args.endpoint = (i->value);
|
||||
} else
|
||||
if (strcmp(i->name, "extension") == 0) {
|
||||
args.extension = (i->value);
|
||||
} else
|
||||
if (strcmp(i->name, "context") == 0) {
|
||||
args.context = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
stasis_http_originate(headers, &args, response);
|
||||
}
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /channels/{channelId}.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_get_channel_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_get_channel_args args = {};
|
||||
struct ast_variable *i;
|
||||
|
||||
for (i = path_vars; i; i = i->next) {
|
||||
if (strcmp(i->name, "channelId") == 0) {
|
||||
args.channel_id = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
stasis_http_get_channel(headers, &args, response);
|
||||
}
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /channels/{channelId}.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_delete_channel_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_delete_channel_args args = {};
|
||||
struct ast_variable *i;
|
||||
|
||||
for (i = path_vars; i; i = i->next) {
|
||||
if (strcmp(i->name, "channelId") == 0) {
|
||||
args.channel_id = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
stasis_http_delete_channel(headers, &args, response);
|
||||
}
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /channels/{channelId}/dial.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_dial_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_dial_args args = {};
|
||||
struct ast_variable *i;
|
||||
|
||||
for (i = get_params; i; i = i->next) {
|
||||
if (strcmp(i->name, "endpoint") == 0) {
|
||||
args.endpoint = (i->value);
|
||||
} else
|
||||
if (strcmp(i->name, "extension") == 0) {
|
||||
args.extension = (i->value);
|
||||
} else
|
||||
if (strcmp(i->name, "context") == 0) {
|
||||
args.context = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
for (i = path_vars; i; i = i->next) {
|
||||
if (strcmp(i->name, "channelId") == 0) {
|
||||
args.channel_id = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
stasis_http_dial(headers, &args, response);
|
||||
}
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /channels/{channelId}/continue.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_continue_in_dialplan_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_continue_in_dialplan_args args = {};
|
||||
struct ast_variable *i;
|
||||
|
||||
for (i = path_vars; i; i = i->next) {
|
||||
if (strcmp(i->name, "channelId") == 0) {
|
||||
args.channel_id = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
stasis_http_continue_in_dialplan(headers, &args, response);
|
||||
}
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /channels/{channelId}/answer.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_answer_channel_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_answer_channel_args args = {};
|
||||
struct ast_variable *i;
|
||||
|
||||
for (i = path_vars; i; i = i->next) {
|
||||
if (strcmp(i->name, "channelId") == 0) {
|
||||
args.channel_id = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
stasis_http_answer_channel(headers, &args, response);
|
||||
}
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /channels/{channelId}/mute.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_mute_channel_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_mute_channel_args args = {};
|
||||
struct ast_variable *i;
|
||||
|
||||
for (i = get_params; i; i = i->next) {
|
||||
if (strcmp(i->name, "direction") == 0) {
|
||||
args.direction = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
for (i = path_vars; i; i = i->next) {
|
||||
if (strcmp(i->name, "channelId") == 0) {
|
||||
args.channel_id = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
stasis_http_mute_channel(headers, &args, response);
|
||||
}
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /channels/{channelId}/unmute.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_unmute_channel_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_unmute_channel_args args = {};
|
||||
struct ast_variable *i;
|
||||
|
||||
for (i = get_params; i; i = i->next) {
|
||||
if (strcmp(i->name, "direction") == 0) {
|
||||
args.direction = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
for (i = path_vars; i; i = i->next) {
|
||||
if (strcmp(i->name, "channelId") == 0) {
|
||||
args.channel_id = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
stasis_http_unmute_channel(headers, &args, response);
|
||||
}
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /channels/{channelId}/hold.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_hold_channel_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_hold_channel_args args = {};
|
||||
struct ast_variable *i;
|
||||
|
||||
for (i = path_vars; i; i = i->next) {
|
||||
if (strcmp(i->name, "channelId") == 0) {
|
||||
args.channel_id = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
stasis_http_hold_channel(headers, &args, response);
|
||||
}
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /channels/{channelId}/unhold.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_unhold_channel_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_unhold_channel_args args = {};
|
||||
struct ast_variable *i;
|
||||
|
||||
for (i = path_vars; i; i = i->next) {
|
||||
if (strcmp(i->name, "channelId") == 0) {
|
||||
args.channel_id = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
stasis_http_unhold_channel(headers, &args, response);
|
||||
}
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /channels/{channelId}/play.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_play_on_channel_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_play_on_channel_args args = {};
|
||||
struct ast_variable *i;
|
||||
|
||||
for (i = get_params; i; i = i->next) {
|
||||
if (strcmp(i->name, "media") == 0) {
|
||||
args.media = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
for (i = path_vars; i; i = i->next) {
|
||||
if (strcmp(i->name, "channelId") == 0) {
|
||||
args.channel_id = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
stasis_http_play_on_channel(headers, &args, response);
|
||||
}
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /channels/{channelId}/record.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_record_channel_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_record_channel_args args = {};
|
||||
struct ast_variable *i;
|
||||
|
||||
for (i = get_params; i; i = i->next) {
|
||||
if (strcmp(i->name, "name") == 0) {
|
||||
args.name = (i->value);
|
||||
} else
|
||||
if (strcmp(i->name, "format") == 0) {
|
||||
args.format = (i->value);
|
||||
} else
|
||||
if (strcmp(i->name, "maxDurationSeconds") == 0) {
|
||||
args.max_duration_seconds = atoi(i->value);
|
||||
} else
|
||||
if (strcmp(i->name, "maxSilenceSeconds") == 0) {
|
||||
args.max_silence_seconds = atoi(i->value);
|
||||
} else
|
||||
if (strcmp(i->name, "append") == 0) {
|
||||
args.append = atoi(i->value);
|
||||
} else
|
||||
if (strcmp(i->name, "beep") == 0) {
|
||||
args.beep = atoi(i->value);
|
||||
} else
|
||||
if (strcmp(i->name, "terminateOn") == 0) {
|
||||
args.terminate_on = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
for (i = path_vars; i; i = i->next) {
|
||||
if (strcmp(i->name, "channelId") == 0) {
|
||||
args.channel_id = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
stasis_http_record_channel(headers, &args, response);
|
||||
}
|
||||
|
||||
/*! \brief REST handler for /api-docs/channels.{format} */
|
||||
static struct stasis_rest_handlers channels_channelId_dial = {
|
||||
.path_segment = "dial",
|
||||
.callbacks = {
|
||||
[AST_HTTP_POST] = stasis_http_dial_cb,
|
||||
},
|
||||
.num_children = 0,
|
||||
.children = { }
|
||||
};
|
||||
/*! \brief REST handler for /api-docs/channels.{format} */
|
||||
static struct stasis_rest_handlers channels_channelId_continue = {
|
||||
.path_segment = "continue",
|
||||
.callbacks = {
|
||||
[AST_HTTP_POST] = stasis_http_continue_in_dialplan_cb,
|
||||
},
|
||||
.num_children = 0,
|
||||
.children = { }
|
||||
};
|
||||
/*! \brief REST handler for /api-docs/channels.{format} */
|
||||
static struct stasis_rest_handlers channels_channelId_answer = {
|
||||
.path_segment = "answer",
|
||||
.callbacks = {
|
||||
[AST_HTTP_POST] = stasis_http_answer_channel_cb,
|
||||
},
|
||||
.num_children = 0,
|
||||
.children = { }
|
||||
};
|
||||
/*! \brief REST handler for /api-docs/channels.{format} */
|
||||
static struct stasis_rest_handlers channels_channelId_mute = {
|
||||
.path_segment = "mute",
|
||||
.callbacks = {
|
||||
[AST_HTTP_POST] = stasis_http_mute_channel_cb,
|
||||
},
|
||||
.num_children = 0,
|
||||
.children = { }
|
||||
};
|
||||
/*! \brief REST handler for /api-docs/channels.{format} */
|
||||
static struct stasis_rest_handlers channels_channelId_unmute = {
|
||||
.path_segment = "unmute",
|
||||
.callbacks = {
|
||||
[AST_HTTP_POST] = stasis_http_unmute_channel_cb,
|
||||
},
|
||||
.num_children = 0,
|
||||
.children = { }
|
||||
};
|
||||
/*! \brief REST handler for /api-docs/channels.{format} */
|
||||
static struct stasis_rest_handlers channels_channelId_hold = {
|
||||
.path_segment = "hold",
|
||||
.callbacks = {
|
||||
[AST_HTTP_POST] = stasis_http_hold_channel_cb,
|
||||
},
|
||||
.num_children = 0,
|
||||
.children = { }
|
||||
};
|
||||
/*! \brief REST handler for /api-docs/channels.{format} */
|
||||
static struct stasis_rest_handlers channels_channelId_unhold = {
|
||||
.path_segment = "unhold",
|
||||
.callbacks = {
|
||||
[AST_HTTP_POST] = stasis_http_unhold_channel_cb,
|
||||
},
|
||||
.num_children = 0,
|
||||
.children = { }
|
||||
};
|
||||
/*! \brief REST handler for /api-docs/channels.{format} */
|
||||
static struct stasis_rest_handlers channels_channelId_play = {
|
||||
.path_segment = "play",
|
||||
.callbacks = {
|
||||
[AST_HTTP_POST] = stasis_http_play_on_channel_cb,
|
||||
},
|
||||
.num_children = 0,
|
||||
.children = { }
|
||||
};
|
||||
/*! \brief REST handler for /api-docs/channels.{format} */
|
||||
static struct stasis_rest_handlers channels_channelId_record = {
|
||||
.path_segment = "record",
|
||||
.callbacks = {
|
||||
[AST_HTTP_POST] = stasis_http_record_channel_cb,
|
||||
},
|
||||
.num_children = 0,
|
||||
.children = { }
|
||||
};
|
||||
/*! \brief REST handler for /api-docs/channels.{format} */
|
||||
static struct stasis_rest_handlers channels_channelId = {
|
||||
.path_segment = "channelId",
|
||||
.is_wildcard = 1,
|
||||
.callbacks = {
|
||||
[AST_HTTP_GET] = stasis_http_get_channel_cb,
|
||||
[AST_HTTP_DELETE] = stasis_http_delete_channel_cb,
|
||||
},
|
||||
.num_children = 9,
|
||||
.children = { &channels_channelId_dial,&channels_channelId_continue,&channels_channelId_answer,&channels_channelId_mute,&channels_channelId_unmute,&channels_channelId_hold,&channels_channelId_unhold,&channels_channelId_play,&channels_channelId_record, }
|
||||
};
|
||||
/*! \brief REST handler for /api-docs/channels.{format} */
|
||||
static struct stasis_rest_handlers channels = {
|
||||
.path_segment = "channels",
|
||||
.callbacks = {
|
||||
[AST_HTTP_GET] = stasis_http_get_channels_cb,
|
||||
[AST_HTTP_POST] = stasis_http_originate_cb,
|
||||
},
|
||||
.num_children = 1,
|
||||
.children = { &channels_channelId, }
|
||||
};
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
return stasis_http_add_handler(&channels);
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
stasis_http_remove_handler(&channels);
|
||||
return 0;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT,
|
||||
"RESTful API module - Channel resources",
|
||||
.load = load_module,
|
||||
.unload = unload_module,
|
||||
.nonoptreq = "res_stasis_http",
|
||||
);
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2012 - 2013, Digium, Inc.
|
||||
*
|
||||
* David M. Lee, II <dlee@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* !!!!! DO NOT EDIT !!!!!
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* This file is generated by a mustache template. Please see the original
|
||||
* template in rest-api-templates/res_stasis_http_resource.c.mustache
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Endpoint resources
|
||||
*
|
||||
* \author David M. Lee, II <dlee@digium.com>
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<depend type="module">res_stasis_http</depend>
|
||||
<support_level>core</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include "asterisk/module.h"
|
||||
#include "stasis_http/resource_endpoints.h"
|
||||
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /endpoints.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_get_endpoints_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_get_endpoints_args args = {};
|
||||
struct ast_variable *i;
|
||||
|
||||
for (i = get_params; i; i = i->next) {
|
||||
if (strcmp(i->name, "withType") == 0) {
|
||||
args.with_type = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
stasis_http_get_endpoints(headers, &args, response);
|
||||
}
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /endpoints/{endpointId}.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_get_endpoint_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_get_endpoint_args args = {};
|
||||
struct ast_variable *i;
|
||||
|
||||
for (i = path_vars; i; i = i->next) {
|
||||
if (strcmp(i->name, "endpointId") == 0) {
|
||||
args.endpoint_id = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
stasis_http_get_endpoint(headers, &args, response);
|
||||
}
|
||||
|
||||
/*! \brief REST handler for /api-docs/endpoints.{format} */
|
||||
static struct stasis_rest_handlers endpoints_endpointId = {
|
||||
.path_segment = "endpointId",
|
||||
.is_wildcard = 1,
|
||||
.callbacks = {
|
||||
[AST_HTTP_GET] = stasis_http_get_endpoint_cb,
|
||||
},
|
||||
.num_children = 0,
|
||||
.children = { }
|
||||
};
|
||||
/*! \brief REST handler for /api-docs/endpoints.{format} */
|
||||
static struct stasis_rest_handlers endpoints = {
|
||||
.path_segment = "endpoints",
|
||||
.callbacks = {
|
||||
[AST_HTTP_GET] = stasis_http_get_endpoints_cb,
|
||||
},
|
||||
.num_children = 1,
|
||||
.children = { &endpoints_endpointId, }
|
||||
};
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
return stasis_http_add_handler(&endpoints);
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
stasis_http_remove_handler(&endpoints);
|
||||
return 0;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT,
|
||||
"RESTful API module - Endpoint resources",
|
||||
.load = load_module,
|
||||
.unload = unload_module,
|
||||
.nonoptreq = "res_stasis_http",
|
||||
);
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2012 - 2013, Digium, Inc.
|
||||
*
|
||||
* David M. Lee, II <dlee@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* !!!!! DO NOT EDIT !!!!!
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* This file is generated by a mustache template. Please see the original
|
||||
* template in rest-api-templates/res_stasis_http_resource.c.mustache
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief WebSocket resource
|
||||
*
|
||||
* \author David M. Lee, II <dlee@digium.com>
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<depend type="module">res_stasis_http</depend>
|
||||
<support_level>core</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include "asterisk/module.h"
|
||||
#include "stasis_http/resource_events.h"
|
||||
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /events.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_event_websocket_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_event_websocket_args args = {};
|
||||
struct ast_variable *i;
|
||||
|
||||
for (i = get_params; i; i = i->next) {
|
||||
if (strcmp(i->name, "app") == 0) {
|
||||
args.app = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
stasis_http_event_websocket(headers, &args, response);
|
||||
}
|
||||
|
||||
/*! \brief REST handler for /api-docs/events.{format} */
|
||||
static struct stasis_rest_handlers events = {
|
||||
.path_segment = "events",
|
||||
.callbacks = {
|
||||
[AST_HTTP_GET] = stasis_http_event_websocket_cb,
|
||||
},
|
||||
.num_children = 0,
|
||||
.children = { }
|
||||
};
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
return stasis_http_add_handler(&events);
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
stasis_http_remove_handler(&events);
|
||||
return 0;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT,
|
||||
"RESTful API module - WebSocket resource",
|
||||
.load = load_module,
|
||||
.unload = unload_module,
|
||||
.nonoptreq = "res_stasis_http",
|
||||
);
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2012 - 2013, Digium, Inc.
|
||||
*
|
||||
* David M. Lee, II <dlee@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* !!!!! DO NOT EDIT !!!!!
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* This file is generated by a mustache template. Please see the original
|
||||
* template in rest-api-templates/res_stasis_http_resource.c.mustache
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Playback control resources
|
||||
*
|
||||
* \author David M. Lee, II <dlee@digium.com>
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<depend type="module">res_stasis_http</depend>
|
||||
<support_level>core</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include "asterisk/module.h"
|
||||
#include "stasis_http/resource_playback.h"
|
||||
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /playback/{playbackId}.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_get_playback_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_get_playback_args args = {};
|
||||
struct ast_variable *i;
|
||||
|
||||
for (i = path_vars; i; i = i->next) {
|
||||
if (strcmp(i->name, "playbackId") == 0) {
|
||||
args.playback_id = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
stasis_http_get_playback(headers, &args, response);
|
||||
}
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /playback/{playbackId}.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_stop_playback_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_stop_playback_args args = {};
|
||||
struct ast_variable *i;
|
||||
|
||||
for (i = path_vars; i; i = i->next) {
|
||||
if (strcmp(i->name, "playbackId") == 0) {
|
||||
args.playback_id = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
stasis_http_stop_playback(headers, &args, response);
|
||||
}
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /playback/{playbackId}/control.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_control_playback_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_control_playback_args args = {};
|
||||
struct ast_variable *i;
|
||||
|
||||
for (i = get_params; i; i = i->next) {
|
||||
if (strcmp(i->name, "operation") == 0) {
|
||||
args.operation = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
for (i = path_vars; i; i = i->next) {
|
||||
if (strcmp(i->name, "playbackId") == 0) {
|
||||
args.playback_id = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
stasis_http_control_playback(headers, &args, response);
|
||||
}
|
||||
|
||||
/*! \brief REST handler for /api-docs/playback.{format} */
|
||||
static struct stasis_rest_handlers playback_playbackId_control = {
|
||||
.path_segment = "control",
|
||||
.callbacks = {
|
||||
[AST_HTTP_POST] = stasis_http_control_playback_cb,
|
||||
},
|
||||
.num_children = 0,
|
||||
.children = { }
|
||||
};
|
||||
/*! \brief REST handler for /api-docs/playback.{format} */
|
||||
static struct stasis_rest_handlers playback_playbackId = {
|
||||
.path_segment = "playbackId",
|
||||
.is_wildcard = 1,
|
||||
.callbacks = {
|
||||
[AST_HTTP_GET] = stasis_http_get_playback_cb,
|
||||
[AST_HTTP_DELETE] = stasis_http_stop_playback_cb,
|
||||
},
|
||||
.num_children = 1,
|
||||
.children = { &playback_playbackId_control, }
|
||||
};
|
||||
/*! \brief REST handler for /api-docs/playback.{format} */
|
||||
static struct stasis_rest_handlers playback = {
|
||||
.path_segment = "playback",
|
||||
.callbacks = {
|
||||
},
|
||||
.num_children = 1,
|
||||
.children = { &playback_playbackId, }
|
||||
};
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
return stasis_http_add_handler(&playback);
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
stasis_http_remove_handler(&playback);
|
||||
return 0;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT,
|
||||
"RESTful API module - Playback control resources",
|
||||
.load = load_module,
|
||||
.unload = unload_module,
|
||||
.nonoptreq = "res_stasis_http",
|
||||
);
|
|
@ -0,0 +1,398 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2012 - 2013, Digium, Inc.
|
||||
*
|
||||
* David M. Lee, II <dlee@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* !!!!! DO NOT EDIT !!!!!
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* This file is generated by a mustache template. Please see the original
|
||||
* template in rest-api-templates/res_stasis_http_resource.c.mustache
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Recording resources
|
||||
*
|
||||
* \author David M. Lee, II <dlee@digium.com>
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<depend type="module">res_stasis_http</depend>
|
||||
<support_level>core</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include "asterisk/module.h"
|
||||
#include "stasis_http/resource_recordings.h"
|
||||
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /recordings.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_get_recordings_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_get_recordings_args args = {};
|
||||
stasis_http_get_recordings(headers, &args, response);
|
||||
}
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /recordings/stored.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_get_stored_recordings_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_get_stored_recordings_args args = {};
|
||||
stasis_http_get_stored_recordings(headers, &args, response);
|
||||
}
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /recordings/stored/{recordingId}.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_get_stored_recording_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_get_stored_recording_args args = {};
|
||||
struct ast_variable *i;
|
||||
|
||||
for (i = path_vars; i; i = i->next) {
|
||||
if (strcmp(i->name, "recordingId") == 0) {
|
||||
args.recording_id = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
stasis_http_get_stored_recording(headers, &args, response);
|
||||
}
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /recordings/stored/{recordingId}.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_delete_stored_recording_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_delete_stored_recording_args args = {};
|
||||
struct ast_variable *i;
|
||||
|
||||
for (i = path_vars; i; i = i->next) {
|
||||
if (strcmp(i->name, "recordingId") == 0) {
|
||||
args.recording_id = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
stasis_http_delete_stored_recording(headers, &args, response);
|
||||
}
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /recordings/live.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_get_live_recordings_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_get_live_recordings_args args = {};
|
||||
stasis_http_get_live_recordings(headers, &args, response);
|
||||
}
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /recordings/live/{recordingId}.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_get_live_recording_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_get_live_recording_args args = {};
|
||||
struct ast_variable *i;
|
||||
|
||||
for (i = path_vars; i; i = i->next) {
|
||||
if (strcmp(i->name, "recordingId") == 0) {
|
||||
args.recording_id = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
stasis_http_get_live_recording(headers, &args, response);
|
||||
}
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /recordings/live/{recordingId}.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_cancel_recording_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_cancel_recording_args args = {};
|
||||
struct ast_variable *i;
|
||||
|
||||
for (i = path_vars; i; i = i->next) {
|
||||
if (strcmp(i->name, "recordingId") == 0) {
|
||||
args.recording_id = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
stasis_http_cancel_recording(headers, &args, response);
|
||||
}
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /recordings/live/{recordingId}/stop.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_stop_recording_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_stop_recording_args args = {};
|
||||
struct ast_variable *i;
|
||||
|
||||
for (i = path_vars; i; i = i->next) {
|
||||
if (strcmp(i->name, "recordingId") == 0) {
|
||||
args.recording_id = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
stasis_http_stop_recording(headers, &args, response);
|
||||
}
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /recordings/live/{recordingId}/pause.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_pause_recording_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_pause_recording_args args = {};
|
||||
struct ast_variable *i;
|
||||
|
||||
for (i = path_vars; i; i = i->next) {
|
||||
if (strcmp(i->name, "recordingId") == 0) {
|
||||
args.recording_id = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
stasis_http_pause_recording(headers, &args, response);
|
||||
}
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /recordings/live/{recordingId}/unpause.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_unpause_recording_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_unpause_recording_args args = {};
|
||||
struct ast_variable *i;
|
||||
|
||||
for (i = path_vars; i; i = i->next) {
|
||||
if (strcmp(i->name, "recordingId") == 0) {
|
||||
args.recording_id = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
stasis_http_unpause_recording(headers, &args, response);
|
||||
}
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /recordings/live/{recordingId}/mute.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_mute_recording_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_mute_recording_args args = {};
|
||||
struct ast_variable *i;
|
||||
|
||||
for (i = path_vars; i; i = i->next) {
|
||||
if (strcmp(i->name, "recordingId") == 0) {
|
||||
args.recording_id = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
stasis_http_mute_recording(headers, &args, response);
|
||||
}
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /recordings/live/{recordingId}/unmute.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_unmute_recording_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_unmute_recording_args args = {};
|
||||
struct ast_variable *i;
|
||||
|
||||
for (i = path_vars; i; i = i->next) {
|
||||
if (strcmp(i->name, "recordingId") == 0) {
|
||||
args.recording_id = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
stasis_http_unmute_recording(headers, &args, response);
|
||||
}
|
||||
|
||||
/*! \brief REST handler for /api-docs/recordings.{format} */
|
||||
static struct stasis_rest_handlers recordings_stored_recordingId = {
|
||||
.path_segment = "recordingId",
|
||||
.is_wildcard = 1,
|
||||
.callbacks = {
|
||||
[AST_HTTP_GET] = stasis_http_get_stored_recording_cb,
|
||||
[AST_HTTP_DELETE] = stasis_http_delete_stored_recording_cb,
|
||||
},
|
||||
.num_children = 0,
|
||||
.children = { }
|
||||
};
|
||||
/*! \brief REST handler for /api-docs/recordings.{format} */
|
||||
static struct stasis_rest_handlers recordings_stored = {
|
||||
.path_segment = "stored",
|
||||
.callbacks = {
|
||||
[AST_HTTP_GET] = stasis_http_get_stored_recordings_cb,
|
||||
},
|
||||
.num_children = 1,
|
||||
.children = { &recordings_stored_recordingId, }
|
||||
};
|
||||
/*! \brief REST handler for /api-docs/recordings.{format} */
|
||||
static struct stasis_rest_handlers recordings_live_recordingId_stop = {
|
||||
.path_segment = "stop",
|
||||
.callbacks = {
|
||||
[AST_HTTP_POST] = stasis_http_stop_recording_cb,
|
||||
},
|
||||
.num_children = 0,
|
||||
.children = { }
|
||||
};
|
||||
/*! \brief REST handler for /api-docs/recordings.{format} */
|
||||
static struct stasis_rest_handlers recordings_live_recordingId_pause = {
|
||||
.path_segment = "pause",
|
||||
.callbacks = {
|
||||
[AST_HTTP_POST] = stasis_http_pause_recording_cb,
|
||||
},
|
||||
.num_children = 0,
|
||||
.children = { }
|
||||
};
|
||||
/*! \brief REST handler for /api-docs/recordings.{format} */
|
||||
static struct stasis_rest_handlers recordings_live_recordingId_unpause = {
|
||||
.path_segment = "unpause",
|
||||
.callbacks = {
|
||||
[AST_HTTP_POST] = stasis_http_unpause_recording_cb,
|
||||
},
|
||||
.num_children = 0,
|
||||
.children = { }
|
||||
};
|
||||
/*! \brief REST handler for /api-docs/recordings.{format} */
|
||||
static struct stasis_rest_handlers recordings_live_recordingId_mute = {
|
||||
.path_segment = "mute",
|
||||
.callbacks = {
|
||||
[AST_HTTP_POST] = stasis_http_mute_recording_cb,
|
||||
},
|
||||
.num_children = 0,
|
||||
.children = { }
|
||||
};
|
||||
/*! \brief REST handler for /api-docs/recordings.{format} */
|
||||
static struct stasis_rest_handlers recordings_live_recordingId_unmute = {
|
||||
.path_segment = "unmute",
|
||||
.callbacks = {
|
||||
[AST_HTTP_POST] = stasis_http_unmute_recording_cb,
|
||||
},
|
||||
.num_children = 0,
|
||||
.children = { }
|
||||
};
|
||||
/*! \brief REST handler for /api-docs/recordings.{format} */
|
||||
static struct stasis_rest_handlers recordings_live_recordingId = {
|
||||
.path_segment = "recordingId",
|
||||
.is_wildcard = 1,
|
||||
.callbacks = {
|
||||
[AST_HTTP_GET] = stasis_http_get_live_recording_cb,
|
||||
[AST_HTTP_DELETE] = stasis_http_cancel_recording_cb,
|
||||
},
|
||||
.num_children = 5,
|
||||
.children = { &recordings_live_recordingId_stop,&recordings_live_recordingId_pause,&recordings_live_recordingId_unpause,&recordings_live_recordingId_mute,&recordings_live_recordingId_unmute, }
|
||||
};
|
||||
/*! \brief REST handler for /api-docs/recordings.{format} */
|
||||
static struct stasis_rest_handlers recordings_live = {
|
||||
.path_segment = "live",
|
||||
.callbacks = {
|
||||
[AST_HTTP_GET] = stasis_http_get_live_recordings_cb,
|
||||
},
|
||||
.num_children = 1,
|
||||
.children = { &recordings_live_recordingId, }
|
||||
};
|
||||
/*! \brief REST handler for /api-docs/recordings.{format} */
|
||||
static struct stasis_rest_handlers recordings = {
|
||||
.path_segment = "recordings",
|
||||
.callbacks = {
|
||||
[AST_HTTP_GET] = stasis_http_get_recordings_cb,
|
||||
},
|
||||
.num_children = 2,
|
||||
.children = { &recordings_stored,&recordings_live, }
|
||||
};
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
return stasis_http_add_handler(&recordings);
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
stasis_http_remove_handler(&recordings);
|
||||
return 0;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT,
|
||||
"RESTful API module - Recording resources",
|
||||
.load = load_module,
|
||||
.unload = unload_module,
|
||||
.nonoptreq = "res_stasis_http",
|
||||
);
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2012 - 2013, Digium, Inc.
|
||||
*
|
||||
* David M. Lee, II <dlee@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* !!!!! DO NOT EDIT !!!!!
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* This file is generated by a mustache template. Please see the original
|
||||
* template in rest-api-templates/res_stasis_http_resource.c.mustache
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Sound resources
|
||||
*
|
||||
* \author David M. Lee, II <dlee@digium.com>
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<depend type="module">res_stasis_http</depend>
|
||||
<support_level>core</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include "asterisk/module.h"
|
||||
#include "stasis_http/resource_sounds.h"
|
||||
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /sounds.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_get_sounds_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_get_sounds_args args = {};
|
||||
struct ast_variable *i;
|
||||
|
||||
for (i = get_params; i; i = i->next) {
|
||||
if (strcmp(i->name, "lang") == 0) {
|
||||
args.lang = (i->value);
|
||||
} else
|
||||
if (strcmp(i->name, "format") == 0) {
|
||||
args.format = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
stasis_http_get_sounds(headers, &args, response);
|
||||
}
|
||||
/*!
|
||||
* \brief Parameter parsing callback for /sounds/{soundId}.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_get_stored_sound_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_get_stored_sound_args args = {};
|
||||
struct ast_variable *i;
|
||||
|
||||
for (i = path_vars; i; i = i->next) {
|
||||
if (strcmp(i->name, "soundId") == 0) {
|
||||
args.sound_id = (i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
stasis_http_get_stored_sound(headers, &args, response);
|
||||
}
|
||||
|
||||
/*! \brief REST handler for /api-docs/sounds.{format} */
|
||||
static struct stasis_rest_handlers sounds_soundId = {
|
||||
.path_segment = "soundId",
|
||||
.is_wildcard = 1,
|
||||
.callbacks = {
|
||||
[AST_HTTP_GET] = stasis_http_get_stored_sound_cb,
|
||||
},
|
||||
.num_children = 0,
|
||||
.children = { }
|
||||
};
|
||||
/*! \brief REST handler for /api-docs/sounds.{format} */
|
||||
static struct stasis_rest_handlers sounds = {
|
||||
.path_segment = "sounds",
|
||||
.callbacks = {
|
||||
[AST_HTTP_GET] = stasis_http_get_sounds_cb,
|
||||
},
|
||||
.num_children = 1,
|
||||
.children = { &sounds_soundId, }
|
||||
};
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
return stasis_http_add_handler(&sounds);
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
stasis_http_remove_handler(&sounds);
|
||||
return 0;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT,
|
||||
"RESTful API module - Sound resources",
|
||||
.load = load_module,
|
||||
.unload = unload_module,
|
||||
.nonoptreq = "res_stasis_http",
|
||||
);
|
|
@ -0,0 +1,51 @@
|
|||
#
|
||||
# Asterisk -- A telephony toolkit for Linux.
|
||||
#
|
||||
# Generated Makefile for res_stasis_http dependencies.
|
||||
#
|
||||
# Copyright (C) 2013, Digium, Inc.
|
||||
#
|
||||
# This program is free software, distributed under the terms of
|
||||
# the GNU General Public License
|
||||
#
|
||||
|
||||
#
|
||||
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
# !!!!! DO NOT EDIT !!!!!
|
||||
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
# This file is generated by a template. Please see the original template at
|
||||
# rest-api-templates/stasis_http.make.mustache
|
||||
#
|
||||
|
||||
res_stasis_http_asterisk.so: stasis_http/resource_asterisk.o
|
||||
|
||||
stasis_http/resource_asterisk.o: _ASTCFLAGS+=$(call MOD_ASTCFLAGS,res_stasis_http_asterisk)
|
||||
|
||||
res_stasis_http_endpoints.so: stasis_http/resource_endpoints.o
|
||||
|
||||
stasis_http/resource_endpoints.o: _ASTCFLAGS+=$(call MOD_ASTCFLAGS,res_stasis_http_endpoints)
|
||||
|
||||
res_stasis_http_channels.so: stasis_http/resource_channels.o
|
||||
|
||||
stasis_http/resource_channels.o: _ASTCFLAGS+=$(call MOD_ASTCFLAGS,res_stasis_http_channels)
|
||||
|
||||
res_stasis_http_bridges.so: stasis_http/resource_bridges.o
|
||||
|
||||
stasis_http/resource_bridges.o: _ASTCFLAGS+=$(call MOD_ASTCFLAGS,res_stasis_http_bridges)
|
||||
|
||||
res_stasis_http_recordings.so: stasis_http/resource_recordings.o
|
||||
|
||||
stasis_http/resource_recordings.o: _ASTCFLAGS+=$(call MOD_ASTCFLAGS,res_stasis_http_recordings)
|
||||
|
||||
res_stasis_http_sounds.so: stasis_http/resource_sounds.o
|
||||
|
||||
stasis_http/resource_sounds.o: _ASTCFLAGS+=$(call MOD_ASTCFLAGS,res_stasis_http_sounds)
|
||||
|
||||
res_stasis_http_playback.so: stasis_http/resource_playback.o
|
||||
|
||||
stasis_http/resource_playback.o: _ASTCFLAGS+=$(call MOD_ASTCFLAGS,res_stasis_http_playback)
|
||||
|
||||
res_stasis_http_events.so: stasis_http/resource_events.o
|
||||
|
||||
stasis_http/resource_events.o: _ASTCFLAGS+=$(call MOD_ASTCFLAGS,res_stasis_http_events)
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/* -*- C -*-
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2012 - 2013, Digium, Inc.
|
||||
*
|
||||
* David M. Lee, II <dlee@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Implementation for stasis-http stubs.
|
||||
*
|
||||
* \author David M. Lee, II <dlee@digium.com>
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<support_level>core</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include "resource_asterisk.h"
|
||||
|
||||
void stasis_http_get_asterisk_info(struct ast_variable *headers, struct ast_get_asterisk_info_args *args, struct stasis_http_response *response)
|
||||
{
|
||||
ast_log(LOG_ERROR, "TODO: stasis_http_get_asterisk_info\n");
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2012 - 2013, Digium, Inc.
|
||||
*
|
||||
* David M. Lee, II <dlee@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Generated file - declares stubs to be implemented in
|
||||
* res/stasis_http/resource_asterisk.c
|
||||
*
|
||||
* Asterisk resources
|
||||
*
|
||||
* \author David M. Lee, II <dlee@digium.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* !!!!! DO NOT EDIT !!!!!
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* This file is generated by a mustache template. Please see the original
|
||||
* template in rest-api-templates/stasis_http_resource.h.mustache
|
||||
*/
|
||||
|
||||
#ifndef _ASTERISK_RESOURCE_ASTERISK_H
|
||||
#define _ASTERISK_RESOURCE_ASTERISK_H
|
||||
|
||||
#include "asterisk/stasis_http.h"
|
||||
|
||||
/*! \brief Argument struct for stasis_http_get_asterisk_info() */
|
||||
struct ast_get_asterisk_info_args {
|
||||
/*! \brief Filter information returned */
|
||||
const char *only;
|
||||
};
|
||||
/*!
|
||||
* \brief Gets Asterisk system information.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_get_asterisk_info(struct ast_variable *headers, struct ast_get_asterisk_info_args *args, struct stasis_http_response *response);
|
||||
|
||||
#endif /* _ASTERISK_RESOURCE_ASTERISK_H */
|
|
@ -0,0 +1,63 @@
|
|||
/* -*- C -*-
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2012 - 2013, Digium, Inc.
|
||||
*
|
||||
* David M. Lee, II <dlee@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Implementation for stasis-http stubs.
|
||||
*
|
||||
* \author David M. Lee, II <dlee@digium.com>
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<support_level>core</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include "resource_bridges.h"
|
||||
|
||||
void stasis_http_add_channel_to_bridge(struct ast_variable *headers, struct ast_add_channel_to_bridge_args *args, struct stasis_http_response *response)
|
||||
{
|
||||
ast_log(LOG_ERROR, "TODO: stasis_http_add_channel_to_bridge\n");
|
||||
}
|
||||
void stasis_http_remove_channel_from_bridge(struct ast_variable *headers, struct ast_remove_channel_from_bridge_args *args, struct stasis_http_response *response)
|
||||
{
|
||||
ast_log(LOG_ERROR, "TODO: stasis_http_remove_channel_from_bridge\n");
|
||||
}
|
||||
void stasis_http_record_bridge(struct ast_variable *headers, struct ast_record_bridge_args *args, struct stasis_http_response *response)
|
||||
{
|
||||
ast_log(LOG_ERROR, "TODO: stasis_http_record_bridge\n");
|
||||
}
|
||||
void stasis_http_get_bridge(struct ast_variable *headers, struct ast_get_bridge_args *args, struct stasis_http_response *response)
|
||||
{
|
||||
ast_log(LOG_ERROR, "TODO: stasis_http_get_bridge\n");
|
||||
}
|
||||
void stasis_http_delete_bridge(struct ast_variable *headers, struct ast_delete_bridge_args *args, struct stasis_http_response *response)
|
||||
{
|
||||
ast_log(LOG_ERROR, "TODO: stasis_http_delete_bridge\n");
|
||||
}
|
||||
void stasis_http_get_bridges(struct ast_variable *headers, struct ast_get_bridges_args *args, struct stasis_http_response *response)
|
||||
{
|
||||
ast_log(LOG_ERROR, "TODO: stasis_http_get_bridges\n");
|
||||
}
|
||||
void stasis_http_new_bridge(struct ast_variable *headers, struct ast_new_bridge_args *args, struct stasis_http_response *response)
|
||||
{
|
||||
ast_log(LOG_ERROR, "TODO: stasis_http_new_bridge\n");
|
||||
}
|
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2012 - 2013, Digium, Inc.
|
||||
*
|
||||
* David M. Lee, II <dlee@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Generated file - declares stubs to be implemented in
|
||||
* res/stasis_http/resource_bridges.c
|
||||
*
|
||||
* Bridge resources
|
||||
*
|
||||
* \author David M. Lee, II <dlee@digium.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* !!!!! DO NOT EDIT !!!!!
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* This file is generated by a mustache template. Please see the original
|
||||
* template in rest-api-templates/stasis_http_resource.h.mustache
|
||||
*/
|
||||
|
||||
#ifndef _ASTERISK_RESOURCE_BRIDGES_H
|
||||
#define _ASTERISK_RESOURCE_BRIDGES_H
|
||||
|
||||
#include "asterisk/stasis_http.h"
|
||||
|
||||
/*! \brief Argument struct for stasis_http_get_bridges() */
|
||||
struct ast_get_bridges_args {
|
||||
};
|
||||
/*!
|
||||
* \brief List active bridges.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_get_bridges(struct ast_variable *headers, struct ast_get_bridges_args *args, struct stasis_http_response *response);
|
||||
/*! \brief Argument struct for stasis_http_new_bridge() */
|
||||
struct ast_new_bridge_args {
|
||||
/*! \brief Type of bridge to create. */
|
||||
const char *type;
|
||||
};
|
||||
/*!
|
||||
* \brief Create a new bridge.
|
||||
*
|
||||
* This bridge persists until it has been shut down, or Asterisk has been shut down.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_new_bridge(struct ast_variable *headers, struct ast_new_bridge_args *args, struct stasis_http_response *response);
|
||||
/*! \brief Argument struct for stasis_http_get_bridge() */
|
||||
struct ast_get_bridge_args {
|
||||
/*! \brief Bridge's id */
|
||||
const char *bridge_id;
|
||||
};
|
||||
/*!
|
||||
* \brief Get bridge details.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_get_bridge(struct ast_variable *headers, struct ast_get_bridge_args *args, struct stasis_http_response *response);
|
||||
/*! \brief Argument struct for stasis_http_delete_bridge() */
|
||||
struct ast_delete_bridge_args {
|
||||
/*! \brief Bridge's id */
|
||||
const char *bridge_id;
|
||||
};
|
||||
/*!
|
||||
* \brief Shut down a bridge bridge.
|
||||
*
|
||||
* If any channels are in this bridge, they will be removed and resume whatever they were doing beforehand.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_delete_bridge(struct ast_variable *headers, struct ast_delete_bridge_args *args, struct stasis_http_response *response);
|
||||
/*! \brief Argument struct for stasis_http_add_channel_to_bridge() */
|
||||
struct ast_add_channel_to_bridge_args {
|
||||
/*! \brief Bridge's id */
|
||||
const char *bridge_id;
|
||||
/*! \brief Channel's id */
|
||||
const char *channel;
|
||||
};
|
||||
/*!
|
||||
* \brief Add a channel to a bridge.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_add_channel_to_bridge(struct ast_variable *headers, struct ast_add_channel_to_bridge_args *args, struct stasis_http_response *response);
|
||||
/*! \brief Argument struct for stasis_http_remove_channel_from_bridge() */
|
||||
struct ast_remove_channel_from_bridge_args {
|
||||
/*! \brief Bridge's id */
|
||||
const char *bridge_id;
|
||||
/*! \brief Channel's id */
|
||||
const char *channel;
|
||||
};
|
||||
/*!
|
||||
* \brief Remove a channel from a bridge.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_remove_channel_from_bridge(struct ast_variable *headers, struct ast_remove_channel_from_bridge_args *args, struct stasis_http_response *response);
|
||||
/*! \brief Argument struct for stasis_http_record_bridge() */
|
||||
struct ast_record_bridge_args {
|
||||
/*! \brief Bridge's id */
|
||||
const char *bridge_id;
|
||||
/*! \brief Recording's filename */
|
||||
const char *name;
|
||||
/*! \brief Maximum duration of the recording, in seconds. 0 for no limit. */
|
||||
int max_duration_seconds;
|
||||
/*! \brief Maximum duration of silence, in seconds. 0 for no limit. */
|
||||
int max_silence_seconds;
|
||||
/*! \brief If true, and recording already exists, append to recording. */
|
||||
int append;
|
||||
/*! \brief Play beep when recording begins */
|
||||
int beep;
|
||||
/*! \brief DTMF input to terminate recording. */
|
||||
const char *terminate_on;
|
||||
};
|
||||
/*!
|
||||
* \brief Start a recording.
|
||||
*
|
||||
* This records the mixed audio from all channels participating in this bridge.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_record_bridge(struct ast_variable *headers, struct ast_record_bridge_args *args, struct stasis_http_response *response);
|
||||
|
||||
#endif /* _ASTERISK_RESOURCE_BRIDGES_H */
|
|
@ -0,0 +1,251 @@
|
|||
/* -*- C -*-
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2012 - 2013, Digium, Inc.
|
||||
*
|
||||
* David M. Lee, II <dlee@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Implementation for stasis-http stubs.
|
||||
*
|
||||
* \author David M. Lee, II <dlee@digium.com>
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<support_level>core</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include "asterisk/stasis_app.h"
|
||||
#include "asterisk/stasis_channels.h"
|
||||
#include "resource_channels.h"
|
||||
|
||||
/*!
|
||||
* \brief Finds the control object for a channel, filling the response with an
|
||||
* error, if appropriate.
|
||||
* \param[out] response Response to fill with an error if control is not found.
|
||||
* \param channel_id ID of the channel to lookup.
|
||||
* \return Channel control object.
|
||||
* \return \c NULL if control object does not exist.
|
||||
*/
|
||||
static struct stasis_app_control *find_control(
|
||||
struct stasis_http_response *response,
|
||||
const char *channel_id)
|
||||
{
|
||||
RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
|
||||
|
||||
ast_assert(response != NULL);
|
||||
|
||||
control = stasis_app_control_find_by_channel_id(channel_id);
|
||||
if (control == NULL) {
|
||||
/* Distinguish between 404 and 409 errors */
|
||||
RAII_VAR(struct ast_channel *, chan, NULL, ao2_cleanup);
|
||||
chan = ast_channel_get_by_name(channel_id);
|
||||
if (chan == NULL) {
|
||||
stasis_http_response_error(response, 404, "Not Found",
|
||||
"Channel not found");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
stasis_http_response_error(response, 409, "Conflict",
|
||||
"Channel not in Stasis application");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ao2_ref(control, +1);
|
||||
return control;
|
||||
}
|
||||
|
||||
void stasis_http_dial(struct ast_variable *headers, struct ast_dial_args *args, struct stasis_http_response *response)
|
||||
{
|
||||
ast_log(LOG_ERROR, "TODO: stasis_http_dial\n");
|
||||
}
|
||||
|
||||
void stasis_http_continue_in_dialplan(
|
||||
struct ast_variable *headers,
|
||||
struct ast_continue_in_dialplan_args *args,
|
||||
struct stasis_http_response *response)
|
||||
{
|
||||
RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
|
||||
|
||||
ast_assert(response != NULL);
|
||||
|
||||
control = find_control(response, args->channel_id);
|
||||
if (control == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
stasis_app_control_continue(control);
|
||||
stasis_http_response_no_content(response);
|
||||
}
|
||||
|
||||
void stasis_http_answer_channel(struct ast_variable *headers,
|
||||
struct ast_answer_channel_args *args,
|
||||
struct stasis_http_response *response)
|
||||
{
|
||||
RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
|
||||
|
||||
control = find_control(response, args->channel_id);
|
||||
if (control == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (stasis_app_control_answer(control) != 0) {
|
||||
stasis_http_response_error(
|
||||
response, 500, "Internal Server Error",
|
||||
"Failed to answer channel");
|
||||
return;
|
||||
}
|
||||
|
||||
stasis_http_response_no_content(response);
|
||||
}
|
||||
|
||||
void stasis_http_mute_channel(struct ast_variable *headers, struct ast_mute_channel_args *args, struct stasis_http_response *response)
|
||||
{
|
||||
ast_log(LOG_ERROR, "TODO: stasis_http_mute_channel\n");
|
||||
}
|
||||
void stasis_http_unmute_channel(struct ast_variable *headers, struct ast_unmute_channel_args *args, struct stasis_http_response *response)
|
||||
{
|
||||
ast_log(LOG_ERROR, "TODO: stasis_http_unmute_channel\n");
|
||||
}
|
||||
void stasis_http_hold_channel(struct ast_variable *headers, struct ast_hold_channel_args *args, struct stasis_http_response *response)
|
||||
{
|
||||
ast_log(LOG_ERROR, "TODO: stasis_http_hold_channel\n");
|
||||
}
|
||||
void stasis_http_unhold_channel(struct ast_variable *headers, struct ast_unhold_channel_args *args, struct stasis_http_response *response)
|
||||
{
|
||||
ast_log(LOG_ERROR, "TODO: stasis_http_unhold_channel\n");
|
||||
}
|
||||
void stasis_http_play_on_channel(struct ast_variable *headers, struct ast_play_on_channel_args *args, struct stasis_http_response *response)
|
||||
{
|
||||
ast_log(LOG_ERROR, "TODO: stasis_http_play_on_channel\n");
|
||||
}
|
||||
void stasis_http_record_channel(struct ast_variable *headers, struct ast_record_channel_args *args, struct stasis_http_response *response)
|
||||
{
|
||||
ast_log(LOG_ERROR, "TODO: stasis_http_record_channel\n");
|
||||
}
|
||||
void stasis_http_get_channel(struct ast_variable *headers,
|
||||
struct ast_get_channel_args *args,
|
||||
struct stasis_http_response *response)
|
||||
{
|
||||
RAII_VAR(struct stasis_caching_topic *, caching_topic, NULL, ao2_cleanup);
|
||||
RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
|
||||
struct ast_channel_snapshot *snapshot;
|
||||
|
||||
caching_topic = ast_channel_topic_all_cached();
|
||||
if (!caching_topic) {
|
||||
stasis_http_response_error(
|
||||
response, 500, "Internal Server Error",
|
||||
"Message bus not initialized");
|
||||
return;
|
||||
}
|
||||
ao2_ref(caching_topic, +1);
|
||||
|
||||
msg = stasis_cache_get(caching_topic, ast_channel_snapshot_type(),
|
||||
args->channel_id);
|
||||
if (!msg) {
|
||||
stasis_http_response_error(
|
||||
response, 404, "Not Found",
|
||||
"Channel not found");
|
||||
return;
|
||||
}
|
||||
|
||||
snapshot = stasis_message_data(msg);
|
||||
ast_assert(snapshot != NULL);
|
||||
|
||||
stasis_http_response_ok(response,
|
||||
ast_channel_snapshot_to_json(snapshot));
|
||||
}
|
||||
|
||||
void stasis_http_delete_channel(struct ast_variable *headers,
|
||||
struct ast_delete_channel_args *args,
|
||||
struct stasis_http_response *response)
|
||||
{
|
||||
RAII_VAR(struct ast_channel *, chan, NULL, ao2_cleanup);
|
||||
|
||||
chan = ast_channel_get_by_name(args->channel_id);
|
||||
if (chan == NULL) {
|
||||
stasis_http_response_error(
|
||||
response, 404, "Not Found",
|
||||
"Channel not found");
|
||||
return;
|
||||
}
|
||||
|
||||
ast_softhangup(chan, AST_SOFTHANGUP_EXPLICIT);
|
||||
|
||||
stasis_http_response_no_content(response);
|
||||
}
|
||||
|
||||
void stasis_http_get_channels(struct ast_variable *headers,
|
||||
struct ast_get_channels_args *args,
|
||||
struct stasis_http_response *response)
|
||||
{
|
||||
RAII_VAR(struct stasis_caching_topic *, caching_topic, NULL, ao2_cleanup);
|
||||
RAII_VAR(struct ao2_container *, snapshots, NULL, ao2_cleanup);
|
||||
RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
|
||||
struct ao2_iterator i;
|
||||
void *obj;
|
||||
|
||||
caching_topic = ast_channel_topic_all_cached();
|
||||
if (!caching_topic) {
|
||||
stasis_http_response_error(
|
||||
response, 500, "Internal Server Error",
|
||||
"Message bus not initialized");
|
||||
return;
|
||||
}
|
||||
ao2_ref(caching_topic, +1);
|
||||
|
||||
snapshots = stasis_cache_dump(caching_topic, ast_channel_snapshot_type());
|
||||
if (!snapshots) {
|
||||
stasis_http_response_alloc_failed(response);
|
||||
return;
|
||||
}
|
||||
|
||||
json = ast_json_array_create();
|
||||
if (!json) {
|
||||
stasis_http_response_alloc_failed(response);
|
||||
return;
|
||||
}
|
||||
|
||||
i = ao2_iterator_init(snapshots, 0);
|
||||
while ((obj = ao2_iterator_next(&i))) {
|
||||
RAII_VAR(struct stasis_message *, msg, obj, ao2_cleanup);
|
||||
struct ast_channel_snapshot *snapshot = stasis_message_data(msg);
|
||||
int r = ast_json_array_append(
|
||||
json, ast_channel_snapshot_to_json(snapshot));
|
||||
if (r != 0) {
|
||||
stasis_http_response_alloc_failed(response);
|
||||
return;
|
||||
}
|
||||
}
|
||||
ao2_iterator_destroy(&i);
|
||||
|
||||
stasis_http_response_ok(response, ast_json_ref(json));
|
||||
}
|
||||
|
||||
void stasis_http_originate(struct ast_variable *headers,
|
||||
struct ast_originate_args *args,
|
||||
struct stasis_http_response *response)
|
||||
{
|
||||
if (args->endpoint) {
|
||||
ast_log(LOG_DEBUG, "Dialing specific endpoint %s\n", args->endpoint);
|
||||
}
|
||||
|
||||
ast_log(LOG_DEBUG, "Dialing %s@%s\n", args->extension, args->context);
|
||||
/* ast_pbx_outgoing_app - originates a channel, putting it into an application */
|
||||
}
|
|
@ -0,0 +1,244 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2012 - 2013, Digium, Inc.
|
||||
*
|
||||
* David M. Lee, II <dlee@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Generated file - declares stubs to be implemented in
|
||||
* res/stasis_http/resource_channels.c
|
||||
*
|
||||
* Channel resources
|
||||
*
|
||||
* \author David M. Lee, II <dlee@digium.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* !!!!! DO NOT EDIT !!!!!
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* This file is generated by a mustache template. Please see the original
|
||||
* template in rest-api-templates/stasis_http_resource.h.mustache
|
||||
*/
|
||||
|
||||
#ifndef _ASTERISK_RESOURCE_CHANNELS_H
|
||||
#define _ASTERISK_RESOURCE_CHANNELS_H
|
||||
|
||||
#include "asterisk/stasis_http.h"
|
||||
|
||||
/*! \brief Argument struct for stasis_http_get_channels() */
|
||||
struct ast_get_channels_args {
|
||||
};
|
||||
/*!
|
||||
* \brief List active channels.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_get_channels(struct ast_variable *headers, struct ast_get_channels_args *args, struct stasis_http_response *response);
|
||||
/*! \brief Argument struct for stasis_http_originate() */
|
||||
struct ast_originate_args {
|
||||
/*! \brief Endpoint to call. If not specified, originate is routed via dialplan */
|
||||
const char *endpoint;
|
||||
/*! \brief Extension to dial */
|
||||
const char *extension;
|
||||
/*! \brief When routing via dialplan, the context use. If omitted, uses 'default' */
|
||||
const char *context;
|
||||
};
|
||||
/*!
|
||||
* \brief Create a new channel (originate).
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_originate(struct ast_variable *headers, struct ast_originate_args *args, struct stasis_http_response *response);
|
||||
/*! \brief Argument struct for stasis_http_get_channel() */
|
||||
struct ast_get_channel_args {
|
||||
/*! \brief Channel's id */
|
||||
const char *channel_id;
|
||||
};
|
||||
/*!
|
||||
* \brief Channel details.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_get_channel(struct ast_variable *headers, struct ast_get_channel_args *args, struct stasis_http_response *response);
|
||||
/*! \brief Argument struct for stasis_http_delete_channel() */
|
||||
struct ast_delete_channel_args {
|
||||
/*! \brief Channel's id */
|
||||
const char *channel_id;
|
||||
};
|
||||
/*!
|
||||
* \brief Delete (i.e. hangup) a channel.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_delete_channel(struct ast_variable *headers, struct ast_delete_channel_args *args, struct stasis_http_response *response);
|
||||
/*! \brief Argument struct for stasis_http_dial() */
|
||||
struct ast_dial_args {
|
||||
/*! \brief Channel's id */
|
||||
const char *channel_id;
|
||||
/*! \brief Endpoint to call. If not specified, dial is routed via dialplan */
|
||||
const char *endpoint;
|
||||
/*! \brief Extension to dial */
|
||||
const char *extension;
|
||||
/*! \brief When routing via dialplan, the context use. If omitted, uses 'default' */
|
||||
const char *context;
|
||||
};
|
||||
/*!
|
||||
* \brief Create a new channel (originate) and bridge to this channel.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_dial(struct ast_variable *headers, struct ast_dial_args *args, struct stasis_http_response *response);
|
||||
/*! \brief Argument struct for stasis_http_continue_in_dialplan() */
|
||||
struct ast_continue_in_dialplan_args {
|
||||
/*! \brief Channel's id */
|
||||
const char *channel_id;
|
||||
};
|
||||
/*!
|
||||
* \brief Exit application; continue execution in the dialplan.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_continue_in_dialplan(struct ast_variable *headers, struct ast_continue_in_dialplan_args *args, struct stasis_http_response *response);
|
||||
/*! \brief Argument struct for stasis_http_answer_channel() */
|
||||
struct ast_answer_channel_args {
|
||||
/*! \brief Channel's id */
|
||||
const char *channel_id;
|
||||
};
|
||||
/*!
|
||||
* \brief Answer a channel.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_answer_channel(struct ast_variable *headers, struct ast_answer_channel_args *args, struct stasis_http_response *response);
|
||||
/*! \brief Argument struct for stasis_http_mute_channel() */
|
||||
struct ast_mute_channel_args {
|
||||
/*! \brief Channel's id */
|
||||
const char *channel_id;
|
||||
/*! \brief Direction in which to mute audio */
|
||||
const char *direction;
|
||||
};
|
||||
/*!
|
||||
* \brief Mute a channel.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_mute_channel(struct ast_variable *headers, struct ast_mute_channel_args *args, struct stasis_http_response *response);
|
||||
/*! \brief Argument struct for stasis_http_unmute_channel() */
|
||||
struct ast_unmute_channel_args {
|
||||
/*! \brief Channel's id */
|
||||
const char *channel_id;
|
||||
/*! \brief Direction in which to unmute audio */
|
||||
const char *direction;
|
||||
};
|
||||
/*!
|
||||
* \brief Unmute a channel.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_unmute_channel(struct ast_variable *headers, struct ast_unmute_channel_args *args, struct stasis_http_response *response);
|
||||
/*! \brief Argument struct for stasis_http_hold_channel() */
|
||||
struct ast_hold_channel_args {
|
||||
/*! \brief Channel's id */
|
||||
const char *channel_id;
|
||||
};
|
||||
/*!
|
||||
* \brief Hold a channel.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_hold_channel(struct ast_variable *headers, struct ast_hold_channel_args *args, struct stasis_http_response *response);
|
||||
/*! \brief Argument struct for stasis_http_unhold_channel() */
|
||||
struct ast_unhold_channel_args {
|
||||
/*! \brief Channel's id */
|
||||
const char *channel_id;
|
||||
};
|
||||
/*!
|
||||
* \brief Remove a channel from hold.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_unhold_channel(struct ast_variable *headers, struct ast_unhold_channel_args *args, struct stasis_http_response *response);
|
||||
/*! \brief Argument struct for stasis_http_play_on_channel() */
|
||||
struct ast_play_on_channel_args {
|
||||
/*! \brief Channel's id */
|
||||
const char *channel_id;
|
||||
/*! \brief Media's URI to play. */
|
||||
const char *media;
|
||||
};
|
||||
/*!
|
||||
* \brief Start playback of media.
|
||||
*
|
||||
* The media URI may be any of a number of URI's. You may use http: and https: URI's, as well as sound: and recording: URI's. This operation creates a playback resource that can be used to control the playback of media (pause, rewind, fast forward, etc.)
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_play_on_channel(struct ast_variable *headers, struct ast_play_on_channel_args *args, struct stasis_http_response *response);
|
||||
/*! \brief Argument struct for stasis_http_record_channel() */
|
||||
struct ast_record_channel_args {
|
||||
/*! \brief Channel's id */
|
||||
const char *channel_id;
|
||||
/*! \brief Recording's filename */
|
||||
const char *name;
|
||||
/*! \brief Format to encode audio in */
|
||||
const char *format;
|
||||
/*! \brief Maximum duration of the recording, in seconds. 0 for no limit */
|
||||
int max_duration_seconds;
|
||||
/*! \brief Maximum duration of silence, in seconds. 0 for no limit */
|
||||
int max_silence_seconds;
|
||||
/*! \brief If true, and recording already exists, append to recording */
|
||||
int append;
|
||||
/*! \brief Play beep when recording begins */
|
||||
int beep;
|
||||
/*! \brief DTMF input to terminate recording */
|
||||
const char *terminate_on;
|
||||
};
|
||||
/*!
|
||||
* \brief Start a recording.
|
||||
*
|
||||
* Record audio from a channel. Note that this will not capture audio sent to the channel. The bridge itself has a record feature if that's what you want.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_record_channel(struct ast_variable *headers, struct ast_record_channel_args *args, struct stasis_http_response *response);
|
||||
|
||||
#endif /* _ASTERISK_RESOURCE_CHANNELS_H */
|
|
@ -0,0 +1,43 @@
|
|||
/* -*- C -*-
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2012 - 2013, Digium, Inc.
|
||||
*
|
||||
* David M. Lee, II <dlee@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Implementation for stasis-http stubs.
|
||||
*
|
||||
* \author David M. Lee, II <dlee@digium.com>
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<support_level>core</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include "resource_endpoints.h"
|
||||
|
||||
void stasis_http_get_endpoint(struct ast_variable *headers, struct ast_get_endpoint_args *args, struct stasis_http_response *response)
|
||||
{
|
||||
ast_log(LOG_ERROR, "TODO: stasis_http_get_endpoint\n");
|
||||
}
|
||||
void stasis_http_get_endpoints(struct ast_variable *headers, struct ast_get_endpoints_args *args, struct stasis_http_response *response)
|
||||
{
|
||||
ast_log(LOG_ERROR, "TODO: stasis_http_get_endpoints\n");
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2012 - 2013, Digium, Inc.
|
||||
*
|
||||
* David M. Lee, II <dlee@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Generated file - declares stubs to be implemented in
|
||||
* res/stasis_http/resource_endpoints.c
|
||||
*
|
||||
* Endpoint resources
|
||||
*
|
||||
* \author David M. Lee, II <dlee@digium.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* !!!!! DO NOT EDIT !!!!!
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* This file is generated by a mustache template. Please see the original
|
||||
* template in rest-api-templates/stasis_http_resource.h.mustache
|
||||
*/
|
||||
|
||||
#ifndef _ASTERISK_RESOURCE_ENDPOINTS_H
|
||||
#define _ASTERISK_RESOURCE_ENDPOINTS_H
|
||||
|
||||
#include "asterisk/stasis_http.h"
|
||||
|
||||
/*! \brief Argument struct for stasis_http_get_endpoints() */
|
||||
struct ast_get_endpoints_args {
|
||||
/*! \brief Filter endpoints by type (sip,iax2,dhadi,...) */
|
||||
const char *with_type;
|
||||
};
|
||||
/*!
|
||||
* \brief List available endoints.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_get_endpoints(struct ast_variable *headers, struct ast_get_endpoints_args *args, struct stasis_http_response *response);
|
||||
/*! \brief Argument struct for stasis_http_get_endpoint() */
|
||||
struct ast_get_endpoint_args {
|
||||
/*! \brief ID of the endpoint */
|
||||
const char *endpoint_id;
|
||||
};
|
||||
/*!
|
||||
* \brief Details for an endpoint.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_get_endpoint(struct ast_variable *headers, struct ast_get_endpoint_args *args, struct stasis_http_response *response);
|
||||
|
||||
#endif /* _ASTERISK_RESOURCE_ENDPOINTS_H */
|
|
@ -0,0 +1,40 @@
|
|||
/* -*- C -*-
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2012 - 2013, Digium, Inc.
|
||||
*
|
||||
* David M. Lee, II <dlee@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Implementation for stasis-http stubs.
|
||||
*
|
||||
* \author David M. Lee, II <dlee@digium.com>
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<support_level>core</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include "resource_events.h"
|
||||
|
||||
void stasis_http_event_websocket(struct ast_variable *headers, struct ast_event_websocket_args *args, struct stasis_http_response *response)
|
||||
{
|
||||
/* TODO: This should promote this socket to a websocket connection */
|
||||
ast_log(LOG_ERROR, "TODO: stasis_http_event_websocket\n");
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2012 - 2013, Digium, Inc.
|
||||
*
|
||||
* David M. Lee, II <dlee@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Generated file - declares stubs to be implemented in
|
||||
* res/stasis_http/resource_events.c
|
||||
*
|
||||
* WebSocket resource
|
||||
*
|
||||
* \author David M. Lee, II <dlee@digium.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* !!!!! DO NOT EDIT !!!!!
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* This file is generated by a mustache template. Please see the original
|
||||
* template in rest-api-templates/stasis_http_resource.h.mustache
|
||||
*/
|
||||
|
||||
#ifndef _ASTERISK_RESOURCE_EVENTS_H
|
||||
#define _ASTERISK_RESOURCE_EVENTS_H
|
||||
|
||||
#include "asterisk/stasis_http.h"
|
||||
|
||||
/*! \brief Argument struct for stasis_http_event_websocket() */
|
||||
struct ast_event_websocket_args {
|
||||
/*! \brief Comma seperated list of applications to subscribe to. */
|
||||
const char *app;
|
||||
/*! \brief RFC6455 header for upgrading a connection to a websocket. */
|
||||
const char *upgrade;
|
||||
};
|
||||
/*!
|
||||
* \brief WebSocket connection for events.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_event_websocket(struct ast_variable *headers, struct ast_event_websocket_args *args, struct stasis_http_response *response);
|
||||
|
||||
#endif /* _ASTERISK_RESOURCE_EVENTS_H */
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2012 - 2013, Digium, Inc.
|
||||
*
|
||||
* David M. Lee, II <dlee@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief /api-docs/playback.{format} implementation- Playback control resources
|
||||
*
|
||||
* \author David M. Lee, II <dlee@digium.com>
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include "resource_playback.h"
|
||||
|
||||
void stasis_http_get_playback(struct ast_variable *headers, struct ast_get_playback_args *args, struct stasis_http_response *response)
|
||||
{
|
||||
ast_log(LOG_ERROR, "TODO: stasis_http_get_playback\n");
|
||||
}
|
||||
void stasis_http_stop_playback(struct ast_variable *headers, struct ast_stop_playback_args *args, struct stasis_http_response *response)
|
||||
{
|
||||
ast_log(LOG_ERROR, "TODO: stasis_http_stop_playback\n");
|
||||
}
|
||||
void stasis_http_control_playback(struct ast_variable *headers, struct ast_control_playback_args *args, struct stasis_http_response *response)
|
||||
{
|
||||
ast_log(LOG_ERROR, "TODO: stasis_http_control_playback\n");
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2012 - 2013, Digium, Inc.
|
||||
*
|
||||
* David M. Lee, II <dlee@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Generated file - declares stubs to be implemented in
|
||||
* res/stasis_http/resource_playback.c
|
||||
*
|
||||
* Playback control resources
|
||||
*
|
||||
* \author David M. Lee, II <dlee@digium.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* !!!!! DO NOT EDIT !!!!!
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* This file is generated by a mustache template. Please see the original
|
||||
* template in rest-api-templates/stasis_http_resource.h.mustache
|
||||
*/
|
||||
|
||||
#ifndef _ASTERISK_RESOURCE_PLAYBACK_H
|
||||
#define _ASTERISK_RESOURCE_PLAYBACK_H
|
||||
|
||||
#include "asterisk/stasis_http.h"
|
||||
|
||||
/*! \brief Argument struct for stasis_http_get_playback() */
|
||||
struct ast_get_playback_args {
|
||||
/*! \brief Playback's id */
|
||||
const char *playback_id;
|
||||
};
|
||||
/*!
|
||||
* \brief Get a playback's details.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_get_playback(struct ast_variable *headers, struct ast_get_playback_args *args, struct stasis_http_response *response);
|
||||
/*! \brief Argument struct for stasis_http_stop_playback() */
|
||||
struct ast_stop_playback_args {
|
||||
/*! \brief Playback's id */
|
||||
const char *playback_id;
|
||||
};
|
||||
/*!
|
||||
* \brief Stop a playback.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_stop_playback(struct ast_variable *headers, struct ast_stop_playback_args *args, struct stasis_http_response *response);
|
||||
/*! \brief Argument struct for stasis_http_control_playback() */
|
||||
struct ast_control_playback_args {
|
||||
/*! \brief Playback's id */
|
||||
const char *playback_id;
|
||||
/*! \brief Operation to perform on the playback. */
|
||||
const char *operation;
|
||||
};
|
||||
/*!
|
||||
* \brief Get a playback's details.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_control_playback(struct ast_variable *headers, struct ast_control_playback_args *args, struct stasis_http_response *response);
|
||||
|
||||
#endif /* _ASTERISK_RESOURCE_PLAYBACK_H */
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2012 - 2013, Digium, Inc.
|
||||
*
|
||||
* David M. Lee, II <dlee@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief /api-docs/recordings.{format} implementation- Recording resources
|
||||
*
|
||||
* \author David M. Lee, II <dlee@digium.com>
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include "resource_recordings.h"
|
||||
|
||||
void stasis_http_get_recordings(struct ast_variable *headers, struct ast_get_recordings_args *args, struct stasis_http_response *response)
|
||||
{
|
||||
ast_log(LOG_ERROR, "TODO: stasis_http_get_recordings\n");
|
||||
}
|
||||
void stasis_http_get_stored_recordings(struct ast_variable *headers, struct ast_get_stored_recordings_args *args, struct stasis_http_response *response)
|
||||
{
|
||||
ast_log(LOG_ERROR, "TODO: stasis_http_get_stored_recordings\n");
|
||||
}
|
||||
void stasis_http_get_stored_recording(struct ast_variable *headers, struct ast_get_stored_recording_args *args, struct stasis_http_response *response)
|
||||
{
|
||||
ast_log(LOG_ERROR, "TODO: stasis_http_get_stored_recording\n");
|
||||
}
|
||||
void stasis_http_delete_stored_recording(struct ast_variable *headers, struct ast_delete_stored_recording_args *args, struct stasis_http_response *response)
|
||||
{
|
||||
ast_log(LOG_ERROR, "TODO: stasis_http_delete_stored_recording\n");
|
||||
}
|
||||
void stasis_http_get_live_recordings(struct ast_variable *headers, struct ast_get_live_recordings_args *args, struct stasis_http_response *response)
|
||||
{
|
||||
ast_log(LOG_ERROR, "TODO: stasis_http_get_live_recordings\n");
|
||||
}
|
||||
void stasis_http_get_live_recording(struct ast_variable *headers, struct ast_get_live_recording_args *args, struct stasis_http_response *response)
|
||||
{
|
||||
ast_log(LOG_ERROR, "TODO: stasis_http_get_live_recording\n");
|
||||
}
|
||||
void stasis_http_cancel_recording(struct ast_variable *headers, struct ast_cancel_recording_args *args, struct stasis_http_response *response)
|
||||
{
|
||||
ast_log(LOG_ERROR, "TODO: stasis_http_cancel_recording\n");
|
||||
}
|
||||
void stasis_http_stop_recording(struct ast_variable *headers, struct ast_stop_recording_args *args, struct stasis_http_response *response)
|
||||
{
|
||||
ast_log(LOG_ERROR, "TODO: stasis_http_stop_recording\n");
|
||||
}
|
||||
void stasis_http_pause_recording(struct ast_variable *headers, struct ast_pause_recording_args *args, struct stasis_http_response *response)
|
||||
{
|
||||
ast_log(LOG_ERROR, "TODO: stasis_http_pause_recording\n");
|
||||
}
|
||||
void stasis_http_unpause_recording(struct ast_variable *headers, struct ast_unpause_recording_args *args, struct stasis_http_response *response)
|
||||
{
|
||||
ast_log(LOG_ERROR, "TODO: stasis_http_unpause_recording\n");
|
||||
}
|
||||
void stasis_http_mute_recording(struct ast_variable *headers, struct ast_mute_recording_args *args, struct stasis_http_response *response)
|
||||
{
|
||||
ast_log(LOG_ERROR, "TODO: stasis_http_mute_recording\n");
|
||||
}
|
||||
void stasis_http_unmute_recording(struct ast_variable *headers, struct ast_unmute_recording_args *args, struct stasis_http_response *response)
|
||||
{
|
||||
ast_log(LOG_ERROR, "TODO: stasis_http_unmute_recording\n");
|
||||
}
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2012 - 2013, Digium, Inc.
|
||||
*
|
||||
* David M. Lee, II <dlee@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Generated file - declares stubs to be implemented in
|
||||
* res/stasis_http/resource_recordings.c
|
||||
*
|
||||
* Recording resources
|
||||
*
|
||||
* \author David M. Lee, II <dlee@digium.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* !!!!! DO NOT EDIT !!!!!
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* This file is generated by a mustache template. Please see the original
|
||||
* template in rest-api-templates/stasis_http_resource.h.mustache
|
||||
*/
|
||||
|
||||
#ifndef _ASTERISK_RESOURCE_RECORDINGS_H
|
||||
#define _ASTERISK_RESOURCE_RECORDINGS_H
|
||||
|
||||
#include "asterisk/stasis_http.h"
|
||||
|
||||
/*! \brief Argument struct for stasis_http_get_recordings() */
|
||||
struct ast_get_recordings_args {
|
||||
};
|
||||
/*!
|
||||
* \brief List all recordings.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_get_recordings(struct ast_variable *headers, struct ast_get_recordings_args *args, struct stasis_http_response *response);
|
||||
/*! \brief Argument struct for stasis_http_get_stored_recordings() */
|
||||
struct ast_get_stored_recordings_args {
|
||||
};
|
||||
/*!
|
||||
* \brief List recordings that are complete.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_get_stored_recordings(struct ast_variable *headers, struct ast_get_stored_recordings_args *args, struct stasis_http_response *response);
|
||||
/*! \brief Argument struct for stasis_http_get_stored_recording() */
|
||||
struct ast_get_stored_recording_args {
|
||||
/*! \brief Recording's id */
|
||||
const char *recording_id;
|
||||
};
|
||||
/*!
|
||||
* \brief Get a stored recording's details.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_get_stored_recording(struct ast_variable *headers, struct ast_get_stored_recording_args *args, struct stasis_http_response *response);
|
||||
/*! \brief Argument struct for stasis_http_delete_stored_recording() */
|
||||
struct ast_delete_stored_recording_args {
|
||||
/*! \brief Recording's id */
|
||||
const char *recording_id;
|
||||
};
|
||||
/*!
|
||||
* \brief Delete a stored recording.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_delete_stored_recording(struct ast_variable *headers, struct ast_delete_stored_recording_args *args, struct stasis_http_response *response);
|
||||
/*! \brief Argument struct for stasis_http_get_live_recordings() */
|
||||
struct ast_get_live_recordings_args {
|
||||
};
|
||||
/*!
|
||||
* \brief List libe recordings.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_get_live_recordings(struct ast_variable *headers, struct ast_get_live_recordings_args *args, struct stasis_http_response *response);
|
||||
/*! \brief Argument struct for stasis_http_get_live_recording() */
|
||||
struct ast_get_live_recording_args {
|
||||
/*! \brief Recording's id */
|
||||
const char *recording_id;
|
||||
};
|
||||
/*!
|
||||
* \brief List live recordings.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_get_live_recording(struct ast_variable *headers, struct ast_get_live_recording_args *args, struct stasis_http_response *response);
|
||||
/*! \brief Argument struct for stasis_http_cancel_recording() */
|
||||
struct ast_cancel_recording_args {
|
||||
/*! \brief Recording's id */
|
||||
const char *recording_id;
|
||||
};
|
||||
/*!
|
||||
* \brief Stop a live recording and discard it.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_cancel_recording(struct ast_variable *headers, struct ast_cancel_recording_args *args, struct stasis_http_response *response);
|
||||
/*! \brief Argument struct for stasis_http_stop_recording() */
|
||||
struct ast_stop_recording_args {
|
||||
/*! \brief Recording's id */
|
||||
const char *recording_id;
|
||||
};
|
||||
/*!
|
||||
* \brief Stop a live recording and store it.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_stop_recording(struct ast_variable *headers, struct ast_stop_recording_args *args, struct stasis_http_response *response);
|
||||
/*! \brief Argument struct for stasis_http_pause_recording() */
|
||||
struct ast_pause_recording_args {
|
||||
/*! \brief Recording's id */
|
||||
const char *recording_id;
|
||||
};
|
||||
/*!
|
||||
* \brief Pause a live recording.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_pause_recording(struct ast_variable *headers, struct ast_pause_recording_args *args, struct stasis_http_response *response);
|
||||
/*! \brief Argument struct for stasis_http_unpause_recording() */
|
||||
struct ast_unpause_recording_args {
|
||||
/*! \brief Recording's id */
|
||||
const char *recording_id;
|
||||
};
|
||||
/*!
|
||||
* \brief Unpause a live recording.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_unpause_recording(struct ast_variable *headers, struct ast_unpause_recording_args *args, struct stasis_http_response *response);
|
||||
/*! \brief Argument struct for stasis_http_mute_recording() */
|
||||
struct ast_mute_recording_args {
|
||||
/*! \brief Recording's id */
|
||||
const char *recording_id;
|
||||
};
|
||||
/*!
|
||||
* \brief Mute a live recording.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_mute_recording(struct ast_variable *headers, struct ast_mute_recording_args *args, struct stasis_http_response *response);
|
||||
/*! \brief Argument struct for stasis_http_unmute_recording() */
|
||||
struct ast_unmute_recording_args {
|
||||
/*! \brief Recording's id */
|
||||
const char *recording_id;
|
||||
};
|
||||
/*!
|
||||
* \brief Unmute a live recording.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_unmute_recording(struct ast_variable *headers, struct ast_unmute_recording_args *args, struct stasis_http_response *response);
|
||||
|
||||
#endif /* _ASTERISK_RESOURCE_RECORDINGS_H */
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2012 - 2013, Digium, Inc.
|
||||
*
|
||||
* David M. Lee, II <dlee@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief /api-docs/sounds.{format} implementation- Sound resources
|
||||
*
|
||||
* \author David M. Lee, II <dlee@digium.com>
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include "resource_sounds.h"
|
||||
|
||||
void stasis_http_get_sounds(struct ast_variable *headers, struct ast_get_sounds_args *args, struct stasis_http_response *response)
|
||||
{
|
||||
ast_log(LOG_ERROR, "TODO: stasis_http_get_sounds\n");
|
||||
}
|
||||
void stasis_http_get_stored_sound(struct ast_variable *headers, struct ast_get_stored_sound_args *args, struct stasis_http_response *response)
|
||||
{
|
||||
ast_log(LOG_ERROR, "TODO: stasis_http_get_stored_sound\n");
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2012 - 2013, Digium, Inc.
|
||||
*
|
||||
* David M. Lee, II <dlee@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Generated file - declares stubs to be implemented in
|
||||
* res/stasis_http/resource_sounds.c
|
||||
*
|
||||
* Sound resources
|
||||
*
|
||||
* \author David M. Lee, II <dlee@digium.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* !!!!! DO NOT EDIT !!!!!
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* This file is generated by a mustache template. Please see the original
|
||||
* template in rest-api-templates/stasis_http_resource.h.mustache
|
||||
*/
|
||||
|
||||
#ifndef _ASTERISK_RESOURCE_SOUNDS_H
|
||||
#define _ASTERISK_RESOURCE_SOUNDS_H
|
||||
|
||||
#include "asterisk/stasis_http.h"
|
||||
|
||||
/*! \brief Argument struct for stasis_http_get_sounds() */
|
||||
struct ast_get_sounds_args {
|
||||
const char *lang;
|
||||
const char *format;
|
||||
};
|
||||
/*!
|
||||
* \brief List all sounds.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_get_sounds(struct ast_variable *headers, struct ast_get_sounds_args *args, struct stasis_http_response *response);
|
||||
/*! \brief Argument struct for stasis_http_get_stored_sound() */
|
||||
struct ast_get_stored_sound_args {
|
||||
/*! \brief Sound's id */
|
||||
const char *sound_id;
|
||||
};
|
||||
/*!
|
||||
* \brief Get a sound's details.
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_get_stored_sound(struct ast_variable *headers, struct ast_get_stored_sound_args *args, struct stasis_http_response *response);
|
||||
|
||||
#endif /* _ASTERISK_RESOURCE_SOUNDS_H */
|
|
@ -0,0 +1,15 @@
|
|||
This directory contains templates and template processing code for generating
|
||||
HTTP bindings for the RESTful API's.
|
||||
|
||||
The RESTful API's are declared using [Swagger][swagger]. While Swagger provides
|
||||
a [code generating toolkit][swagger-codegen], it requires Java to run, which
|
||||
would be an unusual dependency to require for Asterisk developers.
|
||||
|
||||
This code generator is similar, but written in Python. Templates are processed
|
||||
by using [pystache][pystache], which is a fairly simply Python implementation of
|
||||
[mustache][mustache].
|
||||
|
||||
[swagger]: https://github.com/wordnik/swagger-core/wiki
|
||||
[swagger-codegen]: https://github.com/wordnik/swagger-codegen
|
||||
[pystache]: https://github.com/defunkt/pystache
|
||||
[mustache]: http://mustache.github.io/
|
|
@ -0,0 +1,179 @@
|
|||
#
|
||||
# Asterisk -- An open source telephony toolkit.
|
||||
#
|
||||
# Copyright (C) 2013, Digium, Inc.
|
||||
#
|
||||
# David M. Lee, II <dlee@digium.com>
|
||||
#
|
||||
# See http://www.asterisk.org for more information about
|
||||
# the Asterisk project. Please do not directly contact
|
||||
# any of the maintainers of this project for assistance;
|
||||
# the project provides a web site, mailing lists and IRC
|
||||
# channels for your use.
|
||||
#
|
||||
# This program is free software, distributed under the terms of
|
||||
# the GNU General Public License Version 2. See the LICENSE file
|
||||
# at the top of the source tree.
|
||||
#
|
||||
|
||||
"""Implementation of SwaggerPostProcessor which adds fields needed to generate
|
||||
Asterisk RESTful HTTP binding code.
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
from swagger_model import *
|
||||
|
||||
|
||||
def simple_name(name):
|
||||
"""Removes the {markers} from a path segement.
|
||||
|
||||
@param name: Swagger path segement, with {pathVar} markers.
|
||||
"""
|
||||
if name.startswith('{') and name.endswith('}'):
|
||||
return name[1:-1]
|
||||
return name
|
||||
|
||||
|
||||
def snakify(name):
|
||||
"""Helper to take a camelCase or dash-seperated name and make it
|
||||
snake_case.
|
||||
"""
|
||||
r = ''
|
||||
prior_lower = False
|
||||
for c in name:
|
||||
if c.isupper() and prior_lower:
|
||||
r += "_"
|
||||
if c is '-':
|
||||
c = '_'
|
||||
prior_lower = c.islower()
|
||||
r += c.lower()
|
||||
return r
|
||||
|
||||
|
||||
class PathSegment(Stringify):
|
||||
"""Tree representation of a Swagger API declaration.
|
||||
"""
|
||||
def __init__(self, name, parent):
|
||||
"""Ctor.
|
||||
|
||||
@param name: Name of this path segment. May have {pathVar} markers.
|
||||
@param parent: Parent PathSegment.
|
||||
"""
|
||||
#: Segment name, with {pathVar} markers removed
|
||||
self.name = simple_name(name)
|
||||
#: True if segment is a {pathVar}, else None.
|
||||
self.is_wildcard = None
|
||||
#: Underscore seperated name all ancestor segments
|
||||
self.full_name = None
|
||||
#: Dictionary of child PathSegements
|
||||
self.__children = OrderedDict()
|
||||
#: List of operations on this segement
|
||||
self.operations = []
|
||||
|
||||
if self.name != name:
|
||||
self.is_wildcard = True
|
||||
|
||||
if not self.name:
|
||||
assert(not parent)
|
||||
self.full_name = ''
|
||||
if not parent or not parent.name:
|
||||
self.full_name = name
|
||||
else:
|
||||
self.full_name = "%s_%s" % (parent.full_name, self.name)
|
||||
|
||||
def get_child(self, path):
|
||||
"""Walks decendents to get path, creating it if necessary.
|
||||
|
||||
@param path: List of path names.
|
||||
@return: PageSegment corresponding to path.
|
||||
"""
|
||||
assert simple_name(path[0]) == self.name
|
||||
if (len(path) == 1):
|
||||
return self
|
||||
child = self.__children.get(path[1])
|
||||
if not child:
|
||||
child = PathSegment(path[1], self)
|
||||
self.__children[path[1]] = child
|
||||
return child.get_child(path[1:])
|
||||
|
||||
def children(self):
|
||||
"""Gets list of children.
|
||||
"""
|
||||
return self.__children.values()
|
||||
|
||||
def num_children(self):
|
||||
"""Gets count of children.
|
||||
"""
|
||||
return len(self.__children)
|
||||
|
||||
|
||||
class AsteriskProcessor(SwaggerPostProcessor):
|
||||
"""A SwaggerPostProcessor which adds fields needed to generate Asterisk
|
||||
RESTful HTTP binding code.
|
||||
"""
|
||||
|
||||
#: How Swagger types map to C.
|
||||
type_mapping = {
|
||||
'string': 'const char *',
|
||||
'boolean': 'int',
|
||||
'number': 'int',
|
||||
'int': 'int',
|
||||
'long': 'long',
|
||||
'double': 'double',
|
||||
'float': 'float',
|
||||
}
|
||||
|
||||
#: String conversion functions for string to C type.
|
||||
convert_mapping = {
|
||||
'const char *': '',
|
||||
'int': 'atoi',
|
||||
'long': 'atol',
|
||||
'double': 'atof',
|
||||
}
|
||||
|
||||
def process_api(self, resource_api, context):
|
||||
# Derive a resource name from the API declaration's filename
|
||||
resource_api.name = re.sub('\..*', '',
|
||||
os.path.basename(resource_api.path))
|
||||
# Now in all caps, from include guard
|
||||
resource_api.name_caps = resource_api.name.upper()
|
||||
# Construct the PathSegement tree for the API.
|
||||
if resource_api.api_declaration:
|
||||
resource_api.root_path = PathSegment('', None)
|
||||
for api in resource_api.api_declaration.apis:
|
||||
segment = resource_api.root_path.get_child(api.path.split('/'))
|
||||
for operation in api.operations:
|
||||
segment.operations.append(operation)
|
||||
# Since every API path should start with /[resource], root should
|
||||
# have exactly one child.
|
||||
if resource_api.root_path.num_children() != 1:
|
||||
raise SwaggerError(
|
||||
"Should not mix resources in one API declaration", context)
|
||||
# root_path isn't needed any more
|
||||
resource_api.root_path = resource_api.root_path.children()[0]
|
||||
if resource_api.name != resource_api.root_path.name:
|
||||
raise SwaggerError(
|
||||
"API declaration name should match", context)
|
||||
resource_api.root_full_name = resource_api.root_path.full_name
|
||||
|
||||
def process_operation(self, operation, context):
|
||||
# Nicknames are camelcase, Asterisk coding is snake case
|
||||
operation.c_nickname = snakify(operation.nickname)
|
||||
operation.c_http_method = 'AST_HTTP_' + operation.http_method
|
||||
if not operation.summary.endswith("."):
|
||||
raise SwaggerError("Summary should end with .", context)
|
||||
|
||||
def process_parameter(self, parameter, context):
|
||||
if not parameter.data_type in self.type_mapping:
|
||||
raise SwaggerError(
|
||||
"Invalid parameter type %s" % paramter.data_type, context)
|
||||
# Parameter names are camelcase, Asterisk convention is snake case
|
||||
parameter.c_name = snakify(parameter.name)
|
||||
parameter.c_data_type = self.type_mapping[parameter.data_type]
|
||||
parameter.c_convert = self.convert_mapping[parameter.c_data_type]
|
||||
# You shouldn't put a space between 'char *' and the variable
|
||||
if parameter.c_data_type.endswith('*'):
|
||||
parameter.c_space = ''
|
||||
else:
|
||||
parameter.c_space = ' '
|
|
@ -0,0 +1,4 @@
|
|||
{{! A partial for the big warning, so it's not in the template itself }}
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* !!!!! DO NOT EDIT !!!!!
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
@ -0,0 +1,84 @@
|
|||
#!/usr/bin/env python
|
||||
# Asterisk -- An open source telephony toolkit.
|
||||
#
|
||||
# Copyright (C) 2013, Digium, Inc.
|
||||
#
|
||||
# David M. Lee, II <dlee@digium.com>
|
||||
#
|
||||
# See http://www.asterisk.org for more information about
|
||||
# the Asterisk project. Please do not directly contact
|
||||
# any of the maintainers of this project for assistance;
|
||||
# the project provides a web site, mailing lists and IRC
|
||||
# channels for your use.
|
||||
#
|
||||
# This program is free software, distributed under the terms of
|
||||
# the GNU General Public License Version 2. See the LICENSE file
|
||||
# at the top of the source tree.
|
||||
#
|
||||
|
||||
try:
|
||||
import pystache
|
||||
except ImportError:
|
||||
print >> sys.stderr, "Pystache required. Please sudo pip install pystache."
|
||||
|
||||
import os.path
|
||||
import pystache
|
||||
import sys
|
||||
|
||||
from asterisk_processor import AsteriskProcessor
|
||||
from optparse import OptionParser
|
||||
from swagger_model import *
|
||||
from transform import Transform
|
||||
|
||||
TOPDIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
def rel(file):
|
||||
"""Helper to get a file relative to the script's directory
|
||||
|
||||
@parm file: Relative file path.
|
||||
"""
|
||||
return os.path.join(TOPDIR, file)
|
||||
|
||||
API_TRANSFORMS = [
|
||||
Transform(rel('res_stasis_http_resource.c.mustache'),
|
||||
'res_stasis_http_{{name}}.c'),
|
||||
Transform(rel('stasis_http_resource.h.mustache'),
|
||||
'stasis_http/resource_{{name}}.h'),
|
||||
Transform(rel('stasis_http_resource.c.mustache'),
|
||||
'stasis_http/resource_{{name}}.c', False),
|
||||
]
|
||||
|
||||
RESOURCES_TRANSFORMS = [
|
||||
Transform(rel('stasis_http.make.mustache'), 'stasis_http.make'),
|
||||
]
|
||||
|
||||
|
||||
def main(argv):
|
||||
parser = OptionParser(usage="Usage %prog [resources.json] [destdir]")
|
||||
|
||||
(options, args) = parser.parse_args(argv)
|
||||
|
||||
if len(args) != 3:
|
||||
parser.error("Wrong number of arguments")
|
||||
|
||||
source = args[1]
|
||||
dest_dir = args[2]
|
||||
renderer = pystache.Renderer(search_dirs=[TOPDIR], missing_tags='strict')
|
||||
processor = AsteriskProcessor()
|
||||
|
||||
# Build the models
|
||||
base_dir = os.path.dirname(source)
|
||||
resources = ResourceListing().load_file(source, processor)
|
||||
for api in resources.apis:
|
||||
api.load_api_declaration(base_dir, processor)
|
||||
|
||||
# Render the templates
|
||||
for api in resources.apis:
|
||||
for transform in API_TRANSFORMS:
|
||||
transform.render(renderer, api, dest_dir)
|
||||
for transform in RESOURCES_TRANSFORMS:
|
||||
transform.render(renderer, resources, dest_dir)
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main(sys.argv) or 0)
|
|
@ -0,0 +1,261 @@
|
|||
# Downloaded from http://code.activestate.com/recipes/576693/
|
||||
# Licensed under the MIT License
|
||||
|
||||
# Backport of OrderedDict() class that runs on Python 2.4, 2.5, 2.6, 2.7 and pypy.
|
||||
# Passes Python2.7's test suite and incorporates all the latest updates.
|
||||
|
||||
try:
|
||||
from thread import get_ident as _get_ident
|
||||
except ImportError:
|
||||
from dummy_thread import get_ident as _get_ident
|
||||
|
||||
try:
|
||||
from _abcoll import KeysView, ValuesView, ItemsView
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
class OrderedDict(dict):
|
||||
'Dictionary that remembers insertion order'
|
||||
# An inherited dict maps keys to values.
|
||||
# The inherited dict provides __getitem__, __len__, __contains__, and get.
|
||||
# The remaining methods are order-aware.
|
||||
# Big-O running times for all methods are the same as for regular dictionaries.
|
||||
|
||||
# The internal self.__map dictionary maps keys to links in a doubly linked list.
|
||||
# The circular doubly linked list starts and ends with a sentinel element.
|
||||
# The sentinel element never gets deleted (this simplifies the algorithm).
|
||||
# Each link is stored as a list of length three: [PREV, NEXT, KEY].
|
||||
|
||||
def __init__(self, *args, **kwds):
|
||||
'''Initialize an ordered dictionary. Signature is the same as for
|
||||
regular dictionaries, but keyword arguments are not recommended
|
||||
because their insertion order is arbitrary.
|
||||
|
||||
'''
|
||||
if len(args) > 1:
|
||||
raise TypeError('expected at most 1 arguments, got %d' % len(args))
|
||||
try:
|
||||
self.__root
|
||||
except AttributeError:
|
||||
self.__root = root = [] # sentinel node
|
||||
root[:] = [root, root, None]
|
||||
self.__map = {}
|
||||
self.__update(*args, **kwds)
|
||||
|
||||
def __setitem__(self, key, value, dict_setitem=dict.__setitem__):
|
||||
'od.__setitem__(i, y) <==> od[i]=y'
|
||||
# Setting a new item creates a new link which goes at the end of the linked
|
||||
# list, and the inherited dictionary is updated with the new key/value pair.
|
||||
if key not in self:
|
||||
root = self.__root
|
||||
last = root[0]
|
||||
last[1] = root[0] = self.__map[key] = [last, root, key]
|
||||
dict_setitem(self, key, value)
|
||||
|
||||
def __delitem__(self, key, dict_delitem=dict.__delitem__):
|
||||
'od.__delitem__(y) <==> del od[y]'
|
||||
# Deleting an existing item uses self.__map to find the link which is
|
||||
# then removed by updating the links in the predecessor and successor nodes.
|
||||
dict_delitem(self, key)
|
||||
link_prev, link_next, key = self.__map.pop(key)
|
||||
link_prev[1] = link_next
|
||||
link_next[0] = link_prev
|
||||
|
||||
def __iter__(self):
|
||||
'od.__iter__() <==> iter(od)'
|
||||
root = self.__root
|
||||
curr = root[1]
|
||||
while curr is not root:
|
||||
yield curr[2]
|
||||
curr = curr[1]
|
||||
|
||||
def __reversed__(self):
|
||||
'od.__reversed__() <==> reversed(od)'
|
||||
root = self.__root
|
||||
curr = root[0]
|
||||
while curr is not root:
|
||||
yield curr[2]
|
||||
curr = curr[0]
|
||||
|
||||
def clear(self):
|
||||
'od.clear() -> None. Remove all items from od.'
|
||||
try:
|
||||
for node in self.__map.itervalues():
|
||||
del node[:]
|
||||
root = self.__root
|
||||
root[:] = [root, root, None]
|
||||
self.__map.clear()
|
||||
except AttributeError:
|
||||
pass
|
||||
dict.clear(self)
|
||||
|
||||
def popitem(self, last=True):
|
||||
'''od.popitem() -> (k, v), return and remove a (key, value) pair.
|
||||
Pairs are returned in LIFO order if last is true or FIFO order if false.
|
||||
|
||||
'''
|
||||
if not self:
|
||||
raise KeyError('dictionary is empty')
|
||||
root = self.__root
|
||||
if last:
|
||||
link = root[0]
|
||||
link_prev = link[0]
|
||||
link_prev[1] = root
|
||||
root[0] = link_prev
|
||||
else:
|
||||
link = root[1]
|
||||
link_next = link[1]
|
||||
root[1] = link_next
|
||||
link_next[0] = root
|
||||
key = link[2]
|
||||
del self.__map[key]
|
||||
value = dict.pop(self, key)
|
||||
return key, value
|
||||
|
||||
# -- the following methods do not depend on the internal structure --
|
||||
|
||||
def keys(self):
|
||||
'od.keys() -> list of keys in od'
|
||||
return list(self)
|
||||
|
||||
def values(self):
|
||||
'od.values() -> list of values in od'
|
||||
return [self[key] for key in self]
|
||||
|
||||
def items(self):
|
||||
'od.items() -> list of (key, value) pairs in od'
|
||||
return [(key, self[key]) for key in self]
|
||||
|
||||
def iterkeys(self):
|
||||
'od.iterkeys() -> an iterator over the keys in od'
|
||||
return iter(self)
|
||||
|
||||
def itervalues(self):
|
||||
'od.itervalues -> an iterator over the values in od'
|
||||
for k in self:
|
||||
yield self[k]
|
||||
|
||||
def iteritems(self):
|
||||
'od.iteritems -> an iterator over the (key, value) items in od'
|
||||
for k in self:
|
||||
yield (k, self[k])
|
||||
|
||||
def update(*args, **kwds):
|
||||
'''od.update(E, **F) -> None. Update od from dict/iterable E and F.
|
||||
|
||||
If E is a dict instance, does: for k in E: od[k] = E[k]
|
||||
If E has a .keys() method, does: for k in E.keys(): od[k] = E[k]
|
||||
Or if E is an iterable of items, does: for k, v in E: od[k] = v
|
||||
In either case, this is followed by: for k, v in F.items(): od[k] = v
|
||||
|
||||
'''
|
||||
if len(args) > 2:
|
||||
raise TypeError('update() takes at most 2 positional '
|
||||
'arguments (%d given)' % (len(args),))
|
||||
elif not args:
|
||||
raise TypeError('update() takes at least 1 argument (0 given)')
|
||||
self = args[0]
|
||||
# Make progressively weaker assumptions about "other"
|
||||
other = ()
|
||||
if len(args) == 2:
|
||||
other = args[1]
|
||||
if isinstance(other, dict):
|
||||
for key in other:
|
||||
self[key] = other[key]
|
||||
elif hasattr(other, 'keys'):
|
||||
for key in other.keys():
|
||||
self[key] = other[key]
|
||||
else:
|
||||
for key, value in other:
|
||||
self[key] = value
|
||||
for key, value in kwds.items():
|
||||
self[key] = value
|
||||
|
||||
__update = update # let subclasses override update without breaking __init__
|
||||
|
||||
__marker = object()
|
||||
|
||||
def pop(self, key, default=__marker):
|
||||
'''od.pop(k[,d]) -> v, remove specified key and return the corresponding value.
|
||||
If key is not found, d is returned if given, otherwise KeyError is raised.
|
||||
|
||||
'''
|
||||
if key in self:
|
||||
result = self[key]
|
||||
del self[key]
|
||||
return result
|
||||
if default is self.__marker:
|
||||
raise KeyError(key)
|
||||
return default
|
||||
|
||||
def setdefault(self, key, default=None):
|
||||
'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od'
|
||||
if key in self:
|
||||
return self[key]
|
||||
self[key] = default
|
||||
return default
|
||||
|
||||
def __repr__(self, _repr_running={}):
|
||||
'od.__repr__() <==> repr(od)'
|
||||
call_key = id(self), _get_ident()
|
||||
if call_key in _repr_running:
|
||||
return '...'
|
||||
_repr_running[call_key] = 1
|
||||
try:
|
||||
if not self:
|
||||
return '%s()' % (self.__class__.__name__,)
|
||||
return '%s(%r)' % (self.__class__.__name__, self.items())
|
||||
finally:
|
||||
del _repr_running[call_key]
|
||||
|
||||
def __reduce__(self):
|
||||
'Return state information for pickling'
|
||||
items = [[k, self[k]] for k in self]
|
||||
inst_dict = vars(self).copy()
|
||||
for k in vars(OrderedDict()):
|
||||
inst_dict.pop(k, None)
|
||||
if inst_dict:
|
||||
return (self.__class__, (items,), inst_dict)
|
||||
return self.__class__, (items,)
|
||||
|
||||
def copy(self):
|
||||
'od.copy() -> a shallow copy of od'
|
||||
return self.__class__(self)
|
||||
|
||||
@classmethod
|
||||
def fromkeys(cls, iterable, value=None):
|
||||
'''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S
|
||||
and values equal to v (which defaults to None).
|
||||
|
||||
'''
|
||||
d = cls()
|
||||
for key in iterable:
|
||||
d[key] = value
|
||||
return d
|
||||
|
||||
def __eq__(self, other):
|
||||
'''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive
|
||||
while comparison to a regular mapping is order-insensitive.
|
||||
|
||||
'''
|
||||
if isinstance(other, OrderedDict):
|
||||
return len(self)==len(other) and self.items() == other.items()
|
||||
return dict.__eq__(self, other)
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
# -- the following methods are only used in Python 2.7 --
|
||||
|
||||
def viewkeys(self):
|
||||
"od.viewkeys() -> a set-like object providing a view on od's keys"
|
||||
return KeysView(self)
|
||||
|
||||
def viewvalues(self):
|
||||
"od.viewvalues() -> an object providing a view on od's values"
|
||||
return ValuesView(self)
|
||||
|
||||
def viewitems(self):
|
||||
"od.viewitems() -> a set-like object providing a view on od's items"
|
||||
return ItemsView(self)
|
|
@ -0,0 +1,116 @@
|
|||
{{#api_declaration}}
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* {{{copyright}}}
|
||||
*
|
||||
* {{{author}}}
|
||||
{{! Template Copyright
|
||||
* Copyright (C) 2013, Digium, Inc.
|
||||
*
|
||||
* David M. Lee, II <dlee@digium.com>
|
||||
}}
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
{{! Template for rendering the res_ module for an HTTP resource. }}
|
||||
/*
|
||||
{{> do-not-edit}}
|
||||
* This file is generated by a mustache template. Please see the original
|
||||
* template in rest-api-templates/res_stasis_http_resource.c.mustache
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief {{{description}}}
|
||||
*
|
||||
* \author {{{author}}}
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<depend type="module">res_stasis_http</depend>
|
||||
<support_level>core</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include "asterisk/module.h"
|
||||
#include "stasis_http/resource_{{name}}.h"
|
||||
|
||||
{{#apis}}
|
||||
{{#operations}}
|
||||
/*!
|
||||
* \brief Parameter parsing callback for {{path}}.
|
||||
* \param get_params GET parameters in the HTTP request.
|
||||
* \param path_vars Path variables extracted from the request.
|
||||
* \param headers HTTP headers.
|
||||
* \param[out] response Response to the HTTP request.
|
||||
*/
|
||||
static void stasis_http_{{c_nickname}}_cb(
|
||||
struct ast_variable *get_params, struct ast_variable *path_vars,
|
||||
struct ast_variable *headers, struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_{{c_nickname}}_args args = {};
|
||||
{{#has_parameters}}
|
||||
struct ast_variable *i;
|
||||
|
||||
{{#has_query_parameters}}
|
||||
for (i = get_params; i; i = i->next) {
|
||||
{{#query_parameters}}
|
||||
if (strcmp(i->name, "{{name}}") == 0) {
|
||||
args.{{c_name}} = {{c_convert}}(i->value);
|
||||
} else
|
||||
{{/query_parameters}}
|
||||
{}
|
||||
}
|
||||
{{/has_query_parameters}}
|
||||
{{#has_path_parameters}}
|
||||
for (i = path_vars; i; i = i->next) {
|
||||
{{#path_parameters}}
|
||||
if (strcmp(i->name, "{{name}}") == 0) {
|
||||
args.{{c_name}} = {{c_convert}}(i->value);
|
||||
} else
|
||||
{{/path_parameters}}
|
||||
{}
|
||||
}
|
||||
{{/has_path_parameters}}
|
||||
{{/has_parameters}}
|
||||
stasis_http_{{c_nickname}}(headers, &args, response);
|
||||
}
|
||||
{{/operations}}
|
||||
{{/apis}}
|
||||
|
||||
{{! The rest_handler partial expands to the tree of stasis_rest_handlers }}
|
||||
{{#root_path}}
|
||||
{{> rest_handler}}
|
||||
{{/root_path}}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
return stasis_http_add_handler(&{{root_full_name}});
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
stasis_http_remove_handler(&{{root_full_name}});
|
||||
return 0;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT,
|
||||
"RESTful API module - {{{description}}}",
|
||||
.load = load_module,
|
||||
.unload = unload_module,
|
||||
.nonoptreq = "res_stasis_http",
|
||||
);
|
||||
{{/api_declaration}}
|
|
@ -0,0 +1,38 @@
|
|||
{{! -*- C -*-
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2013, Digium, Inc.
|
||||
*
|
||||
* David M. Lee, II <dlee@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
}}
|
||||
{{!
|
||||
* Recursive partial template to render a rest_handler. Used in
|
||||
* res_stasis_http_resource.c.mustache.
|
||||
}}
|
||||
{{#children}}
|
||||
{{> rest_handler}}
|
||||
{{/children}}
|
||||
/*! \brief REST handler for {{path}} */
|
||||
static struct stasis_rest_handlers {{full_name}} = {
|
||||
.path_segment = "{{name}}",
|
||||
{{#is_wildcard}}
|
||||
.is_wildcard = 1,
|
||||
{{/is_wildcard}}
|
||||
.callbacks = {
|
||||
{{#operations}}
|
||||
[{{c_http_method}}] = stasis_http_{{c_nickname}}_cb,
|
||||
{{/operations}}
|
||||
},
|
||||
.num_children = {{num_children}},
|
||||
.children = { {{#children}}&{{full_name}},{{/children}} }
|
||||
};
|
|
@ -0,0 +1,26 @@
|
|||
{{! -*- Makefile -*- }}
|
||||
#
|
||||
# Asterisk -- A telephony toolkit for Linux.
|
||||
#
|
||||
# Generated Makefile for res_stasis_http dependencies.
|
||||
#
|
||||
# Copyright (C) 2013, Digium, Inc.
|
||||
#
|
||||
# This program is free software, distributed under the terms of
|
||||
# the GNU General Public License
|
||||
#
|
||||
|
||||
#
|
||||
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
# !!!!! DO NOT EDIT !!!!!
|
||||
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
# This file is generated by a template. Please see the original template at
|
||||
# rest-api-templates/stasis_http.make.mustache
|
||||
#
|
||||
|
||||
{{#apis}}
|
||||
res_stasis_http_{{name}}.so: stasis_http/resource_{{name}}.o
|
||||
|
||||
stasis_http/resource_{{name}}.o: _ASTCFLAGS+=$(call MOD_ASTCFLAGS,res_stasis_http_{{name}})
|
||||
|
||||
{{/apis}}
|
|
@ -0,0 +1,41 @@
|
|||
{{#api_declaration}}
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* {{{copyright}}}
|
||||
*
|
||||
* {{{author}}}
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief {{{resource_path}}} implementation- {{{description}}}
|
||||
*
|
||||
* \author {{{author}}}
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include "resource_{{name}}.h"
|
||||
|
||||
{{#apis}}
|
||||
{{#operations}}
|
||||
void stasis_http_{{c_nickname}}(struct ast_variable *headers, struct ast_{{c_nickname}}_args *args, struct stasis_http_response *response)
|
||||
{
|
||||
ast_log(LOG_ERROR, "TODO: stasis_http_{{c_nickname}}\n");
|
||||
}
|
||||
{{/operations}}
|
||||
{{/apis}}
|
||||
{{/api_declaration}}
|
|
@ -0,0 +1,68 @@
|
|||
{{#api_declaration}}
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* {{{copyright}}}
|
||||
*
|
||||
* {{{author}}}
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*! \file
|
||||
*
|
||||
* \brief Generated file - declares stubs to be implemented in
|
||||
* res/stasis_http/resource_{{name}}.c
|
||||
*
|
||||
* {{{description}}}
|
||||
*
|
||||
* \author {{{author}}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{> do-not-edit}}
|
||||
* This file is generated by a mustache template. Please see the original
|
||||
* template in rest-api-templates/stasis_http_resource.h.mustache
|
||||
*/
|
||||
|
||||
#ifndef _ASTERISK_RESOURCE_{{name_caps}}_H
|
||||
#define _ASTERISK_RESOURCE_{{name_caps}}_H
|
||||
|
||||
#include "asterisk/stasis_http.h"
|
||||
|
||||
{{#apis}}
|
||||
{{#operations}}
|
||||
/*! \brief Argument struct for stasis_http_{{c_nickname}}() */
|
||||
struct ast_{{c_nickname}}_args {
|
||||
{{#parameters}}
|
||||
{{#description}}
|
||||
/*! \brief {{{description}}} */
|
||||
{{/description}}
|
||||
{{c_data_type}}{{c_space}}{{c_name}};
|
||||
{{/parameters}}
|
||||
};
|
||||
/*!
|
||||
* \brief {{summary}}
|
||||
{{#notes}}
|
||||
*
|
||||
* {{{notes}}}
|
||||
{{/notes}}
|
||||
*
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param[out] response HTTP response
|
||||
*/
|
||||
void stasis_http_{{c_nickname}}(struct ast_variable *headers, struct ast_{{c_nickname}}_args *args, struct stasis_http_response *response);
|
||||
{{/operations}}
|
||||
{{/apis}}
|
||||
|
||||
#endif /* _ASTERISK_RESOURCE_{{name_caps}}_H */
|
||||
{{/api_declaration}}
|
|
@ -0,0 +1,482 @@
|
|||
|
||||
# Asterisk -- An open source telephony toolkit.
|
||||
#
|
||||
# Copyright (C) 2013, Digium, Inc.
|
||||
#
|
||||
# David M. Lee, II <dlee@digium.com>
|
||||
#
|
||||
# See http://www.asterisk.org for more information about
|
||||
# the Asterisk project. Please do not directly contact
|
||||
# any of the maintainers of this project for assistance;
|
||||
# the project provides a web site, mailing lists and IRC
|
||||
# channels for your use.
|
||||
#
|
||||
# This program is free software, distributed under the terms of
|
||||
# the GNU General Public License Version 2. See the LICENSE file
|
||||
# at the top of the source tree.
|
||||
#
|
||||
|
||||
"""Swagger data model objects.
|
||||
|
||||
These objects should map directly to the Swagger api-docs, without a lot of
|
||||
additional fields. In the process of translation, it should also validate the
|
||||
model for consistency against the Swagger spec (i.e., fail if fields are
|
||||
missing, or have incorrect values).
|
||||
|
||||
See https://github.com/wordnik/swagger-core/wiki/API-Declaration for the spec.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os.path
|
||||
import pprint
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
try:
|
||||
from collections import OrderedDict
|
||||
except ImportError:
|
||||
from odict import OrderedDict
|
||||
|
||||
|
||||
SWAGGER_VERSION = "1.1"
|
||||
|
||||
|
||||
class SwaggerError(Exception):
|
||||
"""Raised when an error is encountered mapping the JSON objects into the
|
||||
model.
|
||||
"""
|
||||
|
||||
def __init__(self, msg, context, cause=None):
|
||||
"""Ctor.
|
||||
|
||||
@param msg: String message for the error.
|
||||
@param context: Array of strings for current context in the API.
|
||||
@param cause: Optional exception that caused this one.
|
||||
"""
|
||||
super(Exception, self).__init__(msg, context, cause)
|
||||
|
||||
|
||||
class SwaggerPostProcessor(object):
|
||||
"""Post processing interface for model objects. This processor can add
|
||||
fields to model objects for additional information to use in the
|
||||
templates.
|
||||
"""
|
||||
def process_api(self, resource_api, context):
|
||||
"""Post process a ResourceApi object.
|
||||
|
||||
@param resource_api: ResourceApi object.
|
||||
@param contect: Current context in the API.
|
||||
"""
|
||||
pass
|
||||
|
||||
def process_operation(self, operation, context):
|
||||
"""Post process a Operation object.
|
||||
|
||||
@param operation: Operation object.
|
||||
@param contect: Current context in the API.
|
||||
"""
|
||||
pass
|
||||
|
||||
def process_parameter(self, parameter, context):
|
||||
"""Post process a Parameter object.
|
||||
|
||||
@param parameter: Parameter object.
|
||||
@param contect: Current context in the API.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class Stringify(object):
|
||||
"""Simple mix-in to make the repr of the model classes more meaningful.
|
||||
"""
|
||||
def __repr__(self):
|
||||
return "%s(%s)" % (self.__class__, pprint.saferepr(self.__dict__))
|
||||
|
||||
|
||||
class AllowableRange(Stringify):
|
||||
"""Model of a allowableValues of type RANGE
|
||||
|
||||
See https://github.com/wordnik/swagger-core/wiki/datatypes#complex-types
|
||||
"""
|
||||
def __init__(self, min_value, max_value):
|
||||
self.min_value = min_value
|
||||
self.max_value = max_value
|
||||
|
||||
|
||||
class AllowableList(Stringify):
|
||||
"""Model of a allowableValues of type LIST
|
||||
|
||||
See https://github.com/wordnik/swagger-core/wiki/datatypes#complex-types
|
||||
"""
|
||||
def __init__(self, values):
|
||||
self.values = values
|
||||
|
||||
|
||||
def load_allowable_values(json, context):
|
||||
"""Parse a JSON allowableValues object.
|
||||
|
||||
This returns None, AllowableList or AllowableRange, depending on the
|
||||
valueType in the JSON. If the valueType is not recognized, a SwaggerError
|
||||
is raised.
|
||||
"""
|
||||
if not json:
|
||||
return None
|
||||
|
||||
if not 'valueType' in json:
|
||||
raise SwaggerError("Missing valueType field", context)
|
||||
|
||||
value_type = json['valueType']
|
||||
|
||||
if value_type == 'RANGE':
|
||||
if not 'min' in json:
|
||||
raise SwaggerError("Missing field min", context)
|
||||
if not 'max' in json:
|
||||
raise SwaggerError("Missing field max", context)
|
||||
return AllowableRange(json['min'], json['max'])
|
||||
if value_type == 'LIST':
|
||||
if not 'values' in json:
|
||||
raise SwaggerError("Missing field values", context)
|
||||
return AllowableList(json['values'])
|
||||
raise SwaggerError("Unkown valueType %s" % value_type, context)
|
||||
|
||||
|
||||
class Parameter(Stringify):
|
||||
"""Model of an operation's parameter.
|
||||
|
||||
See https://github.com/wordnik/swagger-core/wiki/parameters
|
||||
"""
|
||||
|
||||
required_fields = ['name', 'paramType', 'dataType']
|
||||
|
||||
def __init__(self):
|
||||
self.param_type = None
|
||||
self.name = None
|
||||
self.description = None
|
||||
self.data_type = None
|
||||
self.required = None
|
||||
self.allowable_values = None
|
||||
self.allow_multiple = None
|
||||
|
||||
def load(self, parameter_json, processor, context):
|
||||
context = add_context(context, parameter_json, 'name')
|
||||
validate_required_fields(parameter_json, self.required_fields, context)
|
||||
self.name = parameter_json.get('name')
|
||||
self.param_type = parameter_json.get('paramType')
|
||||
self.description = parameter_json.get('description') or ''
|
||||
self.data_type = parameter_json.get('dataType')
|
||||
self.required = parameter_json.get('required') or False
|
||||
self.allowable_values = load_allowable_values(
|
||||
parameter_json.get('allowableValues'), context)
|
||||
self.allow_multiple = parameter_json.get('allowMultiple') or False
|
||||
processor.process_parameter(self, context)
|
||||
return self
|
||||
|
||||
def is_type(self, other_type):
|
||||
return self.param_type == other_type
|
||||
|
||||
|
||||
class ErrorResponse(Stringify):
|
||||
"""Model of an error response.
|
||||
|
||||
See https://github.com/wordnik/swagger-core/wiki/errors
|
||||
"""
|
||||
|
||||
required_fields = ['code', 'reason']
|
||||
|
||||
def __init__(self):
|
||||
self.code = None
|
||||
self.reason = None
|
||||
|
||||
def load(self, err_json, processor, context):
|
||||
context = add_context(context, err_json, 'code')
|
||||
validate_required_fields(err_json, self.required_fields, context)
|
||||
self.code = err_json.get('code')
|
||||
self.reason = err_json.get('reason')
|
||||
return self
|
||||
|
||||
|
||||
class Operation(Stringify):
|
||||
"""Model of an operation on an API
|
||||
|
||||
See https://github.com/wordnik/swagger-core/wiki/API-Declaration#apis
|
||||
"""
|
||||
|
||||
required_fields = ['httpMethod', 'nickname', 'responseClass', 'summary']
|
||||
|
||||
def __init__(self):
|
||||
self.http_method = None
|
||||
self.nickname = None
|
||||
self.response_class = None
|
||||
self.parameters = []
|
||||
self.summary = None
|
||||
self.notes = None
|
||||
self.error_responses = []
|
||||
|
||||
def load(self, op_json, processor, context):
|
||||
context = add_context(context, op_json, 'nickname')
|
||||
validate_required_fields(op_json, self.required_fields, context)
|
||||
self.http_method = op_json.get('httpMethod')
|
||||
self.nickname = op_json.get('nickname')
|
||||
self.response_class = op_json.get('responseClass')
|
||||
params_json = op_json.get('parameters') or []
|
||||
self.parameters = [
|
||||
Parameter().load(j, processor, context) for j in params_json]
|
||||
self.query_parameters = [
|
||||
p for p in self.parameters if p.is_type('query')]
|
||||
self.has_query_parameters = self.query_parameters and True
|
||||
self.path_parameters = [
|
||||
p for p in self.parameters if p.is_type('path')]
|
||||
self.has_path_parameters = self.path_parameters and True
|
||||
self.header_parameters = [
|
||||
p for p in self.parameters if p.is_type('header')]
|
||||
self.has_header_parameters = self.header_parameters and True
|
||||
self.has_parameters = self.has_query_parameters or \
|
||||
self.has_path_parameters or self.has_header_parameters
|
||||
self.summary = op_json.get('summary')
|
||||
self.notes = op_json.get('notes')
|
||||
err_json = op_json.get('errorResponses') or []
|
||||
self.error_responses = [
|
||||
ErrorResponse().load(j, processor, context) for j in err_json]
|
||||
processor.process_operation(self, context)
|
||||
return self
|
||||
|
||||
|
||||
class Api(Stringify):
|
||||
"""Model of a single API in an API declaration.
|
||||
|
||||
See https://github.com/wordnik/swagger-core/wiki/API-Declaration
|
||||
"""
|
||||
|
||||
required_fields = ['path', 'operations']
|
||||
|
||||
def __init__(self,):
|
||||
self.path = None
|
||||
self.description = None
|
||||
self.operations = []
|
||||
|
||||
def load(self, api_json, processor, context):
|
||||
context = add_context(context, api_json, 'path')
|
||||
validate_required_fields(api_json, self.required_fields, context)
|
||||
self.path = api_json.get('path')
|
||||
self.description = api_json.get('description')
|
||||
op_json = api_json.get('operations')
|
||||
self.operations = [
|
||||
Operation().load(j, processor, context) for j in op_json]
|
||||
return self
|
||||
|
||||
|
||||
class Property(Stringify):
|
||||
"""Model of a Swagger property.
|
||||
|
||||
See https://github.com/wordnik/swagger-core/wiki/datatypes
|
||||
"""
|
||||
|
||||
required_fields = ['type']
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.type = None
|
||||
self.description = None
|
||||
self.required = None
|
||||
|
||||
def load(self, property_json, processor, context):
|
||||
validate_required_fields(property_json, self.required_fields, context)
|
||||
self.type = property_json.get('type')
|
||||
self.description = property_json.get('description') or ''
|
||||
self.required = property_json.get('required') or False
|
||||
return self
|
||||
|
||||
|
||||
class Model(Stringify):
|
||||
"""Model of a Swagger model.
|
||||
|
||||
See https://github.com/wordnik/swagger-core/wiki/datatypes
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.id = None
|
||||
self.properties = None
|
||||
|
||||
def load(self, model_json, processor, context):
|
||||
context = add_context(context, model_json, 'id')
|
||||
self.id = model_json.get('id')
|
||||
props = model_json.get('properties').items() or []
|
||||
self.properties = [
|
||||
Property(k).load(j, processor, context) for (k, j) in props]
|
||||
return self
|
||||
|
||||
|
||||
class ApiDeclaration(Stringify):
|
||||
"""Model class for an API Declaration.
|
||||
|
||||
See https://github.com/wordnik/swagger-core/wiki/API-Declaration
|
||||
"""
|
||||
|
||||
required_fields = [
|
||||
'swaggerVersion', '_author', '_copyright', 'apiVersion', 'basePath',
|
||||
'resourcePath', 'apis', 'models'
|
||||
]
|
||||
|
||||
def __init__(self):
|
||||
self.swagger_version = None
|
||||
self.author = None
|
||||
self.copyright = None
|
||||
self.api_version = None
|
||||
self.base_path = None
|
||||
self.resource_path = None
|
||||
self.apis = []
|
||||
self.models = []
|
||||
|
||||
def load_file(self, api_declaration_file, processor, context=[]):
|
||||
context = context + [api_declaration_file]
|
||||
try:
|
||||
return self.__load_file(api_declaration_file, processor, context)
|
||||
except SwaggerError:
|
||||
raise
|
||||
except Exception as e:
|
||||
print >> sys.stderr, "Error: ", traceback.format_exc()
|
||||
raise SwaggerError(
|
||||
"Error loading %s" % api_declaration_file, context, e)
|
||||
|
||||
def __load_file(self, api_declaration_file, processor, context):
|
||||
with open(api_declaration_file) as fp:
|
||||
self.load(json.load(fp), processor, context)
|
||||
|
||||
expected_resource_path = '/api-docs/' + \
|
||||
os.path.basename(api_declaration_file) \
|
||||
.replace(".json", ".{format}")
|
||||
|
||||
if self.resource_path != expected_resource_path:
|
||||
print "%s != %s" % (self.resource_path, expected_resource_path)
|
||||
raise SwaggerError("resourcePath has incorrect value", context)
|
||||
|
||||
return self
|
||||
|
||||
def load(self, api_decl_json, processor, context):
|
||||
"""Loads a resource from a single Swagger resource.json file.
|
||||
"""
|
||||
# If the version doesn't match, all bets are off.
|
||||
self.swagger_version = api_decl_json.get('swaggerVersion')
|
||||
if self.swagger_version != SWAGGER_VERSION:
|
||||
raise SwaggerError(
|
||||
"Unsupported Swagger version %s" % swagger_version, context)
|
||||
|
||||
validate_required_fields(api_decl_json, self.required_fields, context)
|
||||
|
||||
self.author = api_decl_json.get('_author')
|
||||
self.copyright = api_decl_json.get('_copyright')
|
||||
self.api_version = api_decl_json.get('apiVersion')
|
||||
self.base_path = api_decl_json.get('basePath')
|
||||
self.resource_path = api_decl_json.get('resourcePath')
|
||||
api_json = api_decl_json.get('apis') or []
|
||||
self.apis = [
|
||||
Api().load(j, processor, context) for j in api_json]
|
||||
models = api_decl_json.get('models').items() or []
|
||||
self.models = OrderedDict(
|
||||
(k, Model().load(j, processor, context)) for (k, j) in models)
|
||||
|
||||
for (name, model) in self.models.items():
|
||||
c = list(context).append('model = %s' % name)
|
||||
if name != model.id:
|
||||
raise SwaggerError("Model id doesn't match name", c)
|
||||
return self
|
||||
|
||||
|
||||
class ResourceApi(Stringify):
|
||||
"""Model of an API listing in the resources.json file.
|
||||
"""
|
||||
|
||||
required_fields = ['path', 'description']
|
||||
|
||||
def __init__(self):
|
||||
self.path = None
|
||||
self.description = None
|
||||
self.api_declaration = None
|
||||
|
||||
def load(self, api_json, processor, context):
|
||||
context = add_context(context, api_json, 'path')
|
||||
validate_required_fields(api_json, self.required_fields, context)
|
||||
self.path = api_json['path']
|
||||
self.description = api_json['description']
|
||||
|
||||
if not self.path or self.path[0] != '/':
|
||||
raise SwaggerError("Path must start with /", context)
|
||||
processor.process_api(self, context)
|
||||
return self
|
||||
|
||||
def load_api_declaration(self, base_dir, processor):
|
||||
self.file = (base_dir + self.path).replace('{format}', 'json')
|
||||
self.api_declaration = ApiDeclaration().load_file(self.file, processor)
|
||||
processor.process_api(self, [self.file])
|
||||
|
||||
|
||||
class ResourceListing(Stringify):
|
||||
"""Model of Swagger's resources.json file.
|
||||
"""
|
||||
|
||||
required_fields = ['apiVersion', 'basePath', 'apis']
|
||||
|
||||
def __init__(self):
|
||||
self.swagger_version = None
|
||||
self.api_version = None
|
||||
self.base_path = None
|
||||
self.apis = None
|
||||
|
||||
def load_file(self, resource_file, processor):
|
||||
context = [resource_file]
|
||||
try:
|
||||
return self.__load_file(resource_file, processor, context)
|
||||
except SwaggerError:
|
||||
raise
|
||||
except Exception as e:
|
||||
print >> sys.stderr, "Error: ", traceback.format_exc()
|
||||
raise SwaggerError(
|
||||
"Error loading %s" % resource_file, context, e)
|
||||
|
||||
def __load_file(self, resource_file, processor, context):
|
||||
with open(resource_file) as fp:
|
||||
return self.load(json.load(fp), processor, context)
|
||||
|
||||
def load(self, resources_json, processor, context):
|
||||
# If the version doesn't match, all bets are off.
|
||||
self.swagger_version = resources_json.get('swaggerVersion')
|
||||
if self.swagger_version != SWAGGER_VERSION:
|
||||
raise SwaggerError(
|
||||
"Unsupported Swagger version %s" % swagger_version, context)
|
||||
|
||||
validate_required_fields(resources_json, self.required_fields, context)
|
||||
self.api_version = resources_json['apiVersion']
|
||||
self.base_path = resources_json['basePath']
|
||||
apis_json = resources_json['apis']
|
||||
self.apis = [
|
||||
ResourceApi().load(j, processor, context) for j in apis_json]
|
||||
return self
|
||||
|
||||
|
||||
def validate_required_fields(json, required_fields, context):
|
||||
"""Checks a JSON object for a set of required fields.
|
||||
|
||||
If any required field is missing, a SwaggerError is raised.
|
||||
|
||||
@param json: JSON object to check.
|
||||
@param required_fields: List of required fields.
|
||||
@param context: Current context in the API.
|
||||
"""
|
||||
missing_fields = [f for f in required_fields if not f in json]
|
||||
|
||||
if missing_fields:
|
||||
raise SwaggerError(
|
||||
"Missing fields: %s" % ', '.join(missing_fields), context)
|
||||
|
||||
|
||||
def add_context(context, json, id_field):
|
||||
"""Returns a new context with a new item added to it.
|
||||
|
||||
@param context: Old context.
|
||||
@param json: Current JSON object.
|
||||
@param id_field: Field identifying this object.
|
||||
@return New context with additional item.
|
||||
"""
|
||||
if not id_field in json:
|
||||
raise SwaggerError("Missing id_field: %s" % id_field, context)
|
||||
return context + ['%s=%s' % (id_field, str(json[id_field]))]
|
|
@ -0,0 +1,53 @@
|
|||
#
|
||||
# Asterisk -- An open source telephony toolkit.
|
||||
#
|
||||
# Copyright (C) 2013, Digium, Inc.
|
||||
#
|
||||
# David M. Lee, II <dlee@digium.com>
|
||||
#
|
||||
# See http://www.asterisk.org for more information about
|
||||
# the Asterisk project. Please do not directly contact
|
||||
# any of the maintainers of this project for assistance;
|
||||
# the project provides a web site, mailing lists and IRC
|
||||
# channels for your use.
|
||||
#
|
||||
# This program is free software, distributed under the terms of
|
||||
# the GNU General Public License Version 2. See the LICENSE file
|
||||
# at the top of the source tree.
|
||||
#
|
||||
|
||||
import os.path
|
||||
import pystache
|
||||
|
||||
|
||||
class Transform(object):
|
||||
"""Transformation for template to code.
|
||||
"""
|
||||
def __init__(self, template_file, dest_file_template_str, overwrite=True):
|
||||
"""Ctor.
|
||||
|
||||
@param template_file: Filename of the mustache template.
|
||||
@param dest_file_template_str: Destination file name. This is a
|
||||
mustache template, so each resource can write to a unique file.
|
||||
@param overwrite: If True, destination file is ovewritten if it exists.
|
||||
"""
|
||||
template_str = unicode(open(template_file, "r").read())
|
||||
self.template = pystache.parse(template_str)
|
||||
dest_file_template_str = unicode(dest_file_template_str)
|
||||
self.dest_file_template = pystache.parse(dest_file_template_str)
|
||||
self.overwrite = overwrite
|
||||
|
||||
def render(self, renderer, model, dest_dir):
|
||||
"""Render a model according to this transformation.
|
||||
|
||||
@param render: Pystache renderer.
|
||||
@param model: Model object to render.
|
||||
@param dest_dir: Destination directory to write generated code.
|
||||
"""
|
||||
dest_file = pystache.render(self.dest_file_template, model)
|
||||
dest_file = os.path.join(dest_dir, dest_file)
|
||||
if os.path.exists(dest_file) and not self.overwrite:
|
||||
return
|
||||
print "Rendering %s" % dest_file
|
||||
with open(dest_file, "w") as out:
|
||||
out.write(renderer.render(self.template, model))
|
|
@ -0,0 +1,9 @@
|
|||
<!-- Written in -*- Markdown -*- -->
|
||||
|
||||
This directory contains the specification for the Asterisk RESTful
|
||||
API. The API is documented using Swagger[1]. This is used to not only
|
||||
generate executable documentation pages for the API, but also to
|
||||
generate a lot of the boilerplate necessary for implementing the API
|
||||
with Asterisk's HTTP server.
|
||||
|
||||
[1]: http://swagger.wordnik.com/
|
|
@ -0,0 +1,47 @@
|
|||
{
|
||||
"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
|
||||
"_author": "David M. Lee, II <dlee@digium.com>",
|
||||
"_svn_revision": "$Revision$",
|
||||
"apiVersion": "0.0.1",
|
||||
"swaggerVersion": "1.1",
|
||||
"basePath": "http://localhost:8088/stasis",
|
||||
"resourcePath": "/api-docs/asterisk.{format}",
|
||||
"apis": [
|
||||
{
|
||||
"path": "/asterisk/info",
|
||||
"description": "Asterisk system information (similar to core show settings)",
|
||||
"operations": [
|
||||
{
|
||||
"httpMethod": "GET",
|
||||
"summary": "Gets Asterisk system information.",
|
||||
"nickname": "getAsteriskInfo",
|
||||
"responseClass": "AsteriskInfo",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "only",
|
||||
"description": "Filter information returned",
|
||||
"paramType": "query",
|
||||
"required": false,
|
||||
"allowMultiple": true,
|
||||
"dataType": "string",
|
||||
"allowableValues": {
|
||||
"valueType": "LIST",
|
||||
"values": [
|
||||
"version",
|
||||
"modules",
|
||||
"uptime"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"models": {
|
||||
"AsteriskInfo": {
|
||||
"id": "AsteriskInfo",
|
||||
"properties": {}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,257 @@
|
|||
{
|
||||
"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
|
||||
"_author": "David M. Lee, II <dlee@digium.com>",
|
||||
"_svn_revision": "$Revision$",
|
||||
"apiVersion": "0.0.1",
|
||||
"swaggerVersion": "1.1",
|
||||
"basePath": "http://localhost:8088/stasis",
|
||||
"resourcePath": "/api-docs/bridges.{format}",
|
||||
"apis": [
|
||||
{
|
||||
"path": "/bridges",
|
||||
"description": "Active bridges",
|
||||
"operations": [
|
||||
{
|
||||
"httpMethod": "GET",
|
||||
"summary": "List active bridges.",
|
||||
"nickname": "getBridges",
|
||||
"responseClass": "List[Bridge]"
|
||||
},
|
||||
{
|
||||
"httpMethod": "POST",
|
||||
"summary": "Create a new bridge.",
|
||||
"notes": "This bridge persists until it has been shut down, or Asterisk has been shut down.",
|
||||
"nickname": "newBridge",
|
||||
"responseClass": "Bridge",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "type",
|
||||
"description": "Type of bridge to create.",
|
||||
"paramType": "query",
|
||||
"required": false,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string",
|
||||
"allowedValues": {
|
||||
"type": "LIST",
|
||||
"values": [
|
||||
"two-party",
|
||||
"multi-party",
|
||||
"holding"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/bridges/{bridgeId}",
|
||||
"description": "Individual bridge",
|
||||
"operations": [
|
||||
{
|
||||
"httpMethod": "GET",
|
||||
"summary": "Get bridge details.",
|
||||
"nickname": "getBridge",
|
||||
"responseClass": "Bridge",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "bridgeId",
|
||||
"description": "Bridge's id",
|
||||
"paramType": "path",
|
||||
"required": true,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"httpMethod": "DELETE",
|
||||
"summary": "Shut down a bridge bridge.",
|
||||
"notes": "If any channels are in this bridge, they will be removed and resume whatever they were doing beforehand.",
|
||||
"nickname": "deleteBridge",
|
||||
"responseClass": "void",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "bridgeId",
|
||||
"description": "Bridge's id",
|
||||
"paramType": "path",
|
||||
"required": true,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/bridges/{bridgeId}/addChannel",
|
||||
"description": "Add a channel to a bridge",
|
||||
"operations": [
|
||||
{
|
||||
"httpMethod": "POST",
|
||||
"summary": "Add a channel to a bridge.",
|
||||
"nickname": "addChannelToBridge",
|
||||
"responseClass": "void",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "bridgeId",
|
||||
"description": "Bridge's id",
|
||||
"paramType": "path",
|
||||
"required": true,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
},
|
||||
{
|
||||
"name": "channel",
|
||||
"description": "Channel's id",
|
||||
"paramType": "query",
|
||||
"required": true,
|
||||
"allowMultiple": true,
|
||||
"dataType": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/bridges/{bridgeId}/removeChannel",
|
||||
"description": "Remove a channel from a bridge",
|
||||
"operations": [
|
||||
{
|
||||
"httpMethod": "POST",
|
||||
"summary": "Remove a channel from a bridge.",
|
||||
"nickname": "removeChannelFromBridge",
|
||||
"responseClass": "void",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "bridgeId",
|
||||
"description": "Bridge's id",
|
||||
"paramType": "path",
|
||||
"required": true,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
},
|
||||
{
|
||||
"name": "channel",
|
||||
"description": "Channel's id",
|
||||
"paramType": "query",
|
||||
"required": true,
|
||||
"allowMultiple": true,
|
||||
"dataType": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/bridges/{bridgeId}/record",
|
||||
"description": "Record audio on a bridge",
|
||||
"operations": [
|
||||
{
|
||||
"httpMethod": "POST",
|
||||
"summary": "Start a recording.",
|
||||
"notes": "This records the mixed audio from all channels participating in this bridge.",
|
||||
"nickname": "recordBridge",
|
||||
"responseClass": "LiveRecording",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "bridgeId",
|
||||
"description": "Bridge's id",
|
||||
"paramType": "path",
|
||||
"required": true,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"description": "Recording's filename",
|
||||
"paramType": "query",
|
||||
"required": true,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
},
|
||||
{
|
||||
"name": "maxDurationSeconds",
|
||||
"description": "Maximum duration of the recording, in seconds. 0 for no limit.",
|
||||
"paramType": "query",
|
||||
"required": false,
|
||||
"allowMultiple": false,
|
||||
"dataType": "int",
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"name": "maxSilenceSeconds",
|
||||
"description": "Maximum duration of silence, in seconds. 0 for no limit.",
|
||||
"paramType": "query",
|
||||
"required": false,
|
||||
"allowMultiple": false,
|
||||
"dataType": "int",
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"name": "append",
|
||||
"description": "If true, and recording already exists, append to recording.",
|
||||
"paramType": "query",
|
||||
"required": false,
|
||||
"allowMultiple": false,
|
||||
"dataType": "boolean",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"name": "beep",
|
||||
"description": "Play beep when recording begins",
|
||||
"paramType": "query",
|
||||
"required": false,
|
||||
"allowMultiple": false,
|
||||
"dataType": "boolean",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"name": "terminateOn",
|
||||
"description": "DTMF input to terminate recording.",
|
||||
"paramType": "query",
|
||||
"required": false,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string",
|
||||
"defaultValue": "none",
|
||||
"allowableValues": {
|
||||
"valueType": "LIST",
|
||||
"values": [
|
||||
"none",
|
||||
"any",
|
||||
"*",
|
||||
"#"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"models": {
|
||||
"Bridge": {
|
||||
"id": "Bridge",
|
||||
"properties": {
|
||||
"bridgeType": {
|
||||
"type": "string",
|
||||
"description": "Type of bridge technology",
|
||||
"required": true,
|
||||
"allowedValues": {
|
||||
"type": "LIST",
|
||||
"values": [
|
||||
"two-party",
|
||||
"multi-party",
|
||||
"holding"
|
||||
]
|
||||
}
|
||||
},
|
||||
"channels": {
|
||||
"type": "List[string]",
|
||||
"description": "Id's of channels participating in this bridge",
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,645 @@
|
|||
{
|
||||
"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
|
||||
"_author": "David M. Lee, II <dlee@digium.com>",
|
||||
"_svn_revision": "$Revision$",
|
||||
"apiVersion": "0.0.1",
|
||||
"swaggerVersion": "1.1",
|
||||
"basePath": "http://localhost:8088/stasis",
|
||||
"resourcePath": "/api-docs/channels.{format}",
|
||||
"apis": [
|
||||
{
|
||||
"path": "/channels",
|
||||
"description": "Active channels",
|
||||
"operations": [
|
||||
{
|
||||
"httpMethod": "GET",
|
||||
"summary": "List active channels.",
|
||||
"nickname": "getChannels",
|
||||
"responseClass": "List[Channel]"
|
||||
},
|
||||
{
|
||||
"httpMethod": "POST",
|
||||
"summary": "Create a new channel (originate).",
|
||||
"nickname": "originate",
|
||||
"responseClass": "Originated",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "endpoint",
|
||||
"description": "Endpoint to call. If not specified, originate is routed via dialplan",
|
||||
"paramType": "query",
|
||||
"required": false,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
},
|
||||
{
|
||||
"name": "extension",
|
||||
"description": "Extension to dial",
|
||||
"paramType": "query",
|
||||
"required": false,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
},
|
||||
{
|
||||
"name": "context",
|
||||
"description": "When routing via dialplan, the context use. If omitted, uses 'default'",
|
||||
"paramType": "query",
|
||||
"required": false,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/channels/{channelId}",
|
||||
"description": "Active channel",
|
||||
"operations": [
|
||||
{
|
||||
"httpMethod": "GET",
|
||||
"summary": "Channel details.",
|
||||
"nickname": "getChannel",
|
||||
"responseClass": "Channel",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "channelId",
|
||||
"description": "Channel's id",
|
||||
"paramType": "path",
|
||||
"required": true,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
}
|
||||
],
|
||||
"errorResponses": [
|
||||
{
|
||||
"code": 404,
|
||||
"reason": "Channel not found"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"httpMethod": "DELETE",
|
||||
"summary": "Delete (i.e. hangup) a channel.",
|
||||
"nickname": "deleteChannel",
|
||||
"responseClass": "void",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "channelId",
|
||||
"description": "Channel's id",
|
||||
"paramType": "path",
|
||||
"required": true,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
}
|
||||
],
|
||||
"errorResponses": [
|
||||
{
|
||||
"code": 404,
|
||||
"reason": "Channel not found"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/channels/{channelId}/dial",
|
||||
"description": "Create a new channel (originate) and bridge to this channel",
|
||||
"operations": [
|
||||
{
|
||||
"httpMethod": "POST",
|
||||
"summary": "Create a new channel (originate) and bridge to this channel.",
|
||||
"nickname": "dial",
|
||||
"responseClass": "Dialed",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "channelId",
|
||||
"description": "Channel's id",
|
||||
"paramType": "path",
|
||||
"required": true,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
},
|
||||
{
|
||||
"name": "endpoint",
|
||||
"description": "Endpoint to call. If not specified, dial is routed via dialplan",
|
||||
"paramType": "query",
|
||||
"required": false,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
},
|
||||
{
|
||||
"name": "extension",
|
||||
"description": "Extension to dial",
|
||||
"paramType": "query",
|
||||
"required": false,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
},
|
||||
{
|
||||
"name": "context",
|
||||
"description": "When routing via dialplan, the context use. If omitted, uses 'default'",
|
||||
"paramType": "query",
|
||||
"required": false,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
}
|
||||
],
|
||||
"errorResponses": [
|
||||
{
|
||||
"code": 404,
|
||||
"reason": "Channel not found"
|
||||
},
|
||||
{
|
||||
"code": 409,
|
||||
"reason": "Channel not in a Stasis application"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/channels/{channelId}/continue",
|
||||
"description": "Exit application; continue execution in the dialplan",
|
||||
"operations": [
|
||||
{
|
||||
"httpMethod": "POST",
|
||||
"summary": "Exit application; continue execution in the dialplan.",
|
||||
"nickname": "continueInDialplan",
|
||||
"responseClass": "void",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "channelId",
|
||||
"description": "Channel's id",
|
||||
"paramType": "path",
|
||||
"required": true,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
}
|
||||
],
|
||||
"errorResponses": [
|
||||
{
|
||||
"code": 404,
|
||||
"reason": "Channel not found"
|
||||
},
|
||||
{
|
||||
"code": 409,
|
||||
"reason": "Channel not in a Stasis application"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/channels/{channelId}/answer",
|
||||
"description": "Answer a channel",
|
||||
"operations": [
|
||||
{
|
||||
"httpMethod": "POST",
|
||||
"summary": "Answer a channel.",
|
||||
"nickname": "answerChannel",
|
||||
"responseClass": "void",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "channelId",
|
||||
"description": "Channel's id",
|
||||
"paramType": "path",
|
||||
"required": true,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
}
|
||||
],
|
||||
"errorResponses": [
|
||||
{
|
||||
"code": 404,
|
||||
"reason": "Channel not found"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/channels/{channelId}/mute",
|
||||
"description": "Mute a channel",
|
||||
"operations": [
|
||||
{
|
||||
"httpMethod": "POST",
|
||||
"summary": "Mute a channel.",
|
||||
"nickname": "muteChannel",
|
||||
"responseClass": "void",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "channelId",
|
||||
"description": "Channel's id",
|
||||
"paramType": "path",
|
||||
"required": true,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
},
|
||||
{
|
||||
"name": "direction",
|
||||
"description": "Direction in which to mute audio",
|
||||
"paramType": "query",
|
||||
"required": false,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string",
|
||||
"defaultValue": "both",
|
||||
"allowableValues": {
|
||||
"valueType": "LIST",
|
||||
"values": [
|
||||
"both",
|
||||
"in",
|
||||
"out"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"errorResponses": [
|
||||
{
|
||||
"code": 404,
|
||||
"reason": "Channel not found"
|
||||
},
|
||||
{
|
||||
"code": 409,
|
||||
"reason": "Channel not in a Stasis application"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/channels/{channelId}/unmute",
|
||||
"description": "Unmute a channel",
|
||||
"operations": [
|
||||
{
|
||||
"httpMethod": "POST",
|
||||
"summary": "Unmute a channel.",
|
||||
"nickname": "unmuteChannel",
|
||||
"responseClass": "void",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "channelId",
|
||||
"description": "Channel's id",
|
||||
"paramType": "path",
|
||||
"required": true,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
},
|
||||
{
|
||||
"name": "direction",
|
||||
"description": "Direction in which to unmute audio",
|
||||
"paramType": "query",
|
||||
"required": false,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string",
|
||||
"defaultValue": "both",
|
||||
"allowableValues": {
|
||||
"valueType": "LIST",
|
||||
"values": [
|
||||
"both",
|
||||
"in",
|
||||
"out"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"errorResponses": [
|
||||
{
|
||||
"code": 404,
|
||||
"reason": "Channel not found"
|
||||
},
|
||||
{
|
||||
"code": 409,
|
||||
"reason": "Channel not in a Stasis application"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/channels/{channelId}/hold",
|
||||
"description": "Put a channel on hold",
|
||||
"operations": [
|
||||
{
|
||||
"httpMethod": "POST",
|
||||
"summary": "Hold a channel.",
|
||||
"nickname": "holdChannel",
|
||||
"responseClass": "void",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "channelId",
|
||||
"description": "Channel's id",
|
||||
"paramType": "path",
|
||||
"required": true,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
}
|
||||
],
|
||||
"errorResponses": [
|
||||
{
|
||||
"code": 404,
|
||||
"reason": "Channel not found"
|
||||
},
|
||||
{
|
||||
"code": 409,
|
||||
"reason": "Channel not in a Stasis application"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/channels/{channelId}/unhold",
|
||||
"description": "Remove a channel from hold",
|
||||
"operations": [
|
||||
{
|
||||
"httpMethod": "POST",
|
||||
"summary": "Remove a channel from hold.",
|
||||
"nickname": "unholdChannel",
|
||||
"responseClass": "void",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "channelId",
|
||||
"description": "Channel's id",
|
||||
"paramType": "path",
|
||||
"required": true,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
}
|
||||
],
|
||||
"errorResponses": [
|
||||
{
|
||||
"code": 404,
|
||||
"reason": "Channel not found"
|
||||
},
|
||||
{
|
||||
"code": 409,
|
||||
"reason": "Channel not in a Stasis application"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/channels/{channelId}/play",
|
||||
"description": "Play media to a channel",
|
||||
"operations": [
|
||||
{
|
||||
"httpMethod": "POST",
|
||||
"summary": "Start playback of media.",
|
||||
"notes": "The media URI may be any of a number of URI's. You may use http: and https: URI's, as well as sound: and recording: URI's. This operation creates a playback resource that can be used to control the playback of media (pause, rewind, fast forward, etc.)",
|
||||
"nickname": "playOnChannel",
|
||||
"responseClass": "Playback",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "channelId",
|
||||
"description": "Channel's id",
|
||||
"paramType": "path",
|
||||
"required": true,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
},
|
||||
{
|
||||
"name": "media",
|
||||
"description": "Media's URI to play.",
|
||||
"paramType": "query",
|
||||
"required": true,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
}
|
||||
],
|
||||
"errorResponses": [
|
||||
{
|
||||
"code": 404,
|
||||
"reason": "Channel not found"
|
||||
},
|
||||
{
|
||||
"code": 409,
|
||||
"reason": "Channel not in a Stasis application"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/channels/{channelId}/record",
|
||||
"description": "Record audio from a channel",
|
||||
"operations": [
|
||||
{
|
||||
"httpMethod": "POST",
|
||||
"summary": "Start a recording.",
|
||||
"notes": "Record audio from a channel. Note that this will not capture audio sent to the channel. The bridge itself has a record feature if that's what you want.",
|
||||
"nickname": "recordChannel",
|
||||
"responseClass": "void",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "channelId",
|
||||
"description": "Channel's id",
|
||||
"paramType": "path",
|
||||
"required": true,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"description": "Recording's filename",
|
||||
"paramType": "query",
|
||||
"required": true,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
},
|
||||
{
|
||||
"name": "format",
|
||||
"description": "Format to encode audio in",
|
||||
"paramType": "query",
|
||||
"required": true,
|
||||
"allowMultiple": true,
|
||||
"dataType": "string"
|
||||
},
|
||||
{
|
||||
"name": "maxDurationSeconds",
|
||||
"description": "Maximum duration of the recording, in seconds. 0 for no limit",
|
||||
"paramType": "query",
|
||||
"required": false,
|
||||
"allowMultiple": false,
|
||||
"dataType": "int",
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"name": "maxSilenceSeconds",
|
||||
"description": "Maximum duration of silence, in seconds. 0 for no limit",
|
||||
"paramType": "query",
|
||||
"required": false,
|
||||
"allowMultiple": false,
|
||||
"dataType": "int",
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"name": "append",
|
||||
"description": "If true, and recording already exists, append to recording",
|
||||
"paramType": "query",
|
||||
"required": false,
|
||||
"allowMultiple": false,
|
||||
"dataType": "boolean",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"name": "beep",
|
||||
"description": "Play beep when recording begins",
|
||||
"paramType": "query",
|
||||
"required": false,
|
||||
"allowMultiple": false,
|
||||
"dataType": "boolean",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"name": "terminateOn",
|
||||
"description": "DTMF input to terminate recording",
|
||||
"paramType": "query",
|
||||
"required": false,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string",
|
||||
"defaultValue": "none",
|
||||
"allowableValues": {
|
||||
"valueType": "LIST",
|
||||
"values": [
|
||||
"none",
|
||||
"any",
|
||||
"*",
|
||||
"#"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"errorResponses": [
|
||||
{
|
||||
"code": 404,
|
||||
"reason": "Channel not found"
|
||||
},
|
||||
{
|
||||
"code": 409,
|
||||
"reason": "Channel is not in a Stasis application."
|
||||
},
|
||||
{
|
||||
"code": 409,
|
||||
"reason": "The channel is currently bridges with other channels."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"models": {
|
||||
"Originated": {
|
||||
"id": "Originated",
|
||||
"properties": {}
|
||||
},
|
||||
"Dialed": {
|
||||
"id": "Dialed",
|
||||
"properties": {}
|
||||
},
|
||||
"DialplanCEP": {
|
||||
"id": "DialplanCEP",
|
||||
"properties": {
|
||||
"context": {
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"description": "Context in the dialplan"
|
||||
},
|
||||
"exten": {
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"description": "Extension in the dialplan"
|
||||
},
|
||||
"priority": {
|
||||
"required": true,
|
||||
"type": "long",
|
||||
"description": "Priority in the dialplan"
|
||||
}
|
||||
}
|
||||
},
|
||||
"CallerID": {
|
||||
"id": "CallerID",
|
||||
"properties": {
|
||||
"name": {
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
"number": {
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Channel": {
|
||||
"id": "Channel",
|
||||
"properties": {
|
||||
"uniqueid": {
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"description": "Unique identifier of the channel"
|
||||
},
|
||||
"name": {
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"description": "Name of the channel (i.e. SIP/foo-0000a7e3)"
|
||||
},
|
||||
"state": {
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
"accountcode": {
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
"peeraccount": {
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
"userfield": {
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
"linkedid": {
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
"parkinglot": {
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
"hangupsource": {
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
"appl": {
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"description": "Currently executing dialplan application"
|
||||
},
|
||||
"data": {
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"description": "Arguments passed to appl"
|
||||
},
|
||||
"dialplan": {
|
||||
"required": true,
|
||||
"type": "DialplanCEP",
|
||||
"description": "Current location in the dialplan"
|
||||
},
|
||||
"caller": {
|
||||
"required": true,
|
||||
"type": "CallerID"
|
||||
},
|
||||
"connected": {
|
||||
"required": true,
|
||||
"type": "CallerID"
|
||||
},
|
||||
"creationtime": {
|
||||
"required": true,
|
||||
"type": "Date",
|
||||
"description": "Timestamp when channel was created"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
{
|
||||
"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
|
||||
"_author": "David M. Lee, II <dlee@digium.com>",
|
||||
"_svn_revision": "$Revision$",
|
||||
"apiVersion": "0.0.1",
|
||||
"swaggerVersion": "1.1",
|
||||
"basePath": "http://localhost:8088/stasis",
|
||||
"resourcePath": "/api-docs/endpoints.{format}",
|
||||
"apis": [
|
||||
{
|
||||
"path": "/endpoints",
|
||||
"description": "Asterisk endpoints",
|
||||
"operations": [
|
||||
{
|
||||
"httpMethod": "GET",
|
||||
"summary": "List available endoints.",
|
||||
"nickname": "getEndpoints",
|
||||
"responseClass": "List[Endpoint]",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "withType",
|
||||
"description": "Filter endpoints by type (sip,iax2,dhadi,...)",
|
||||
"paramType": "query",
|
||||
"required": false,
|
||||
"allowMultiple": true,
|
||||
"dataType": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/endpoints/{endpointId}",
|
||||
"description": "Single endpoint",
|
||||
"operations": [
|
||||
{
|
||||
"httpMethod": "GET",
|
||||
"summary": "Details for an endpoint.",
|
||||
"nickname": "getEndpoint",
|
||||
"responseClass": "Endpoint",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "endpointId",
|
||||
"description": "ID of the endpoint",
|
||||
"paramType": "path",
|
||||
"dataType": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"models": {
|
||||
"Endpoint": {
|
||||
"id": "Endpoint",
|
||||
"properties": {
|
||||
"technology": {
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
{
|
||||
"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
|
||||
"_author": "David M. Lee, II <dlee@digium.com>",
|
||||
"_svn_revision": "$Revision$",
|
||||
"apiVersion": "0.0.1",
|
||||
"swaggerVersion": "1.1",
|
||||
"basePath": "http://localhost:8088/stasis",
|
||||
"resourcePath": "/api-docs/events.{format}",
|
||||
"apis": [
|
||||
{
|
||||
"path": "/events",
|
||||
"description": "Events from Asterisk to applications",
|
||||
"operations": [
|
||||
{
|
||||
"httpMethod": "GET",
|
||||
"summary": "WebSocket connection for events.",
|
||||
"nickname": "eventWebsocket",
|
||||
"responseClass": "Event",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "app",
|
||||
"description": "Comma seperated list of applications to subscribe to.",
|
||||
"paramType": "query",
|
||||
"required": true,
|
||||
"allowMultiple": true,
|
||||
"dataType": "string"
|
||||
},
|
||||
{
|
||||
"name": "Upgrade",
|
||||
"description": "RFC6455 header for upgrading a connection to a websocket.",
|
||||
"paramType": "header",
|
||||
"required": true,
|
||||
"dataType": "string",
|
||||
"allowableValues": {
|
||||
"valueType": "LIST",
|
||||
"values": [
|
||||
"websocket"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"models": {
|
||||
"Event": {
|
||||
"id": "Event",
|
||||
"description": "Asynchronous events from Asterisk. The non-required fields of this object are mutually exclusive.",
|
||||
"properties": {
|
||||
"application": {
|
||||
"type": "string",
|
||||
"description": "Name of the application receiving the event.",
|
||||
"required": true
|
||||
},
|
||||
"application_replaced": { "type": "ApplicationReplaced" },
|
||||
"bridge_created": { "type": "BridgeCreated" },
|
||||
"bridge_destroyed": { "type": "BridgeDestroyed" },
|
||||
"channel_entered_bridge": { "type": "ChannelEnteredBridge" },
|
||||
"channel_left_bridge": { "type": "ChannelLeftBridge" },
|
||||
"channel_state_change": { "type": "ChannelStateChange" },
|
||||
"dtmf_received": { "type": "DtmfReceived" },
|
||||
"stasis_end": { "type": "StasisEnd" },
|
||||
"stasis_start": { "type": "StasisStart" }
|
||||
}
|
||||
},
|
||||
"ApplicationReplaced": {
|
||||
"id": "ApplicationReplaced",
|
||||
"description": "Notification that another WebSocket has taken over for an application.",
|
||||
"notes": "An application may only be subscribed to by a single WebSocket at a time. If multiple WebSockets attempt to subscribe to the same application, the newer WebSocket wins, and the older one receives this event.",
|
||||
"properties": {
|
||||
"application": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"BridgeCreated": {
|
||||
"id": "BridgeCreated",
|
||||
"description": "Notification that a bridge has been created.",
|
||||
"properties": {
|
||||
"bridge": {
|
||||
"type": "Bridge"
|
||||
}
|
||||
}
|
||||
},
|
||||
"BridgeDestroyed": {
|
||||
"id": "BridgeDestroyed",
|
||||
"description": "Notification that a bridge has been destroyed.",
|
||||
"properties": {
|
||||
"bridge": {
|
||||
"type": "Bridge"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ChannelEnteredBridge": {
|
||||
"id": "ChannelEnteredBridge",
|
||||
"description": "Notification that a channel has entered a bridge.",
|
||||
"properties": {
|
||||
"bridge": {
|
||||
"type": "Bridge"
|
||||
},
|
||||
"channel": {
|
||||
"type": "Channel"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ChannelLeftBridge": {
|
||||
"id": "ChannelLeftBridge",
|
||||
"description": "Notification that a channel has left a bridge.",
|
||||
"properties": {
|
||||
"bridge": {
|
||||
"type": "Bridge"
|
||||
},
|
||||
"channel": {
|
||||
"type": "Channel"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ChannelStateChange": {
|
||||
"id": "ChannelStateChange",
|
||||
"description": "Notification of a channel's state change.",
|
||||
"properties": {
|
||||
"channel_info": {
|
||||
"type": "Channel"
|
||||
}
|
||||
}
|
||||
},
|
||||
"DtmfReceived": {
|
||||
"id": "DtmfReceived",
|
||||
"description": "DTMF received on a channel.",
|
||||
"notes": "This event is sent when the DTMF ends. There is no notification about the start of DTMF",
|
||||
"properties": {
|
||||
"digit": {
|
||||
"type": "string",
|
||||
"description": "DTMF digit received (0-9, A-E, # or *)"
|
||||
},
|
||||
"channel": {
|
||||
"type": "Channel",
|
||||
"description": "The channel on which DTMF was received"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StasisEnd": {
|
||||
"id": "StasisEnd",
|
||||
"description": "Notification that a channel has left a Stasis appliction.",
|
||||
"properties": {
|
||||
"channel_info": {
|
||||
"type": "Channel"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StasisStart": {
|
||||
"id": "StasisStart",
|
||||
"description": "Notification that a channel has entered a Stasis appliction.",
|
||||
"properties": {
|
||||
"args": {
|
||||
"type": "List[string]",
|
||||
"description": "Arguments to the application"
|
||||
},
|
||||
"channel_info": {
|
||||
"type": "Channel"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
{
|
||||
"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
|
||||
"_author": "David M. Lee, II <dlee@digium.com>",
|
||||
"_svn_revision": "$Revision$",
|
||||
"apiVersion": "0.0.1",
|
||||
"swaggerVersion": "1.1",
|
||||
"basePath": "http://localhost:8088/stasis",
|
||||
"resourcePath": "/api-docs/playback.{format}",
|
||||
"apis": [
|
||||
{
|
||||
"path": "/playback/{playbackId}",
|
||||
"description": "Control object for a playback operation.",
|
||||
"operations": [
|
||||
{
|
||||
"httpMethod": "GET",
|
||||
"summary": "Get a playback's details.",
|
||||
"nickname": "getPlayback",
|
||||
"responseClass": "Playback",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "playbackId",
|
||||
"description": "Playback's id",
|
||||
"paramType": "path",
|
||||
"required": true,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"httpMethod": "DELETE",
|
||||
"summary": "Stop a playback.",
|
||||
"nickname": "stopPlayback",
|
||||
"responseClass": "Playback",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "playbackId",
|
||||
"description": "Playback's id",
|
||||
"paramType": "path",
|
||||
"required": true,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/playback/{playbackId}/control",
|
||||
"description": "Control object for a playback operation.",
|
||||
"operations": [
|
||||
{
|
||||
"httpMethod": "POST",
|
||||
"summary": "Get a playback's details.",
|
||||
"nickname": "controlPlayback",
|
||||
"responseClass": "Playback",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "playbackId",
|
||||
"description": "Playback's id",
|
||||
"paramType": "path",
|
||||
"required": true,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
},
|
||||
{
|
||||
"name": "operation",
|
||||
"description": "Operation to perform on the playback.",
|
||||
"paramType": "query",
|
||||
"required": true,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string",
|
||||
"allowableValues": {
|
||||
"valueType": "LIST",
|
||||
"values": [
|
||||
"play",
|
||||
"pause",
|
||||
"rewind",
|
||||
"fast-forward",
|
||||
"speed-up",
|
||||
"slow-down"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"models": {
|
||||
"Playback": {
|
||||
"id": "Playback",
|
||||
"properties": {
|
||||
"id": {
|
||||
"required": true,
|
||||
"description": "Playback's identifier.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,270 @@
|
|||
{
|
||||
"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
|
||||
"_author": "David M. Lee, II <dlee@digium.com>",
|
||||
"_svn_revision": "$Revision$",
|
||||
"apiVersion": "0.0.1",
|
||||
"swaggerVersion": "1.1",
|
||||
"basePath": "http://localhost:8088/stasis",
|
||||
"resourcePath": "/api-docs/recordings.{format}",
|
||||
"apis": [
|
||||
{
|
||||
"path": "/recordings",
|
||||
"description": "Recordings",
|
||||
"operations": [
|
||||
{
|
||||
"httpMethod": "GET",
|
||||
"summary": "List all recordings.",
|
||||
"nickname": "getRecordings",
|
||||
"responseClass": "List[Recording]"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/recordings/stored",
|
||||
"description": "Recordings",
|
||||
"operations": [
|
||||
{
|
||||
"httpMethod": "GET",
|
||||
"summary": "List recordings that are complete.",
|
||||
"nickname": "getStoredRecordings",
|
||||
"responseClass": "List[StoredRecording]"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/recordings/stored/{recordingId}",
|
||||
"description": "Individual recording",
|
||||
"operations": [
|
||||
{
|
||||
"httpMethod": "GET",
|
||||
"summary": "Get a stored recording's details.",
|
||||
"nickname": "getStoredRecording",
|
||||
"responseClass": "StoredRecording",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "recordingId",
|
||||
"description": "Recording's id",
|
||||
"paramType": "path",
|
||||
"required": true,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"httpMethod": "DELETE",
|
||||
"summary": "Delete a stored recording.",
|
||||
"nickname": "deleteStoredRecording",
|
||||
"responseClass": "void",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "recordingId",
|
||||
"description": "Recording's id",
|
||||
"paramType": "path",
|
||||
"required": true,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/recordings/live",
|
||||
"description": "Recordings that are in progress",
|
||||
"operations": [
|
||||
{
|
||||
"httpMethod": "GET",
|
||||
"summary": "List libe recordings.",
|
||||
"nickname": "getLiveRecordings",
|
||||
"responseClass": "List[LiveRecording]"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/recordings/live/{recordingId}",
|
||||
"description": "A recording that is in progress",
|
||||
"operations": [
|
||||
{
|
||||
"httpMethod": "GET",
|
||||
"summary": "List live recordings.",
|
||||
"nickname": "getLiveRecording",
|
||||
"responseClass": "LiveRecording",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "recordingId",
|
||||
"description": "Recording's id",
|
||||
"paramType": "path",
|
||||
"required": true,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"httpMethod": "DELETE",
|
||||
"summary": "Stop a live recording and discard it.",
|
||||
"nickname": "cancelRecording",
|
||||
"responseClass": "void",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "recordingId",
|
||||
"description": "Recording's id",
|
||||
"paramType": "path",
|
||||
"required": true,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/recordings/live/{recordingId}/stop",
|
||||
"operations": [
|
||||
{
|
||||
"httpMethod": "POST",
|
||||
"summary": "Stop a live recording and store it.",
|
||||
"nickname": "stopRecording",
|
||||
"responseClass": "void",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "recordingId",
|
||||
"description": "Recording's id",
|
||||
"paramType": "path",
|
||||
"required": true,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/recordings/live/{recordingId}/pause",
|
||||
"operations": [
|
||||
{
|
||||
"httpMethod": "POST",
|
||||
"summary": "Pause a live recording.",
|
||||
"nickname": "pauseRecording",
|
||||
"responseClass": "void",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "recordingId",
|
||||
"description": "Recording's id",
|
||||
"paramType": "path",
|
||||
"required": true,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/recordings/live/{recordingId}/unpause",
|
||||
"operations": [
|
||||
{
|
||||
"httpMethod": "POST",
|
||||
"summary": "Unpause a live recording.",
|
||||
"nickname": "unpauseRecording",
|
||||
"responseClass": "void",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "recordingId",
|
||||
"description": "Recording's id",
|
||||
"paramType": "path",
|
||||
"required": true,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/recordings/live/{recordingId}/mute",
|
||||
"operations": [
|
||||
{
|
||||
"httpMethod": "POST",
|
||||
"summary": "Mute a live recording.",
|
||||
"nickname": "muteRecording",
|
||||
"responseClass": "void",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "recordingId",
|
||||
"description": "Recording's id",
|
||||
"paramType": "path",
|
||||
"required": true,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/recordings/live/{recordingId}/unmute",
|
||||
"operations": [
|
||||
{
|
||||
"httpMethod": "POST",
|
||||
"summary": "Unmute a live recording.",
|
||||
"nickname": "unmuteRecording",
|
||||
"responseClass": "void",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "recordingId",
|
||||
"description": "Recording's id",
|
||||
"paramType": "path",
|
||||
"required": true,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"models": {
|
||||
"Recording": {
|
||||
"id": "Recording",
|
||||
"properties": {
|
||||
"id": {
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StoredRecording": {
|
||||
"id": "StoredRecording",
|
||||
"properties": {
|
||||
"id": {
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
"formats": {
|
||||
"required": true,
|
||||
"type": "List[string]"
|
||||
},
|
||||
"durationSeconds": {
|
||||
"required": false,
|
||||
"type": "int"
|
||||
},
|
||||
"time": {
|
||||
"description": "Time recording was started",
|
||||
"required": false,
|
||||
"type": "Date"
|
||||
}
|
||||
}
|
||||
},
|
||||
"LiveRecording": {
|
||||
"id": "LiveRecording",
|
||||
"properties": {
|
||||
"id": {
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
{
|
||||
"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
|
||||
"_author": "David M. Lee, II <dlee@digium.com>",
|
||||
"_svn_revision": "$Revision$",
|
||||
"apiVersion": "0.0.1",
|
||||
"swaggerVersion": "1.1",
|
||||
"basePath": "http://localhost:8088/stasis",
|
||||
"resourcePath": "/api-docs/sounds.{format}",
|
||||
"apis": [
|
||||
{
|
||||
"path": "/sounds",
|
||||
"description": "Sounds",
|
||||
"operations": [
|
||||
{
|
||||
"httpMethod": "GET",
|
||||
"summary": "List all sounds.",
|
||||
"nickname": "getSounds",
|
||||
"responseClass": "List[Sound]",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "lang",
|
||||
"paramType": "query",
|
||||
"dataType": "string",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"name": "format",
|
||||
"paramType": "query",
|
||||
"dataType": "string",
|
||||
"required": false,
|
||||
"__note": "core show translation can show translation paths between formats, along with relative costs. so this could be just installed format, or we could follow that for transcoded formats."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "/sounds/{soundId}",
|
||||
"description": "Individual sound",
|
||||
"operations": [
|
||||
{
|
||||
"httpMethod": "GET",
|
||||
"summary": "Get a sound's details.",
|
||||
"nickname": "getStoredSound",
|
||||
"responseClass": "Sound",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "soundId",
|
||||
"description": "Sound's id",
|
||||
"paramType": "path",
|
||||
"required": true,
|
||||
"allowMultiple": false,
|
||||
"dataType": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"models": {
|
||||
"Sound": {
|
||||
"id": "Sound",
|
||||
"properties": {
|
||||
"id": {
|
||||
"required": true,
|
||||
"description": "Sound's identifier.",
|
||||
"type": "string"
|
||||
},
|
||||
"text": {
|
||||
"required": false,
|
||||
"description": "Text description of the sound, usually the words spoken.",
|
||||
"type": "string"
|
||||
},
|
||||
"lang": {
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
"formats": {
|
||||
"required": true,
|
||||
"type": "List[string]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
|
||||
"_author": "David M. Lee, II <dlee@digium.com>",
|
||||
"_svn_revision": "$Revision$",
|
||||
"apiVersion": "0.0.1",
|
||||
"swaggerVersion": "1.1",
|
||||
"basePath": "http://localhost:8088/stasis",
|
||||
"apis": [
|
||||
{
|
||||
"path": "/api-docs/asterisk.{format}",
|
||||
"description": "Asterisk resources"
|
||||
},
|
||||
{
|
||||
"path": "/api-docs/endpoints.{format}",
|
||||
"description": "Endpoint resources"
|
||||
},
|
||||
{
|
||||
"path": "/api-docs/channels.{format}",
|
||||
"description": "Channel resources"
|
||||
},
|
||||
{
|
||||
"path": "/api-docs/bridges.{format}",
|
||||
"description": "Bridge resources"
|
||||
},
|
||||
{
|
||||
"path": "/api-docs/recordings.{format}",
|
||||
"description": "Recording resources"
|
||||
},
|
||||
{
|
||||
"path": "/api-docs/sounds.{format}",
|
||||
"description": "Sound resources"
|
||||
},
|
||||
{
|
||||
"path": "/api-docs/playback.{format}",
|
||||
"description": "Playback control resources"
|
||||
},
|
||||
{
|
||||
"path": "/api-docs/events.{format}",
|
||||
"description": "WebSocket resource"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -645,7 +645,6 @@ AST_TEST_DEFINE(cache)
|
|||
RAII_VAR(struct stasis_message *, test_message1_clear, NULL, ao2_cleanup);
|
||||
int actual_len;
|
||||
struct stasis_cache_update *actual_update;
|
||||
struct ao2_container *cache_dump;
|
||||
|
||||
switch (cmd) {
|
||||
case TEST_INIT:
|
||||
|
@ -681,12 +680,6 @@ AST_TEST_DEFINE(cache)
|
|||
actual_len = consumer_wait_for(consumer, 2);
|
||||
ast_test_validate(test, 2 == actual_len);
|
||||
|
||||
/* Dump the cache to ensure that it has the correct number of items in it */
|
||||
cache_dump = stasis_cache_dump(caching_topic, NULL);
|
||||
ast_test_validate(test, 2 == ao2_container_count(cache_dump));
|
||||
ao2_ref(cache_dump, -1);
|
||||
cache_dump = NULL;
|
||||
|
||||
/* Check for new snapshot messages */
|
||||
ast_test_validate(test, stasis_cache_update_type() == stasis_message_type(consumer->messages_rxed[0]));
|
||||
actual_update = stasis_message_data(consumer->messages_rxed[0]);
|
||||
|
@ -722,12 +715,6 @@ AST_TEST_DEFINE(cache)
|
|||
/* stasis_cache_get returned a ref, so unref test_message2_2 */
|
||||
ao2_ref(test_message2_2, -1);
|
||||
|
||||
/* Dump the cache to ensure that it has the correct number of items in it */
|
||||
cache_dump = stasis_cache_dump(caching_topic, NULL);
|
||||
ast_test_validate(test, 2 == ao2_container_count(cache_dump));
|
||||
ao2_ref(cache_dump, -1);
|
||||
cache_dump = NULL;
|
||||
|
||||
/* Clear snapshot 1 */
|
||||
test_message1_clear = stasis_cache_clear_create(cache_type, "1");
|
||||
ast_test_validate(test, NULL != test_message1_clear);
|
||||
|
@ -742,17 +729,109 @@ AST_TEST_DEFINE(cache)
|
|||
ast_test_validate(test, NULL == actual_update->new_snapshot);
|
||||
ast_test_validate(test, NULL == stasis_cache_get(caching_topic, cache_type, "1"));
|
||||
|
||||
/* Dump the cache to ensure that it has the correct number of items in it */
|
||||
return AST_TEST_PASS;
|
||||
}
|
||||
|
||||
AST_TEST_DEFINE(cache_dump)
|
||||
{
|
||||
RAII_VAR(struct stasis_message_type *, cache_type, NULL, ao2_cleanup);
|
||||
RAII_VAR(struct stasis_topic *, topic, NULL, ao2_cleanup);
|
||||
RAII_VAR(struct stasis_caching_topic *, caching_topic, NULL, stasis_caching_unsubscribe);
|
||||
RAII_VAR(struct consumer *, consumer, NULL, ao2_cleanup);
|
||||
RAII_VAR(struct stasis_subscription *, sub, NULL, stasis_unsubscribe);
|
||||
RAII_VAR(struct stasis_message *, test_message1_1, NULL, ao2_cleanup);
|
||||
RAII_VAR(struct stasis_message *, test_message2_1, NULL, ao2_cleanup);
|
||||
RAII_VAR(struct stasis_message *, test_message2_2, NULL, ao2_cleanup);
|
||||
RAII_VAR(struct stasis_message *, test_message1_clear, NULL, ao2_cleanup);
|
||||
RAII_VAR(struct ao2_container *, cache_dump, NULL, ao2_cleanup);
|
||||
int actual_len;
|
||||
struct ao2_iterator i;
|
||||
void *obj;
|
||||
|
||||
switch (cmd) {
|
||||
case TEST_INIT:
|
||||
info->name = __func__;
|
||||
info->category = test_category;
|
||||
info->summary = "Test passing messages through cache topic unscathed.";
|
||||
info->description = "Test passing messages through cache topic unscathed.";
|
||||
return AST_TEST_NOT_RUN;
|
||||
case TEST_EXECUTE:
|
||||
break;
|
||||
}
|
||||
|
||||
cache_type = stasis_message_type_create("Cacheable");
|
||||
ast_test_validate(test, NULL != cache_type);
|
||||
topic = stasis_topic_create("SomeTopic");
|
||||
ast_test_validate(test, NULL != topic);
|
||||
caching_topic = stasis_caching_topic_create(topic, cache_test_data_id);
|
||||
ast_test_validate(test, NULL != caching_topic);
|
||||
consumer = consumer_create(1);
|
||||
ast_test_validate(test, NULL != consumer);
|
||||
sub = stasis_subscribe(stasis_caching_get_topic(caching_topic), consumer_exec, consumer);
|
||||
ast_test_validate(test, NULL != sub);
|
||||
ao2_ref(consumer, +1);
|
||||
|
||||
test_message1_1 = cache_test_message_create(cache_type, "1", "1");
|
||||
ast_test_validate(test, NULL != test_message1_1);
|
||||
test_message2_1 = cache_test_message_create(cache_type, "2", "1");
|
||||
ast_test_validate(test, NULL != test_message2_1);
|
||||
|
||||
/* Post a couple of snapshots */
|
||||
stasis_publish(topic, test_message1_1);
|
||||
stasis_publish(topic, test_message2_1);
|
||||
actual_len = consumer_wait_for(consumer, 2);
|
||||
ast_test_validate(test, 2 == actual_len);
|
||||
|
||||
/* Check the cache */
|
||||
cache_dump = stasis_cache_dump(caching_topic, NULL);
|
||||
ast_test_validate(test, NULL != cache_dump);
|
||||
ast_test_validate(test, 2 == ao2_container_count(cache_dump));
|
||||
i = ao2_iterator_init(cache_dump, 0);
|
||||
while ((obj = ao2_iterator_next(&i))) {
|
||||
RAII_VAR(struct stasis_message *, actual_cache_entry, obj, ao2_cleanup);
|
||||
ast_test_validate(test, actual_cache_entry == test_message1_1 || actual_cache_entry == test_message2_1);
|
||||
}
|
||||
|
||||
/* Update snapshot 2 */
|
||||
test_message2_2 = cache_test_message_create(cache_type, "2", "2");
|
||||
ast_test_validate(test, NULL != test_message2_2);
|
||||
stasis_publish(topic, test_message2_2);
|
||||
|
||||
actual_len = consumer_wait_for(consumer, 3);
|
||||
ast_test_validate(test, 3 == actual_len);
|
||||
|
||||
/* Check the cache */
|
||||
cache_dump = stasis_cache_dump(caching_topic, NULL);
|
||||
ast_test_validate(test, NULL != cache_dump);
|
||||
ast_test_validate(test, 2 == ao2_container_count(cache_dump));
|
||||
i = ao2_iterator_init(cache_dump, 0);
|
||||
while ((obj = ao2_iterator_next(&i))) {
|
||||
RAII_VAR(struct stasis_message *, actual_cache_entry, obj, ao2_cleanup);
|
||||
ast_test_validate(test, actual_cache_entry == test_message1_1 || actual_cache_entry == test_message2_2);
|
||||
}
|
||||
|
||||
/* Clear snapshot 1 */
|
||||
test_message1_clear = stasis_cache_clear_create(cache_type, "1");
|
||||
ast_test_validate(test, NULL != test_message1_clear);
|
||||
stasis_publish(topic, test_message1_clear);
|
||||
|
||||
actual_len = consumer_wait_for(consumer, 4);
|
||||
ast_test_validate(test, 4 == actual_len);
|
||||
|
||||
/* Check the cache */
|
||||
cache_dump = stasis_cache_dump(caching_topic, NULL);
|
||||
ast_test_validate(test, NULL != cache_dump);
|
||||
ast_test_validate(test, 1 == ao2_container_count(cache_dump));
|
||||
ao2_ref(cache_dump, -1);
|
||||
cache_dump = NULL;
|
||||
i = ao2_iterator_init(cache_dump, 0);
|
||||
while ((obj = ao2_iterator_next(&i))) {
|
||||
RAII_VAR(struct stasis_message *, actual_cache_entry, obj, ao2_cleanup);
|
||||
ast_test_validate(test, actual_cache_entry == test_message2_2);
|
||||
}
|
||||
|
||||
/* Dump the cache to ensure that it has no subscription change items in it since those aren't cached */
|
||||
ao2_cleanup(cache_dump);
|
||||
cache_dump = stasis_cache_dump(caching_topic, stasis_subscription_change_type());
|
||||
ast_test_validate(test, 0 == ao2_container_count(cache_dump));
|
||||
ao2_ref(cache_dump, -1);
|
||||
cache_dump = NULL;
|
||||
|
||||
return AST_TEST_PASS;
|
||||
}
|
||||
|
@ -909,6 +988,7 @@ static int unload_module(void)
|
|||
AST_TEST_UNREGISTER(forward);
|
||||
AST_TEST_UNREGISTER(cache_passthrough);
|
||||
AST_TEST_UNREGISTER(cache);
|
||||
AST_TEST_UNREGISTER(cache_dump);
|
||||
AST_TEST_UNREGISTER(route_conflicts);
|
||||
AST_TEST_UNREGISTER(router);
|
||||
AST_TEST_UNREGISTER(interleaving);
|
||||
|
@ -925,6 +1005,7 @@ static int load_module(void)
|
|||
AST_TEST_REGISTER(forward);
|
||||
AST_TEST_REGISTER(cache_passthrough);
|
||||
AST_TEST_REGISTER(cache);
|
||||
AST_TEST_REGISTER(cache_dump);
|
||||
AST_TEST_REGISTER(route_conflicts);
|
||||
AST_TEST_REGISTER(router);
|
||||
AST_TEST_REGISTER(interleaving);
|
||||
|
|
|
@ -0,0 +1,547 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2013, Digium, Inc.
|
||||
*
|
||||
* David M. Lee, II <dlee@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \file \brief Test Stasis HTTP API.
|
||||
* \author\verbatim David M. Lee, II <dlee@digium.com> \endverbatim
|
||||
*
|
||||
* \ingroup tests
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<depend>TEST_FRAMEWORK</depend>
|
||||
<depend>res_stasis_http</depend>
|
||||
<support_level>core</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/test.h"
|
||||
#include "asterisk/stasis_http.h"
|
||||
|
||||
/*!@{*/
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* The following code defines a simple RESTful API for unit testing. The
|
||||
* response encodes the inputs of the invocation. The invocation_count
|
||||
* counter is also incremented.
|
||||
*
|
||||
* - /foo (GET)
|
||||
* - /foo/bar (GET, POST)
|
||||
* - /foo/{bam} (GET)
|
||||
* - /foo/{bam}/bang (GET, POST, DE1LETE)
|
||||
*/
|
||||
|
||||
static int invocation_count;
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* Shared code for all handlers
|
||||
*/
|
||||
static void handler(const char *name,
|
||||
int response_code,
|
||||
struct ast_variable *get_params,
|
||||
struct ast_variable *path_vars,
|
||||
struct ast_variable *headers,
|
||||
struct stasis_http_response *response)
|
||||
{
|
||||
struct ast_json *message = ast_json_pack("{s: s, s: {}, s: {}, s: {}}",
|
||||
"name", name,
|
||||
"get_params",
|
||||
"path_vars",
|
||||
"headers");
|
||||
struct ast_json *get_params_obj = ast_json_object_get(message, "get_params");
|
||||
struct ast_json *path_vars_obj = ast_json_object_get(message, "path_vars");
|
||||
struct ast_json *headers_obj = ast_json_object_get(message, "headers");
|
||||
|
||||
for (; get_params != NULL; get_params = get_params->next) {
|
||||
ast_json_object_set(get_params_obj, get_params->name, ast_json_string_create(get_params->value));
|
||||
}
|
||||
|
||||
for (; path_vars != NULL; path_vars = path_vars->next) {
|
||||
ast_json_object_set(path_vars_obj, path_vars->name, ast_json_string_create(path_vars->value));
|
||||
}
|
||||
|
||||
for (; headers != NULL; headers = headers->next) {
|
||||
ast_json_object_set(headers_obj, headers->name, ast_json_string_create(headers->value));
|
||||
}
|
||||
|
||||
++invocation_count;
|
||||
response->response_code = response_code;
|
||||
response->message = message;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* Macro to reduce the handler definition boiler-plate.
|
||||
*/
|
||||
#define HANDLER(name, response_code) \
|
||||
static void name(struct ast_variable *get_params, \
|
||||
struct ast_variable *path_vars, \
|
||||
struct ast_variable *headers, \
|
||||
struct stasis_http_response *response) \
|
||||
{ \
|
||||
handler(#name, response_code, get_params, path_vars, headers, response); \
|
||||
}
|
||||
|
||||
HANDLER(bang_get, 200)
|
||||
HANDLER(bang_post, 200)
|
||||
HANDLER(bang_delete, 204)
|
||||
HANDLER(bar_get, 200)
|
||||
HANDLER(bar_post, 200)
|
||||
HANDLER(bam_get, 200)
|
||||
HANDLER(foo_get, 200)
|
||||
|
||||
static struct stasis_rest_handlers bang = {
|
||||
.path_segment = "bang",
|
||||
.callbacks = {
|
||||
[AST_HTTP_GET] = bang_get,
|
||||
[AST_HTTP_POST] = bang_post,
|
||||
[AST_HTTP_DELETE] = bang_delete,
|
||||
},
|
||||
.num_children = 0
|
||||
};
|
||||
static struct stasis_rest_handlers bar = {
|
||||
.path_segment = "bar",
|
||||
.callbacks = {
|
||||
[AST_HTTP_GET] = bar_get,
|
||||
[AST_HTTP_POST] = bar_post,
|
||||
},
|
||||
.num_children = 0
|
||||
};
|
||||
static struct stasis_rest_handlers bam = {
|
||||
.path_segment = "bam",
|
||||
.is_wildcard = 1,
|
||||
.callbacks = {
|
||||
[AST_HTTP_GET] = bam_get,
|
||||
},
|
||||
.num_children = 1,
|
||||
.children = { &bang }
|
||||
};
|
||||
static struct stasis_rest_handlers test_root = {
|
||||
.path_segment = "foo",
|
||||
.callbacks = {
|
||||
[AST_HTTP_GET] = foo_get,
|
||||
},
|
||||
.num_children = 3,
|
||||
.children = { &bar, &bam, &bang }
|
||||
};
|
||||
/*!@}*/
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \c stasis_http_response constructor.
|
||||
*/
|
||||
static struct stasis_http_response *response_alloc(void)
|
||||
{
|
||||
struct stasis_http_response *resp = ast_calloc(1, sizeof(struct stasis_http_response));
|
||||
resp->headers = ast_str_create(24);
|
||||
return resp;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \c stasis_http_response destructor.
|
||||
*/
|
||||
static void response_free(struct stasis_http_response *resp)
|
||||
{
|
||||
ast_free(resp->headers);
|
||||
ast_json_unref(resp->message);
|
||||
ast_free(resp);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \ internal
|
||||
* Setup test fixture for invocation tests.
|
||||
*/
|
||||
static void *setup_invocation_test(void) {
|
||||
int r;
|
||||
invocation_count = 0;
|
||||
r = stasis_http_add_handler(&test_root);
|
||||
ast_assert(r == 0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \ internal
|
||||
* Tear down test fixture for invocation tests.
|
||||
*/
|
||||
static void tear_down_invocation_test(void *ignore) {
|
||||
stasis_http_remove_handler(&test_root);
|
||||
}
|
||||
|
||||
|
||||
AST_TEST_DEFINE(get_docs)
|
||||
{
|
||||
RAII_VAR(struct stasis_http_response *, response, response_alloc(), response_free);
|
||||
RAII_VAR(struct ast_variable *, headers, NULL, ast_variables_destroy);
|
||||
struct ast_json *basePathJson;
|
||||
const char *basePath;
|
||||
|
||||
switch (cmd) {
|
||||
case TEST_INIT:
|
||||
info->name = __func__;
|
||||
info->category = "/stasis/http/";
|
||||
info->summary = "Test simple API get.";
|
||||
info->description = "Test Stasis HTTP binding logic.";
|
||||
return AST_TEST_NOT_RUN;
|
||||
case TEST_EXECUTE:
|
||||
break;
|
||||
}
|
||||
|
||||
headers = ast_variable_new("Host", "stasis.asterisk.org", __FILE__);
|
||||
stasis_http_get_docs("resources.json", headers, response);
|
||||
ast_test_validate(test, 200 == response->response_code);
|
||||
|
||||
/* basePath should be relative to the Host header */
|
||||
basePathJson = ast_json_object_get(response->message, "basePath");
|
||||
ast_test_validate(test, NULL != basePathJson);
|
||||
basePath = ast_json_string_get(basePathJson);
|
||||
ast_test_validate(test, 0 == strcmp("http://stasis.asterisk.org/stasis", basePath));
|
||||
|
||||
return AST_TEST_PASS;
|
||||
}
|
||||
|
||||
AST_TEST_DEFINE(get_docs_nohost)
|
||||
{
|
||||
RAII_VAR(struct stasis_http_response *, response, response_alloc(), response_free);
|
||||
struct ast_variable *headers = NULL;
|
||||
struct ast_json *basePathJson;
|
||||
|
||||
switch (cmd) {
|
||||
case TEST_INIT:
|
||||
info->name = __func__;
|
||||
info->category = "/stasis/http/";
|
||||
info->summary = "Test API get without a Host header";
|
||||
info->description = "Test Stasis HTTP binding logic.";
|
||||
return AST_TEST_NOT_RUN;
|
||||
case TEST_EXECUTE:
|
||||
break;
|
||||
}
|
||||
|
||||
stasis_http_get_docs("resources.json", headers, response);
|
||||
ast_test_validate(test, 200 == response->response_code);
|
||||
|
||||
/* basePath should be relative to the Host header */
|
||||
basePathJson = ast_json_object_get(response->message, "basePath");
|
||||
ast_test_validate(test, NULL == basePathJson);
|
||||
|
||||
return AST_TEST_PASS;
|
||||
}
|
||||
|
||||
AST_TEST_DEFINE(get_docs_notfound)
|
||||
{
|
||||
RAII_VAR(struct stasis_http_response *, response, response_alloc(), response_free);
|
||||
struct ast_variable *headers = NULL;
|
||||
|
||||
switch (cmd) {
|
||||
case TEST_INIT:
|
||||
info->name = __func__;
|
||||
info->category = "/stasis/http/";
|
||||
info->summary = "Test API get for invalid resource";
|
||||
info->description = "Test Stasis HTTP binding logic.";
|
||||
return AST_TEST_NOT_RUN;
|
||||
case TEST_EXECUTE:
|
||||
break;
|
||||
}
|
||||
|
||||
stasis_http_get_docs("i-am-not-a-resource.json", headers, response);
|
||||
ast_test_validate(test, 404 == response->response_code);
|
||||
|
||||
return AST_TEST_PASS;
|
||||
}
|
||||
|
||||
AST_TEST_DEFINE(get_docs_hackerz)
|
||||
{
|
||||
RAII_VAR(struct stasis_http_response *, response, response_alloc(), response_free);
|
||||
struct ast_variable *headers = NULL;
|
||||
|
||||
switch (cmd) {
|
||||
case TEST_INIT:
|
||||
info->name = __func__;
|
||||
info->category = "/stasis/http/";
|
||||
info->summary = "Test API get for a file outside the rest-api path";
|
||||
info->description = "Test Stasis HTTP binding logic.";
|
||||
return AST_TEST_NOT_RUN;
|
||||
case TEST_EXECUTE:
|
||||
break;
|
||||
}
|
||||
|
||||
stasis_http_get_docs("../../../../sbin/asterisk", headers, response);
|
||||
ast_test_validate(test, 404 == response->response_code);
|
||||
|
||||
return AST_TEST_PASS;
|
||||
}
|
||||
|
||||
AST_TEST_DEFINE(invoke_get)
|
||||
{
|
||||
RAII_VAR(void *, fixture, setup_invocation_test(), tear_down_invocation_test);
|
||||
RAII_VAR(struct stasis_http_response *, response, response_alloc(), response_free);
|
||||
RAII_VAR(struct ast_json *, expected, NULL, ast_json_unref);
|
||||
struct ast_variable *get_params = NULL;
|
||||
struct ast_variable *headers = NULL;
|
||||
|
||||
switch (cmd) {
|
||||
case TEST_INIT:
|
||||
info->name = __func__;
|
||||
info->category = "/stasis/http/";
|
||||
info->summary = "Test simple GET of an HTTP resource.";
|
||||
info->description = "Test Stasis HTTP binding logic.";
|
||||
return AST_TEST_NOT_RUN;
|
||||
case TEST_EXECUTE:
|
||||
break;
|
||||
}
|
||||
|
||||
get_params = ast_variable_new("get1", "get-one", __FILE__);
|
||||
ast_assert(get_params != NULL);
|
||||
get_params->next = ast_variable_new("get2", "get-two", __FILE__);
|
||||
ast_assert(get_params->next != NULL);
|
||||
|
||||
headers = ast_variable_new("head1", "head-one", __FILE__);
|
||||
ast_assert(headers != NULL);
|
||||
headers->next = ast_variable_new("head2", "head-two", __FILE__);
|
||||
ast_assert(headers->next != NULL);
|
||||
|
||||
expected = ast_json_pack("{s: s, s: {s: s, s: s}, s: {s: s, s: s}, s: {}}",
|
||||
"name", "foo_get",
|
||||
"get_params",
|
||||
"get1", "get-one",
|
||||
"get2", "get-two",
|
||||
"headers",
|
||||
"head1", "head-one",
|
||||
"head2", "head-two",
|
||||
"path_vars");
|
||||
|
||||
stasis_http_invoke("foo", AST_HTTP_GET, get_params, headers, response);
|
||||
|
||||
ast_test_validate(test, 1 == invocation_count);
|
||||
ast_test_validate(test, 200 == response->response_code);
|
||||
ast_test_validate(test, ast_json_equal(expected, response->message));
|
||||
|
||||
return AST_TEST_PASS;
|
||||
}
|
||||
|
||||
AST_TEST_DEFINE(invoke_wildcard)
|
||||
{
|
||||
RAII_VAR(void *, fixture, setup_invocation_test(), tear_down_invocation_test);
|
||||
RAII_VAR(struct stasis_http_response *, response, response_alloc(), response_free);
|
||||
RAII_VAR(struct ast_json *, expected, NULL, ast_json_unref);
|
||||
struct ast_variable *get_params = NULL;
|
||||
struct ast_variable *headers = NULL;
|
||||
|
||||
switch (cmd) {
|
||||
case TEST_INIT:
|
||||
info->name = __func__;
|
||||
info->category = "/stasis/http/";
|
||||
info->summary = "Test GET of a wildcard resource.";
|
||||
info->description = "Test Stasis HTTP binding logic.";
|
||||
return AST_TEST_NOT_RUN;
|
||||
case TEST_EXECUTE:
|
||||
break;
|
||||
}
|
||||
|
||||
expected = ast_json_pack("{s: s, s: {}, s: {}, s: {s: s}}",
|
||||
"name", "bam_get",
|
||||
"get_params",
|
||||
"headers",
|
||||
"path_vars",
|
||||
"bam", "foshizzle");
|
||||
|
||||
stasis_http_invoke("foo/foshizzle", AST_HTTP_GET, get_params, headers, response);
|
||||
|
||||
ast_test_validate(test, 1 == invocation_count);
|
||||
ast_test_validate(test, 200 == response->response_code);
|
||||
ast_test_validate(test, ast_json_equal(expected, response->message));
|
||||
|
||||
return AST_TEST_PASS;
|
||||
}
|
||||
|
||||
AST_TEST_DEFINE(invoke_delete)
|
||||
{
|
||||
RAII_VAR(void *, fixture, setup_invocation_test(), tear_down_invocation_test);
|
||||
RAII_VAR(struct stasis_http_response *, response, response_alloc(), response_free);
|
||||
RAII_VAR(struct ast_json *, expected, NULL, ast_json_unref);
|
||||
struct ast_variable *get_params = NULL;
|
||||
struct ast_variable *headers = NULL;
|
||||
|
||||
switch (cmd) {
|
||||
case TEST_INIT:
|
||||
info->name = __func__;
|
||||
info->category = "/stasis/http/";
|
||||
info->summary = "Test DELETE of an HTTP resource.";
|
||||
info->description = "Test Stasis HTTP binding logic.";
|
||||
return AST_TEST_NOT_RUN;
|
||||
case TEST_EXECUTE:
|
||||
break;
|
||||
}
|
||||
|
||||
expected = ast_json_pack("{s: s, s: {}, s: {}, s: {s: s}}",
|
||||
"name", "bang_delete",
|
||||
"get_params",
|
||||
"headers",
|
||||
"path_vars",
|
||||
"bam", "foshizzle");
|
||||
|
||||
stasis_http_invoke("foo/foshizzle/bang", AST_HTTP_DELETE, get_params, headers, response);
|
||||
|
||||
ast_test_validate(test, 1 == invocation_count);
|
||||
ast_test_validate(test, 204 == response->response_code);
|
||||
ast_test_validate(test, ast_json_equal(expected, response->message));
|
||||
|
||||
return AST_TEST_PASS;
|
||||
}
|
||||
|
||||
AST_TEST_DEFINE(invoke_post)
|
||||
{
|
||||
RAII_VAR(void *, fixture, setup_invocation_test(), tear_down_invocation_test);
|
||||
RAII_VAR(struct stasis_http_response *, response, response_alloc(), response_free);
|
||||
RAII_VAR(struct ast_json *, expected, NULL, ast_json_unref);
|
||||
struct ast_variable *get_params = NULL;
|
||||
struct ast_variable *headers = NULL;
|
||||
|
||||
switch (cmd) {
|
||||
case TEST_INIT:
|
||||
info->name = __func__;
|
||||
info->category = "/stasis/http/";
|
||||
info->summary = "Test POST of an HTTP resource.";
|
||||
info->description = "Test Stasis HTTP binding logic.";
|
||||
return AST_TEST_NOT_RUN;
|
||||
case TEST_EXECUTE:
|
||||
break;
|
||||
}
|
||||
|
||||
get_params = ast_variable_new("get1", "get-one", __FILE__);
|
||||
ast_assert(get_params != NULL);
|
||||
get_params->next = ast_variable_new("get2", "get-two", __FILE__);
|
||||
ast_assert(get_params->next != NULL);
|
||||
|
||||
headers = ast_variable_new("head1", "head-one", __FILE__);
|
||||
ast_assert(headers != NULL);
|
||||
headers->next = ast_variable_new("head2", "head-two", __FILE__);
|
||||
ast_assert(headers->next != NULL);
|
||||
|
||||
expected = ast_json_pack("{s: s, s: {s: s, s: s}, s: {s: s, s: s}, s: {}}",
|
||||
"name", "bar_post",
|
||||
"get_params",
|
||||
"get1", "get-one",
|
||||
"get2", "get-two",
|
||||
"headers",
|
||||
"head1", "head-one",
|
||||
"head2", "head-two",
|
||||
"path_vars");
|
||||
|
||||
stasis_http_invoke("foo/bar", AST_HTTP_POST, get_params, headers, response);
|
||||
|
||||
ast_test_validate(test, 1 == invocation_count);
|
||||
ast_test_validate(test, 200 == response->response_code);
|
||||
ast_test_validate(test, ast_json_equal(expected, response->message));
|
||||
|
||||
return AST_TEST_PASS;
|
||||
}
|
||||
|
||||
AST_TEST_DEFINE(invoke_bad_post)
|
||||
{
|
||||
RAII_VAR(void *, fixture, setup_invocation_test(), tear_down_invocation_test);
|
||||
RAII_VAR(struct stasis_http_response *, response, response_alloc(), response_free);
|
||||
struct ast_variable *get_params = NULL;
|
||||
struct ast_variable *headers = NULL;
|
||||
|
||||
switch (cmd) {
|
||||
case TEST_INIT:
|
||||
info->name = __func__;
|
||||
info->category = "/stasis/http/";
|
||||
info->summary = "Test POST on a resource that doesn't support it.";
|
||||
info->description = "Test Stasis HTTP binding logic.";
|
||||
return AST_TEST_NOT_RUN;
|
||||
case TEST_EXECUTE:
|
||||
break;
|
||||
}
|
||||
|
||||
stasis_http_invoke("foo", AST_HTTP_POST, get_params, headers, response);
|
||||
|
||||
ast_test_validate(test, 0 == invocation_count);
|
||||
ast_test_validate(test, 405 == response->response_code);
|
||||
|
||||
return AST_TEST_PASS;
|
||||
}
|
||||
|
||||
AST_TEST_DEFINE(invoke_not_found)
|
||||
{
|
||||
RAII_VAR(void *, fixture, setup_invocation_test(), tear_down_invocation_test);
|
||||
RAII_VAR(struct stasis_http_response *, response, response_alloc(), response_free);
|
||||
struct ast_variable *get_params = NULL;
|
||||
struct ast_variable *headers = NULL;
|
||||
|
||||
switch (cmd) {
|
||||
case TEST_INIT:
|
||||
info->name = __func__;
|
||||
info->category = "/stasis/http/";
|
||||
info->summary = "Test GET on a resource that does not exist.";
|
||||
info->description = "Test Stasis HTTP binding logic.";
|
||||
return AST_TEST_NOT_RUN;
|
||||
case TEST_EXECUTE:
|
||||
break;
|
||||
}
|
||||
|
||||
stasis_http_invoke("foo/fizzle/i-am-not-a-resource", AST_HTTP_GET, get_params, headers, response);
|
||||
|
||||
ast_test_validate(test, 0 == invocation_count);
|
||||
ast_test_validate(test, 404 == response->response_code);
|
||||
|
||||
return AST_TEST_PASS;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
AST_TEST_UNREGISTER(get_docs);
|
||||
AST_TEST_UNREGISTER(get_docs_nohost);
|
||||
AST_TEST_UNREGISTER(get_docs_notfound);
|
||||
AST_TEST_UNREGISTER(get_docs_hackerz);
|
||||
AST_TEST_UNREGISTER(invoke_get);
|
||||
AST_TEST_UNREGISTER(invoke_wildcard);
|
||||
AST_TEST_UNREGISTER(invoke_delete);
|
||||
AST_TEST_UNREGISTER(invoke_post);
|
||||
AST_TEST_UNREGISTER(invoke_bad_post);
|
||||
AST_TEST_UNREGISTER(invoke_not_found);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
AST_TEST_REGISTER(get_docs);
|
||||
AST_TEST_REGISTER(get_docs_nohost);
|
||||
AST_TEST_REGISTER(get_docs_notfound);
|
||||
AST_TEST_REGISTER(get_docs_hackerz);
|
||||
AST_TEST_REGISTER(invoke_get);
|
||||
AST_TEST_REGISTER(invoke_wildcard);
|
||||
AST_TEST_REGISTER(invoke_delete);
|
||||
AST_TEST_REGISTER(invoke_post);
|
||||
AST_TEST_REGISTER(invoke_bad_post);
|
||||
AST_TEST_REGISTER(invoke_not_found);
|
||||
return AST_MODULE_LOAD_SUCCESS;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Stasis HTTP testing",
|
||||
.load = load_module,
|
||||
.unload = unload_module,
|
||||
.nonoptreq = "res_stasis_http",
|
||||
);
|
|
@ -251,15 +251,78 @@ cleanup:
|
|||
return res;
|
||||
}
|
||||
|
||||
AST_TEST_DEFINE(begins_with_test)
|
||||
{
|
||||
switch (cmd) {
|
||||
case TEST_INIT:
|
||||
info->name = "begins_with";
|
||||
info->category = "/main/strings/";
|
||||
info->summary = "Test ast_begins_with";
|
||||
info->description = "Test ast_begins_with";
|
||||
return AST_TEST_NOT_RUN;
|
||||
case TEST_EXECUTE:
|
||||
break;
|
||||
}
|
||||
|
||||
// prefixes
|
||||
ast_test_validate(test, 1 == ast_begins_with("foobar", "foobar"));
|
||||
ast_test_validate(test, 1 == ast_begins_with("foobar", "foo"));
|
||||
ast_test_validate(test, 1 == ast_begins_with("foobar", ""));
|
||||
ast_test_validate(test, 1 == ast_begins_with("", ""));
|
||||
|
||||
// not prefixes
|
||||
ast_test_validate(test, 0 == ast_begins_with("foobar", "bang"));
|
||||
ast_test_validate(test, 0 == ast_begins_with("foobar", "foobat"));
|
||||
ast_test_validate(test, 0 == ast_begins_with("boo", "boom"));
|
||||
ast_test_validate(test, 0 == ast_begins_with("", "blitz"));
|
||||
|
||||
// nothing failed; we're all good!
|
||||
return AST_TEST_PASS;
|
||||
}
|
||||
|
||||
AST_TEST_DEFINE(ends_with_test)
|
||||
{
|
||||
switch (cmd) {
|
||||
case TEST_INIT:
|
||||
info->name = "ends_with";
|
||||
info->category = "/main/strings/";
|
||||
info->summary = "Test ast_ends_with";
|
||||
info->description = "Test ast_ends_with";
|
||||
return AST_TEST_NOT_RUN;
|
||||
case TEST_EXECUTE:
|
||||
break;
|
||||
}
|
||||
|
||||
// prefixes
|
||||
ast_test_validate(test, 1 == ast_ends_with("foobar", "foobar"));
|
||||
ast_test_validate(test, 1 == ast_ends_with("foobar", "bar"));
|
||||
ast_test_validate(test, 1 == ast_ends_with("foobar", ""));
|
||||
ast_test_validate(test, 1 == ast_ends_with("", ""));
|
||||
|
||||
// not suffixes
|
||||
ast_test_validate(test, 0 == ast_ends_with("bar", "bbar"));
|
||||
ast_test_validate(test, 0 == ast_ends_with("foobar", "bang"));
|
||||
ast_test_validate(test, 0 == ast_ends_with("foobar", "foobat"));
|
||||
ast_test_validate(test, 0 == ast_ends_with("boo", "boom"));
|
||||
ast_test_validate(test, 0 == ast_ends_with("", "blitz"));
|
||||
|
||||
// nothing failed; we're all good!
|
||||
return AST_TEST_PASS;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
AST_TEST_UNREGISTER(str_test);
|
||||
AST_TEST_UNREGISTER(begins_with_test);
|
||||
AST_TEST_UNREGISTER(ends_with_test);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
AST_TEST_REGISTER(str_test);
|
||||
AST_TEST_REGISTER(begins_with_test);
|
||||
AST_TEST_REGISTER(ends_with_test);
|
||||
return AST_MODULE_LOAD_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue