Skip to content

Commit

Permalink
Fixed parsing Response messages that do not contain Key: Value format.
Browse files Browse the repository at this point in the history
* Added CHANGELOG.md file
* New `ResponseData` Message attribute field. It is filled with the data
  that does not match key: value format.
Steve Pallen committed Jun 29, 2018
1 parent b136796 commit 2344181
Showing 6 changed files with 95 additions and 20 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# ExAmi Changelog

## 0.4.0 (2018-06-29)

### Enhancements

* Added CHANGELOG.md file
* New `ResponseData` Message attribute field. It is filled with the data that does
not match key: value format.

### Bug Fixes

* Fixed parsing Response messages that do not contain Key: Value format.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -37,7 +37,7 @@ Add ex_ami to your `mix.exs` dependencies and start the application:
end
defp deps do
[{:ex_ami, "~> 0.3"}]
[{:ex_ami, "~> 0.4"}]
end
```

38 changes: 24 additions & 14 deletions lib/ex_ami/message.ex
Original file line number Diff line number Diff line change
@@ -62,6 +62,15 @@ defmodule ExAmi.Message do
|> new_message(message.variables)
end

def add_response_data(%Message{attributes: attributes} = message, line) do
response_data =
case attributes["ResponseData"] do
nil -> line
acc -> acc <> "\n" <> line
end
%Message{message | attributes: Map.put(attributes, "ResponseData", response_data)}
end

def set_all(%Message{} = message, attributes) do
Enum.reduce attributes, message, fn({key, value}, acc) -> set(acc, key, value) end
end
@@ -105,21 +114,22 @@ defmodule ExAmi.Message do
end

def unmarshall(text) do
Enum.reduce explode_lines(text), new_message(), fn(line, acc) ->
# some responses don't user a : to separate the key from the value
sep = if Regex.match?(~r/^[^\s]+\s*?:/, line), do: ":", else: " "
line
|> String.split(sep, trim: true, parts: 2)
|> Enum.map(&(String.trim(&1)))
|> _unmarshall(acc)
end
do_unmarshall(new_message(), explode_lines(text))
end
defp _unmarshall([key,value], %Message{} = message), do: set(message, key, value)
defp _unmarshall([], %Message{} = message), do: message
defp _unmarshall(["ReportBlock"], %Message{} = message), do: message
defp _unmarshall(other, %Message{} = message) do
Logger.error("_unmarshall invalid input #{inspect other}")
message

defp do_unmarshall(message, []), do: message

defp do_unmarshall(message, [line | tail]) do
~r/^([^\s]+): (.*)/
|> Regex.run(line)
|> case do
[_, key, value] ->
set(message, key, value)

nil ->
add_response_data(message, line)
end
|> do_unmarshall(tail)
end

def is_response(%Message{} = message), do: is_type(message, "Response")
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ defmodule ExAmi.Mixfile do
def project do
[
app: :ex_ami,
version: "0.3.3",
version: "0.4.0",
elixir: "~> 1.5",
package: package(),
name: "ExAmi",
10 changes: 6 additions & 4 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
%{"covertool": {:git, "git://github.com/idubrov/covertool.git", "efac1a45d713690582c0e46f92e042f61d90777a", [ref: "HEAD"]},
%{
"covertool": {:git, "git://github.com/idubrov/covertool.git", "efac1a45d713690582c0e46f92e042f61d90777a", [ref: "HEAD"]},
"gen_fsm": {:hex, :gen_fsm, "0.1.0", "5097308e244e25dbb2aa0ee5b736bb1ab79c3f8ca889a9e5b3aabd8beee6fc88", [:mix], []},
"gen_fsm_helpers": {:hex, :gen_fsm_helpers, "0.1.0", "47734683834c5c43b8395daef098ce285f2c27b26eb40dfd5201f3426c8f750a", [:mix], [], "hexpm"},
"gen_state_machine": {:hex, :gen_state_machine, "2.0.0", "f3bc7d961e4cd9f37944b379137e25fd063feffc42800bed69197fd1b78b3151", [], [], "hexpm"},
"gen_state_machine_helpers": {:hex, :gen_state_machine_helpers, "0.1.0", "d6cd29d6b0a5ffeacd6e6d007733231916c228f2a0f758ff93d5361656a22133", [], [], "hexpm"},
"gen_state_machine": {:hex, :gen_state_machine, "2.0.0", "f3bc7d961e4cd9f37944b379137e25fd063feffc42800bed69197fd1b78b3151", [:mix], [], "hexpm"},
"gen_state_machine_helpers": {:hex, :gen_state_machine_helpers, "0.1.0", "d6cd29d6b0a5ffeacd6e6d007733231916c228f2a0f758ff93d5361656a22133", [:mix], [], "hexpm"},
"goldrush": {:git, "git://github.com/DeadZen/goldrush.git", "71e63212f12c25827e0c1b4198d37d5d018a7fec", [tag: "0.1.6"]},
"lager": {:git, "git://github.com/basho/lager.git", "b7984d4628d0a7780166f3f26207882fddb31251", [ref: "master"]},
"meck": {:hex, :meck, "0.8.4", "59ca1cd971372aa223138efcf9b29475bde299e1953046a0c727184790ab1520", [:rebar, :make], []},
"pavlov": {:hex, :pavlov, "0.2.3", "9072029af61301463a6e273e2829e9eab14bd1e7a5c381886d5166e6739e6fcf", [:mix], [{:meck, "~> 0.8.2", [hex: :meck, optional: false]}]},
"rebar": {:git, "https://github.com/rebar/rebar.git", "e9f62c45807ce2db39e0606c4d97cd071416bd64", [tag: "2.5.1"]}}
"rebar": {:git, "https://github.com/rebar/rebar.git", "e9f62c45807ce2db39e0606c4d97cd071416bd64", [tag: "2.5.1"]},
}
50 changes: 50 additions & 0 deletions test/message_test.exs
Original file line number Diff line number Diff line change
@@ -67,6 +67,25 @@ defmodule ExAmi.MessageTest do
assert unmarshalled == expected
assert Message.is_response(unmarshalled) == true
end

test "handles all key/value pairs" do
%Message.Message{attributes: attributes} = Message.unmarshall(all_key_value_pairs())
assert attributes["Event"] == "SuccessfulAuth"
assert attributes["Privilege"] == "security,all"
assert attributes["SessionID"] == "0x7f0c50000910"
end

test "response follows ResponseData" do
%Message.Message{attributes: attributes} = Message.unmarshall(response_follows_message())
assert attributes["Response"] == "Follows"
assert attributes["Privilege"] == "Command"
assert attributes["ActionID"] == "1530285340189277"
[one, two, three, four] = attributes["ResponseData"] |> String.split("\n", trim: true)
assert String.match?(one, ~r/Name\/username\s+Host\s+Dyn Forcerport Comedia ACL Port Status Description/)
assert String.match?(two, ~r/200\s+\(Unspecified\)\s+D No\s+No\s+A 0\s+UNKNOWN/)
assert three == "1 sip peers [Monitored: 0 online, 1 offline Unmonitored: 0 online, 0 offline]"
assert four == "--END COMMAND--"
end
end

describe "queries" do
@@ -107,4 +126,35 @@ defmodule ExAmi.MessageTest do
assert Message.set("Message", "more here") |> Message.is_event_last_for_response == false
end
end

defp convert_newlines(text) do
text
|> String.split("\n", trim: true)
|> Enum.join("\r\n")
end

defp all_key_value_pairs, do: """
Event: SuccessfulAuth
Privilege: security,all
EventTV: 2018-06-29T10:20:05.681-0500
Severity: Informational
Service: AMI
EventVersion: 1
AccountID: infinity_one
SessionID: 0x7f0c50000910
LocalAddress: IPV4/TCP/0.0.0.0/5038
RemoteAddress: IPV4/TCP/10.30.50.10/42465
UsingPassword: 0
SessionTV: 2018-06-29T10:20:05.681-0500
""" |> convert_newlines()

defp response_follows_message, do: """
Response: Follows
Privilege: Command
ActionID: 1530285340189277
Name/username Host Dyn Forcerport Comedia ACL Port Status Description
200 (Unspecified) D No No A 0 UNKNOWN
1 sip peers [Monitored: 0 online, 1 offline Unmonitored: 0 online, 0 offline]
--END COMMAND--
""" |> convert_newlines()
end

0 comments on commit 2344181

Please sign in to comment.