lokalise_manager 1.2.1 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +57 -0
- data/README.md +41 -10
- data/lib/lokalise_manager/global_config.rb +7 -1
- data/lib/lokalise_manager/task_definitions/base.rb +5 -4
- data/lib/lokalise_manager/task_definitions/exporter.rb +34 -13
- data/lib/lokalise_manager/version.rb +1 -1
- data/lokalise_manager.gemspec +2 -2
- data/spec/lib/lokalise_manager/global_config_spec.rb +5 -0
- data/spec/lib/lokalise_manager/task_definitions/base_spec.rb +5 -5
- data/spec/lib/lokalise_manager/task_definitions/exporter_spec.rb +56 -29
- data/spec/lib/lokalise_manager/task_definitions/importer_spec.rb +24 -21
- data/spec/support/vcr.rb +3 -1
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fa7eb812b614ec768710c4f085879dfaf6cf89f72e25b1a777f34af3e9cd8be5
|
4
|
+
data.tar.gz: afdd4c08bf9672302c279cd88b4c4caf63317b1aca98036a5e8c1dad361289f0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5857c35bde158e85bdec5c2e4618ebf1ddc7768f5822d5492109347fa93907434869d23c5053f6c7b98da8016e4e22c40fa8fa4b513c347630aeb18288f8d18d
|
7
|
+
data.tar.gz: 02bbc800a21396c6f106b2c18df5eee2f7247d43fac80da656496d4a8ec6a50c8d9438bb3514d13b57647f29aebfb1caf1e633a79d97eb37e30ae6bbb2634ecd
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,62 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 2.2.0 (23-Feb-22)
|
4
|
+
|
5
|
+
* Use ruby-lokalise-api v5
|
6
|
+
* Don't use any compression options (compression is now enabled by default)
|
7
|
+
* Update tests
|
8
|
+
|
9
|
+
## 2.1.0 (27-Jan-22)
|
10
|
+
|
11
|
+
* **Breaking change**: `export!` will now return an array of objects responding to the following methods:
|
12
|
+
+ `success` — usually returns `true` (to learn more, check documentation for the `:raise_on_export_fail` option below)
|
13
|
+
+ `process` — returns an object (an instance of the `Lokalise::Resources::QueuedProcess`) representing a [queued background process](https://lokalise.github.io/ruby-lokalise-api/api/queued-processes) as uploading is done in the background on Lokalise. You can use this object to check the process status (whether the uploading is completed or not).
|
14
|
+
+ `path` — returns an instance of the `Pathname` class which represent the file being uploaded.
|
15
|
+
* Here's an example:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
def uploaded?(process)
|
19
|
+
5.times do # try to check the status 5 times
|
20
|
+
process = process.reload_data # load new data
|
21
|
+
return(true) if process.status == 'finished' # return true is the upload has finished
|
22
|
+
sleep 1 # wait for 1 second, adjust this number with regards to the upload size
|
23
|
+
end
|
24
|
+
|
25
|
+
false # if all 5 checks failed, return false (probably something is wrong)
|
26
|
+
end
|
27
|
+
|
28
|
+
processes = exporter.export!
|
29
|
+
puts "Checking status for the #{processes[0].path} file"
|
30
|
+
uploaded? processes[0].process
|
31
|
+
```
|
32
|
+
|
33
|
+
* Introduced a new option `raise_on_export_fail` (`boolean`) which is `true` by default. When this option is enabled, LokaliseManager will re-raise any exceptions that happened during the file uploading. When this option is disabled, the exporting process will continue even if something goes wrong. In this case you'll probably need to check the result yourself and make the necessary actions. For example:
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
processes = exporter.export!
|
37
|
+
|
38
|
+
processes.each do |proc_data|
|
39
|
+
if proc_data.success
|
40
|
+
# Everything is good, the uploading is queued
|
41
|
+
puts "#{proc_data.path} is sent to Lokalise!"
|
42
|
+
process = proc_data.process
|
43
|
+
puts "Current process status is #{process.status}"
|
44
|
+
else
|
45
|
+
# Something bad has happened
|
46
|
+
puts "Could not send #{proc_data.path} to Lokalise"
|
47
|
+
puts "Error #{proc_data.error.class}: #{proc_data.error.message}"
|
48
|
+
# Or you could re-raise this exception:
|
49
|
+
# raise proc_data.error.class
|
50
|
+
end
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
54
|
+
## 2.0.0 (27-Jan-22)
|
55
|
+
|
56
|
+
* `export!` method is now taking advantage of multi-threading (as Lokalise API allows to send requests in parallel since January 2022)
|
57
|
+
* Test with Ruby 3.1.0
|
58
|
+
* Other minor fixes
|
59
|
+
|
3
60
|
## 1.2.1 (26-Nov-21)
|
4
61
|
|
5
62
|
* Use refinements instead of monkey patching to add hash methods
|
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# LokaliseManager
|
2
2
|
|
3
3
|
![Gem](https://img.shields.io/gem/v/lokalise_manager)
|
4
|
-
|
4
|
+
![CI](https://github.com/bodrovis/lokalise_manager/actions/workflows/ci.yml/badge.svg)
|
5
5
|
[![Test Coverage](https://codecov.io/gh/bodrovis/lokalise_manager/graph/badge.svg)](https://codecov.io/gh/bodrovis/lokalise_manager)
|
6
6
|
![Downloads total](https://img.shields.io/gem/dt/lokalise_manager)
|
7
7
|
|
@@ -43,7 +43,7 @@ importer = LokaliseManager.importer api_token: '1234abc', project_id: '123.abc'
|
|
43
43
|
exporter = LokaliseManager.exporter api_token: '1234abc', project_id: '123.abc'
|
44
44
|
```
|
45
45
|
|
46
|
-
You *must* provide an API token and a project ID (your project ID can be found under Lokalise project settings). [Other options can be customized as well (see below)](
|
46
|
+
You *must* provide an API token and a project ID (your project ID can be found under Lokalise project settings). [Other options can be customized as well (see below)](#configuration) but they have sensible defaults.
|
47
47
|
|
48
48
|
### Importing files from Lokalise into your project
|
49
49
|
|
@@ -55,7 +55,7 @@ result = importer.import!
|
|
55
55
|
|
56
56
|
The `result` will contain a boolean value which says whether the operation was successfull or not.
|
57
57
|
|
58
|
-
Please note that upon importing translations any duplicating files inside the `locales` directory (or any other directory that you've specified in the options) will be overwritten
|
58
|
+
Please note that upon importing translations any duplicating files inside the `locales` directory (or any other directory that you've specified in the options) **will be overwritten**! You can enable [safe mode](#import-config) to check whether the folder is empty or not.
|
59
59
|
|
60
60
|
### Exporting files from your project to Lokalise
|
61
61
|
|
@@ -65,13 +65,21 @@ To upload your translation files from a local directory (defaults to `locales/`)
|
|
65
65
|
processes = exporter.export!
|
66
66
|
```
|
67
67
|
|
68
|
-
|
68
|
+
The uploading process is multi-threaded.
|
69
|
+
|
70
|
+
`processes` will contain an array of objects responding to the following methods:
|
71
|
+
|
72
|
+
* `#success` — usually returns `true` (to learn more, check documentation for the `:raise_on_export_fail` option below)
|
73
|
+
* `#process` — returns an object (an instance of the `Lokalise::Resources::QueuedProcess`) representing a [queued background process](https://lokalise.github.io/ruby-lokalise-api/api/queued-processes) as uploading is done in the background on Lokalise.
|
74
|
+
* `#path` — returns an instance of the `Pathname` class which represent the file being uploaded.
|
75
|
+
|
76
|
+
You can perform periodic checks to read the status of the process. Here's a very simple example:
|
69
77
|
|
70
78
|
```ruby
|
71
79
|
def uploaded?(process)
|
72
80
|
5.times do # try to check the status 5 times
|
73
|
-
process = process.reload_data # load new
|
74
|
-
return(true) if process.status == 'finished' # return true
|
81
|
+
process = process.reload_data # load new info about this process
|
82
|
+
return(true) if process.status == 'finished' # return true if the upload has finished
|
75
83
|
sleep 1 # wait for 1 second, adjust this number with regards to the upload size
|
76
84
|
end
|
77
85
|
|
@@ -79,7 +87,8 @@ def uploaded?(process)
|
|
79
87
|
end
|
80
88
|
|
81
89
|
processes = exporter.export!
|
82
|
-
|
90
|
+
puts "Checking status for the #{processes[0].path} file"
|
91
|
+
uploaded? processes[0].process
|
83
92
|
```
|
84
93
|
|
85
94
|
Please don't forget that Lokalise API has rate limiting and you cannot send more than six requests per second.
|
@@ -162,7 +171,29 @@ In this case the `export_opts` will have `detect_icu_plurals` set to `true` and
|
|
162
171
|
c.skip_file_export = ->(file) { f.split[1].to_s.include?('fr') }
|
163
172
|
```
|
164
173
|
|
165
|
-
* `max_retries_export` (`integer`) — this option is introduced to properly handle Lokalise API rate limiting. If the HTTP status code 429 (too many requests) has been received,
|
174
|
+
* `max_retries_export` (`integer`) — this option is introduced to properly handle Lokalise API rate limiting. If the HTTP status code 429 (too many requests) has been received, LokaliseManager will apply an exponential backoff mechanism with a very simple formula: `2 ** retries` (initially `retries` is `0`). If the maximum number of retries has been reached, a `Lokalise::Error::TooManyRequests` exception will be raised and the export operation will be halted. By default, LokaliseManager will make up to `5` retries which potentially means `1 + 2 + 4 + 8 + 16 + 32 = 63` seconds of waiting time. If the `max_retries_export` is less than `1`, LokaliseManager will not perform any retries and give up immediately after receiving error 429.
|
175
|
+
* `raise_on_export_fail` (`boolean`) — default is `true`. When this option is enabled, LokaliseManager will re-raise any exceptions that happened during the file uploading. In other words, if any uploading thread raised an exception, your exporting process will exit with an exception. Suppose, you are uploading 12 translation files; these files will be split in 2 groups with 6 files each, and each group will be uploaded in parallel (using threads). However, suppose some exception happens when uploading the first group. By default this exception will be re-raised for the whole process and the script will never try to upload the second group. If you would like to continue uploading even if an exception happened, set the `raise_on_export_fail` to `false`. In this case the `export!` method will return an array with scheduled processes and with information about processes that were not successfully scheduled. This information is represented as an object with three methods: `path` (contains an instance of the `Pathname` class which says which file could not be uploaded), `error` (the actual exception), and `success` (returns `false`). So, you can use the following snippet to check your processes:
|
176
|
+
|
177
|
+
```ruby
|
178
|
+
processes = exporter.export!
|
179
|
+
|
180
|
+
processes.each do |proc_data|
|
181
|
+
if proc_data.success
|
182
|
+
# Everything is good, the uploading is queued
|
183
|
+
puts "#{proc_data.path} is sent to Lokalise!"
|
184
|
+
process = proc_data.process
|
185
|
+
puts "Current process status is #{process.status}"
|
186
|
+
else
|
187
|
+
# Something bad has happened
|
188
|
+
puts "Could not send #{proc_data.path} to Lokalise"
|
189
|
+
puts "Error #{proc_data.error.class}: #{proc_data.error.message}"
|
190
|
+
# Or you could re-raise this exception:
|
191
|
+
# raise proc_data.error.class
|
192
|
+
end
|
193
|
+
end
|
194
|
+
```
|
195
|
+
|
196
|
+
* For example, you could collect all the files that were uploaded successfully, re-create the exporter object with the `skip_file_export` option (skipping all files that were successfully imported), and re-run the whole exporting process once again.
|
166
197
|
|
167
198
|
### Config to work with formats other than YAML
|
168
199
|
|
@@ -193,7 +224,7 @@ importer = LokaliseManager.importer api_token: '1234abc',
|
|
193
224
|
|
194
225
|
These options will be merged with the default ones. Please note that per-client config has the highest priority.
|
195
226
|
|
196
|
-
You can also adjust individual options later using the `#config` instance variable (it contains
|
227
|
+
You can also adjust individual options later using the `#config` instance variable (it contains a Struct object):
|
197
228
|
|
198
229
|
```
|
199
230
|
importer.config.project_id = '678xyz'
|
@@ -271,4 +302,4 @@ importer.import!
|
|
271
302
|
|
272
303
|
## License
|
273
304
|
|
274
|
-
Copyright (c) [Lokalise team](http://lokalise.com), [Ilya Bodrov](http://bodrovis.tech). License type is [MIT](https://github.com/bodrovis/lokalise_manager/blob/master/LICENSE).
|
305
|
+
Copyright (c) [Lokalise team](http://lokalise.com), [Ilya Bodrov](http://bodrovis.tech). License type is [MIT](https://github.com/bodrovis/lokalise_manager/blob/master/LICENSE).
|
@@ -7,13 +7,19 @@ module LokaliseManager
|
|
7
7
|
attr_writer :import_opts, :import_safe_mode, :export_opts, :locales_path,
|
8
8
|
:file_ext_regexp, :skip_file_export, :branch, :timeouts,
|
9
9
|
:translations_loader, :translations_converter, :lang_iso_inferer,
|
10
|
-
:max_retries_export, :max_retries_import, :use_oauth2_token, :silent_mode
|
10
|
+
:max_retries_export, :max_retries_import, :use_oauth2_token, :silent_mode,
|
11
|
+
:raise_on_export_fail
|
11
12
|
|
12
13
|
# Main interface to provide configuration options
|
13
14
|
def config
|
14
15
|
yield self
|
15
16
|
end
|
16
17
|
|
18
|
+
# When enabled, will re-raise any exception that happens during file exporting
|
19
|
+
def raise_on_export_fail
|
20
|
+
@raise_on_export_fail || true
|
21
|
+
end
|
22
|
+
|
17
23
|
# When enabled, won't print any debugging info to $stdout
|
18
24
|
def silent_mode
|
19
25
|
@silent_mode || false
|
@@ -35,15 +35,16 @@ module LokaliseManager
|
|
35
35
|
#
|
36
36
|
# @return [Lokalise::Client]
|
37
37
|
def api_client
|
38
|
-
client_opts = [config.api_token,
|
39
|
-
client_method = config.use_oauth2_token ? :
|
38
|
+
client_opts = [config.api_token, config.timeouts]
|
39
|
+
client_method = config.use_oauth2_token ? :oauth2_client : :client
|
40
40
|
|
41
|
-
@api_client
|
41
|
+
@api_client = ::Lokalise.send(client_method, *client_opts)
|
42
42
|
end
|
43
43
|
|
44
44
|
# Resets API client
|
45
45
|
def reset_api_client!
|
46
|
-
Lokalise.reset_client!
|
46
|
+
::Lokalise.reset_client!
|
47
|
+
::Lokalise.reset_oauth2_client!
|
47
48
|
@api_client = nil
|
48
49
|
end
|
49
50
|
|
@@ -5,6 +5,9 @@ require 'base64'
|
|
5
5
|
module LokaliseManager
|
6
6
|
module TaskDefinitions
|
7
7
|
class Exporter < Base
|
8
|
+
# Lokalise allows no more than 6 requests per second
|
9
|
+
MAX_THREADS = 6
|
10
|
+
|
8
11
|
# Performs translation file export to Lokalise and returns an array of queued processes
|
9
12
|
#
|
10
13
|
# @return [Array]
|
@@ -12,10 +15,13 @@ module LokaliseManager
|
|
12
15
|
check_options_errors!
|
13
16
|
|
14
17
|
queued_processes = []
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
18
|
+
|
19
|
+
all_files.each_slice(MAX_THREADS) do |files_group|
|
20
|
+
parallel_upload(files_group).each do |thr|
|
21
|
+
raise_on_fail(thr) if config.raise_on_export_fail
|
22
|
+
|
23
|
+
queued_processes.push thr
|
24
|
+
end
|
19
25
|
end
|
20
26
|
|
21
27
|
$stdout.print('Task complete!') unless config.silent_mode
|
@@ -25,28 +31,43 @@ module LokaliseManager
|
|
25
31
|
|
26
32
|
private
|
27
33
|
|
34
|
+
def parallel_upload(files_group)
|
35
|
+
files_group.map do |file_data|
|
36
|
+
do_upload(*file_data)
|
37
|
+
end.map(&:value)
|
38
|
+
end
|
39
|
+
|
40
|
+
def raise_on_fail(thread)
|
41
|
+
raise(thread.error.class, "Error while trying to upload #{thread.path}: #{thread.error.message}") unless thread.success
|
42
|
+
end
|
43
|
+
|
28
44
|
# Performs the actual file uploading to Lokalise. If the API rate limit is exceeed,
|
29
45
|
# applies exponential backoff
|
30
46
|
def do_upload(f_path, r_path)
|
31
|
-
|
32
|
-
|
47
|
+
proc_klass = Struct.new(:success, :process, :path, :error, keyword_init: true)
|
48
|
+
|
49
|
+
Thread.new do
|
50
|
+
process = with_exp_backoff(config.max_retries_export) do
|
51
|
+
api_client.upload_file project_id_with_branch, opts(f_path, r_path)
|
52
|
+
end
|
53
|
+
proc_klass.new success: true, process: process, path: f_path
|
54
|
+
rescue StandardError => e
|
55
|
+
proc_klass.new success: false, path: f_path, error: e
|
33
56
|
end
|
34
57
|
end
|
35
58
|
|
36
|
-
#
|
37
|
-
def
|
38
|
-
return unless block_given?
|
39
|
-
|
59
|
+
# Gets translation files from the specified directory
|
60
|
+
def all_files
|
40
61
|
loc_path = config.locales_path
|
41
|
-
Dir["#{loc_path}/**/*"].
|
62
|
+
Dir["#{loc_path}/**/*"].map do |f|
|
42
63
|
full_path = Pathname.new f
|
43
64
|
|
44
65
|
next unless file_matches_criteria? full_path
|
45
66
|
|
46
67
|
relative_path = full_path.relative_path_from Pathname.new(loc_path)
|
47
68
|
|
48
|
-
|
49
|
-
end
|
69
|
+
[full_path, relative_path]
|
70
|
+
end.compact
|
50
71
|
end
|
51
72
|
|
52
73
|
# Generates export options
|
data/lokalise_manager.gemspec
CHANGED
@@ -23,7 +23,7 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.extra_rdoc_files = ['README.md']
|
24
24
|
spec.require_paths = ['lib']
|
25
25
|
|
26
|
-
spec.add_dependency 'ruby-lokalise-api', '~>
|
26
|
+
spec.add_dependency 'ruby-lokalise-api', '~> 5.0'
|
27
27
|
spec.add_dependency 'rubyzip', '~> 2.3'
|
28
28
|
|
29
29
|
spec.add_development_dependency 'codecov', '~> 0.2'
|
@@ -32,7 +32,7 @@ Gem::Specification.new do |spec|
|
|
32
32
|
spec.add_development_dependency 'rspec', '~> 3.6'
|
33
33
|
spec.add_development_dependency 'rubocop', '~> 1.0'
|
34
34
|
spec.add_development_dependency 'rubocop-performance', '~> 1.5'
|
35
|
-
spec.add_development_dependency 'rubocop-rspec', '~> 2.6
|
35
|
+
spec.add_development_dependency 'rubocop-rspec', '~> 2.6'
|
36
36
|
spec.add_development_dependency 'simplecov', '~> 0.16'
|
37
37
|
spec.add_development_dependency 'vcr', '~> 6.0'
|
38
38
|
spec.metadata = {
|
@@ -14,6 +14,11 @@ describe LokaliseManager::GlobalConfig do
|
|
14
14
|
fake_class.project_id = '123.abc'
|
15
15
|
end
|
16
16
|
|
17
|
+
it 'is possible to set raise_on_export_fail' do
|
18
|
+
allow(fake_class).to receive(:raise_on_export_fail=).with(false)
|
19
|
+
fake_class.raise_on_export_fail = false
|
20
|
+
end
|
21
|
+
|
17
22
|
it 'is possible to set silent_mode' do
|
18
23
|
allow(fake_class).to receive(:silent_mode=).with(true)
|
19
24
|
fake_class.silent_mode = true
|
@@ -24,7 +24,7 @@ describe LokaliseManager::TaskDefinitions::Base do
|
|
24
24
|
specify '.reset_client!' do
|
25
25
|
expect(described_object.api_client).to be_an_instance_of(Lokalise::Client)
|
26
26
|
described_object.reset_api_client!
|
27
|
-
current_client = described_object.instance_variable_get
|
27
|
+
current_client = described_object.instance_variable_get :@api_client
|
28
28
|
expect(current_client).to be_nil
|
29
29
|
end
|
30
30
|
|
@@ -63,14 +63,14 @@ describe LokaliseManager::TaskDefinitions::Base do
|
|
63
63
|
it 'raises an error when the API key is not set' do
|
64
64
|
allow(LokaliseManager::GlobalConfig).to receive(:api_token).and_return(nil)
|
65
65
|
|
66
|
-
expect
|
66
|
+
expect { described_object.send(:check_options_errors!) }.to raise_error(LokaliseManager::Error, /API token is not set/i)
|
67
67
|
|
68
68
|
expect(LokaliseManager::GlobalConfig).to have_received(:api_token)
|
69
69
|
end
|
70
70
|
|
71
71
|
it 'returns an error when the project_id is not set' do
|
72
72
|
allow_project_id described_object, nil do
|
73
|
-
expect
|
73
|
+
expect { described_object.send(:check_options_errors!) }.to raise_error(LokaliseManager::Error, /ID is not set/i)
|
74
74
|
end
|
75
75
|
end
|
76
76
|
end
|
@@ -96,7 +96,7 @@ describe LokaliseManager::TaskDefinitions::Base do
|
|
96
96
|
|
97
97
|
client = described_object.api_client
|
98
98
|
expect(client).to be_an_instance_of(Lokalise::Client)
|
99
|
-
expect(client).not_to be_an_instance_of(Lokalise::
|
99
|
+
expect(client).not_to be_an_instance_of(Lokalise::OAuth2Client)
|
100
100
|
expect(client.open_timeout).to eq(100)
|
101
101
|
expect(client.timeout).to eq(500)
|
102
102
|
end
|
@@ -106,7 +106,7 @@ describe LokaliseManager::TaskDefinitions::Base do
|
|
106
106
|
|
107
107
|
client = described_object.api_client
|
108
108
|
|
109
|
-
expect(client).to be_an_instance_of(Lokalise::
|
109
|
+
expect(client).to be_an_instance_of(Lokalise::OAuth2Client)
|
110
110
|
expect(client).not_to be_an_instance_of(Lokalise::Client)
|
111
111
|
end
|
112
112
|
end
|
@@ -27,7 +27,7 @@ describe LokaliseManager::TaskDefinitions::Exporter do
|
|
27
27
|
process = nil
|
28
28
|
|
29
29
|
VCR.use_cassette('upload_files_multiple') do
|
30
|
-
expect
|
30
|
+
expect { process = described_object.export!.first.process }.to output(/complete!/).to_stdout
|
31
31
|
end
|
32
32
|
|
33
33
|
expect(process.project_id).to eq(project_id)
|
@@ -35,17 +35,40 @@ describe LokaliseManager::TaskDefinitions::Exporter do
|
|
35
35
|
end
|
36
36
|
|
37
37
|
it 'handles too many requests' do
|
38
|
+
allow(described_object.config).to receive(:max_retries_export).and_return(1)
|
38
39
|
allow(described_object).to receive(:sleep).and_return(0)
|
39
40
|
|
40
41
|
fake_client = instance_double('Lokalise::Client')
|
42
|
+
allow(fake_client).to receive(:token).with(any_args).and_return('fake_token')
|
41
43
|
allow(fake_client).to receive(:upload_file).with(any_args).and_raise(Lokalise::Error::TooManyRequests)
|
42
44
|
allow(described_object).to receive(:api_client).and_return(fake_client)
|
43
45
|
|
44
|
-
expect
|
46
|
+
expect { described_object.export! }.to raise_error(Lokalise::Error::TooManyRequests, /Gave up after 1 retries/i)
|
45
47
|
|
46
|
-
expect(described_object).to have_received(:sleep).exactly(
|
47
|
-
expect(described_object).to have_received(:api_client).
|
48
|
-
expect(fake_client).to have_received(:upload_file).exactly(
|
48
|
+
expect(described_object).to have_received(:sleep).exactly(6).times
|
49
|
+
expect(described_object).to have_received(:api_client).at_least(12).times
|
50
|
+
expect(fake_client).to have_received(:upload_file).exactly(12).times
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'handles too many requests but does not re-raise anything when raise_on_export_fail is false' do
|
54
|
+
allow(described_object.config).to receive(:max_retries_export).and_return(1)
|
55
|
+
allow(described_object.config).to receive(:raise_on_export_fail).and_return(false)
|
56
|
+
allow(described_object).to receive(:sleep).and_return(0)
|
57
|
+
|
58
|
+
fake_client = instance_double('Lokalise::Client')
|
59
|
+
allow(fake_client).to receive(:token).with(any_args).and_return('fake_token')
|
60
|
+
allow(fake_client).to receive(:upload_file).with(any_args).and_raise(Lokalise::Error::TooManyRequests)
|
61
|
+
allow(described_object).to receive(:api_client).and_return(fake_client)
|
62
|
+
processes = []
|
63
|
+
expect { processes = described_object.export! }.not_to raise_error
|
64
|
+
|
65
|
+
expect(processes[0].success).to be false
|
66
|
+
expect(processes[1].error.class).to eq(Lokalise::Error::TooManyRequests)
|
67
|
+
expect(processes.count).to eq(7)
|
68
|
+
|
69
|
+
expect(described_object).to have_received(:sleep).exactly(7).times
|
70
|
+
expect(described_object).to have_received(:api_client).at_least(14).times
|
71
|
+
expect(fake_client).to have_received(:upload_file).exactly(14).times
|
49
72
|
end
|
50
73
|
end
|
51
74
|
end
|
@@ -66,7 +89,7 @@ describe LokaliseManager::TaskDefinitions::Exporter do
|
|
66
89
|
process = nil
|
67
90
|
|
68
91
|
VCR.use_cassette('upload_files') do
|
69
|
-
expect
|
92
|
+
expect { process = described_object.export!.first.process }.not_to output(/complete!/).to_stdout
|
70
93
|
end
|
71
94
|
|
72
95
|
expect(process.status).to eq('queued')
|
@@ -76,7 +99,7 @@ describe LokaliseManager::TaskDefinitions::Exporter do
|
|
76
99
|
it 'sends a proper API request' do
|
77
100
|
process = VCR.use_cassette('upload_files') do
|
78
101
|
described_object.export!
|
79
|
-
end.first
|
102
|
+
end.first.process
|
80
103
|
|
81
104
|
expect(process.project_id).to eq(project_id)
|
82
105
|
expect(process.status).to eq('queued')
|
@@ -85,11 +108,16 @@ describe LokaliseManager::TaskDefinitions::Exporter do
|
|
85
108
|
it 'sends a proper API request when a different branch is provided' do
|
86
109
|
allow(described_object.config).to receive(:branch).and_return('develop')
|
87
110
|
|
88
|
-
|
111
|
+
process_data = VCR.use_cassette('upload_files_branch') do
|
89
112
|
described_object.export!
|
90
113
|
end.first
|
91
114
|
|
92
115
|
expect(described_object.config).to have_received(:branch).at_most(2).times
|
116
|
+
expect(process_data.success).to be true
|
117
|
+
expect(process_data.path.to_s).to include('en.yml')
|
118
|
+
|
119
|
+
process = process_data.process
|
120
|
+
expect(process).to be_an_instance_of(Lokalise::Resources::QueuedProcess)
|
93
121
|
expect(process.project_id).to eq(project_id)
|
94
122
|
expect(process.status).to eq('queued')
|
95
123
|
end
|
@@ -97,20 +125,20 @@ describe LokaliseManager::TaskDefinitions::Exporter do
|
|
97
125
|
it 'halts when the API key is not set' do
|
98
126
|
allow(described_object.config).to receive(:api_token).and_return(nil)
|
99
127
|
|
100
|
-
expect
|
128
|
+
expect { described_object.export! }.to raise_error(LokaliseManager::Error, /API token is not set/i)
|
101
129
|
expect(described_object.config).to have_received(:api_token)
|
102
130
|
end
|
103
131
|
|
104
132
|
it 'halts when the project_id is not set' do
|
105
133
|
allow_project_id described_object, nil do
|
106
|
-
expect
|
134
|
+
expect { described_object.export! }.to raise_error(LokaliseManager::Error, /ID is not set/i)
|
107
135
|
end
|
108
136
|
end
|
109
137
|
end
|
110
138
|
|
111
|
-
describe '
|
139
|
+
describe '#all_files' do
|
112
140
|
it 'yield proper arguments' do
|
113
|
-
expect
|
141
|
+
expect(described_object.send(:all_files).flatten).to include(
|
114
142
|
Pathname.new(path),
|
115
143
|
Pathname.new(relative_name)
|
116
144
|
)
|
@@ -180,30 +208,29 @@ describe LokaliseManager::TaskDefinitions::Exporter do
|
|
180
208
|
end
|
181
209
|
end
|
182
210
|
|
183
|
-
describe '
|
184
|
-
it '
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
]
|
211
|
+
describe '#all_files' do
|
212
|
+
it 'returns all files' do
|
213
|
+
files = described_object.send(:all_files).flatten
|
214
|
+
expect(files).to include(
|
215
|
+
Pathname.new(path),
|
216
|
+
Pathname.new(relative_name)
|
217
|
+
)
|
218
|
+
expect(files).to include(
|
219
|
+
Pathname.new(path_ru),
|
220
|
+
Pathname.new(filename_ru)
|
194
221
|
)
|
195
222
|
end
|
196
223
|
|
197
|
-
it 'does not
|
224
|
+
it 'does not return files that have to be skipped' do
|
198
225
|
allow(described_object.config).to receive(:skip_file_export).twice.and_return(
|
199
226
|
->(f) { f.split[1].to_s.include?('ru') }
|
200
227
|
)
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
]
|
228
|
+
files = described_object.send(:all_files).sort
|
229
|
+
expect(files[0]).to include(
|
230
|
+
Pathname.new(path),
|
231
|
+
Pathname.new(relative_name)
|
206
232
|
)
|
233
|
+
expect(files.count).to eq(1)
|
207
234
|
|
208
235
|
expect(described_object.config).to have_received(:skip_file_export).twice
|
209
236
|
end
|
@@ -9,21 +9,25 @@ describe LokaliseManager::TaskDefinitions::Importer do
|
|
9
9
|
let(:loc_path) { described_object.config.locales_path }
|
10
10
|
let(:project_id) { ENV['LOKALISE_PROJECT_ID'] }
|
11
11
|
let(:local_trans) { "#{Dir.getwd}/spec/fixtures/trans.zip" }
|
12
|
-
let(:faulty_trans) { "#{Dir.getwd}/spec/fixtures/faulty_trans.zip" }
|
13
12
|
|
14
|
-
describe '
|
13
|
+
describe '#open_and_process_zip' do
|
15
14
|
it 're-raises errors during file processing' do
|
16
|
-
|
17
|
-
|
15
|
+
entry = double
|
16
|
+
allow(entry).to receive(:name).and_return('fail.yml')
|
17
|
+
allow(described_object).to receive(:data_from).with(entry).and_raise(EncodingError)
|
18
|
+
expect { described_object.send(:process!, entry) }.
|
19
|
+
to raise_error(EncodingError, /Error when trying to process fail\.yml/)
|
20
|
+
|
21
|
+
expect(described_object).to have_received(:data_from)
|
18
22
|
end
|
19
23
|
|
20
24
|
it 're-raises errors during file opening' do
|
21
|
-
expect
|
25
|
+
expect { described_object.send(:open_and_process_zip, 'http://fake.url/wrong/path.zip') }.
|
22
26
|
to raise_error(SocketError, /Failed to open TCP connection/)
|
23
27
|
end
|
24
28
|
end
|
25
29
|
|
26
|
-
describe '
|
30
|
+
describe '#download_files' do
|
27
31
|
it 'returns a proper download URL' do
|
28
32
|
response = VCR.use_cassette('download_files') do
|
29
33
|
described_object.send :download_files
|
@@ -31,14 +35,13 @@ describe LokaliseManager::TaskDefinitions::Importer do
|
|
31
35
|
|
32
36
|
expect(response['project_id']).to eq('672198945b7d72fc048021.15940510')
|
33
37
|
expect(response['bundle_url']).to include('s3-eu-west-1.amazonaws.com')
|
34
|
-
expect(described_object.api_client.enable_compression).to eq(true)
|
35
38
|
end
|
36
39
|
|
37
40
|
it 're-raises errors during file download' do
|
38
41
|
allow_project_id described_object, 'invalid'
|
39
42
|
|
40
43
|
VCR.use_cassette('download_files_error') do
|
41
|
-
expect
|
44
|
+
expect { described_object.send :download_files }.
|
42
45
|
to raise_error(Lokalise::Error::BadRequest, /Invalid `project_id` parameter/)
|
43
46
|
end
|
44
47
|
end
|
@@ -53,7 +56,7 @@ describe LokaliseManager::TaskDefinitions::Importer do
|
|
53
56
|
allow(fake_client).to receive(:download_files).and_raise(Lokalise::Error::TooManyRequests)
|
54
57
|
allow(described_object).to receive(:api_client).and_return(fake_client)
|
55
58
|
|
56
|
-
expect
|
59
|
+
expect { described_object.import! }.to raise_error(Lokalise::Error::TooManyRequests, /Gave up after 2 retries/i)
|
57
60
|
|
58
61
|
expect(described_object).to have_received(:sleep).exactly(2).times
|
59
62
|
expect(described_object).to have_received(:api_client).exactly(3).times
|
@@ -62,14 +65,14 @@ describe LokaliseManager::TaskDefinitions::Importer do
|
|
62
65
|
|
63
66
|
it 'halts when the API key is not set' do
|
64
67
|
allow(described_object.config).to receive(:api_token).and_return(nil)
|
65
|
-
expect
|
68
|
+
expect { described_object.import! }.to raise_error(LokaliseManager::Error, /API token is not set/i)
|
66
69
|
expect(described_object.config).to have_received(:api_token)
|
67
70
|
expect(count_translations).to eq(0)
|
68
71
|
end
|
69
72
|
|
70
73
|
it 'halts when the project_id is not set' do
|
71
74
|
allow_project_id described_object, nil do
|
72
|
-
expect
|
75
|
+
expect { described_object.import! }.to raise_error(LokaliseManager::Error, /ID is not set/i)
|
73
76
|
expect(count_translations).to eq(0)
|
74
77
|
end
|
75
78
|
end
|
@@ -106,14 +109,14 @@ describe LokaliseManager::TaskDefinitions::Importer do
|
|
106
109
|
result = nil
|
107
110
|
|
108
111
|
VCR.use_cassette('import') do
|
109
|
-
expect
|
112
|
+
expect { result = described_object.import! }.to output(/complete!/).to_stdout
|
110
113
|
end
|
111
114
|
|
112
115
|
expect(result).to be true
|
113
116
|
|
114
|
-
expect(count_translations).to eq(
|
115
|
-
expect_file_exist loc_path, '
|
116
|
-
expect_file_exist loc_path, '
|
117
|
+
expect(count_translations).to eq(24)
|
118
|
+
expect_file_exist loc_path, 'en_1.yml'
|
119
|
+
expect_file_exist loc_path, 'ru_2.yml'
|
117
120
|
end
|
118
121
|
|
119
122
|
it 'runs import successfully but does not provide any output when silent_mode is enabled' do
|
@@ -121,12 +124,12 @@ describe LokaliseManager::TaskDefinitions::Importer do
|
|
121
124
|
result = nil
|
122
125
|
|
123
126
|
VCR.use_cassette('import') do
|
124
|
-
expect
|
127
|
+
expect { result = described_object.import! }.not_to output(/complete!/).to_stdout
|
125
128
|
end
|
126
129
|
|
127
130
|
expect(result).to be true
|
128
|
-
expect_file_exist loc_path, '
|
129
|
-
expect_file_exist loc_path, '
|
131
|
+
expect_file_exist loc_path, 'en_1.yml'
|
132
|
+
expect_file_exist loc_path, 'ru_2.yml'
|
130
133
|
expect(described_object.config).to have_received(:silent_mode).at_most(1).times
|
131
134
|
end
|
132
135
|
end
|
@@ -160,7 +163,7 @@ describe LokaliseManager::TaskDefinitions::Importer do
|
|
160
163
|
)
|
161
164
|
|
162
165
|
allow($stdin).to receive(:gets).and_return('Y')
|
163
|
-
expect
|
166
|
+
expect { safe_mode_obj.import! }.to output(/is not empty/).to_stdout
|
164
167
|
|
165
168
|
expect(count_translations).to eq(5)
|
166
169
|
expect($stdin).to have_received(:gets)
|
@@ -173,7 +176,7 @@ describe LokaliseManager::TaskDefinitions::Importer do
|
|
173
176
|
it 'import halts when a user chooses not to proceed' do
|
174
177
|
allow(safe_mode_obj).to receive(:download_files).at_most(0).times
|
175
178
|
allow($stdin).to receive(:gets).and_return('N')
|
176
|
-
expect
|
179
|
+
expect { safe_mode_obj.import! }.to output(/cancelled/).to_stdout
|
177
180
|
|
178
181
|
expect(safe_mode_obj).not_to have_received(:download_files)
|
179
182
|
expect($stdin).to have_received(:gets)
|
@@ -184,7 +187,7 @@ describe LokaliseManager::TaskDefinitions::Importer do
|
|
184
187
|
allow(safe_mode_obj.config).to receive(:silent_mode).and_return(true)
|
185
188
|
allow(safe_mode_obj).to receive(:download_files).at_most(0).times
|
186
189
|
allow($stdin).to receive(:gets).and_return('N')
|
187
|
-
expect
|
190
|
+
expect { safe_mode_obj.import! }.not_to output(/cancelled/).to_stdout
|
188
191
|
|
189
192
|
expect(safe_mode_obj).not_to have_received(:download_files)
|
190
193
|
expect(safe_mode_obj.config).to have_received(:silent_mode)
|
data/spec/support/vcr.rb
CHANGED
@@ -6,6 +6,8 @@ VCR.configure do |c|
|
|
6
6
|
c.ignore_hosts 'codeclimate.com'
|
7
7
|
c.hook_into :faraday
|
8
8
|
c.cassette_library_dir = File.join(File.dirname(__FILE__), '..', 'fixtures', 'vcr_cassettes')
|
9
|
-
c.filter_sensitive_data('<LOKALISE_TOKEN>')
|
9
|
+
c.filter_sensitive_data('<LOKALISE_TOKEN>') do |_i|
|
10
|
+
ENV.fetch('LOKALISE_API_TOKEN')
|
11
|
+
end
|
10
12
|
c.configure_rspec_metadata!
|
11
13
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lokalise_manager
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ilya Bodrov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-02-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ruby-lokalise-api
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '5.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '5.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rubyzip
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -128,14 +128,14 @@ dependencies:
|
|
128
128
|
requirements:
|
129
129
|
- - "~>"
|
130
130
|
- !ruby/object:Gem::Version
|
131
|
-
version: 2.6
|
131
|
+
version: '2.6'
|
132
132
|
type: :development
|
133
133
|
prerelease: false
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
135
135
|
requirements:
|
136
136
|
- - "~>"
|
137
137
|
- !ruby/object:Gem::Version
|
138
|
-
version: 2.6
|
138
|
+
version: '2.6'
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
140
|
name: simplecov
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
@@ -220,7 +220,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
220
220
|
- !ruby/object:Gem::Version
|
221
221
|
version: '0'
|
222
222
|
requirements: []
|
223
|
-
rubygems_version: 3.
|
223
|
+
rubygems_version: 3.3.7
|
224
224
|
signing_key:
|
225
225
|
specification_version: 4
|
226
226
|
summary: Lokalise integration for Ruby
|