media_types-serialization 2.0.0 → 2.0.4

Sign up to get free protection for your applications and to get access to all the features.
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 -33
  4. data/.github/workflows/publish-sid.yml +34 -33
  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 +200 -190
  16. data/CODE_OF_CONDUCT.md +74 -74
  17. data/Gemfile +4 -4
  18. data/Gemfile.lock +169 -0
  19. data/LICENSE.txt +21 -21
  20. data/README.md +1048 -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 +135 -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 +93 -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 -682
  41. data/media_types-serialization.gemspec +48 -48
  42. metadata +10 -9
@@ -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
+
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