wovnrb 3.11.0 → 3.13.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/.rubocop.yml +3 -0
- data/README.en.md +256 -256
- data/README.ja.md +218 -218
- data/docker/rails/TestSite/config/application.rb +1 -2
- data/docker/rails/TestSite/yarn.lock +7449 -7353
- data/lib/wovnrb/api_translator.rb +186 -183
- data/lib/wovnrb/headers.rb +192 -192
- data/lib/wovnrb/lang.rb +260 -260
- data/lib/wovnrb/services/html_converter.rb +240 -222
- data/lib/wovnrb/services/html_replace_marker.rb +48 -48
- data/lib/wovnrb/settings.rb +2 -2
- data/lib/wovnrb/store.rb +230 -222
- data/lib/wovnrb/version.rb +3 -3
- data/lib/wovnrb.rb +146 -145
- data/test/lib/api_translator_test.rb +228 -217
- data/test/lib/custom_domain/custom_domain_langs_test.rb +2 -2
- data/test/lib/lang_test.rb +59 -59
- data/test/lib/services/html_converter_test.rb +570 -532
- data/test/lib/services/html_replace_marker_test.rb +149 -149
- data/test/lib/url_language_switcher_test.rb +1097 -1097
- data/test/lib/wovnrb_test.rb +477 -454
- data/test/test_helper.rb +1 -0
- metadata +6 -6
|
@@ -1,183 +1,186 @@
|
|
|
1
|
-
require 'addressable'
|
|
2
|
-
require 'digest'
|
|
3
|
-
require 'json'
|
|
4
|
-
require 'zlib'
|
|
5
|
-
|
|
6
|
-
module Wovnrb
|
|
7
|
-
class ApiTranslator
|
|
8
|
-
def initialize(store, headers, uuid)
|
|
9
|
-
@store = store
|
|
10
|
-
@headers = headers
|
|
11
|
-
@uuid = uuid
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
connection.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
'
|
|
69
|
-
'Content-
|
|
70
|
-
'Content-
|
|
71
|
-
'
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
'
|
|
83
|
-
'
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
'
|
|
98
|
-
'
|
|
99
|
-
'
|
|
100
|
-
'
|
|
101
|
-
'
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
'
|
|
111
|
-
'
|
|
112
|
-
'
|
|
113
|
-
'
|
|
114
|
-
'
|
|
115
|
-
'
|
|
116
|
-
'
|
|
117
|
-
'
|
|
118
|
-
'
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
result
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
1
|
+
require 'addressable'
|
|
2
|
+
require 'digest'
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'zlib'
|
|
5
|
+
|
|
6
|
+
module Wovnrb
|
|
7
|
+
class ApiTranslator
|
|
8
|
+
def initialize(store, headers, uuid, response_status_code)
|
|
9
|
+
@store = store
|
|
10
|
+
@headers = headers
|
|
11
|
+
@uuid = uuid
|
|
12
|
+
@response_status_code = response_status_code
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def translate(body)
|
|
16
|
+
connection = prepare_connection
|
|
17
|
+
request = prepare_request(body)
|
|
18
|
+
|
|
19
|
+
begin
|
|
20
|
+
response = connection.request(request)
|
|
21
|
+
rescue => e
|
|
22
|
+
WovnLogger.error("\"#{e.message}\" error occurred when contacting WOVNio translation API")
|
|
23
|
+
return body
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
case response
|
|
27
|
+
when Net::HTTPSuccess
|
|
28
|
+
begin
|
|
29
|
+
raw_response_body = @store.dev_mode? ? response.body : Zlib::GzipReader.new(StringIO.new(response.body)).read
|
|
30
|
+
rescue Zlib::GzipFile::Error
|
|
31
|
+
raw_response_body = response.body
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
begin
|
|
35
|
+
JSON.parse(raw_response_body)['body'] || body
|
|
36
|
+
rescue JSON::JSONError
|
|
37
|
+
body
|
|
38
|
+
end
|
|
39
|
+
else
|
|
40
|
+
WovnLogger.error("HTML-swapper call failed. Received \"#{response.message}\" from WOVNio translation API.")
|
|
41
|
+
body
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def prepare_connection
|
|
48
|
+
connection = Net::HTTP.new(api_uri.host, api_uri.port)
|
|
49
|
+
|
|
50
|
+
connection.open_timeout = api_timeout
|
|
51
|
+
connection.read_timeout = api_timeout
|
|
52
|
+
|
|
53
|
+
connection
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def prepare_request(body)
|
|
57
|
+
if @store.compress_api_requests?
|
|
58
|
+
gzip_request(body)
|
|
59
|
+
else
|
|
60
|
+
json_request(body)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def gzip_request(html_body)
|
|
65
|
+
api_params = build_api_params(html_body)
|
|
66
|
+
compressed_body = compress_request_data(api_params.to_json)
|
|
67
|
+
request = Net::HTTP::Post.new(request_path(html_body), {
|
|
68
|
+
'Accept-Encoding' => 'gzip',
|
|
69
|
+
'Content-Type' => 'application/json',
|
|
70
|
+
'Content-Encoding' => 'gzip',
|
|
71
|
+
'Content-Length' => compressed_body.bytesize.to_s,
|
|
72
|
+
'X-Request-Id' => @uuid
|
|
73
|
+
})
|
|
74
|
+
request.body = compressed_body
|
|
75
|
+
|
|
76
|
+
request
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def json_request(html_body)
|
|
80
|
+
api_params = build_api_params(html_body)
|
|
81
|
+
request = Net::HTTP::Post.new(request_path(html_body), {
|
|
82
|
+
'Accept-Encoding' => 'gzip',
|
|
83
|
+
'Content-Type' => 'application/json',
|
|
84
|
+
'X-Request-Id' => @uuid
|
|
85
|
+
})
|
|
86
|
+
request.body = api_params.to_json
|
|
87
|
+
|
|
88
|
+
request
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def request_path(body)
|
|
92
|
+
"#{api_uri.path}/translation?cache_key=#{cache_key(body)}"
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def cache_key(body)
|
|
96
|
+
cache_key_components = {
|
|
97
|
+
'token' => token,
|
|
98
|
+
'settings_hash' => settings_hash,
|
|
99
|
+
'body_hash' => Digest::MD5.hexdigest(body),
|
|
100
|
+
'path' => page_pathname,
|
|
101
|
+
'lang' => lang_code,
|
|
102
|
+
'version' => "wovnrb_#{VERSION}"
|
|
103
|
+
}.map { |k, v| "#{k}=#{v}" }.join('&')
|
|
104
|
+
|
|
105
|
+
CGI.escape("(#{cache_key_components})")
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def build_api_params(body)
|
|
109
|
+
result = {
|
|
110
|
+
'url' => page_url,
|
|
111
|
+
'token' => token,
|
|
112
|
+
'lang_code' => lang_code,
|
|
113
|
+
'url_pattern' => url_pattern,
|
|
114
|
+
'lang_param_name' => lang_param_name,
|
|
115
|
+
'translate_canonical_tag' => translate_canonical_tag,
|
|
116
|
+
'insert_hreflangs' => insert_hreflangs,
|
|
117
|
+
'hreflang_x_default_lang' => @store.settings['hreflang_x_default_lang'], # should not use fallback, so html swapper can calculate the default lang from js_data
|
|
118
|
+
'product' => 'WOVN.rb',
|
|
119
|
+
'version' => VERSION,
|
|
120
|
+
'page_status_code' => @response_status_code,
|
|
121
|
+
'body' => body
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
result['custom_lang_aliases'] = JSON.dump(custom_lang_aliases) unless custom_lang_aliases.empty?
|
|
125
|
+
result['custom_domain_langs'] = JSON.dump(custom_domain_langs) unless custom_domain_langs.empty?
|
|
126
|
+
|
|
127
|
+
result
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def compress_request_data(data_hash)
|
|
131
|
+
ActiveSupport::Gzip.compress(data_hash)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def api_uri
|
|
135
|
+
Addressable::URI.parse("#{@store.settings['api_url']}/v0")
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def api_timeout
|
|
139
|
+
@headers.search_engine_bot? ? @store.settings['api_timeout_search_engine_bots'] : @store.settings['api_timeout_seconds']
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def settings_hash
|
|
143
|
+
Digest::MD5.hexdigest(JSON.dump(@store.settings))
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def token
|
|
147
|
+
@store.settings['project_token']
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def lang_code
|
|
151
|
+
@headers.lang_code
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def url_pattern
|
|
155
|
+
@store.settings['url_pattern']
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def lang_param_name
|
|
159
|
+
@store.settings['lang_param_name']
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def custom_lang_aliases
|
|
163
|
+
@store.settings['custom_lang_aliases']
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def translate_canonical_tag
|
|
167
|
+
@store.settings['translate_canonical_tag']
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def insert_hreflangs
|
|
171
|
+
@store.settings['insert_hreflangs']
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def custom_domain_langs
|
|
175
|
+
@store.custom_domain_langs.to_html_swapper_hash
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def page_url
|
|
179
|
+
"#{@headers.protocol}://#{@headers.url}"
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def page_pathname
|
|
183
|
+
@headers.pathname_with_trailing_slash_if_present
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|