ncco 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|