glib-web 0.0.20 → 0.0.21
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 +5 -5
- data/app/controllers/concerns/application/json/libs.rb +0 -0
- data/app/controllers/concerns/application/json/transformation.rb +0 -0
- data/app/controllers/concerns/application/json/ui.rb +0 -0
- data/app/controllers/concerns/application/json/validation.rb +0 -0
- data/app/controllers/glib/home_controller.rb +0 -0
- data/app/helpers/glib/json_ui/abstract_builder.rb +0 -0
- data/app/helpers/glib/json_ui/action_builder.rb +0 -0
- data/app/helpers/glib/json_ui/json_ui_helper.rb +0 -0
- data/app/helpers/glib/json_ui/list_builders.rb +0 -0
- data/app/helpers/glib/json_ui/menu_builder.rb +0 -0
- data/app/helpers/glib/json_ui/page_helper.rb +0 -0
- data/app/helpers/glib/json_ui/response_helper.rb +0 -0
- data/app/helpers/glib/json_ui/table_builders.rb +0 -0
- data/app/helpers/glib/json_ui/view_builder/fields.rb +0 -0
- data/app/helpers/glib/json_ui/view_builder/panels.rb +0 -0
- data/app/helpers/glib/json_ui/view_builder.rb +1 -1
- data/app/views/app/views/json_ui/vue/renderer.html.erb +0 -0
- data/app/views/json_ui/garage/_nav_menu.json.jbuilder +0 -0
- data/app/views/json_ui/garage/actions/index.json.jbuilder +6 -6
- data/app/views/json_ui/garage/forms/basic.json.jbuilder +0 -0
- data/app/views/json_ui/garage/forms/basic_post.json.jbuilder +0 -0
- data/app/views/json_ui/garage/forms/file_upload.json.jbuilder +0 -0
- data/app/views/json_ui/garage/forms/floating_submit.json.jbuilder +0 -0
- data/app/views/json_ui/garage/forms/generic_post.json.jbuilder +0 -0
- data/app/views/json_ui/garage/forms/index.json.jbuilder +0 -0
- data/app/views/json_ui/garage/forms/submit_indicator.json.jbuilder +0 -0
- data/app/views/json_ui/garage/forms/submit_indicator_post.json.jbuilder +0 -0
- data/app/views/json_ui/garage/home/index.json.jbuilder +0 -0
- data/app/views/json_ui/garage/lists/_infinite_scroll_section.json.jbuilder +0 -0
- data/app/views/json_ui/garage/lists/index.json.jbuilder +0 -0
- data/app/views/json_ui/garage/lists/infinite_scroll.json.jbuilder +0 -0
- data/app/views/json_ui/garage/lists/templating.json.jbuilder +0 -0
- data/app/views/json_ui/garage/pages/full_width_height.json.jbuilder +0 -0
- data/app/views/json_ui/garage/pages/index.json.jbuilder +0 -0
- data/app/views/json_ui/garage/pages/layout.json.jbuilder +0 -0
- data/app/views/json_ui/garage/pages/nav_buttons.json.jbuilder +0 -0
- data/app/views/json_ui/garage/pages/tab_bar.json.jbuilder +0 -0
- data/app/views/json_ui/garage/panels/carousel.json.jbuilder +0 -0
- data/app/views/json_ui/garage/panels/horizontal.json.jbuilder +0 -0
- data/app/views/json_ui/garage/panels/index.json.jbuilder +0 -0
- data/app/views/json_ui/garage/panels/split.json.jbuilder +0 -0
- data/app/views/json_ui/garage/panels/vertical.json.jbuilder +0 -0
- data/app/views/json_ui/garage/views/basic.json.jbuilder +1 -1
- data/app/views/json_ui/garage/views/carousels.json.jbuilder +0 -0
- data/app/views/json_ui/garage/views/images.json.jbuilder +0 -0
- data/app/views/json_ui/garage/views/index.json.jbuilder +0 -0
- data/app/views/json_ui/garage/views/map_data.json.jbuilder +30 -0
- data/config/routes.rb +0 -0
- data/lib/glib/engine.rb +0 -0
- data/lib/glib/json_crawler/action_crawler.rb +33 -0
- data/lib/glib/json_crawler/action_crawlers/action_http.rb +14 -0
- data/lib/glib/json_crawler/action_crawlers/nav_initiate.rb +14 -0
- data/lib/glib/json_crawler/action_crawlers/windows_open.rb +18 -0
- data/lib/glib/json_crawler/http.rb +98 -0
- data/lib/glib/json_crawler/router.rb +43 -0
- data/lib/glib/json_crawler.rb +7 -0
- data/lib/glib/value.rb +0 -0
- data/lib/glib/version.rb +0 -0
- data/lib/glib-web.rb +1 -0
- metadata +10 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: '095a0c14d85e8c91b506ea4cef39fece00672209c3adc99cab6289ee1eeb6141'
|
|
4
|
+
data.tar.gz: bc0fd6f195c6bc50d5f3f5bba81d9d0ad3cf3bc3dd0ffab277277c2d156dcff6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d3290e5b381ea98e0fac49a3bab0f3ba7c4aca018f057fe96ff44e75bce11f0a21f4a439e01120bb391ce1c21e6316c74cdd738d62c520e3b6c0c40f4b46769f
|
|
7
|
+
data.tar.gz: 9b61796a396e1da87d985f8d5cdd1a435cfb0d52f425701d9f924b1f37297179efbd335de76772269fa13ab6c2a1e599f6d7541794752586f368e785fcd849b4
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -3,13 +3,13 @@ json.title 'Actions'
|
|
|
3
3
|
render "#{@path_prefix}/nav_menu", json: json
|
|
4
4
|
|
|
5
5
|
json_ui_page json do |page|
|
|
6
|
-
|
|
6
|
+
|
|
7
7
|
page.list firstSection: ->(section) do
|
|
8
8
|
section.rows do |row|
|
|
9
9
|
row.thumbnail title: 'dialogs/alert', onClick: ->(action) do
|
|
10
10
|
action.dialogs_alert message: 'This is an alert'
|
|
11
11
|
end
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
# row.thumbnail title: 'dialogs/confirm (TODO)', onClick: ->(action) do
|
|
14
14
|
# action.dialogs_confirm message: 'Confirm?', onConfirm: ->(action) do
|
|
15
15
|
# action.dialogs_alert message: 'Good'
|
|
@@ -20,10 +20,10 @@ json_ui_page json do |page|
|
|
|
20
20
|
|
|
21
21
|
row.thumbnail title: 'dialogs/option', onClick: ->(action) do
|
|
22
22
|
action.dialogs_options message: 'Select one', buttons: ->(menu) do
|
|
23
|
-
menu.button title: 'Option1', onClick: ->(action) do
|
|
23
|
+
menu.button title: 'Option1', onClick: ->(action) do
|
|
24
24
|
action.dialogs_alert message: 'Option 1'
|
|
25
25
|
end
|
|
26
|
-
menu.button title: 'Option2', onClick: ->(action) do
|
|
26
|
+
menu.button title: 'Option2', onClick: ->(action) do
|
|
27
27
|
action.dialogs_alert message: 'Option 2'
|
|
28
28
|
end
|
|
29
29
|
menu.button title: 'Cancel'
|
|
@@ -34,11 +34,11 @@ json_ui_page json do |page|
|
|
|
34
34
|
action.dialogs_open url: json_ui_garage_url(path: 'forms/basic')
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
row.thumbnail title: 'dialogs/snackbar
|
|
37
|
+
row.thumbnail title: 'dialogs/snackbar', onClick: ->(action) do
|
|
38
38
|
action.dialogs_snackbar message: 'This is a snackbar'
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
end
|
|
42
|
-
|
|
42
|
+
|
|
43
43
|
end
|
|
44
44
|
end
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -6,7 +6,7 @@ json_ui_page json do |page|
|
|
|
6
6
|
page.scroll childViews: ->(scroll) do
|
|
7
7
|
scroll.label text: "\n"
|
|
8
8
|
scroll.h1 text: 'Map'
|
|
9
|
-
scroll.map latitude: 13.4837, longitude: 144.7917, zoom: 11, height: '
|
|
9
|
+
scroll.map latitude: 13.4837, longitude: 144.7917, zoom: 11, height: 250, dataUrl: json_ui_garage_url(path: 'views/map_data')
|
|
10
10
|
end
|
|
11
11
|
end
|
|
12
12
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
|
|
2
|
+
json.pins do
|
|
3
|
+
json.child! do
|
|
4
|
+
json.latitude 13.516680
|
|
5
|
+
json.longitude 144.817322
|
|
6
|
+
json.infoWindow do
|
|
7
|
+
json.title 'Place 1'
|
|
8
|
+
json.subtitle 'Big shopping mall'
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
json.child! do
|
|
13
|
+
json.latitude 13.473001
|
|
14
|
+
json.longitude 144.771303
|
|
15
|
+
json.infoWindow do
|
|
16
|
+
json.title 'Place 2'
|
|
17
|
+
json.subtitle 'High class restaurant'
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
json.child! do
|
|
22
|
+
json.latitude 13.375903
|
|
23
|
+
json.longitude 144.749791
|
|
24
|
+
json.infoWindow do
|
|
25
|
+
json.title 'Place 3'
|
|
26
|
+
json.subtitle 'High class restaurant'
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
end
|
data/config/routes.rb
CHANGED
|
File without changes
|
data/lib/glib/engine.rb
CHANGED
|
File without changes
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module Glib
|
|
2
|
+
module JsonCrawler
|
|
3
|
+
class ActionCrawler
|
|
4
|
+
def click object
|
|
5
|
+
JsonCrawler::Router.step(@http, object)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def crawl views
|
|
9
|
+
if views.is_a? Array
|
|
10
|
+
views.each do |view|
|
|
11
|
+
click view
|
|
12
|
+
|
|
13
|
+
# Generic view children
|
|
14
|
+
crawl view['childViews']
|
|
15
|
+
|
|
16
|
+
# panels/split
|
|
17
|
+
crawl view['leftViews']
|
|
18
|
+
crawl view['rightViews']
|
|
19
|
+
|
|
20
|
+
# Table/List
|
|
21
|
+
if (sections = view['sections']).is_a? Array
|
|
22
|
+
sections.each do |section|
|
|
23
|
+
# Table
|
|
24
|
+
crawl section['rows']
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module Glib
|
|
2
|
+
module JsonCrawler
|
|
3
|
+
class ActionHttp < ActionCrawler
|
|
4
|
+
def initialize(method, http, args, controller)
|
|
5
|
+
@http = http
|
|
6
|
+
json = @http.send(method, args['url'], controller, args.fetch('formData', {}))
|
|
7
|
+
|
|
8
|
+
unless json.nil?
|
|
9
|
+
click(json)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module Glib
|
|
2
|
+
module JsonCrawler
|
|
3
|
+
class NavInitiate < ActionCrawler
|
|
4
|
+
def initialize http, args, controller
|
|
5
|
+
@http = http
|
|
6
|
+
json = @http.get args['url'], controller
|
|
7
|
+
|
|
8
|
+
unless json.nil?
|
|
9
|
+
crawl json['leftDrawer']&.[]('items')
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Glib
|
|
2
|
+
module JsonCrawler
|
|
3
|
+
class WindowsOpen < ActionCrawler
|
|
4
|
+
def initialize http, args, controller
|
|
5
|
+
@http = http
|
|
6
|
+
json = @http.get args['url'], controller
|
|
7
|
+
|
|
8
|
+
unless json.nil?
|
|
9
|
+
crawl json['content']&.[]('childViews')
|
|
10
|
+
|
|
11
|
+
json['rightNavButtons']&.each do |button|
|
|
12
|
+
click button
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
module Glib
|
|
2
|
+
module JsonCrawler
|
|
3
|
+
class Http
|
|
4
|
+
attr_accessor :history, :response_times, :user
|
|
5
|
+
|
|
6
|
+
URI_REGEXP = /\A#{URI::regexp}\z/
|
|
7
|
+
|
|
8
|
+
VALID_RESPONSE_CODES = [
|
|
9
|
+
(200..299).to_a,
|
|
10
|
+
## Note, the JSON API does not allow redirects
|
|
11
|
+
401, ## UNAUTHORIZED Should be used for not-logged-in
|
|
12
|
+
403, ## FORBIDDEN Should be used for logged-in but not allowed to access
|
|
13
|
+
].flatten
|
|
14
|
+
|
|
15
|
+
def initialize(context, user)
|
|
16
|
+
@context = context
|
|
17
|
+
@history = []
|
|
18
|
+
@response_times = []
|
|
19
|
+
@user = user
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def get(url, controller)
|
|
23
|
+
|
|
24
|
+
http_header = {
|
|
25
|
+
'Client-DeviceOS' => user[:device],
|
|
26
|
+
'Client-Version' => user[:version],
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if url.blank? || history.include?(url)
|
|
30
|
+
nil
|
|
31
|
+
else
|
|
32
|
+
@context.assert_match URI_REGEXP, url
|
|
33
|
+
history << url
|
|
34
|
+
@context.get url, params: {}, headers: http_header
|
|
35
|
+
response = @context.response
|
|
36
|
+
JsonCrawler::Router.log controller, url, response
|
|
37
|
+
if (code = response.code.to_i) == 302
|
|
38
|
+
redirect_uri = URI(response.headers['Location'])
|
|
39
|
+
redirect_uri.query = [redirect_uri.query, "format=json"].compact.join('&')
|
|
40
|
+
return get redirect_uri.to_s, controller
|
|
41
|
+
else
|
|
42
|
+
response_times << response.headers['X-Runtime'].to_f
|
|
43
|
+
@context.assert_includes VALID_RESPONSE_CODES, code, "Expected a valid response but was [#{response.code}] #{Rack::Utils::HTTP_STATUS_CODES[response.code.to_i]}:\n#{url}"
|
|
44
|
+
if response.content_type == 'application/json'
|
|
45
|
+
JSON.parse(response.body)
|
|
46
|
+
else
|
|
47
|
+
nil
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# response_times << response.headers['X-Runtime'].to_f
|
|
52
|
+
# @context.assert_includes VALID_RESPONSE_CODES, response.code.to_i, "Expected a valid response but was [#{response.code}] #{Rack::Utils::HTTP_STATUS_CODES[response.code.to_i]}:\n#{url}"
|
|
53
|
+
# if response.content_type == 'application/json'
|
|
54
|
+
# return response, JSON.parse(response.body)
|
|
55
|
+
# else
|
|
56
|
+
# return response, nil
|
|
57
|
+
# end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def post(url, controller, params)
|
|
62
|
+
fetch(:post, url, controller, params)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def delete(url, controller, params = {})
|
|
66
|
+
fetch(:delete, url, controller, {})
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
def fetch(method, url, controller, params = {})
|
|
71
|
+
if url.blank? || history.include?(url)
|
|
72
|
+
nil
|
|
73
|
+
else
|
|
74
|
+
history << url
|
|
75
|
+
@context.assert_match(URI_REGEXP, url)
|
|
76
|
+
|
|
77
|
+
http_header = {
|
|
78
|
+
'Client-DeviceOS' => user[:device],
|
|
79
|
+
'Client-Version' => user[:version],
|
|
80
|
+
'X-CSRF-Token' => user[:token]
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
@context.send(method, url, params: params, headers: http_header)
|
|
84
|
+
response = @context.response
|
|
85
|
+
response_times << response.headers['X-Runtime'].to_f
|
|
86
|
+
@context.assert_includes VALID_RESPONSE_CODES, response.code.to_i, "Expected a valid response but was [#{response.code}] #{Rack::Utils::HTTP_STATUS_CODES[response.code.to_i]}:\n#{url}"
|
|
87
|
+
JsonCrawler::Router.log(controller, url, response)
|
|
88
|
+
|
|
89
|
+
if response.content_type == 'application/json'
|
|
90
|
+
JSON.parse(response.body)
|
|
91
|
+
else
|
|
92
|
+
nil
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module Glib
|
|
2
|
+
module JsonCrawler
|
|
3
|
+
class Router
|
|
4
|
+
cattr_accessor :depth
|
|
5
|
+
cattr_accessor :logger
|
|
6
|
+
|
|
7
|
+
def self.log controller, url, response = nil
|
|
8
|
+
@@logger.puts ' ' * @@depth + [
|
|
9
|
+
controller,
|
|
10
|
+
response.present? ? response.code : nil,
|
|
11
|
+
url
|
|
12
|
+
].compact.join(' :: ')
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.step(http, args)
|
|
16
|
+
if args.is_a?(Hash) && args['rel'] != 'nofollow'
|
|
17
|
+
if (onClick = (args.fetch('onClick', nil) || args.fetch('onResponse', nil)))
|
|
18
|
+
controller = onClick['action']
|
|
19
|
+
params = onClick
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
if controller.present?
|
|
23
|
+
@@depth += 1
|
|
24
|
+
child = case controller
|
|
25
|
+
when 'initiate_navigation'
|
|
26
|
+
JsonCrawler::NavInitiate.new(http, params, controller)
|
|
27
|
+
when 'windows/open-v1'
|
|
28
|
+
JsonCrawler::WindowsOpen.new(http, params, controller)
|
|
29
|
+
when 'http/delete-v1'
|
|
30
|
+
JsonCrawler::ActionHttp.new(:delete, http, params, controller)
|
|
31
|
+
when 'http/post-v1'
|
|
32
|
+
JsonCrawler::ActionHttp.new(:post, http, params, controller)
|
|
33
|
+
else
|
|
34
|
+
self.log controller, params['url']
|
|
35
|
+
end
|
|
36
|
+
@@depth -= 1
|
|
37
|
+
return child
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
require_relative './json_crawler/http'
|
|
2
|
+
require_relative './json_crawler/router'
|
|
3
|
+
require_relative './json_crawler/action_crawler'
|
|
4
|
+
|
|
5
|
+
require_relative './json_crawler/action_crawlers/nav_initiate'
|
|
6
|
+
require_relative './json_crawler/action_crawlers/windows_open'
|
|
7
|
+
require_relative './json_crawler/action_crawlers/action_http'
|
data/lib/glib/value.rb
CHANGED
|
File without changes
|
data/lib/glib/version.rb
CHANGED
|
File without changes
|
data/lib/glib-web.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: glib-web
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.21
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- ''
|
|
@@ -76,9 +76,17 @@ files:
|
|
|
76
76
|
- app/views/json_ui/garage/views/carousels.json.jbuilder
|
|
77
77
|
- app/views/json_ui/garage/views/images.json.jbuilder
|
|
78
78
|
- app/views/json_ui/garage/views/index.json.jbuilder
|
|
79
|
+
- app/views/json_ui/garage/views/map_data.json.jbuilder
|
|
79
80
|
- config/routes.rb
|
|
80
81
|
- lib/glib-web.rb
|
|
81
82
|
- lib/glib/engine.rb
|
|
83
|
+
- lib/glib/json_crawler.rb
|
|
84
|
+
- lib/glib/json_crawler/action_crawler.rb
|
|
85
|
+
- lib/glib/json_crawler/action_crawlers/action_http.rb
|
|
86
|
+
- lib/glib/json_crawler/action_crawlers/nav_initiate.rb
|
|
87
|
+
- lib/glib/json_crawler/action_crawlers/windows_open.rb
|
|
88
|
+
- lib/glib/json_crawler/http.rb
|
|
89
|
+
- lib/glib/json_crawler/router.rb
|
|
82
90
|
- lib/glib/value.rb
|
|
83
91
|
- lib/glib/version.rb
|
|
84
92
|
homepage:
|
|
@@ -100,7 +108,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
100
108
|
version: '0'
|
|
101
109
|
requirements: []
|
|
102
110
|
rubyforge_project:
|
|
103
|
-
rubygems_version: 2.6
|
|
111
|
+
rubygems_version: 2.7.6
|
|
104
112
|
signing_key:
|
|
105
113
|
specification_version: 4
|
|
106
114
|
summary: ''
|