ari: Add events for playback and recording.
authorJoshua Colp <jcolp@digium.com>
Sat, 23 Nov 2013 12:52:54 +0000 (12:52 +0000)
committerJoshua Colp <jcolp@digium.com>
Sat, 23 Nov 2013 12:52:54 +0000 (12:52 +0000)
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
res/ari/ari_model_validators.h
res/res_stasis_playback.c
res/res_stasis_recording.c
rest-api/api-docs/events.json
rest-api/api-docs/recordings.json

index 9634a47..9ea2f94 100644 (file)
@@ -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;
index 8724b64..a1e5b09 100644 (file)
@@ -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)
index f112e8b..f78ccf0 100644 (file)
@@ -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;
index bd2177a..ecf0cfa 100644 (file)
@@ -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;
index 9f82d19..e30a193 100644 (file)
                                        "type": "LiveRecording",
                                        "description": "Recording control object",
                                        "required": true
-                               },
-                               "cause": {
-                                       "type": "string",
-                                       "description": "Cause for the recording failure",
-                                       "required": true
                                }
                        }
                },
index 590a1b8..8e25da5 100644 (file)
                                                ]
                                        }
                                },
+                               "cause": {
+                                       "required": false,
+                                       "type": "string",
+                                       "description": "Cause for recording failure if failed"
+                               },
                                "state": {
                                        "required": true,
                                        "type": "string"