media_types-serialization 2.0.4 → 2.1.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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +32 -32
  3. data/.github/workflows/publish-bookworm.yml +34 -34
  4. data/.github/workflows/publish-sid.yml +34 -34
  5. data/.gitignore +22 -22
  6. data/.idea/.rakeTasks +7 -7
  7. data/.idea/dictionaries/Derk_Jan.xml +6 -6
  8. data/.idea/encodings.xml +3 -3
  9. data/.idea/inspectionProfiles/Project_Default.xml +5 -5
  10. data/.idea/media_types-serialization.iml +76 -76
  11. data/.idea/misc.xml +6 -6
  12. data/.idea/modules.xml +7 -7
  13. data/.idea/runConfigurations/test.xml +19 -19
  14. data/.idea/vcs.xml +5 -5
  15. data/CHANGELOG.md +207 -200
  16. data/CODE_OF_CONDUCT.md +74 -74
  17. data/Gemfile +4 -4
  18. data/Gemfile.lock +176 -169
  19. data/LICENSE.txt +21 -21
  20. data/README.md +1058 -1048
  21. data/Rakefile +10 -10
  22. data/bin/console +14 -14
  23. data/bin/setup +8 -8
  24. data/lib/media_types/problem.rb +67 -67
  25. data/lib/media_types/serialization/base.rb +269 -269
  26. data/lib/media_types/serialization/error.rb +193 -193
  27. data/lib/media_types/serialization/fake_validator.rb +53 -53
  28. data/lib/media_types/serialization/serialization_dsl.rb +139 -135
  29. data/lib/media_types/serialization/serialization_registration.rb +245 -245
  30. data/lib/media_types/serialization/serializers/api_viewer.rb +383 -383
  31. data/lib/media_types/serialization/serializers/common_css.rb +212 -212
  32. data/lib/media_types/serialization/serializers/endpoint_description_serializer.rb +80 -80
  33. data/lib/media_types/serialization/serializers/fallback_not_acceptable_serializer.rb +85 -85
  34. data/lib/media_types/serialization/serializers/fallback_unsupported_media_type_serializer.rb +58 -58
  35. data/lib/media_types/serialization/serializers/input_validation_error_serializer.rb +95 -93
  36. data/lib/media_types/serialization/serializers/problem_serializer.rb +111 -111
  37. data/lib/media_types/serialization/utils/accept_header.rb +77 -77
  38. data/lib/media_types/serialization/utils/accept_language_header.rb +82 -82
  39. data/lib/media_types/serialization/version.rb +7 -7
  40. data/lib/media_types/serialization.rb +689 -689
  41. data/media_types-serialization.gemspec +48 -48
  42. metadata +3 -3
@@ -1,212 +1,212 @@
1
- # frozen_string_literal: true
2
-
3
- require 'erb'
4
- require 'base64'
5
- require 'active_support'
6
-
7
- module MediaTypes
8
- module Serialization
9
- module Serializers
10
- module CommonCSS
11
-
12
- mattr_accessor :logo_data, :logo_media_type, :logo_width, :background, :custom_css
13
-
14
- self.background = 'linear-gradient(245deg, rgba(255,89,89,1) 0%, rgba(255,164,113,1) 100%)'
15
- self.logo_media_type = 'image/svg+xml'
16
- self.logo_width = 8
17
- self.logo_data = <<-HERE
18
- <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 114 93">
19
- <title>Delft Solutions</title>
20
-
21
- <filter id="dropshadow">
22
- <feGaussianBlur in="SourceAlpha" stdDeviation="1"></feGaussianBlur> <!-- stdDeviation is how much to blur -->
23
- <feOffset dx="2" dy="1" result="offsetblur"></feOffset> <!-- how much to offset -->
24
- <feComponentTransfer>
25
- <feFuncA type="linear" slope="0.5"></feFuncA> <!-- slope is the opacity of the shadow -->
26
- </feComponentTransfer>
27
- <feMerge>
28
- <feMergeNode></feMergeNode> <!-- this contains the offset blurred image -->
29
- <feMergeNode in="SourceGraphic"></feMergeNode> <!-- this contains the element that the filter is applied to -->
30
- </feMerge>
31
- </filter>
32
-
33
- <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
34
- <g fill="#FFFFFF" fill-rule="nonzero">
35
-
36
- <path d="M81.5784638,1.07718279e-13 C82.7664738,1.07718279e-13 83.8032488,0.734079641 84.4157016,1.75205281 L109.531129,43.5095713 C110.813908,45.6417099 110.657922,48.2974919 109.15835,50.2831454 L80.6973102,87.9923196 C80.0557678,88.7870619 79.0855103,89.3973447 78.0602378,89.3973447 L42.8594985,89.3973447 L14.6289023,43.5796094 L38.1043811,13.5281311 L47.8307983,13.5281311 L25.7347121,43.6175319 L48.0361926,79.9158441 L75.0253918,79.9158441 L101.326814,46.2820182 L73.5454136,1.07718279e-13 L81.5784638,1.07718279e-13 Z M68.8174965,0.000338312914 L96.4191607,45.9808751 L73.2382461,75.6684695 L61.4283598,75.6684695 L84.975762,45.385564 L63.4142078,9.46643441 L36.1380842,9.46643441 L9.60299852,43.3032035 L35.9112712,85.3931029 L38.1241857,89.3191214 L29.1498474,89.3973434 C27.9592604,89.4075947 26.8506993,88.7919375 26.2302294,87.7757572 L0.893096605,46.2796422 C-0.418595034,44.1314075 -0.274907213,41.3978442 1.25477457,39.3989643 L30.388821,1.32865425 L30.4563519,1.24328222 C31.0981823,0.458113729 32.0600455,0.000338312914 33.0779839,0.000338312914 L68.8174965,0.000338312914 Z" id="logo-mark-colour"></path>
37
- </g>
38
- </g>
39
- </svg>
40
- HERE
41
-
42
- def self.logo_url
43
- "data:#{logo_media_type};base64,#{Base64.encode64(logo_data).tr("\n", '')}"
44
- end
45
-
46
- def self.css
47
- template = ERB.new <<-TEMPLATE
48
- html {
49
- min-height: 100%;
50
- background: <%= background %>;
51
- }
52
-
53
- body {
54
- color: #fff;
55
- margin-left: 2em;
56
- margin-right: 2em;
57
- margin-top: 1em;
58
- max-width: 1200px;
59
-
60
- font-family: -apple-system, ".SFNSText-Regular", "San Francisco", "Roboto", "Segoe UI", "Helvetica Neue", "Lucida Grande", sans-serif;
61
- font-size: 16px;
62
- line-height: 1.6;
63
- -webkit-font-feature-settings: "kern","liga","clig","calt";
64
- font-feature-settings: "kern","liga","clig","calt";
65
- -webkit-font-smoothing: antialiased;
66
- -moz-osx-font-smoothing: grayscale
67
- overflow-x: hidden;
68
- }
69
-
70
- main {
71
- max-width: 100%;
72
- }
73
-
74
- header {
75
- display: inline-block;
76
- max-width: 100%;
77
- }
78
-
79
- a:link {
80
- color: #007bff;
81
- }
82
- a:visited {
83
- color: #9c27b0;
84
- }
85
- a:hover {
86
- color: #00bcd4;
87
- }
88
-
89
- #logo {
90
- width: <%= logo_width %>em;
91
- height: 5em;
92
- background-repeat: no-repeat;
93
- background-position-x: right;
94
- background-position-y: center;
95
- background-image: url(<%= logo_url %>);
96
- float: left;
97
- margin-right: 2em;
98
- }
99
-
100
- header h1 {
101
- clear: right;
102
- text-overflow: ellipsis;
103
- white-space: nowrap;
104
- overflow: hidden;
105
-
106
- font-size: 1.5em;
107
- line-height: 2em;
108
- }
109
-
110
- #content {
111
- margin-top: 1em;
112
- padding-left: 1em;
113
- padding-right: 1em;
114
- padding-bottom: 1em;
115
- color: #060B34;
116
- background-color: #fff;
117
- border-radius: 1em;
118
- border: 1px solid #E0E1E4;
119
- overflow: auto;
120
- }
121
-
122
- nav h2 {
123
- font-size: 1em;
124
- line-height: 1.25em;
125
- margin-bottom: 0;
126
- }
127
- nav .label {
128
- float: left;
129
- }
130
-
131
- nav ul {
132
- clear: right;
133
- display: inline-block;
134
- margin: 0;
135
- padding: 0;
136
- }
137
- nav li {
138
- float: left;
139
- list-style: none;
140
- margin-right: 0.3em;
141
- }
142
- nav li a.active {
143
- font-weight: bold;
144
- }
145
- nav li a.active:link, nav li a.active:visited {
146
- color: #060B34;
147
- }
148
- nav li + li:before {
149
- content: "|";
150
- margin-right: 0.3em;
151
- }
152
-
153
- nav #representations {
154
- margin-bottom: 1em;
155
- }
156
- nav hr {
157
- border: none;
158
- border-top: solid 1px #E0E1E4;
159
- }
160
-
161
- nav #links {
162
- display: inline-block;
163
- margin-bottom: 1em;
164
- }
165
- nav #links ul {
166
- float: left;
167
- }
168
-
169
- #output {
170
- font-size: .9em;
171
- }
172
-
173
- #reply .reply-indent {
174
- border-left: 0.5em solid lightgray;
175
- padding-left: 1em;
176
- }
177
-
178
- #reply .form-table {
179
- display: table;
180
- border-spacing: 1em 0;
181
- margin-left: -1em;
182
- margin-bottom: 0.2em;
183
- }
184
-
185
- #reply .form-row {
186
- display: table-row;
187
- }
188
-
189
- #reply .cell {
190
- display: table-cell;
191
- }
192
-
193
- #reply textarea {
194
- width: 100%;
195
- height: 6em;
196
- clear: both;
197
- }
198
-
199
- #reply #reply-status-code {
200
- font-style: italic;
201
- font-size: 80%;
202
- }
203
-
204
- TEMPLATE
205
- template = ERB.new custom_css unless custom_css.nil?
206
-
207
- template.result(binding)
208
- end
209
- end
210
- end
211
- end
212
- end
1
+ # frozen_string_literal: true
2
+
3
+ require 'erb'
4
+ require 'base64'
5
+ require 'active_support'
6
+
7
+ module MediaTypes
8
+ module Serialization
9
+ module Serializers
10
+ module CommonCSS
11
+
12
+ mattr_accessor :logo_data, :logo_media_type, :logo_width, :background, :custom_css
13
+
14
+ self.background = 'linear-gradient(245deg, rgba(255,89,89,1) 0%, rgba(255,164,113,1) 100%)'
15
+ self.logo_media_type = 'image/svg+xml'
16
+ self.logo_width = 8
17
+ self.logo_data = <<-HERE
18
+ <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 114 93">
19
+ <title>Delft Solutions</title>
20
+
21
+ <filter id="dropshadow">
22
+ <feGaussianBlur in="SourceAlpha" stdDeviation="1"></feGaussianBlur> <!-- stdDeviation is how much to blur -->
23
+ <feOffset dx="2" dy="1" result="offsetblur"></feOffset> <!-- how much to offset -->
24
+ <feComponentTransfer>
25
+ <feFuncA type="linear" slope="0.5"></feFuncA> <!-- slope is the opacity of the shadow -->
26
+ </feComponentTransfer>
27
+ <feMerge>
28
+ <feMergeNode></feMergeNode> <!-- this contains the offset blurred image -->
29
+ <feMergeNode in="SourceGraphic"></feMergeNode> <!-- this contains the element that the filter is applied to -->
30
+ </feMerge>
31
+ </filter>
32
+
33
+ <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
34
+ <g fill="#FFFFFF" fill-rule="nonzero">
35
+
36
+ <path d="M81.5784638,1.07718279e-13 C82.7664738,1.07718279e-13 83.8032488,0.734079641 84.4157016,1.75205281 L109.531129,43.5095713 C110.813908,45.6417099 110.657922,48.2974919 109.15835,50.2831454 L80.6973102,87.9923196 C80.0557678,88.7870619 79.0855103,89.3973447 78.0602378,89.3973447 L42.8594985,89.3973447 L14.6289023,43.5796094 L38.1043811,13.5281311 L47.8307983,13.5281311 L25.7347121,43.6175319 L48.0361926,79.9158441 L75.0253918,79.9158441 L101.326814,46.2820182 L73.5454136,1.07718279e-13 L81.5784638,1.07718279e-13 Z M68.8174965,0.000338312914 L96.4191607,45.9808751 L73.2382461,75.6684695 L61.4283598,75.6684695 L84.975762,45.385564 L63.4142078,9.46643441 L36.1380842,9.46643441 L9.60299852,43.3032035 L35.9112712,85.3931029 L38.1241857,89.3191214 L29.1498474,89.3973434 C27.9592604,89.4075947 26.8506993,88.7919375 26.2302294,87.7757572 L0.893096605,46.2796422 C-0.418595034,44.1314075 -0.274907213,41.3978442 1.25477457,39.3989643 L30.388821,1.32865425 L30.4563519,1.24328222 C31.0981823,0.458113729 32.0600455,0.000338312914 33.0779839,0.000338312914 L68.8174965,0.000338312914 Z" id="logo-mark-colour"></path>
37
+ </g>
38
+ </g>
39
+ </svg>
40
+ HERE
41
+
42
+ def self.logo_url
43
+ "data:#{logo_media_type};base64,#{Base64.encode64(logo_data).tr("\n", '')}"
44
+ end
45
+
46
+ def self.css
47
+ template = ERB.new <<-TEMPLATE
48
+ html {
49
+ min-height: 100%;
50
+ background: <%= background %>;
51
+ }
52
+
53
+ body {
54
+ color: #fff;
55
+ margin-left: 2em;
56
+ margin-right: 2em;
57
+ margin-top: 1em;
58
+ max-width: 1200px;
59
+
60
+ font-family: -apple-system, ".SFNSText-Regular", "San Francisco", "Roboto", "Segoe UI", "Helvetica Neue", "Lucida Grande", sans-serif;
61
+ font-size: 16px;
62
+ line-height: 1.6;
63
+ -webkit-font-feature-settings: "kern","liga","clig","calt";
64
+ font-feature-settings: "kern","liga","clig","calt";
65
+ -webkit-font-smoothing: antialiased;
66
+ -moz-osx-font-smoothing: grayscale
67
+ overflow-x: hidden;
68
+ }
69
+
70
+ main {
71
+ max-width: 100%;
72
+ }
73
+
74
+ header {
75
+ display: inline-block;
76
+ max-width: 100%;
77
+ }
78
+
79
+ a:link {
80
+ color: #007bff;
81
+ }
82
+ a:visited {
83
+ color: #9c27b0;
84
+ }
85
+ a:hover {
86
+ color: #00bcd4;
87
+ }
88
+
89
+ #logo {
90
+ width: <%= logo_width %>em;
91
+ height: 5em;
92
+ background-repeat: no-repeat;
93
+ background-position-x: right;
94
+ background-position-y: center;
95
+ background-image: url(<%= logo_url %>);
96
+ float: left;
97
+ margin-right: 2em;
98
+ }
99
+
100
+ header h1 {
101
+ clear: right;
102
+ text-overflow: ellipsis;
103
+ white-space: nowrap;
104
+ overflow: hidden;
105
+
106
+ font-size: 1.5em;
107
+ line-height: 2em;
108
+ }
109
+
110
+ #content {
111
+ margin-top: 1em;
112
+ padding-left: 1em;
113
+ padding-right: 1em;
114
+ padding-bottom: 1em;
115
+ color: #060B34;
116
+ background-color: #fff;
117
+ border-radius: 1em;
118
+ border: 1px solid #E0E1E4;
119
+ overflow: auto;
120
+ }
121
+
122
+ nav h2 {
123
+ font-size: 1em;
124
+ line-height: 1.25em;
125
+ margin-bottom: 0;
126
+ }
127
+ nav .label {
128
+ float: left;
129
+ }
130
+
131
+ nav ul {
132
+ clear: right;
133
+ display: inline-block;
134
+ margin: 0;
135
+ padding: 0;
136
+ }
137
+ nav li {
138
+ float: left;
139
+ list-style: none;
140
+ margin-right: 0.3em;
141
+ }
142
+ nav li a.active {
143
+ font-weight: bold;
144
+ }
145
+ nav li a.active:link, nav li a.active:visited {
146
+ color: #060B34;
147
+ }
148
+ nav li + li:before {
149
+ content: "|";
150
+ margin-right: 0.3em;
151
+ }
152
+
153
+ nav #representations {
154
+ margin-bottom: 1em;
155
+ }
156
+ nav hr {
157
+ border: none;
158
+ border-top: solid 1px #E0E1E4;
159
+ }
160
+
161
+ nav #links {
162
+ display: inline-block;
163
+ margin-bottom: 1em;
164
+ }
165
+ nav #links ul {
166
+ float: left;
167
+ }
168
+
169
+ #output {
170
+ font-size: .9em;
171
+ }
172
+
173
+ #reply .reply-indent {
174
+ border-left: 0.5em solid lightgray;
175
+ padding-left: 1em;
176
+ }
177
+
178
+ #reply .form-table {
179
+ display: table;
180
+ border-spacing: 1em 0;
181
+ margin-left: -1em;
182
+ margin-bottom: 0.2em;
183
+ }
184
+
185
+ #reply .form-row {
186
+ display: table-row;
187
+ }
188
+
189
+ #reply .cell {
190
+ display: table-cell;
191
+ }
192
+
193
+ #reply textarea {
194
+ width: 100%;
195
+ height: 6em;
196
+ clear: both;
197
+ }
198
+
199
+ #reply #reply-status-code {
200
+ font-style: italic;
201
+ font-size: 80%;
202
+ }
203
+
204
+ TEMPLATE
205
+ template = ERB.new custom_css unless custom_css.nil?
206
+
207
+ template.result(binding)
208
+ end
209
+ end
210
+ end
211
+ end
212
+ end
@@ -1,80 +1,80 @@
1
-
2
- # frozen_string_literal: true
3
-
4
- require 'media_types/serialization/base'
5
-
6
- module MediaTypes
7
- module Serialization
8
- module Serializers
9
- class EndpointDescriptionSerializer < MediaTypes::Serialization::Base
10
-
11
- unvalidated 'application/vnd.delftsolutions.endpoint_description'
12
-
13
- disable_wildcards
14
-
15
- def self.to_input_identifiers(serializers)
16
- serializers.flat_map do |s|
17
- s[:serializer].inputs_for(views: [s[:view]]).registrations.keys
18
- end
19
- end
20
- def self.to_output_identifiers(serializers)
21
- serializers.flat_map do |s|
22
- s[:serializer].outputs_for(views: [s[:view]]).registrations.keys
23
- end
24
- end
25
-
26
- output version: 1 do |input, version, context|
27
- request_path = context.request.original_fullpath.split('?')[0]
28
-
29
- path_prefix = ENV.fetch('RAILS_RELATIVE_URL_ROOT') { '' }
30
- request_path = request_path.sub(path_prefix, '')
31
-
32
- my_controller = Rails.application.routes.recognize_path request_path
33
-
34
- methods_available = {}
35
- methods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']
36
- methods.each do |m|
37
- begin
38
- found_controller = Rails.application.routes.recognize_path request_path, method: m
39
- if found_controller[:controller] == my_controller[:controller]
40
- methods_available[m] = found_controller[:action]
41
- end
42
- rescue ActionController::RoutingError
43
- # not available
44
- end
45
- end
46
-
47
- input_definitions = input[:actions][:input] || {}
48
- output_definitions = input[:actions][:output] || {}
49
- viewer_definitions = input[:api_viewer] || {}
50
-
51
- result = {}
52
- global_in = input_definitions['all_actions'] || []
53
- global_out = output_definitions['all_actions'] || []
54
- global_viewer = viewer_definitions['all_actions'] || false
55
-
56
- viewer_uri = URI.parse(context.request.original_url)
57
- query_parts = viewer_uri.query&.split('&') || []
58
- query_parts = query_parts.select { |q| !q.start_with? 'api_viewer=' }
59
- viewer_uri.query = (query_parts + ["api_viewer=last"]).join('&')
60
-
61
- methods_available.each do |method, action|
62
- has_viewer = viewer_definitions[action] || global_viewer
63
- input_serializers = global_in + (input_definitions[action] || [])
64
- output_serializers = global_out + (output_definitions[action] || [])
65
- result[method] = {
66
- input: to_input_identifiers(input_serializers),
67
- output: to_output_identifiers(output_serializers),
68
- }
69
-
70
- result[method].delete(:input) if method == 'GET'
71
- result[method][:api_viewer] = viewer_uri.to_s if has_viewer
72
- end
73
-
74
- result
75
- end
76
-
77
- end
78
- end
79
- end
80
- end
1
+ # frozen_string_literal: true
2
+
3
+ require 'media_types/serialization/base'
4
+
5
+ module MediaTypes
6
+ module Serialization
7
+ module Serializers
8
+ class EndpointDescriptionSerializer < MediaTypes::Serialization::Base
9
+ unvalidated 'application/vnd.delftsolutions.endpoint_description'
10
+
11
+ disable_wildcards
12
+
13
+ def self.to_input_identifiers(serializers)
14
+ serializers
15
+ .flat_map do |s|
16
+ s[:serializer].inputs_for(views: [s[:view]]).registrations.keys
17
+ end
18
+ .uniq
19
+ end
20
+
21
+ def self.to_output_identifiers(serializers)
22
+ serializers
23
+ .flat_map do |s|
24
+ s[:serializer].outputs_for(views: [s[:view]]).registrations.keys
25
+ end
26
+ .uniq
27
+ end
28
+
29
+ output version: 1 do |input, _version, context|
30
+ request_path = context.request.original_fullpath.split('?')[0]
31
+
32
+ path_prefix = ENV.fetch('RAILS_RELATIVE_URL_ROOT', '')
33
+ request_path = request_path.sub(path_prefix, '')
34
+
35
+ my_controller = Rails.application.routes.recognize_path request_path
36
+
37
+ methods_available = {}
38
+ methods = %w[GET POST PUT PATCH DELETE]
39
+ methods.each do |m|
40
+ found_controller = Rails.application.routes.recognize_path request_path, method: m
41
+ if found_controller[:controller] == my_controller[:controller]
42
+ methods_available[m] = found_controller[:action]
43
+ end
44
+ rescue ActionController::RoutingError
45
+ # not available
46
+ end
47
+
48
+ input_definitions = input[:actions][:input] || {}
49
+ output_definitions = input[:actions][:output] || {}
50
+ viewer_definitions = input[:api_viewer] || {}
51
+
52
+ result = {}
53
+ global_in = input_definitions['all_actions'] || []
54
+ global_out = output_definitions['all_actions'] || []
55
+ global_viewer = viewer_definitions['all_actions'] || false
56
+
57
+ viewer_uri = URI.parse(context.request.original_url)
58
+ query_parts = viewer_uri.query&.split('&') || []
59
+ query_parts = query_parts.reject { |q| q.start_with? 'api_viewer=' }
60
+ viewer_uri.query = (query_parts + ['api_viewer=last']).join('&')
61
+
62
+ methods_available.each do |method, action|
63
+ has_viewer = viewer_definitions[action] || global_viewer
64
+ input_serializers = global_in + (input_definitions[action] || [])
65
+ output_serializers = global_out + (output_definitions[action] || [])
66
+ result[method] = {
67
+ input: to_input_identifiers(input_serializers),
68
+ output: to_output_identifiers(output_serializers),
69
+ }
70
+
71
+ result[method].delete(:input) if method == 'GET'
72
+ result[method][:api_viewer] = viewer_uri.to_s if has_viewer
73
+ end
74
+
75
+ result
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end