ncco 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -1
- data/Gemfile.lock +2 -2
- data/lib/ncco.rb +24 -16
- data/lib/ncco/data/errors.yml +3 -1
- data/lib/ncco/predicates.rb +10 -0
- data/lib/ncco/schemas/base_schema.rb +5 -1
- data/lib/ncco/schemas/connect.rb +41 -7
- data/lib/ncco/utils.rb +32 -0
- data/lib/ncco/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8e630a1ba1f9ba6e1d6e7e77a733ae22e28e699d49bfdb594ab5a450817f2575
|
4
|
+
data.tar.gz: 1bfda713d0bf605db1565781aaff68971d4056f73bccb9b745f1a3b4c313452f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4fa63d3d0c007fcd89b991494f3c0b3790e30d16852669a705f277500a86a82e9e1b4a96f00887af762fcfaa5d717ef998b6178e6881b389ad4b3180eabd2069
|
7
|
+
data.tar.gz: f6b5d7979e7d1d2321c289a45fe150c26937f5bc85dc43c4ed434a34097cfc1e5627676d4d3102d4aa4b34027ac5e042a067fdd2da9b8b7bf5b45f80c132c9e3
|
data/.rubocop.yml
CHANGED
data/Gemfile.lock
CHANGED
data/lib/ncco.rb
CHANGED
@@ -10,6 +10,7 @@ require "ncco/schemas/input"
|
|
10
10
|
require "ncco/schemas/record"
|
11
11
|
require "ncco/schemas/stream"
|
12
12
|
require "ncco/schemas/talk"
|
13
|
+
require "ncco/utils"
|
13
14
|
|
14
15
|
module NCCO
|
15
16
|
class InvalidActionError < StandardError; end
|
@@ -42,7 +43,7 @@ module NCCO
|
|
42
43
|
# invalid and why.
|
43
44
|
def self.build(actions)
|
44
45
|
actions.
|
45
|
-
map { |action|
|
46
|
+
map { |action| Utils.deep_transform_keys_to_symbols(action) }.
|
46
47
|
each_with_index { |action, index| validate_action!(action, index: index) }
|
47
48
|
|
48
49
|
actions
|
@@ -65,9 +66,9 @@ module NCCO
|
|
65
66
|
end
|
66
67
|
|
67
68
|
result = schema.call(action)
|
68
|
-
|
69
|
+
error_message = get_error_message_from_result(result)
|
69
70
|
|
70
|
-
raise_invalid_error(
|
71
|
+
raise_invalid_error(error_message, index: index) if error_message
|
71
72
|
end
|
72
73
|
|
73
74
|
# Raises an InvalidActionError, featuring the human-readable index of the action
|
@@ -78,7 +79,7 @@ module NCCO
|
|
78
79
|
# object
|
79
80
|
# @raise [InvalidActionError]
|
80
81
|
def raise_invalid_error(error_message, index:)
|
81
|
-
raise InvalidActionError, "The #{
|
82
|
+
raise InvalidActionError, "The #{ordinalised_number(index + 1)} action is " \
|
82
83
|
"invalid: #{error_message}"
|
83
84
|
end
|
84
85
|
|
@@ -89,8 +90,8 @@ module NCCO
|
|
89
90
|
# @param number [Integer] the number to fetch the ordinal string for
|
90
91
|
# @return [String] the number, followed by the ordinal string corresponding to the
|
91
92
|
# number
|
92
|
-
def
|
93
|
-
"#{number}#{
|
93
|
+
def ordinalised_number(number)
|
94
|
+
"#{number}#{ordinal_string_for_number(number)}"
|
94
95
|
end
|
95
96
|
|
96
97
|
# Turns a number into an "ordinal string" used to denote its position in an ordered
|
@@ -98,7 +99,7 @@ module NCCO
|
|
98
99
|
#
|
99
100
|
# @param number [Integer] the number to fetch the ordinal string for
|
100
101
|
# @return [String] the ordinal string corresponding to the number
|
101
|
-
def
|
102
|
+
def ordinal_string_for_number(number)
|
102
103
|
case number.digits.last
|
103
104
|
when 0 then "th"
|
104
105
|
when 1 then "st"
|
@@ -108,19 +109,26 @@ module NCCO
|
|
108
109
|
end
|
109
110
|
end
|
110
111
|
|
111
|
-
# Gets the error messages from `Dry::Validation::Result
|
112
|
-
#
|
113
|
-
# `String` error messages, whereas for unrecognised attributes (which is a bit of a
|
114
|
-
# hack), we just get back an array. This handles either gracefully.
|
112
|
+
# Gets the error messages from a `Dry::Validation::Result`, if there is one, dealing
|
113
|
+
# with the slightly mad `Result` API
|
115
114
|
#
|
116
|
-
# @param result [Dry::
|
115
|
+
# @param result [Dry::Validation::Result] the result from validating an action against
|
117
116
|
# a schema
|
118
|
-
# @return [
|
119
|
-
def
|
117
|
+
# @return [String, nil] an error message to display, if there was an error
|
118
|
+
def get_error_message_from_result(result)
|
120
119
|
error_messages = result.messages(full: true)
|
121
|
-
return
|
120
|
+
return if error_messages.none?
|
122
121
|
|
123
|
-
error_messages
|
122
|
+
transform_error_message(error_messages)
|
123
|
+
end
|
124
|
+
|
125
|
+
def transform_error_message(error_messages)
|
126
|
+
case error_messages
|
127
|
+
when String then errror_messages
|
128
|
+
when Array then error_messages.first
|
129
|
+
when Hash then transform_error_message(error_messages.values.first)
|
130
|
+
else raise ArgumentError, "Unable to parse error message"
|
131
|
+
end
|
124
132
|
end
|
125
133
|
end
|
126
134
|
end
|
data/lib/ncco/data/errors.yml
CHANGED
@@ -7,4 +7,6 @@ en:
|
|
7
7
|
phone_keypad_digits?: must be a series of one or more digits from a phone keypad, as a string
|
8
8
|
e164?: must be a valid E.164-formatted phone number
|
9
9
|
websocket_url?: must be a valid Websocket URL
|
10
|
-
|
10
|
+
sip_uri?: must be a valid SIP URI
|
11
|
+
hash_with_string_keys_and_values?: must be a hash with String keys and values
|
12
|
+
anything?: can be anything
|
data/lib/ncco/predicates.rb
CHANGED
@@ -19,6 +19,12 @@ module NCCO
|
|
19
19
|
false
|
20
20
|
end
|
21
21
|
|
22
|
+
SIP_URI_REGEX = /\Asip:/i.freeze
|
23
|
+
|
24
|
+
predicate(:sip_uri?) do |value|
|
25
|
+
value =~ SIP_URI_REGEX
|
26
|
+
end
|
27
|
+
|
22
28
|
# TODO: Check what HTTP methods are supported by Nexmo - presumably not the full set?
|
23
29
|
# <https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods>
|
24
30
|
predicate(:supported_http_method?) do |value|
|
@@ -49,5 +55,9 @@ module NCCO
|
|
49
55
|
value.keys.map(&:class).uniq == [String] &&
|
50
56
|
value.values.map(&:class).uniq == [String]
|
51
57
|
end
|
58
|
+
|
59
|
+
predicate(:anything?) do |_|
|
60
|
+
true
|
61
|
+
end
|
52
62
|
end
|
53
63
|
end
|
@@ -13,7 +13,11 @@ module NCCO
|
|
13
13
|
|
14
14
|
# Used to validate that the input only includes keys that are defined in
|
15
15
|
# the schema, implementing a slightly hacky "whitelisting" behaviour (which
|
16
|
-
# for some reason isn't
|
16
|
+
# for some reason isn't included in `dry-validations`!).
|
17
|
+
#
|
18
|
+
# In some places, we have to hack around this a bit by using our special
|
19
|
+
# `:anything?` predicate to whitelist an attribute which we have no rules
|
20
|
+
# to define about.
|
17
21
|
def strict_keys?(input)
|
18
22
|
(input.keys - rules.keys).empty?
|
19
23
|
end
|
data/lib/ncco/schemas/connect.rb
CHANGED
@@ -2,21 +2,55 @@
|
|
2
2
|
|
3
3
|
module NCCO
|
4
4
|
module Schemas
|
5
|
-
|
6
|
-
|
7
|
-
required(:type).value(included_in?: %w[phone websocket sip])
|
5
|
+
ConnectPhoneEndpoint = Dry::Validation.Schema(BaseSchema) do
|
6
|
+
required(:type).value(eql?: "phone")
|
8
7
|
|
9
|
-
|
10
|
-
optional(:number).value(:e164?)
|
8
|
+
required(:number).value(:e164?)
|
11
9
|
optional(:onAnswer).value(:http_or_https_url?)
|
12
10
|
optional(:dtmfAnswer).value(:phone_keypad_digits?)
|
11
|
+
end
|
12
|
+
|
13
|
+
ConnectSipEndpoint = Dry::Validation.Schema(BaseSchema) do
|
14
|
+
required(:type).value(eql?: "sip")
|
13
15
|
|
14
|
-
|
15
|
-
|
16
|
+
required(:uri).value(:sip_uri?)
|
17
|
+
end
|
18
|
+
|
19
|
+
ConnectWebSocketEndpoint = Dry::Validation.Schema(BaseSchema) do
|
20
|
+
required(:type).value(eql?: "websocket")
|
21
|
+
|
22
|
+
required(:uri).value(:websocket_url?)
|
16
23
|
optional("content-type").value(eql?: "audio/l16;rate=16000")
|
17
24
|
optional(:headers).value(:hash_with_string_keys_and_values?)
|
18
25
|
end
|
19
26
|
|
27
|
+
ConnectEndpoint = Dry::Validation.Schema(BaseSchema) do
|
28
|
+
required(:type).value(included_in?: %w[phone websocket sip])
|
29
|
+
|
30
|
+
# How we validate the endpoint (i.e. what schema we should use) depends on the type
|
31
|
+
rule(phone_endpoint: [:type]) do |type|
|
32
|
+
type.eql?("phone") > schema(ConnectPhoneEndpoint)
|
33
|
+
end
|
34
|
+
|
35
|
+
rule(sip_endpoint: [:type]) do |type|
|
36
|
+
type.eql?("sip") > schema(ConnectSipEndpoint)
|
37
|
+
end
|
38
|
+
|
39
|
+
rule(websocket_endpoint: [:type]) do |type|
|
40
|
+
type.eql?("websocket") > schema(ConnectWebSocketEndpoint)
|
41
|
+
end
|
42
|
+
|
43
|
+
# We use this special `anything?` predicate to declare and whitelist the
|
44
|
+
# attribute without setting any rules for they must look like. The values
|
45
|
+
# are validated by our endpoint-specific schemas.
|
46
|
+
optional(:number).value(:anything?)
|
47
|
+
optional(:onAnswer).value(:anything?)
|
48
|
+
optional(:dtmfAnswer).value(:anything?)
|
49
|
+
optional(:uri).value(:anything?)
|
50
|
+
optional("content-type").value(:anything?)
|
51
|
+
optional(:headers).value(:anything?)
|
52
|
+
end
|
53
|
+
|
20
54
|
Connect = Dry::Validation.Schema(BaseSchema) do
|
21
55
|
required(:action).value(eql?: "connect")
|
22
56
|
required(:endpoint).schema(ConnectEndpoint)
|
data/lib/ncco/utils.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NCCO
|
4
|
+
module Utils
|
5
|
+
# Transforms the keys of a Hash with the provided block recursively, walking through
|
6
|
+
# nested hashes
|
7
|
+
#
|
8
|
+
# @param hash [Hash] the hash to transform
|
9
|
+
# @yieldparam the key to transform
|
10
|
+
# @return [Hash] the transformed hash, with the block recursively applied to its keys
|
11
|
+
def self.deep_transform_keys(hash, &block)
|
12
|
+
result = {}
|
13
|
+
|
14
|
+
hash.each do |key, value|
|
15
|
+
result[yield(key)] = if value.is_a?(Hash)
|
16
|
+
deep_transform_keys(value, &block)
|
17
|
+
else
|
18
|
+
value
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
result
|
23
|
+
end
|
24
|
+
|
25
|
+
# Transforms the keys of Hash into symbols recursively, walking through nested hashes
|
26
|
+
#
|
27
|
+
# @param hash [Hash] the hash to transform
|
28
|
+
def self.deep_transform_keys_to_symbols(hash)
|
29
|
+
deep_transform_keys(hash, &:to_sym)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/ncco/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ncco
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tim Rogers
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-11-
|
11
|
+
date: 2018-11-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dry-validation
|
@@ -153,6 +153,7 @@ files:
|
|
153
153
|
- lib/ncco/schemas/record.rb
|
154
154
|
- lib/ncco/schemas/stream.rb
|
155
155
|
- lib/ncco/schemas/talk.rb
|
156
|
+
- lib/ncco/utils.rb
|
156
157
|
- lib/ncco/version.rb
|
157
158
|
- ncco.gemspec
|
158
159
|
homepage: https://github.com/timrogers/ncco-ruby
|