glib-web 5.0.4 → 5.0.5
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/app/helpers/glib/json_ui/view_builder/fields.rb +6 -2
- data/app/models/concerns/glib/enum_symbolization.rb +26 -0
- data/app/models/glib/application_record.rb +1 -0
- data/app/views/json_ui/garage/test_page/_header.json.jbuilder +31 -28
- data/app/views/json_ui/garage/test_page/auth.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/auto_validate.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/browsers.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/calendar.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/carousel.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/charts.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/column.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/commands.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/components.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/cookies.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/custom.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/dialog.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/dirty_state.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/fields.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/fields_captcha.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/fields_creditCard.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/fields_date_time.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/fields_dynamicSelect.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/fields_location.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/fields_otp.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/fields_phone.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/fields_radio.json.jbuilder +293 -0
- data/app/views/json_ui/garage/test_page/fields_rating.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/fields_richText.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/fields_select.json.jbuilder +3 -2
- data/app/views/json_ui/garage/test_page/fields_sign.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/fields_stripeExternalAccount.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/fields_stripeToken.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/fields_text.json.jbuilder +312 -0
- data/app/views/json_ui/garage/test_page/fields_timer.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/fields_upload.json.jbuilder +66 -4
- data/app/views/json_ui/garage/test_page/fields_url_fragment.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/flow.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/form.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/form_dynamic.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/forms.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/grid.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/horizontal.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/http.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/image.json.jbuilder +26 -2
- data/app/views/json_ui/garage/test_page/lifecycle.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/list.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/list_editable.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/lists_append.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/logics_set.json.jbuilder +0 -1
- data/app/views/json_ui/garage/test_page/multimedia_video.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/pagination.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/panels.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/panels_bulkEdit2.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/popovers.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/progressCircle.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/responsive.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/scroll.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/selectable.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/sheets.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/snackbars.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/split.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/storage_items.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/switch.json.jbuilder +56 -0
- data/app/views/json_ui/garage/test_page/tabBar.json.jbuilder +144 -0
- data/app/views/json_ui/garage/test_page/table.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/timeline.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/timeouts.json.jbuilder +9 -3
- data/app/views/json_ui/garage/test_page/ul.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/vertical.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/web.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/window.json.jbuilder +2 -1
- data/app/views/json_ui/garage/test_page/windows.json.jbuilder +2 -1
- data/lib/glib/json_crawler/router.rb +39 -0
- data/lib/glib/test_helpers.rb +68 -2
- metadata +6 -1
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
json.title 'Test Page (tabBar)'
|
|
2
|
+
|
|
3
|
+
page = json_ui_page json
|
|
4
|
+
|
|
5
|
+
render 'json_ui/garage/test_page/header', json: json, page: page
|
|
6
|
+
|
|
7
|
+
active_index = params[:tab].to_i
|
|
8
|
+
|
|
9
|
+
page.body(
|
|
10
|
+
childViews: ->(body) do
|
|
11
|
+
body.panels_responsive(
|
|
12
|
+
padding: glib_json_padding_body,
|
|
13
|
+
childViews: ->(res) do
|
|
14
|
+
res.h2 text: 'Overview'
|
|
15
|
+
res.p(
|
|
16
|
+
text: 'tabBar renders a row of icon/text buttons used for primary navigation. ' \
|
|
17
|
+
'It supports badges, active-tab highlighting, style variants, and custom onClick actions.'
|
|
18
|
+
)
|
|
19
|
+
res.spacer height: 12
|
|
20
|
+
res.hr width: 'matchParent'
|
|
21
|
+
res.h2 text: 'Variants and Props'
|
|
22
|
+
res.spacer height: 8
|
|
23
|
+
res.label text: 'With badges'
|
|
24
|
+
res.spacer height: 6
|
|
25
|
+
res.tabBar(
|
|
26
|
+
activeIndex: 0,
|
|
27
|
+
buttons: ->(menu) do
|
|
28
|
+
[
|
|
29
|
+
{ text: 'Inbox', icon: 'inbox', badge: { text: 5, backgroundColor: '#272551' } },
|
|
30
|
+
{ text: 'Alerts', icon: 'notifications', badge: { text: '!', backgroundColor: '#cc0000' } },
|
|
31
|
+
{ text: 'Messages', icon: 'chat', badge: { text: nil, backgroundColor: nil } }
|
|
32
|
+
].each do |tab|
|
|
33
|
+
menu.button(
|
|
34
|
+
icon: tab[:icon],
|
|
35
|
+
text: tab[:text],
|
|
36
|
+
badge: tab[:badge],
|
|
37
|
+
disabled: false,
|
|
38
|
+
onClick: ->(action) do
|
|
39
|
+
action.snackbars_alert message: "Tapped #{tab[:text]}"
|
|
40
|
+
end
|
|
41
|
+
)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
)
|
|
45
|
+
res.spacer height: 12
|
|
46
|
+
res.label text: "Style: 'full-width-divider'"
|
|
47
|
+
res.spacer height: 6
|
|
48
|
+
res.tabBar(
|
|
49
|
+
activeIndex: 0,
|
|
50
|
+
styleClass: 'full-width-divider',
|
|
51
|
+
buttons: ->(menu) do
|
|
52
|
+
4.times do |index|
|
|
53
|
+
menu.button(
|
|
54
|
+
text: "Tab #{index}",
|
|
55
|
+
disabled: index == 0,
|
|
56
|
+
onClick: ->(action) do
|
|
57
|
+
action.snackbars_alert message: "Tapped tab #{index}"
|
|
58
|
+
end
|
|
59
|
+
)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
)
|
|
63
|
+
res.spacer height: 12
|
|
64
|
+
res.label text: "Style: 'no-grow'"
|
|
65
|
+
res.spacer height: 6
|
|
66
|
+
res.tabBar(
|
|
67
|
+
activeIndex: 0,
|
|
68
|
+
styleClass: 'no-grow',
|
|
69
|
+
buttons: ->(menu) do
|
|
70
|
+
3.times do |index|
|
|
71
|
+
menu.button(
|
|
72
|
+
text: "Tab #{index}",
|
|
73
|
+
disabled: index == 0,
|
|
74
|
+
onClick: ->(action) do
|
|
75
|
+
action.snackbars_alert message: "Tapped tab #{index}"
|
|
76
|
+
end
|
|
77
|
+
)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
res.spacer height: 12
|
|
83
|
+
res.hr width: 'matchParent'
|
|
84
|
+
res.h2 text: 'Edge and Advanced'
|
|
85
|
+
res.spacer height: 8
|
|
86
|
+
res.label text: 'All tabs disabled'
|
|
87
|
+
res.spacer height: 6
|
|
88
|
+
res.tabBar(
|
|
89
|
+
activeIndex: -1,
|
|
90
|
+
buttons: ->(menu) do
|
|
91
|
+
['One', 'Two', 'Three'].each do |label|
|
|
92
|
+
menu.button(
|
|
93
|
+
text: label,
|
|
94
|
+
disabled: true,
|
|
95
|
+
onClick: ->(action) do
|
|
96
|
+
action.snackbars_alert message: "Tapped #{label}"
|
|
97
|
+
end
|
|
98
|
+
)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
)
|
|
102
|
+
res.spacer height: 12
|
|
103
|
+
res.label text: 'Many tabs (overflow)'
|
|
104
|
+
res.spacer height: 6
|
|
105
|
+
res.tabBar(
|
|
106
|
+
activeIndex: 0,
|
|
107
|
+
buttons: ->(menu) do
|
|
108
|
+
8.times do |index|
|
|
109
|
+
menu.button(
|
|
110
|
+
text: "Tab #{index}",
|
|
111
|
+
disabled: index == 0,
|
|
112
|
+
onClick: ->(action) do
|
|
113
|
+
action.snackbars_alert message: "Tapped tab #{index}"
|
|
114
|
+
end
|
|
115
|
+
)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
)
|
|
119
|
+
res.spacer height: 12
|
|
120
|
+
res.label text: 'Long badge text'
|
|
121
|
+
res.spacer height: 6
|
|
122
|
+
res.tabBar(
|
|
123
|
+
activeIndex: 0,
|
|
124
|
+
buttons: ->(menu) do
|
|
125
|
+
menu.button(
|
|
126
|
+
icon: 'star',
|
|
127
|
+
text: 'Stars',
|
|
128
|
+
disabled: true,
|
|
129
|
+
badge: { text: '⭐⭐⭐', backgroundColor: '#008000' },
|
|
130
|
+
onClick: ->(action) { action.snackbars_alert message: 'Stars' }
|
|
131
|
+
)
|
|
132
|
+
menu.button(
|
|
133
|
+
icon: 'inbox',
|
|
134
|
+
text: 'Inbox',
|
|
135
|
+
disabled: false,
|
|
136
|
+
badge: { text: 999, backgroundColor: '#272551' },
|
|
137
|
+
onClick: ->(action) { action.snackbars_alert message: 'Inbox' }
|
|
138
|
+
)
|
|
139
|
+
end
|
|
140
|
+
)
|
|
141
|
+
end
|
|
142
|
+
)
|
|
143
|
+
end
|
|
144
|
+
)
|
|
@@ -2,9 +2,10 @@ json.title 'Test Page (Table)'
|
|
|
2
2
|
|
|
3
3
|
page = json_ui_page json
|
|
4
4
|
|
|
5
|
+
render 'json_ui/garage/test_page/header', json: json, page: page
|
|
6
|
+
|
|
5
7
|
page.body(
|
|
6
8
|
childViews: ->(body) do
|
|
7
|
-
render 'json_ui/garage/test_page/header', view: body
|
|
8
9
|
body.panels_responsive(
|
|
9
10
|
padding: glib_json_padding_body,
|
|
10
11
|
childViews: ->(res) do
|
|
@@ -2,9 +2,10 @@ json.title 'Test Page (Timeline)'
|
|
|
2
2
|
|
|
3
3
|
page = json_ui_page json
|
|
4
4
|
|
|
5
|
+
render 'json_ui/garage/test_page/header', json: json, page: page
|
|
6
|
+
|
|
5
7
|
page.body(
|
|
6
8
|
childViews: ->(body) do
|
|
7
|
-
render 'json_ui/garage/test_page/header', view: body
|
|
8
9
|
body.panels_responsive(
|
|
9
10
|
padding: glib_json_padding_body,
|
|
10
11
|
childViews: ->(res) do
|
|
@@ -2,9 +2,10 @@ json.title 'Test Page (Timeouts)'
|
|
|
2
2
|
|
|
3
3
|
page = json_ui_page json
|
|
4
4
|
|
|
5
|
+
render 'json_ui/garage/test_page/header', json: json, page: page
|
|
6
|
+
|
|
5
7
|
page.body(
|
|
6
8
|
childViews: ->(body) do
|
|
7
|
-
render 'json_ui/garage/test_page/header', view: body
|
|
8
9
|
body.panels_responsive(
|
|
9
10
|
padding: glib_json_padding_body,
|
|
10
11
|
childViews: ->(res) do
|
|
@@ -58,8 +59,13 @@ page.body(
|
|
|
58
59
|
interval: 2000,
|
|
59
60
|
repeat: true,
|
|
60
61
|
onTimeout: ->(subaction) do
|
|
61
|
-
subaction.logics_set
|
|
62
|
-
|
|
62
|
+
subaction.logics_set(
|
|
63
|
+
targetId: 'timeout_repeat_status',
|
|
64
|
+
data: { text: 'Last tick fired' },
|
|
65
|
+
onSet: ->(ssaction) do
|
|
66
|
+
ssaction.snackbars_alert message: 'Repeat timeout elapsed'
|
|
67
|
+
end
|
|
68
|
+
)
|
|
63
69
|
end
|
|
64
70
|
)
|
|
65
71
|
end
|
|
@@ -2,8 +2,9 @@ json.title 'Test Page (Ul)'
|
|
|
2
2
|
|
|
3
3
|
page = json_ui_page json
|
|
4
4
|
|
|
5
|
+
render 'json_ui/garage/test_page/header', json: json, page: page
|
|
6
|
+
|
|
5
7
|
page.body childViews: ->(body) do
|
|
6
|
-
render 'json_ui/garage/test_page/header', view: body
|
|
7
8
|
|
|
8
9
|
body.panels_responsive padding: glib_json_padding_body, childViews: ->(res) do
|
|
9
10
|
res.h2 text: 'Overview'
|
|
@@ -2,8 +2,9 @@ json.title 'Test Page (Vertical)'
|
|
|
2
2
|
|
|
3
3
|
page = json_ui_page json
|
|
4
4
|
|
|
5
|
+
render 'json_ui/garage/test_page/header', json: json, page: page
|
|
6
|
+
|
|
5
7
|
page.body childViews: ->(body) do
|
|
6
|
-
render 'json_ui/garage/test_page/header', view: body
|
|
7
8
|
|
|
8
9
|
body.panels_responsive padding: glib_json_padding_body, childViews: ->(res) do
|
|
9
10
|
res.h2 text: 'Overview'
|
|
@@ -2,8 +2,9 @@ json.title 'Test Page (Web)'
|
|
|
2
2
|
|
|
3
3
|
page = json_ui_page json
|
|
4
4
|
|
|
5
|
+
render 'json_ui/garage/test_page/header', json: json, page: page
|
|
6
|
+
|
|
5
7
|
page.body childViews: ->(body) do
|
|
6
|
-
render 'json_ui/garage/test_page/header', view: body
|
|
7
8
|
|
|
8
9
|
body.panels_responsive padding: glib_json_padding_body, childViews: ->(res) do
|
|
9
10
|
res.h2 text: 'Overview'
|
|
@@ -3,9 +3,10 @@ json.title 'Test Page (Form)'
|
|
|
3
3
|
|
|
4
4
|
page = json_ui_page json
|
|
5
5
|
|
|
6
|
+
render 'json_ui/garage/test_page/header', json: json, page: page
|
|
7
|
+
|
|
6
8
|
page.body(
|
|
7
9
|
childViews: ->(body) do
|
|
8
|
-
render 'json_ui/garage/test_page/header', view: body
|
|
9
10
|
body.panels_responsive(
|
|
10
11
|
padding: glib_json_padding_body,
|
|
11
12
|
childViews: ->(res) do
|
|
@@ -2,9 +2,10 @@ json.title 'Test Page (Windows)'
|
|
|
2
2
|
|
|
3
3
|
page = json_ui_page json
|
|
4
4
|
|
|
5
|
+
render 'json_ui/garage/test_page/header', json: json, page: page
|
|
6
|
+
|
|
5
7
|
page.body(
|
|
6
8
|
childViews: ->(body) do
|
|
7
|
-
render 'json_ui/garage/test_page/header', view: body
|
|
8
9
|
body.panels_responsive(
|
|
9
10
|
padding: glib_json_padding_body,
|
|
10
11
|
childViews: ->(res) do
|
|
@@ -74,6 +74,16 @@ module Glib
|
|
|
74
74
|
JsonCrawler::FormsSubmit.new(http, args)
|
|
75
75
|
@depth -= 1
|
|
76
76
|
return
|
|
77
|
+
when 'panels/web-v1', 'panels/web'
|
|
78
|
+
# A panels/web embeds content by URL in an inline viewer -- a file
|
|
79
|
+
# preview (PDF/image), an inline HTML preview, etc. There's no onClick
|
|
80
|
+
# action to catch here, so when the URL is one of our own (same-host)
|
|
81
|
+
# endpoints we record it directly: the client fetches it to render the
|
|
82
|
+
# panel, so its authorization should be exercised by the permission test
|
|
83
|
+
# (it replays each per user and snapshots the response). We only record
|
|
84
|
+
# (no fetch); external embeds are filtered out by internal_url?.
|
|
85
|
+
url = args['url']
|
|
86
|
+
http_actions.add(['panels/web-v1', url]) if url.present? && internal_url?(url)
|
|
77
87
|
end
|
|
78
88
|
|
|
79
89
|
if args.is_a?(Hash) && args['rel'] != 'nofollow'
|
|
@@ -111,6 +121,24 @@ module Glib
|
|
|
111
121
|
http_actions.add([action, params['url']])
|
|
112
122
|
JsonCrawler::WindowsOpen.new(http, params, action)
|
|
113
123
|
else
|
|
124
|
+
# IMPORTANT — do not drop the `http_actions.add` below.
|
|
125
|
+
#
|
|
126
|
+
# This `else` is reached by BOTH genuinely external links AND by
|
|
127
|
+
# same-host file/download endpoints that `allowed?` rejected for the
|
|
128
|
+
# file-extension rule (e.g. *.pdf). Recording the same-host ones is the
|
|
129
|
+
# ONLY way their file authorization ever gets exercised: the permission
|
|
130
|
+
# test replays each recorded action per user and snapshots the response.
|
|
131
|
+
# Remove this line and every file/download endpoint silently falls out
|
|
132
|
+
# of permission coverage -- a wrong authorization on one would then ship
|
|
133
|
+
# unnoticed (exactly the gap this was added to close).
|
|
134
|
+
#
|
|
135
|
+
# We do NOT traverse/download them (no WindowsOpen crawler is created
|
|
136
|
+
# here), and the permission replay runs with inspect_http:false so it
|
|
137
|
+
# never follows the storage redirect. External links are filtered out by
|
|
138
|
+
# `internal_url?`. Behaviour is guarded by
|
|
139
|
+
# test/dummy-app/test/json_crawler/router_test.rb -- if you delete the
|
|
140
|
+
# line below, that test goes red.
|
|
141
|
+
http_actions.add([action, params['url']]) if internal_url?(params['url'])
|
|
114
142
|
self.log action, params['url']
|
|
115
143
|
end
|
|
116
144
|
when 'dialogs/show-v1', 'dialogs/show', 'popovers/show-v1', 'popovers/show'
|
|
@@ -227,6 +255,17 @@ module Glib
|
|
|
227
255
|
end
|
|
228
256
|
|
|
229
257
|
private
|
|
258
|
+
# True when `url` is one of our OWN (same-host) endpoints, used to tell an
|
|
259
|
+
# internal endpoint we want recorded for the permission test from a
|
|
260
|
+
# genuinely external link we ignore (in the openWeb `else`-branch and the
|
|
261
|
+
# panels/web case). Anchored at the start and required to end at a path
|
|
262
|
+
# boundary (`/`, `?`, `#`, or end-of-string), so a host that merely appears
|
|
263
|
+
# INSIDE an external URL's path or query is NOT treated as internal --
|
|
264
|
+
# unlike a bare substring match.
|
|
265
|
+
def internal_url?(url)
|
|
266
|
+
%r{\Ahttps?://#{Regexp.escape(host)}(?:[/?#]|\z)}.match?(url.to_s)
|
|
267
|
+
end
|
|
268
|
+
|
|
230
269
|
def execute_crawler_action(http, action, url, params)
|
|
231
270
|
params = JSON.parse(params) if params.is_a?(String)
|
|
232
271
|
params ||= {}
|
data/lib/glib/test_helpers.rb
CHANGED
|
@@ -66,6 +66,44 @@ module Glib
|
|
|
66
66
|
File.join(file_dir || __crawler_log_dir, filename)
|
|
67
67
|
end
|
|
68
68
|
|
|
69
|
+
# Asserts that every model with ActiveStorage attachments is actually
|
|
70
|
+
# *exercised* by the crawler: at least one of its file endpoints must appear
|
|
71
|
+
# in the recorded crawler output. A structural "is this type authorized?"
|
|
72
|
+
# check can only prove a type is handled, not that its file authorization is
|
|
73
|
+
# covered end-to-end. This proves the latter -- the crawler recorded the
|
|
74
|
+
# endpoint, so the permission test replays it per user -- which is what
|
|
75
|
+
# catches a *wrong* authorization on a newly-added attachable type (or a
|
|
76
|
+
# fixture that was never made reachable in the crawl).
|
|
77
|
+
#
|
|
78
|
+
# attachable_types: model classes (or names) that must be covered.
|
|
79
|
+
# router_dir: dir holding the crawler router CSVs (the dump_path).
|
|
80
|
+
# signed_id_from: ->(url) returning the blob signed_id for a file URL, or
|
|
81
|
+
# nil for non-file URLs. Route-specific, e.g.
|
|
82
|
+
# ->(u) { u[%r{/blobs/([^/]+)/}, 1] }.
|
|
83
|
+
# exempt: type names whose files are served outside the crawl.
|
|
84
|
+
# pending: type names known-uncovered -- an explicit burn-down
|
|
85
|
+
# list, kept honest by the stale-pending check below.
|
|
86
|
+
def assert_attachable_types_crawl_covered(attachable_types:, router_dir:, signed_id_from:, exempt: [], pending: [])
|
|
87
|
+
required = attachable_types.map(&:to_s) - exempt.map(&:to_s) - pending.map(&:to_s)
|
|
88
|
+
covered = __crawler_covered_record_types(router_dir, signed_id_from)
|
|
89
|
+
|
|
90
|
+
uncovered = (required - covered).sort
|
|
91
|
+
assert_empty(
|
|
92
|
+
uncovered,
|
|
93
|
+
'No crawled file endpoint covers these attachable types, so the permission test ' \
|
|
94
|
+
'never exercises their file authorization (a wrong policy on them would ship ' \
|
|
95
|
+
'silently). Give each a fixture whose file renders in a crawled page and regenerate ' \
|
|
96
|
+
"the crawler snapshots, or list it in `exempt:`/`pending:` -- #{uncovered.join(', ')}"
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
graduated = (pending.map(&:to_s) & covered).sort
|
|
100
|
+
assert_empty(
|
|
101
|
+
graduated,
|
|
102
|
+
'These `pending:` types are now crawl-covered -- remove them from the burn-down ' \
|
|
103
|
+
"list so it keeps reflecting the real gap: #{graduated.join(', ')}"
|
|
104
|
+
)
|
|
105
|
+
end
|
|
106
|
+
|
|
69
107
|
def retrace_json_pages(user, past_actions:)
|
|
70
108
|
__execute_crawler(user, inspect_http: false) do |router, http|
|
|
71
109
|
# router.follow(http, guiding_routers)
|
|
@@ -92,7 +130,7 @@ module Glib
|
|
|
92
130
|
# @param method [Symbol, nil] Optional HTTP method override
|
|
93
131
|
#
|
|
94
132
|
# @example
|
|
95
|
-
# get new_chat_url(
|
|
133
|
+
# get new_chat_url(post_id: @post.id), params: json_params
|
|
96
134
|
# submit_form(response.body, { chat: { message_body: 'Hello' } })
|
|
97
135
|
#
|
|
98
136
|
def submit_form(page_payload, data, url: nil, method: nil)
|
|
@@ -211,7 +249,7 @@ module Glib
|
|
|
211
249
|
|
|
212
250
|
block.call(view)
|
|
213
251
|
|
|
214
|
-
['childViews', 'header', 'body', 'footer', 'left', 'right', 'panels'].each do |key|
|
|
252
|
+
['childViews', 'header', 'body', 'footer', 'left', 'right', 'panels', 'fullPageForm'].each do |key|
|
|
215
253
|
child = view[key]
|
|
216
254
|
if child.is_a?(Array)
|
|
217
255
|
__traverse_form_views(child, &block)
|
|
@@ -223,6 +261,34 @@ module Glib
|
|
|
223
261
|
|
|
224
262
|
# --- Crawler helpers ---
|
|
225
263
|
|
|
264
|
+
# Reads the crawler router CSVs and returns the distinct ActiveStorage
|
|
265
|
+
# `record_type`s reachable through the recorded file endpoints. Each row's
|
|
266
|
+
# URL is mapped to a blob via the caller-supplied `signed_id_from` (the
|
|
267
|
+
# only route-specific bit), then blob -> attachment -> record_type.
|
|
268
|
+
def __crawler_covered_record_types(router_dir, signed_id_from)
|
|
269
|
+
require 'csv'
|
|
270
|
+
|
|
271
|
+
Dir.glob(File.join(router_dir, '*.csv')).flat_map do |path|
|
|
272
|
+
CSV.read(path).filter_map do |row|
|
|
273
|
+
url = row[1]
|
|
274
|
+
next if url.blank?
|
|
275
|
+
|
|
276
|
+
signed_id = signed_id_from.call(url)
|
|
277
|
+
next if signed_id.blank?
|
|
278
|
+
|
|
279
|
+
blob =
|
|
280
|
+
begin
|
|
281
|
+
ActiveStorage::Blob.find_signed(signed_id)
|
|
282
|
+
rescue StandardError
|
|
283
|
+
nil
|
|
284
|
+
end
|
|
285
|
+
next if blob.nil?
|
|
286
|
+
|
|
287
|
+
ActiveStorage::Attachment.find_by(blob: blob)&.record_type
|
|
288
|
+
end
|
|
289
|
+
end.compact.uniq
|
|
290
|
+
end
|
|
291
|
+
|
|
226
292
|
def __execute_crawler(user, inspect_http:, log_file: nil, &block)
|
|
227
293
|
auth_token = login user
|
|
228
294
|
user[:token] = auth_token
|
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: 5.0.
|
|
4
|
+
version: 5.0.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- ''
|
|
@@ -220,6 +220,7 @@ files:
|
|
|
220
220
|
- app/helpers/glib/json_ui/view_builder/panels.rb
|
|
221
221
|
- app/helpers/glib/urls_helper.rb
|
|
222
222
|
- app/models/concerns/glib/enum_humanization.rb
|
|
223
|
+
- app/models/concerns/glib/enum_symbolization.rb
|
|
223
224
|
- app/models/concerns/glib/nilify_blanks.rb
|
|
224
225
|
- app/models/concerns/glib/soft_deletable.rb
|
|
225
226
|
- app/models/glib/active_storage/attachment.rb
|
|
@@ -405,12 +406,14 @@ files:
|
|
|
405
406
|
- app/views/json_ui/garage/test_page/fields_location.json.jbuilder
|
|
406
407
|
- app/views/json_ui/garage/test_page/fields_otp.json.jbuilder
|
|
407
408
|
- app/views/json_ui/garage/test_page/fields_phone.json.jbuilder
|
|
409
|
+
- app/views/json_ui/garage/test_page/fields_radio.json.jbuilder
|
|
408
410
|
- app/views/json_ui/garage/test_page/fields_rating.json.jbuilder
|
|
409
411
|
- app/views/json_ui/garage/test_page/fields_richText.json.jbuilder
|
|
410
412
|
- app/views/json_ui/garage/test_page/fields_select.json.jbuilder
|
|
411
413
|
- app/views/json_ui/garage/test_page/fields_sign.json.jbuilder
|
|
412
414
|
- app/views/json_ui/garage/test_page/fields_stripeExternalAccount.json.jbuilder
|
|
413
415
|
- app/views/json_ui/garage/test_page/fields_stripeToken.json.jbuilder
|
|
416
|
+
- app/views/json_ui/garage/test_page/fields_text.json.jbuilder
|
|
414
417
|
- app/views/json_ui/garage/test_page/fields_timer.json.jbuilder
|
|
415
418
|
- app/views/json_ui/garage/test_page/fields_upload.json.jbuilder
|
|
416
419
|
- app/views/json_ui/garage/test_page/fields_url_fragment.json.jbuilder
|
|
@@ -440,6 +443,8 @@ files:
|
|
|
440
443
|
- app/views/json_ui/garage/test_page/snackbars.json.jbuilder
|
|
441
444
|
- app/views/json_ui/garage/test_page/split.json.jbuilder
|
|
442
445
|
- app/views/json_ui/garage/test_page/storage_items.json.jbuilder
|
|
446
|
+
- app/views/json_ui/garage/test_page/switch.json.jbuilder
|
|
447
|
+
- app/views/json_ui/garage/test_page/tabBar.json.jbuilder
|
|
443
448
|
- app/views/json_ui/garage/test_page/table.json.jbuilder
|
|
444
449
|
- app/views/json_ui/garage/test_page/timeline.json.jbuilder
|
|
445
450
|
- app/views/json_ui/garage/test_page/timeouts.json.jbuilder
|