From 14a74529344ef5229f100c81bc969f34e27112b3 Mon Sep 17 00:00:00 2001 From: Joshua Colp Date: Sat, 23 Nov 2013 12:52:54 +0000 Subject: [PATCH] ari: Add events for playback and recording. While there were events defined for playback and recording these were not actually sent. This change implements the to_json handlers which produces them. (closes issue ASTERISK-22710) Reported by: Jonathan Rose Review: https://reviewboard.asterisk.org/r/3026/ ........ Merged revisions 403119 from http://svn.asterisk.org/svn/asterisk/branches/12 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@403120 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- res/ari/ari_model_validators.c | 25 +++++-------- res/ari/ari_model_validators.h | 2 +- res/res_stasis_playback.c | 28 +++++++++++++-- res/res_stasis_recording.c | 58 +++++++++++++++++++++++++------ rest-api/api-docs/events.json | 5 --- rest-api/api-docs/recordings.json | 5 +++ 6 files changed, 89 insertions(+), 34 deletions(-) diff --git a/res/ari/ari_model_validators.c b/res/ari/ari_model_validators.c index 9634a473cc..9ea2f94cdf 100644 --- a/res/ari/ari_model_validators.c +++ b/res/ari/ari_model_validators.c @@ -996,6 +996,15 @@ int ast_ari_validate_live_recording(struct ast_json *json) int has_state = 0; for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { + if (strcmp("cause", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + prop_is_valid = ast_ari_validate_string( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI LiveRecording field cause failed validation\n"); + res = 0; + } + } else if (strcmp("format", ast_json_object_iter_key(iter)) == 0) { int prop_is_valid; has_format = 1; @@ -3292,20 +3301,9 @@ int ast_ari_validate_recording_failed(struct ast_json *json) { int res = 1; struct ast_json_iter *iter; - int has_cause = 0; int has_recording = 0; for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { - if (strcmp("cause", ast_json_object_iter_key(iter)) == 0) { - int prop_is_valid; - has_cause = 1; - prop_is_valid = ast_ari_validate_string( - ast_json_object_iter_value(iter)); - if (!prop_is_valid) { - ast_log(LOG_ERROR, "ARI RecordingFailed field cause failed validation\n"); - res = 0; - } - } else if (strcmp("recording", ast_json_object_iter_key(iter)) == 0) { int prop_is_valid; has_recording = 1; @@ -3324,11 +3322,6 @@ int ast_ari_validate_recording_failed(struct ast_json *json) } } - if (!has_cause) { - ast_log(LOG_ERROR, "ARI RecordingFailed missing required field cause\n"); - res = 0; - } - if (!has_recording) { ast_log(LOG_ERROR, "ARI RecordingFailed missing required field recording\n"); res = 0; diff --git a/res/ari/ari_model_validators.h b/res/ari/ari_model_validators.h index 8724b642ab..a1e5b099e9 100644 --- a/res/ari/ari_model_validators.h +++ b/res/ari/ari_model_validators.h @@ -1032,6 +1032,7 @@ ari_validator ast_ari_validate_application_fn(void); * - id: string (required) * - technology: string (required) * LiveRecording + * - cause: string * - format: string (required) * - name: string (required) * - state: string (required) @@ -1167,7 +1168,6 @@ ari_validator ast_ari_validate_application_fn(void); * - timestamp: Date * - playback: Playback (required) * RecordingFailed - * - cause: string (required) * - recording: LiveRecording (required) * RecordingFinished * - recording: LiveRecording (required) diff --git a/res/res_stasis_playback.c b/res/res_stasis_playback.c index f112e8b250..f78ccf0ba5 100644 --- a/res/res_stasis_playback.c +++ b/res/res_stasis_playback.c @@ -57,8 +57,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #define SOUND_URI_SCHEME "sound:" #define RECORDING_URI_SCHEME "recording:" -STASIS_MESSAGE_TYPE_DEFN(stasis_app_playback_snapshot_type); - /*! Container of all current playbacks */ static struct ao2_container *playbacks; @@ -87,6 +85,32 @@ struct stasis_app_playback { enum stasis_app_playback_state state; }; +static struct ast_json *playback_to_json(struct stasis_message *message, + const struct stasis_message_sanitizer *sanitize) +{ + struct ast_channel_blob *channel_blob = stasis_message_data(message); + struct ast_json *blob = channel_blob->blob; + const char *state = + ast_json_string_get(ast_json_object_get(blob, "state")); + const char *type; + + if (!strcmp(state, "playing")) { + type = "PlaybackStarted"; + } else if (!strcmp(state, "done")) { + type = "PlaybackFinished"; + } else { + return NULL; + } + + return ast_json_pack("{s: s, s: O}", + "type", type, + "playback", blob); +} + +STASIS_MESSAGE_TYPE_DEFN(stasis_app_playback_snapshot_type, + .to_json = playback_to_json, +); + static void playback_dtor(void *obj) { struct stasis_app_playback *playback = obj; diff --git a/res/res_stasis_recording.c b/res/res_stasis_recording.c index bd2177a762..ecf0cfa49f 100644 --- a/res/res_stasis_recording.c +++ b/res/res_stasis_recording.c @@ -49,8 +49,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") /*! Recording check is unimplemented. le sigh */ #define RECORDING_CHECK 0 -STASIS_MESSAGE_TYPE_DEFN(stasis_app_recording_snapshot_type); - /*! Container of all current recordings */ static struct ao2_container *recordings; @@ -61,13 +59,40 @@ struct stasis_app_recording { char *absolute_name; /*! Control object for the channel we're recording */ struct stasis_app_control *control; - /*! Current state of the recording. */ enum stasis_app_recording_state state; /*! Indicates whether the recording is currently muted */ int muted:1; }; +static struct ast_json *recording_to_json(struct stasis_message *message, + const struct stasis_message_sanitizer *sanitize) +{ + struct ast_channel_blob *channel_blob = stasis_message_data(message); + struct ast_json *blob = channel_blob->blob; + const char *state = + ast_json_string_get(ast_json_object_get(blob, "state")); + const char *type; + + if (!strcmp(state, "recording")) { + type = "RecordingStarted"; + } else if (!strcmp(state, "done") || !strcasecmp(state, "canceled")) { + type = "RecordingFinished"; + } else if (!strcmp(state, "failed")) { + type = "RecordingFailed"; + } else { + return NULL; + } + + return ast_json_pack("{s: s, s: O}", + "type", type, + "recording", blob); +} + +STASIS_MESSAGE_TYPE_DEFN(stasis_app_recording_snapshot_type, + .to_json = recording_to_json, +); + static int recording_hash(const void *obj, int flags) { const struct stasis_app_recording *recording = obj; @@ -183,7 +208,7 @@ enum ast_record_if_exists stasis_app_recording_if_exists_parse( return -1; } -static void recording_publish(struct stasis_app_recording *recording) +static void recording_publish(struct stasis_app_recording *recording, const char *cause) { RAII_VAR(struct ast_json *, json, NULL, ast_json_unref); RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup); @@ -196,6 +221,19 @@ static void recording_publish(struct stasis_app_recording *recording) return; } + if (!ast_strlen_zero(cause)) { + struct ast_json *failure_cause = ast_json_string_create(cause); + + if (!failure_cause) { + return; + } + + if (ast_json_object_set(json, "cause", failure_cause)) { + ast_json_unref(failure_cause); + return; + } + } + message = ast_channel_blob_create_from_cache( stasis_app_control_get_channel_id(recording->control), stasis_app_recording_snapshot_type(), json); @@ -206,11 +244,11 @@ static void recording_publish(struct stasis_app_recording *recording) stasis_app_control_publish(recording->control, message); } -static void recording_fail(struct stasis_app_recording *recording) +static void recording_fail(struct stasis_app_recording *recording, const char *cause) { SCOPED_AO2LOCK(lock, recording); recording->state = STASIS_APP_RECORDING_STATE_FAILED; - recording_publish(recording); + recording_publish(recording, cause); } static void recording_cleanup(struct stasis_app_recording *recording) @@ -233,7 +271,7 @@ static void *record_file(struct stasis_app_control *control, if (stasis_app_get_bridge(control)) { ast_log(LOG_ERROR, "Cannot record channel while in bridge\n"); - recording_fail(recording); + recording_fail(recording, "Cannot record channel while in bridge"); return NULL; } @@ -255,13 +293,13 @@ static void *record_file(struct stasis_app_control *control, if (res != 0) { ast_debug(3, "%s: Failed to answer\n", ast_channel_uniqueid(chan)); - recording_fail(recording); + recording_fail(recording, "Failed to answer channel"); return NULL; } ao2_lock(recording); recording->state = STASIS_APP_RECORDING_STATE_RECORDING; - recording_publish(recording); + recording_publish(recording, NULL); ao2_unlock(recording); ast_play_and_record_full(chan, @@ -284,7 +322,7 @@ static void *record_file(struct stasis_app_control *control, ao2_lock(recording); recording->state = STASIS_APP_RECORDING_STATE_COMPLETE; - recording_publish(recording); + recording_publish(recording, NULL); ao2_unlock(recording); return NULL; diff --git a/rest-api/api-docs/events.json b/rest-api/api-docs/events.json index 9f82d19f59..e30a193c11 100644 --- a/rest-api/api-docs/events.json +++ b/rest-api/api-docs/events.json @@ -153,11 +153,6 @@ "type": "LiveRecording", "description": "Recording control object", "required": true - }, - "cause": { - "type": "string", - "description": "Cause for the recording failure", - "required": true } } }, diff --git a/rest-api/api-docs/recordings.json b/rest-api/api-docs/recordings.json index 590a1b84db..8e25da5823 100644 --- a/rest-api/api-docs/recordings.json +++ b/rest-api/api-docs/recordings.json @@ -307,6 +307,11 @@ ] } }, + "cause": { + "required": false, + "type": "string", + "description": "Cause for recording failure if failed" + }, "state": { "required": true, "type": "string"