Skip to content

Commit

Permalink
CreateRecording (#88)
Browse files Browse the repository at this point in the history
* Onvif.Recording.CreateRecording

* rename recording schema
  • Loading branch information
paoloo authored Dec 3, 2024
1 parent 36ecdc7 commit e6d3ea3
Show file tree
Hide file tree
Showing 4 changed files with 327 additions and 0 deletions.
71 changes: 71 additions & 0 deletions lib/recording/create_recording.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
defmodule Onvif.Recording.CreateRecording do
import SweetXml
import XmlBuilder
require Logger

def soap_action, do: "http://www.onvif.org/ver10/recording/wsdl/CreateRecording"

def request(device, args) do
Onvif.Recording.request(device, args, __MODULE__)
end

def request_body(config: %Onvif.Recording.Recording.Configuration{} = config) do
element(:"s:Body", [
element(:"trc:CreateRecording", [
element(:"trc:RecordingConfiguration", [
element(:"tt:Source", [
gen_source_id(config.source.source_id),
gen_name(config.source.name),
gen_location(config.source.location),
gen_description(config.source.description),
gen_address(config.source.address)
]),
gen_content(config.content),
gen_maximum_retention_time(config.maximum_retention_time)
])
])
])
end

def gen_source_id(nil), do: []
def gen_source_id(""), do: []
def gen_source_id(source_id), do: element(:"tt:SourceId", source_id)

def gen_name(nil), do: []
def gen_name(""), do: []
def gen_name(name), do: element(:"tt:Name", name)

def gen_location(nil), do: []
def gen_location(""), do: []
def gen_location(location), do: element(:"tt:Location", location)

def gen_description(nil), do: []
def gen_description(""), do: []
def gen_description(description), do: element(:"tt:Description", description)

def gen_address(nil), do: []
def gen_address(""), do: []
def gen_address(address), do: element(:"tt:Address", address)

def gen_content(nil), do: []
def gen_content(""), do: []
def gen_content(content), do: element(:"tt:Content", content)

def gen_maximum_retention_time(nil), do: []
def gen_maximum_retention_time(""), do: []
def gen_maximum_retention_time(maximum_retention_time), do: element(:"tt:MaximumRetentionTime", maximum_retention_time)


def response(xml_response_body) do
recording_token =
xml_response_body
|> parse(namespace_conformant: true, quiet: true)
|> xpath(
~x"//s:Envelope/s:Body/trc:CreateRecordingResponse/trc:RecordingToken/text()"s
|> add_namespace("s", "http://www.w3.org/2003/05/soap-envelope")
|> add_namespace("trc", "http://www.onvif.org/ver10/recording/wsdl")
)

{:ok, recording_token}
end
end
168 changes: 168 additions & 0 deletions lib/recording/recording.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
defmodule Onvif.Recording.Recording do
@moduledoc """
Onvif.Recording.Recordings schema.
"""

use Ecto.Schema
import Ecto.Changeset
import SweetXml

@primary_key false
@derive Jason.Encoder
@required [:recording_token]
@optional []

embedded_schema do
field(:recording_token, :string)

embeds_one :configuration, Configuration, primary_key: false, on_replace: :update do
@derive Jason.Encoder
field(:content, :string)
field(:maximum_retention_time, :string)

embeds_one(:source, Source, primary_key: false, on_replace: :update) do
@derive Jason.Encoder
field(:source_id, :string)
field(:name, :string)
field(:location, :string)
field(:description, :string)
field(:address, :string)
end
end

embeds_one(:tracks, Tracks, primary_key: false, on_replace: :update) do
@derive Jason.Encoder
embeds_many(:track, Track, primary_key: false, on_replace: :delete) do
@derive Jason.Encoder
field(:track_token, :string)

embeds_one(:configuration, Configuration, primary_key: false, on_replace: :update) do
@derive Jason.Encoder
field(:track_type, :string)
field(:description, :string)
end
end
end
end

def parse(nil), do: nil
def parse([]), do: nil

def parse(doc) do
xmap(
doc,
recording_token: ~x"./tt:RecordingToken/text()"so,
configuration: ~x"./tt:Configuration"eo |> transform_by(&parse_configuration/1),
tracks: ~x"./tt:Tracks"eo |> transform_by(&parse_tracks/1)
)
end

def parse_configuration([]), do: nil
def parse_configuration(nil), do: nil

def parse_configuration(doc) do
xmap(
doc,
content: ~x"./tt:Content/text()"so,
maximum_retention_time: ~x"./tt:MaximumRetentionTime/text()"so,
source: ~x"./tt:Source"eo |> transform_by(&parse_source/1)
)
end

def parse_source([]), do: nil
def parse_source(nil), do: nil

def parse_source(doc) do
xmap(
doc,
source_id: ~x"./tt:SourceId/text()"so,
name: ~x"./tt:Name/text()"so,
location: ~x"./tt:Location/text()"so,
description: ~x"./tt:Description/text()"so,
address: ~x"./tt:Address/text()"so
)
end

def parse_tracks([]), do: nil
def parse_tracks(nil), do: nil

def parse_tracks(doc) do
xmap(
doc,
track: ~x"./tt:Track"elo |> transform_by(&parse_track/1)
)
end

def parse_track([]), do: nil
def parse_track(nil), do: nil

def parse_track(docs) do
Enum.map(docs, fn doc ->
xmap(
doc,
track_token: ~x"./tt:TrackToken/text()"so,
configuration: ~x"./tt:Configuration"eo |> transform_by(&parse_track_configuration/1)
)
end)
end

def parse_track_configuration([]), do: nil
def parse_track_configuration(nil), do: nil

def parse_track_configuration(doc) do
xmap(
doc,
track_type: ~x"./tt:TrackType/text()"so,
description: ~x"./tt:Description/text()"so
)
end

def to_struct(parsed) do
%__MODULE__{}
|> changeset(parsed)
|> apply_action(:validate)
end

@spec to_json(%Onvif.Recording.Recording{}) ::
{:error,
%{
:__exception__ => any,
:__struct__ => Jason.EncodeError | Protocol.UndefinedError,
optional(atom) => any
}}
| {:ok, binary}
def to_json(%__MODULE__{} = schema) do
Jason.encode(schema)
end

def changeset(module, attrs) do
module
|> cast(attrs, @required ++ @optional)
|> validate_required(@required)
|> cast_embed(:configuration, with: &configuration_changeset/2)
|> cast_embed(:tracks, with: &tracks_changeset/2)
end

def configuration_changeset(module, attrs) do
cast(module, attrs, [:content, :maximum_retention_time])
|> cast_embed(:source, with: &source_changeset/2)
end

def source_changeset(module, attrs) do
cast(module, attrs, [:source_id, :name, :location, :description, :address])
end

def tracks_changeset(module, attrs) do
cast(module, attrs, [])
|> cast_embed(:track, with: &track_changeset/2)
end

def track_changeset(module, attrs) do
cast(module, attrs, [:track_token])
|> cast_embed(:configuration, with: &track_configuration_changeset/2)
end

def track_configuration_changeset(module, attrs) do
cast(module, attrs, [:track_type, :description])
end
end
29 changes: 29 additions & 0 deletions test/recording/create_recording_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
defmodule Onvif.Recording.CreateRecordingTest do
use ExUnit.Case, async: true

@moduletag capture_log: true

describe "CreateRecording/2" do
test "create a recording" do
xml_response = File.read!("test/recording/fixture/create_recording_success.xml")

device = Onvif.Factory.device()

Mimic.expect(Tesla, :request, fn _client, _opts ->
{:ok, %{status: 200, body: xml_response}}
end)

{:ok, response_uri} = Onvif.Recording.CreateRecording.request(
device,
config: %Onvif.Recording.Recording.Configuration{
content: "test",
maximum_retention_time: "PT1H",
source: %Onvif.Recording.Recording.Configuration.Source{
name: "test",
}
})

assert response_uri == "SD_DISK_20200422_123501_A2388AB3"
end
end
end
59 changes: 59 additions & 0 deletions test/recording/fixture/create_recording_success.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope"
xmlns:SOAP-ENC="http://www.w3.org/2003/05/soap-encoding"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:c14n="http://www.w3.org/2001/10/xml-exc-c14n#"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
xmlns:wsa5="http://www.w3.org/2005/08/addressing"
xmlns:xmime="http://tempuri.org/xmime.xsd"
xmlns:xop="http://www.w3.org/2004/08/xop/include"
xmlns:wsrfbf="http://docs.oasis-open.org/wsrf/bf-2"
xmlns:wstop="http://docs.oasis-open.org/wsn/t-1"
xmlns:tt="http://www.onvif.org/ver10/schema"
xmlns:acert="http://www.axis.com/vapix/ws/cert"
xmlns:wsrfr="http://docs.oasis-open.org/wsrf/r-2"
xmlns:aa="http://www.axis.com/vapix/ws/action1"
xmlns:acertificates="http://www.axis.com/vapix/ws/certificates"
xmlns:aentry="http://www.axis.com/vapix/ws/entry"
xmlns:aev="http://www.axis.com/vapix/ws/event1"
xmlns:aeva="http://www.axis.com/vapix/ws/embeddedvideoanalytics1"
xmlns:ali1="http://www.axis.com/vapix/ws/light/CommonBinding"
xmlns:ali2="http://www.axis.com/vapix/ws/light/IntensityBinding"
xmlns:ali3="http://www.axis.com/vapix/ws/light/AngleOfIlluminationBinding"
xmlns:ali4="http://www.axis.com/vapix/ws/light/DayNightSynchronizeBinding"
xmlns:ali="http://www.axis.com/vapix/ws/light"
xmlns:apc="http://www.axis.com/vapix/ws/panopsiscalibration1"
xmlns:arth="http://www.axis.com/vapix/ws/recordedtour1"
xmlns:asd="http://www.axis.com/vapix/ws/shockdetection"
xmlns:aweb="http://www.axis.com/vapix/ws/webserver"
xmlns:tan1="http://www.onvif.org/ver20/analytics/wsdl/RuleEngineBinding"
xmlns:tan2="http://www.onvif.org/ver20/analytics/wsdl/AnalyticsEngineBinding"
xmlns:tan="http://www.onvif.org/ver20/analytics/wsdl"
xmlns:tds="http://www.onvif.org/ver10/device/wsdl"
xmlns:tev1="http://www.onvif.org/ver10/events/wsdl/NotificationProducerBinding"
xmlns:tev2="http://www.onvif.org/ver10/events/wsdl/EventBinding"
xmlns:tev3="http://www.onvif.org/ver10/events/wsdl/SubscriptionManagerBinding"
xmlns:wsnt="http://docs.oasis-open.org/wsn/b-2"
xmlns:tev4="http://www.onvif.org/ver10/events/wsdl/PullPointSubscriptionBinding"
xmlns:tev="http://www.onvif.org/ver10/events/wsdl"
xmlns:timg="http://www.onvif.org/ver20/imaging/wsdl"
xmlns:tmd="http://www.onvif.org/ver10/deviceIO/wsdl"
xmlns:tptz="http://www.onvif.org/ver20/ptz/wsdl"
xmlns:tr2="http://www.onvif.org/ver20/media/wsdl"
xmlns:trc="http://www.onvif.org/ver10/recording/wsdl"
xmlns:trp="http://www.onvif.org/ver10/replay/wsdl"
xmlns:trt="http://www.onvif.org/ver10/media/wsdl"
xmlns:tse="http://www.onvif.org/ver10/search/wsdl"
xmlns:ter="http://www.onvif.org/ver10/error"
xmlns:tns1="http://www.onvif.org/ver10/topics"
xmlns:tnsaxis="http://www.axis.com/2009/event/topics">
<SOAP-ENV:Body>
<trc:CreateRecordingResponse>
<trc:RecordingToken>SD_DISK_20200422_123501_A2388AB3</trc:RecordingToken>
</trc:CreateRecordingResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

0 comments on commit e6d3ea3

Please sign in to comment.