Module: NCCO::Schemas

Defined in:
lib/ncco/schemas/talk.rb,
lib/ncco/schemas/input.rb,
lib/ncco/schemas/record.rb,
lib/ncco/schemas/stream.rb,
lib/ncco/schemas/connect.rb,
lib/ncco/schemas/base_schema.rb,
lib/ncco/schemas/conversation.rb

Constant Summary collapse

Talk =
Dry::Validation.Schema(BaseSchema) do
  required(:action).value(eql?: "talk")
  required(:text).value(:filled?, type?: String)
  optional(:bargeIn).value(:bool?)
  optional(:loop).value(type?: Integer)
  optional(:level).value(included_in?: [-1, 0, 1])
  optional(:voiceName).value(:filled?, type?: String)
end
Input =
Dry::Validation.Schema(BaseSchema) do
  required(:action).value(eql?: "input")
  optional(:timeOut).value(type?: Integer, gteq?: 1, lteq?: 10)
  optional(:maxDigits).value(type?: Integer, gteq?: 1, lteq?: 20)
  optional(:submitOnHash).value(:bool?)
  optional(:eventUrl).value(:http_or_https_url?)
  optional(:eventMethod).value(:supported_http_method?)
end
DIGITS =
(0..9).map(&:to_s).freeze
Record =
Dry::Validation.Schema(BaseSchema) do
  required(:action).value(eql?: "record")
  optional(:format).value(included_in?: %w[mp3 wav ogg])
  # TODO: Is nil allowed? What are the other options?
  optional(:split).value(included_in?: [nil, "conversation"])
  optional(:channels).value(type?: Integer, gteq?: 1, lteq?: 32)
  optional(:endOnSilence).value(type?: Integer, gteq?: 3, lteq?: 10)
  optional(:endOnKey).value(:phone_keypad_digit?)
  optional(:beepStart).value(:bool?)
  optional(:timeout).value(type?: Integer, gteq?: 3, lteq?: 7200)
  optional(:eventUrl).value(:http_or_https_url?)
  optional(:eventMethod).value(:supported_http_method?)
end
Stream =
Dry::Validation.Schema(BaseSchema) do
  required(:action).value(eql?: "stream")
  required(:streamUrl).value(:http_or_https_url?)
  optional(:bargeIn).value(:bool?)
  optional(:loop).value(type?: Integer)
  optional(:level).value(included_in?: [-1, 0, 1])
end
ConnectPhoneEndpoint =
Dry::Validation.Schema(BaseSchema) do
  required(:type).value(eql?: "phone")

  required(:number).value(:e164?)
  optional(:onAnswer).value(:http_or_https_url?)
  optional(:dtmfAnswer).value(:phone_keypad_digits?)
end
ConnectSipEndpoint =
Dry::Validation.Schema(BaseSchema) do
  required(:type).value(eql?: "sip")

  required(:uri).value(:sip_uri?)
end
ConnectWebSocketEndpoint =
Dry::Validation.Schema(BaseSchema) do
  required(:type).value(eql?: "websocket")

  required(:uri).value(:websocket_url?)
  optional("content-type").value(eql?: "audio/l16;rate=16000")
  optional(:headers).value(:hash_with_string_keys_and_values?)
end
ConnectEndpoint =
Dry::Validation.Schema(BaseSchema) do
  required(:type).value(included_in?: %w[phone websocket sip])

  # How we validate the endpoint (i.e. what schema we should use) depends on the type
  rule(phone_endpoint: [:type]) do |type|
    type.eql?("phone") > schema(ConnectPhoneEndpoint)
  end

  rule(sip_endpoint: [:type]) do |type|
    type.eql?("sip") > schema(ConnectSipEndpoint)
  end

  rule(websocket_endpoint: [:type]) do |type|
    type.eql?("websocket") > schema(ConnectWebSocketEndpoint)
  end

  # We use this special `anything?` predicate to declare and whitelist the
  # attribute without setting any rules for they must look like. The values
  # are validated by our endpoint-specific schemas.
  optional(:number).value(:anything?)
  optional(:onAnswer).value(:anything?)
  optional(:dtmfAnswer).value(:anything?)
  optional(:uri).value(:anything?)
  optional("content-type").value(:anything?)
  optional(:headers).value(:anything?)
end
Connect =
Dry::Validation.Schema(BaseSchema) do
  required(:action).value(eql?: "connect")
  required(:endpoint).schema(ConnectEndpoint)
  optional(:from).value(:e164?)
  # TODO: What are the other options? (e.g. what is the default?)
  optional(:eventType).value(included_in?: ["synchronous"])
  # TODO: Are there any limitations on timeout?
  optional(:timeout).value(type?: Integer, gteq?: 1)
  optional(:limit).value(type?: Integer, gteq?: 1, lteq?: 7200)
  optional(:machineDetection).value(included_in?: %w[continue hangup])
  optional(:eventUrl).value(:http_or_https_url?)
  optional(:eventMethod).value(:supported_http_method?)
end
BaseSchema =
Dry::Validation.Schema do
  input :hash?, :strict_keys?

  configure do
    # `dry-validations` includes a bunch of "predicates" which you can use to
    # validate values. This includes some custom Nexmo-specific ones (e.g. for
    # phone digits and URLs).
    predicates(Predicates)

    # Used to validate that the input only includes keys that are defined in
    # the schema, implementing a slightly hacky "whitelisting" behaviour (which
    # for some reason isn't included in `dry-validations`!).
    #
    # In some places, we have to hack around this a bit by using our special
    # `:anything?` predicate to whitelist an attribute which we have no rules
    # to define about.
    def strict_keys?(input)
      (input.keys - rules.keys).empty?
    end

    config.messages_file = File.join(File.dirname(__FILE__), "../data/errors.yml")
  end
end
Conversation =
Dry::Validation.Schema(BaseSchema) do
  required(:action).value(eql?: "conversation")
  required(:name).value(:filled?, type?: String)
  optional(:musicOnHoldUrl).value(:http_or_https_url?)
  optional(:startOnEnter).value(:bool?)
  optional(:endOnExit).value(:bool?)
  optional(:record).value(:bool?)
  optional(:eventUrl).value(:http_or_https_url?)
  optional(:eventMethod).value(:supported_http_method?)
end