marathon-api 0.9.0 → 1.0.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/.simplecov +2 -2
- data/.travis.yml +1 -1
- data/README.md +40 -14
- data/bin/marathon +242 -0
- data/fixtures/marathon_docker_sample_2.json +2 -1
- data/fixtures/vcr/Marathon_App/_restart/restarts_an_app.yml +32 -0
- data/fixtures/vcr/Marathon_Group/_changes/changes_the_group.yml +61 -0
- data/fixtures/vcr/Marathon_Group/_delete/deletes_the_group.yml +32 -0
- data/fixtures/vcr/Marathon_Group/_delete/fails_deleting_not_existing_app.yml +32 -0
- data/fixtures/vcr/Marathon_Group/_get/fails_getting_not_existing_app.yml +32 -0
- data/fixtures/vcr/Marathon_Group/_get/gets_the_group.yml +32 -0
- data/fixtures/vcr/Marathon_Group/_list/lists_apps.yml +33 -0
- data/fixtures/vcr/Marathon_Group/_start/fails_getting_not_existing_group.yml +32 -0
- data/fixtures/vcr/Marathon_Group/_start/starts_the_group.yml +35 -0
- data/lib/marathon.rb +33 -5
- data/lib/marathon/app.rb +72 -22
- data/lib/marathon/base.rb +32 -0
- data/lib/marathon/connection.rb +32 -22
- data/lib/marathon/constraint.rb +39 -0
- data/lib/marathon/container.rb +41 -0
- data/lib/marathon/container_docker.rb +33 -0
- data/lib/marathon/container_docker_port_mapping.rb +32 -0
- data/lib/marathon/container_volume.rb +31 -0
- data/lib/marathon/deployment.rb +5 -15
- data/lib/marathon/deployment_info.rb +20 -0
- data/lib/marathon/error.rb +8 -2
- data/lib/marathon/group.rb +166 -0
- data/lib/marathon/health_check.rb +35 -0
- data/lib/marathon/queue.rb +4 -13
- data/lib/marathon/task.rb +15 -12
- data/lib/marathon/util.rb +47 -3
- data/lib/marathon/version.rb +1 -1
- data/marathon-api.gemspec +4 -3
- data/spec/marathon/app_spec.rb +108 -50
- data/spec/marathon/base_spec.rb +53 -0
- data/spec/marathon/connection_spec.rb +1 -1
- data/spec/marathon/constraint_spec.rb +27 -0
- data/spec/marathon/container_docker_port_mapping_spec.rb +55 -0
- data/spec/marathon/container_docker_spec.rb +42 -0
- data/spec/marathon/container_spec.rb +40 -0
- data/spec/marathon/container_volume_spec.rb +50 -0
- data/spec/marathon/deployment_info_spec.rb +43 -0
- data/spec/marathon/deployment_spec.rb +15 -16
- data/spec/marathon/error_spec.rb +17 -0
- data/spec/marathon/group_spec.rb +172 -0
- data/spec/marathon/health_check_spec.rb +50 -0
- data/spec/marathon/marathon_spec.rb +31 -0
- data/spec/marathon/queue_spec.rb +2 -2
- data/spec/marathon/task_spec.rb +24 -11
- data/spec/marathon/util_spec.rb +21 -1
- metadata +58 -6
@@ -0,0 +1,32 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: delete
|
5
|
+
uri: http://localhost:8080/v2/groups//test-group?force=true
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
Content-Type:
|
11
|
+
- application/json
|
12
|
+
Accept:
|
13
|
+
- application/json
|
14
|
+
User-Agent:
|
15
|
+
- ub0r/Marathon-API 0.9.0
|
16
|
+
response:
|
17
|
+
status:
|
18
|
+
code: 200
|
19
|
+
message: OK
|
20
|
+
headers:
|
21
|
+
Content-Type:
|
22
|
+
- application/json
|
23
|
+
Transfer-Encoding:
|
24
|
+
- chunked
|
25
|
+
Server:
|
26
|
+
- Jetty(8.y.z-SNAPSHOT)
|
27
|
+
body:
|
28
|
+
encoding: UTF-8
|
29
|
+
string: '{"version":"2015-03-09T10:33:40.335Z","deploymentId":"c5ad3775-a422-48f0-99ee-e7e7f098a832"}'
|
30
|
+
http_version:
|
31
|
+
recorded_at: Mon, 09 Mar 2015 10:33:40 GMT
|
32
|
+
recorded_with: VCR 2.9.3
|
@@ -0,0 +1,32 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: delete
|
5
|
+
uri: http://localhost:8080/v2/groups/fooo%20group
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
Content-Type:
|
11
|
+
- application/json
|
12
|
+
Accept:
|
13
|
+
- application/json
|
14
|
+
User-Agent:
|
15
|
+
- ub0r/Marathon-API 0.9.0
|
16
|
+
response:
|
17
|
+
status:
|
18
|
+
code: 404
|
19
|
+
message: Not Found
|
20
|
+
headers:
|
21
|
+
Content-Type:
|
22
|
+
- application/json
|
23
|
+
Transfer-Encoding:
|
24
|
+
- chunked
|
25
|
+
Server:
|
26
|
+
- Jetty(8.y.z-SNAPSHOT)
|
27
|
+
body:
|
28
|
+
encoding: UTF-8
|
29
|
+
string: '{"message":"Group ''/fooo group'' does not exist"}'
|
30
|
+
http_version:
|
31
|
+
recorded_at: Mon, 09 Mar 2015 10:33:40 GMT
|
32
|
+
recorded_with: VCR 2.9.3
|
@@ -0,0 +1,32 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: http://localhost:8080/v2/groups/fooo%20group
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
Content-Type:
|
11
|
+
- application/json
|
12
|
+
Accept:
|
13
|
+
- application/json
|
14
|
+
User-Agent:
|
15
|
+
- ub0r/Marathon-API 0.9.0
|
16
|
+
response:
|
17
|
+
status:
|
18
|
+
code: 404
|
19
|
+
message: Not Found
|
20
|
+
headers:
|
21
|
+
Content-Type:
|
22
|
+
- application/json
|
23
|
+
Transfer-Encoding:
|
24
|
+
- chunked
|
25
|
+
Server:
|
26
|
+
- Jetty(8.y.z-SNAPSHOT)
|
27
|
+
body:
|
28
|
+
encoding: UTF-8
|
29
|
+
string: '{"message":"Group ''/fooo group'' does not exist"}'
|
30
|
+
http_version:
|
31
|
+
recorded_at: Mon, 09 Mar 2015 10:33:40 GMT
|
32
|
+
recorded_with: VCR 2.9.3
|
@@ -0,0 +1,32 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: http://localhost:8080/v2/groups//test-group
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
Content-Type:
|
11
|
+
- application/json
|
12
|
+
Accept:
|
13
|
+
- application/json
|
14
|
+
User-Agent:
|
15
|
+
- ub0r/Marathon-API 0.9.0
|
16
|
+
response:
|
17
|
+
status:
|
18
|
+
code: 200
|
19
|
+
message: OK
|
20
|
+
headers:
|
21
|
+
Content-Type:
|
22
|
+
- application/json
|
23
|
+
Transfer-Encoding:
|
24
|
+
- chunked
|
25
|
+
Server:
|
26
|
+
- Jetty(8.y.z-SNAPSHOT)
|
27
|
+
body:
|
28
|
+
encoding: UTF-8
|
29
|
+
string: '{"id":"/test-group","apps":[{"id":"/test-group/app","cmd":"sleep 30","args":null,"user":null,"env":{},"instances":1,"cpus":1.0,"mem":128.0,"disk":0.0,"executor":"","constraints":[],"uris":[],"storeUrls":[],"ports":[10000],"requirePorts":false,"backoffSeconds":1,"backoffFactor":1.15,"maxLaunchDelaySeconds":3600,"container":null,"healthChecks":[],"dependencies":[],"upgradeStrategy":{"minimumHealthCapacity":1.0,"maximumOverCapacity":0.0},"labels":{},"version":"2015-03-09T10:33:39.889Z"}],"groups":[],"dependencies":[],"version":"2015-03-09T10:33:39.889Z"}'
|
30
|
+
http_version:
|
31
|
+
recorded_at: Mon, 09 Mar 2015 10:33:40 GMT
|
32
|
+
recorded_with: VCR 2.9.3
|
@@ -0,0 +1,33 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: http://localhost:8080/v2/groups
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
Content-Type:
|
11
|
+
- application/json
|
12
|
+
Accept:
|
13
|
+
- application/json
|
14
|
+
User-Agent:
|
15
|
+
- ub0r/Marathon-API 0.9.0
|
16
|
+
response:
|
17
|
+
status:
|
18
|
+
code: 200
|
19
|
+
message: OK
|
20
|
+
headers:
|
21
|
+
Content-Type:
|
22
|
+
- application/json
|
23
|
+
Transfer-Encoding:
|
24
|
+
- chunked
|
25
|
+
Server:
|
26
|
+
- Jetty(8.y.z-SNAPSHOT)
|
27
|
+
body:
|
28
|
+
encoding: UTF-8
|
29
|
+
string: '{"id":"/","apps":[],"groups":[{"id":"/ubuntu2","apps":[],"groups":[],"dependencies":[],"version":"2015-03-09T10:33:39.889Z"},{"id":"/test-group","apps":[{"id":"/test-group/app","cmd":"sleep
|
30
|
+
30","args":null,"user":null,"env":{},"instances":1,"cpus":1.0,"mem":128.0,"disk":0.0,"executor":"","constraints":[],"uris":[],"storeUrls":[],"ports":[10000],"requirePorts":false,"backoffSeconds":1,"backoffFactor":1.15,"maxLaunchDelaySeconds":3600,"container":null,"healthChecks":[],"dependencies":[],"upgradeStrategy":{"minimumHealthCapacity":1.0,"maximumOverCapacity":0.0},"labels":{},"version":"2015-03-09T10:33:39.889Z"}],"groups":[],"dependencies":[],"version":"2015-03-09T10:33:39.889Z"}],"dependencies":[],"version":"2015-03-09T10:33:39.889Z"}'
|
31
|
+
http_version:
|
32
|
+
recorded_at: Mon, 09 Mar 2015 10:33:40 GMT
|
33
|
+
recorded_with: VCR 2.9.3
|
@@ -0,0 +1,32 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: http://localhost:8080/v2/groups/fooo%20group
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
Content-Type:
|
11
|
+
- application/json
|
12
|
+
Accept:
|
13
|
+
- application/json
|
14
|
+
User-Agent:
|
15
|
+
- ub0r/Marathon-API 0.9.0
|
16
|
+
response:
|
17
|
+
status:
|
18
|
+
code: 404
|
19
|
+
message: Not Found
|
20
|
+
headers:
|
21
|
+
Content-Type:
|
22
|
+
- application/json
|
23
|
+
Transfer-Encoding:
|
24
|
+
- chunked
|
25
|
+
Server:
|
26
|
+
- Jetty(8.y.z-SNAPSHOT)
|
27
|
+
body:
|
28
|
+
encoding: UTF-8
|
29
|
+
string: '{"message":"Group ''/fooo group'' does not exist"}'
|
30
|
+
http_version:
|
31
|
+
recorded_at: Mon, 09 Mar 2015 10:33:40 GMT
|
32
|
+
recorded_with: VCR 2.9.3
|
@@ -0,0 +1,35 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: post
|
5
|
+
uri: http://localhost:8080/v2/groups
|
6
|
+
body:
|
7
|
+
encoding: UTF-8
|
8
|
+
string: '{"id":"/test-group","apps":[{"backoffFactor":1.15,"backoffSeconds":1,"maxLaunchDelaySeconds":3600,"cmd":"sleep
|
9
|
+
30","constraints":[],"cpus":1.0,"dependencies":[],"disk":0.0,"env":{},"executor":"","id":"app","instances":1,"mem":128.0,"ports":[10000],"requirePorts":false,"storeUrls":[],"upgradeStrategy":{"minimumHealthCapacity":1.0},"tasks":[]}],"dependencies":[],"groups":[]}'
|
10
|
+
headers:
|
11
|
+
Content-Type:
|
12
|
+
- application/json
|
13
|
+
Accept:
|
14
|
+
- application/json
|
15
|
+
User-Agent:
|
16
|
+
- ub0r/Marathon-API 0.9.0
|
17
|
+
response:
|
18
|
+
status:
|
19
|
+
code: 201
|
20
|
+
message: Created
|
21
|
+
headers:
|
22
|
+
Location:
|
23
|
+
- http://localhost:8080/v2/groups/test-group
|
24
|
+
Content-Type:
|
25
|
+
- application/json
|
26
|
+
Transfer-Encoding:
|
27
|
+
- chunked
|
28
|
+
Server:
|
29
|
+
- Jetty(8.y.z-SNAPSHOT)
|
30
|
+
body:
|
31
|
+
encoding: UTF-8
|
32
|
+
string: '{"version":"2015-03-09T10:33:39.889Z","deploymentId":"ad618509-e8ee-4949-8267-ef35a619426f"}'
|
33
|
+
http_version:
|
34
|
+
recorded_at: Mon, 09 Mar 2015 10:33:40 GMT
|
35
|
+
recorded_with: VCR 2.9.3
|
data/lib/marathon.rb
CHANGED
@@ -9,16 +9,25 @@ module Marathon
|
|
9
9
|
|
10
10
|
attr_accessor :logger
|
11
11
|
|
12
|
+
require 'marathon/version'
|
12
13
|
require 'marathon/util'
|
13
14
|
require 'marathon/error'
|
14
15
|
require 'marathon/connection'
|
16
|
+
require 'marathon/base'
|
17
|
+
require 'marathon/constraint'
|
18
|
+
require 'marathon/container_docker_port_mapping'
|
19
|
+
require 'marathon/container_docker'
|
20
|
+
require 'marathon/container_volume'
|
21
|
+
require 'marathon/container'
|
22
|
+
require 'marathon/health_check'
|
23
|
+
require 'marathon/deployment_info'
|
24
|
+
require 'marathon/group'
|
15
25
|
require 'marathon/app'
|
16
26
|
require 'marathon/deployment'
|
17
27
|
require 'marathon/event_subscriptions'
|
18
28
|
require 'marathon/leader'
|
19
29
|
require 'marathon/queue'
|
20
30
|
require 'marathon/task'
|
21
|
-
require 'marathon/version'
|
22
31
|
|
23
32
|
DEFAULT_URL = 'http://localhost:8080'
|
24
33
|
|
@@ -27,21 +36,40 @@ module Marathon
|
|
27
36
|
ENV['MARATHON_URL']
|
28
37
|
end
|
29
38
|
|
30
|
-
# Get
|
39
|
+
# Get marathon options from environment
|
40
|
+
def env_options
|
41
|
+
opts = {}
|
42
|
+
opts[:username] = ENV['MARATHON_USER'] if ENV['MARATHON_USER']
|
43
|
+
opts[:password] = ENV['MARATHON_PASSWORD'] if ENV['MARATHON_PASSWORD']
|
44
|
+
opts
|
45
|
+
end
|
46
|
+
|
47
|
+
# Get the marathon API URL
|
31
48
|
def url
|
32
49
|
@url ||= env_url || DEFAULT_URL
|
33
50
|
@url
|
34
51
|
end
|
35
52
|
|
53
|
+
# Get options for connecting to marathon API
|
54
|
+
def options
|
55
|
+
@options ||= env_options
|
56
|
+
end
|
57
|
+
|
36
58
|
# Set a new url
|
37
59
|
def url=(new_url)
|
38
60
|
@url = new_url
|
39
61
|
reset_connection!
|
40
62
|
end
|
41
63
|
|
64
|
+
# Set new options
|
65
|
+
def options=(new_options)
|
66
|
+
@options = env_options.merge(new_options || {})
|
67
|
+
reset_connection!
|
68
|
+
end
|
69
|
+
|
42
70
|
# Set a new connection
|
43
71
|
def connection
|
44
|
-
@connection ||= Connection.new(url)
|
72
|
+
@connection ||= Connection.new(url, options)
|
45
73
|
end
|
46
74
|
|
47
75
|
# Reset the connection
|
@@ -59,7 +87,7 @@ module Marathon
|
|
59
87
|
connection.get('/ping')
|
60
88
|
end
|
61
89
|
|
62
|
-
module_function :connection, :env_url, :info, :logger, :logger=,
|
63
|
-
:
|
90
|
+
module_function :connection, :env_options, :env_url, :info, :logger, :logger=, :ping,
|
91
|
+
:options, :options=, :url, :url= ,:reset_connection!
|
64
92
|
|
65
93
|
end
|
data/lib/marathon/app.rb
CHANGED
@@ -1,24 +1,29 @@
|
|
1
1
|
# This class represents a Marathon App.
|
2
2
|
# See https://mesosphere.github.io/marathon/docs/rest-api.html#apps for full list of API's methods.
|
3
|
-
class Marathon::App
|
3
|
+
class Marathon::App < Marathon::Base
|
4
4
|
|
5
|
-
|
5
|
+
ACCESSORS = %w[ id args cmd cpus disk env executor instances mem ports requirePorts
|
6
|
+
storeUris tasksRunning tasksStaged uris user version ]
|
7
|
+
|
8
|
+
DEFAULTS = {
|
9
|
+
:constraints => [],
|
10
|
+
:env => {},
|
11
|
+
:ports => [],
|
12
|
+
:uris => []
|
13
|
+
}
|
14
|
+
|
15
|
+
attr_reader :read_only
|
6
16
|
|
7
17
|
# Create a new application object.
|
8
18
|
# ++hash++: Hash including all attributes.
|
9
19
|
# See https://mesosphere.github.io/marathon/docs/rest-api.html#post-/v2/apps for full details.
|
10
20
|
# ++read_only++: prevent actions on this application
|
11
|
-
def initialize(hash
|
12
|
-
|
21
|
+
def initialize(hash, read_only = false)
|
22
|
+
super(Marathon::Util.merge_keywordized_hash(DEFAULTS, hash), ACCESSORS)
|
23
|
+
raise ArgumentError, 'App must have an id' unless id
|
13
24
|
@read_only = read_only
|
14
25
|
end
|
15
26
|
|
16
|
-
# Shortcuts for reaching attributes
|
17
|
-
%w[ id args cmd cpus disk env executor instances mem ports requirePorts
|
18
|
-
storeUris tasksRunning tasksStaged uris user version ].each do |method|
|
19
|
-
define_method(method) { |*args, &block| info[method] }
|
20
|
-
end
|
21
|
-
|
22
27
|
# Prevent actions on read only instances.
|
23
28
|
# Raises an ArgumentError when triying to change read_only instances.
|
24
29
|
def check_read_only
|
@@ -27,17 +32,38 @@ class Marathon::App
|
|
27
32
|
end
|
28
33
|
end
|
29
34
|
|
35
|
+
# Get container info.
|
36
|
+
# This is read only!
|
37
|
+
def container
|
38
|
+
if @info[:container]
|
39
|
+
Marathon::Container.new(@info[:container])
|
40
|
+
else
|
41
|
+
nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Get constrains.
|
46
|
+
# This is read only!
|
47
|
+
def constraints
|
48
|
+
@info[:constraints].map { |e| Marathon::Constraint.new(e) }
|
49
|
+
end
|
50
|
+
|
51
|
+
# Get health checks.
|
52
|
+
# This is read only!
|
53
|
+
def healthChecks
|
54
|
+
@info[:healthChecks].map { |e| Marathon::HealthCheck.new(e) }
|
55
|
+
end
|
56
|
+
|
30
57
|
# List all running tasks for the application.
|
31
|
-
#
|
58
|
+
# This is read only!
|
32
59
|
def tasks
|
33
60
|
check_read_only
|
34
|
-
unless @info[
|
61
|
+
unless @info[:tasks]
|
35
62
|
refresh
|
36
63
|
end
|
37
64
|
|
38
|
-
raise
|
39
|
-
|
40
|
-
@info['tasks'].map { |e| Marathon::Task.new(e) }
|
65
|
+
raise UnexpectedResponseError, "Expected to find tasks element in app's info" unless @info[:tasks]
|
66
|
+
@info[:tasks].map { |e| Marathon::Task.new(e) }
|
41
67
|
end
|
42
68
|
|
43
69
|
# List the versions of the application.
|
@@ -111,12 +137,36 @@ class Marathon::App
|
|
111
137
|
end
|
112
138
|
|
113
139
|
def to_s
|
114
|
-
"Marathon::App { :id => #{
|
140
|
+
"Marathon::App { :id => #{id} }"
|
141
|
+
end
|
142
|
+
|
143
|
+
# Returns a string for listing the application.
|
144
|
+
def to_pretty_s
|
145
|
+
%Q[
|
146
|
+
App ID: #{id}
|
147
|
+
Instances: #{tasks.size}/#{instances}
|
148
|
+
Command: #{cmd}
|
149
|
+
CPUs: #{cpus}
|
150
|
+
Memory: #{mem} MB
|
151
|
+
#{pretty_uris()}
|
152
|
+
#{pretty_env()}
|
153
|
+
#{pretty_constraints()}
|
154
|
+
Version: #{version}
|
155
|
+
].gsub(/\n\n+/, "\n").strip
|
156
|
+
end
|
157
|
+
|
158
|
+
private
|
159
|
+
|
160
|
+
def pretty_env
|
161
|
+
env.map { |k,v| "ENV: #{k}=#{v}" }.join("\n")
|
162
|
+
end
|
163
|
+
|
164
|
+
def pretty_uris
|
165
|
+
uris.map { |e| "URI: #{e}" }.join("\n")
|
115
166
|
end
|
116
167
|
|
117
|
-
|
118
|
-
|
119
|
-
info.to_json
|
168
|
+
def pretty_constraints
|
169
|
+
constraints.map { |e| "Constraint: #{e.to_pretty_s}" }.join("\n")
|
120
170
|
end
|
121
171
|
|
122
172
|
class << self
|
@@ -166,7 +216,7 @@ class Marathon::App
|
|
166
216
|
query = {}
|
167
217
|
query[:force] = true if force
|
168
218
|
json = Marathon.connection.post("/v2/apps/#{id}/restart", query)
|
169
|
-
|
219
|
+
Marathon::DeploymentInfo.new(json)
|
170
220
|
end
|
171
221
|
|
172
222
|
# Change parameters of a running application. The new application parameters apply only to subsequently
|
@@ -179,7 +229,7 @@ class Marathon::App
|
|
179
229
|
query = {}
|
180
230
|
query[:force] = true if force
|
181
231
|
json = Marathon.connection.put("/v2/apps/#{id}", query, :body => hash)
|
182
|
-
|
232
|
+
Marathon::DeploymentInfo.new(json)
|
183
233
|
end
|
184
234
|
|
185
235
|
# List the versions of the application with id.
|
@@ -197,4 +247,4 @@ class Marathon::App
|
|
197
247
|
new(json, true)
|
198
248
|
end
|
199
249
|
end
|
200
|
-
end
|
250
|
+
end
|