crowdin-api 0.4.1 → 1.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 (38) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/build-and-publish.yml +30 -0
  3. data/.github/workflows/test-and-lint.yml +31 -0
  4. data/.gitignore +12 -0
  5. data/.rspec +2 -0
  6. data/.rubocop.yml +5 -0
  7. data/.rubocop_todo.yml +139 -0
  8. data/CODE_OF_CONDUCT.md +128 -0
  9. data/CONTRIBUTING.md +71 -0
  10. data/Gemfile +5 -0
  11. data/LICENSE +1 -1
  12. data/README.md +114 -267
  13. data/Rakefile +19 -0
  14. data/bin/crowdin-console +54 -0
  15. data/bin/setup +6 -0
  16. data/crowdin-api.gemspec +33 -0
  17. data/lib/crowdin-api/api-resources/languages.rb +81 -0
  18. data/lib/crowdin-api/api-resources/projects.rb +134 -0
  19. data/lib/crowdin-api/api-resources/source_files.rb +303 -0
  20. data/lib/crowdin-api/api-resources/source_strings.rb +74 -0
  21. data/lib/crowdin-api/api-resources/storages.rb +102 -0
  22. data/lib/crowdin-api/api-resources/translation_status.rb +89 -0
  23. data/lib/crowdin-api/api-resources/translations.rb +160 -0
  24. data/lib/crowdin-api/api-resources/workflows.rb +59 -0
  25. data/lib/crowdin-api/client/client.rb +82 -0
  26. data/lib/crowdin-api/client/configuration.rb +44 -0
  27. data/lib/crowdin-api/client/version.rb +7 -0
  28. data/lib/crowdin-api/core/api_errors_raiser.rb +29 -0
  29. data/lib/crowdin-api/core/errors.rb +7 -0
  30. data/lib/crowdin-api/core/request.rb +115 -0
  31. data/lib/crowdin-api.rb +23 -127
  32. data/spec/core/config-instance_spec.rb +72 -0
  33. data/spec/crowdin-api_spec.rb +7 -0
  34. data/spec/spec_helper.rb +9 -0
  35. metadata +129 -57
  36. data/lib/crowdin-api/errors.rb +0 -23
  37. data/lib/crowdin-api/methods.rb +0 -427
  38. data/lib/crowdin-api/version.rb +0 -5
@@ -0,0 +1,160 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Crowdin
4
+ module ApiResources
5
+ module Translations
6
+ def pre_translation_status(pre_translation_id = nil, project_id = config.project_id)
7
+ pre_translation_id || raise_parameter_is_required_error(:pre_translation_id)
8
+ project_id || raise_project_id_is_required_error
9
+
10
+ request = Web::Request.new(
11
+ self,
12
+ :get,
13
+ "/projects/#{project_id}/pre-translations/#{pre_translation_id}"
14
+ )
15
+
16
+ request.perform
17
+ end
18
+
19
+ def apply_pre_translation(query = {}, project_id = config.project_id)
20
+ project_id || raise_project_id_is_required_error
21
+
22
+ request = Web::Request.new(
23
+ self,
24
+ :post,
25
+ "/projects/#{project_id}/pre-translations",
26
+ query
27
+ )
28
+
29
+ request.perform
30
+ end
31
+
32
+ def build_project_directory_translation(directory_id = nil, query = {}, project_id = config.project_id)
33
+ directory_id || raise_parameter_is_required_error(:directory_id)
34
+ project_id || raise_project_id_is_required_error
35
+
36
+ request = Web::Request.new(
37
+ self,
38
+ :post,
39
+ "/projects/#{project_id}/translations/builds/directories/#{directory_id}",
40
+ query
41
+ )
42
+
43
+ request.perform
44
+ end
45
+
46
+ def build_project_file_translation(file_id = nil, query = {}, project_id = config.project_id)
47
+ file_id || raise_parameter_is_required_error(:file_id)
48
+ project_id || raise_project_id_is_required_error
49
+
50
+ headers = query[:eTag] ? { 'If-None-Match' => query[:eTag] } : {}
51
+
52
+ request = Web::Request.new(
53
+ self,
54
+ :post,
55
+ "/projects/#{project_id}/translations/builds/files/#{file_id}",
56
+ query,
57
+ headers
58
+ )
59
+
60
+ request.perform
61
+ end
62
+
63
+ def list_project_builds(query = {}, project_id = config.project_id)
64
+ project_id || raise_project_id_is_required_error
65
+
66
+ request = Web::Request.new(
67
+ self,
68
+ :get,
69
+ "/projects/#{project_id}/translations/builds",
70
+ query
71
+ )
72
+
73
+ request.perform
74
+ end
75
+
76
+ def build_project_translation(query = {}, project_id = config.project_id)
77
+ project_id || raise_project_id_is_required_error
78
+
79
+ request = Web::Request.new(
80
+ self,
81
+ :post,
82
+ "/projects/#{project_id}/translations/builds",
83
+ query
84
+ )
85
+
86
+ request.perform
87
+ end
88
+
89
+ def upload_translations(language_id = nil, query = {}, project_id = config.project_id)
90
+ language_id || raise_parameter_is_required_error(:language_id)
91
+ project_id || raise_project_id_is_required_error
92
+
93
+ request = Web::Request.new(
94
+ self,
95
+ :post,
96
+ "/projects/#{project_id}/translations/#{language_id}",
97
+ query
98
+ )
99
+
100
+ request.perform
101
+ end
102
+
103
+ def download_project_translations(destination = nil, build_id = nil, project_id = config.project_id)
104
+ destination || raise_parameter_is_required_error(:destination)
105
+ build_id || raise_parameter_is_required_error(:build_id)
106
+ project_id || raise_project_id_is_required_error
107
+
108
+ request = Web::Request.new(
109
+ self,
110
+ :get,
111
+ "/projects/#{project_id}/translations/builds/#{build_id}/download",
112
+ {},
113
+ {},
114
+ destination
115
+ )
116
+
117
+ request.perform
118
+ end
119
+
120
+ def check_project_build_status(build_id = nil, project_id = config.project_id)
121
+ build_id || raise_parameter_is_required_error(:build_id)
122
+ project_id || raise_project_id_is_required_error
123
+
124
+ request = Web::Request.new(
125
+ self,
126
+ :get,
127
+ "/projects/#{project_id}/translations/builds/#{build_id}"
128
+ )
129
+
130
+ request.perform
131
+ end
132
+
133
+ def cancel_build(build_id = nil, project_id = config.project_id)
134
+ build_id || raise_parameter_is_required_error(:build_id)
135
+ project_id || raise_project_id_is_required_error
136
+
137
+ request = Web::Request.new(
138
+ self,
139
+ :delete,
140
+ "/projects/#{project_id}/translations/builds/#{build_id}"
141
+ )
142
+
143
+ request.perform
144
+ end
145
+
146
+ def export_project_translation(query = {}, project_id = config.project_id)
147
+ project_id || raise_project_id_is_required_error
148
+
149
+ request = Web::Request.new(
150
+ self,
151
+ :post,
152
+ "/projects/#{project_id}/translations/exports",
153
+ query
154
+ )
155
+
156
+ request.perform
157
+ end
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Crowdin
4
+ module ApiResources
5
+ module Workflows
6
+ def list_workflow_steps(query = {}, project_id = config.project_id)
7
+ config.enterprise_mode? || raise_only_for_enterprise_mode_error
8
+ project_id || raise_project_id_is_required_error
9
+
10
+ request = Web::Request.new(
11
+ self,
12
+ :get,
13
+ "/projects/#{project_id}/workflow-steps",
14
+ query
15
+ )
16
+
17
+ request.perform
18
+ end
19
+
20
+ def get_workflow_step(step_id = nil, project_id = config.project_id)
21
+ config.enterprise_mode? || raise_only_for_enterprise_mode_error
22
+ step_id || raise_parameter_is_required_error(:step_id)
23
+ project_id || raise_project_id_is_required_error
24
+
25
+ request = Web::Request.new(
26
+ self,
27
+ :get,
28
+ "/projects/#{project_id}/workflow-steps/#{step_id}"
29
+ )
30
+
31
+ request.perform
32
+ end
33
+
34
+ def list_workflow_templates(query = {})
35
+ request = Web::Request.new(
36
+ self,
37
+ :get,
38
+ '/workflow-templates',
39
+ query
40
+ )
41
+
42
+ request.perform
43
+ end
44
+
45
+ def get_workflow_template(template_id = nil)
46
+ config.enterprise_mode? || raise_only_for_enterprise_mode_error
47
+ template_id || raise_parameter_is_required_error(:template_id)
48
+
49
+ request = Web::Request.new(
50
+ self,
51
+ :get,
52
+ "/workflow-templates/#{template_id}"
53
+ )
54
+
55
+ request.perform
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # The Crowdin::Client library is used for interactions with a crowdin.com website.
5
+ #
6
+ # == Example
7
+ #
8
+ # require 'crowdin-api'
9
+ #
10
+ # crowdin = Crowdin::Client.new do |config|
11
+ # config.api_token = 'YOUR_API_TOKEN'
12
+ # end
13
+ #
14
+ # crowdin.list_projects
15
+ #
16
+ module Crowdin
17
+ class Client
18
+ # A wrapper and interface to the Crowdin api. Please visit the Crowdin developers
19
+ # site for a full explanation of what each of the Crowdin api methods
20
+ # expect and perform.
21
+ #
22
+ # https://support.crowdin.com/api/v2/
23
+ #
24
+ include ApiResources::Languages
25
+ include ApiResources::Projects
26
+ include ApiResources::SourceFiles
27
+ include ApiResources::Storages
28
+ include ApiResources::TranslationStatus
29
+ include ApiResources::Translations
30
+ include ApiResources::Workflows
31
+ include ApiResources::SourceStrings
32
+
33
+ include Errors::ApiErrorsRaiser
34
+
35
+ attr_accessor :logger
36
+
37
+ attr_reader :config
38
+ attr_reader :connection
39
+ attr_reader :options
40
+
41
+ def initialize(&block)
42
+ build_configuration(&block)
43
+
44
+ check_logger
45
+ check_rest_client_proxy
46
+
47
+ build_connection
48
+ end
49
+
50
+ def log!(message)
51
+ !config.logger_enabled? || logger.debug(message)
52
+ end
53
+
54
+ private
55
+
56
+ def build_configuration
57
+ @config = Crowdin::Configuration.new
58
+ yield config if block_given?
59
+ end
60
+
61
+ def build_connection
62
+ @connection ||= ::RestClient::Resource.new(config.base_url, build_options)
63
+ end
64
+
65
+ def build_options
66
+ @options ||= config.options.merge(headers: config.headers)
67
+ end
68
+
69
+ def set_default_logger
70
+ require 'logger'
71
+ @logger ||= Logger.new($stderr)
72
+ end
73
+
74
+ def check_rest_client_proxy
75
+ ENV['http_proxy'] ? ::RestClient.proxy = ENV['http_proxy'] : false
76
+ end
77
+
78
+ def check_logger
79
+ config.logger_enabled? ? set_default_logger : config.enable_logger = false
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Crowdin
4
+ class Configuration
5
+ attr_accessor :api_token
6
+ attr_accessor :project_id
7
+ attr_accessor :organization_domain
8
+ attr_accessor :enable_logger
9
+
10
+ attr_reader :target_api_url
11
+
12
+ alias logger_enabled? enable_logger
13
+ alias enterprise_mode? organization_domain
14
+
15
+ def initialize
16
+ @target_api_url = '/api/v2'
17
+ end
18
+
19
+ def options
20
+ {
21
+ headers: {},
22
+ timeout: nil,
23
+ json: true
24
+ }
25
+ end
26
+
27
+ def headers
28
+ {
29
+ 'Accept' => 'application/json',
30
+ 'Authorization' => "Bearer #{api_token}",
31
+ 'Content-Type' => 'application/json',
32
+ 'User-Agent' => "crowdin-rb/#{Crowdin::Client::VERSION}/#{RUBY_VERSION}/#{RUBY_PLATFORM}"
33
+ }
34
+ end
35
+
36
+ def base_url
37
+ if enterprise_mode?
38
+ organization_domain.include?('.com') ? organization_domain : "https://#{organization_domain}.api.crowdin.com"
39
+ else
40
+ 'https://api.crowdin.com'
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Crowdin
4
+ class Client
5
+ VERSION = '1.1.0'
6
+ end
7
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Crowdin
4
+ module Errors
5
+ module ApiErrorsRaiser
6
+ def raise_only_for_enterprise_mode_error
7
+ raise(OnlyForEnterpriseMode, 'This method can be called only for Enterprise mode')
8
+ end
9
+
10
+ def raise_project_id_is_required_error
11
+ raise(ArgumentError, ':project_id is required in parameters or while Client initialization')
12
+ end
13
+
14
+ def raise_parameter_is_required_error(parameter)
15
+ raise(ArgumentError, ":#{parameter} is required")
16
+ end
17
+
18
+ # crowdin-console errors
19
+
20
+ def raise_api_token_is_required_error
21
+ raise(ArgumentError, '--api-token option is required')
22
+ end
23
+
24
+ def raise_organization_domain_is_required_error
25
+ raise(ArgumentError, '--organization-domain option is required for Enterprise mode')
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Crowdin
4
+ module Errors
5
+ class OnlyForEnterpriseMode < StandardError; end
6
+ end
7
+ end
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Crowdin
4
+ module Web
5
+ class Request
6
+ attr_reader :client
7
+
8
+ def initialize(client, method, path, query = {}, headers = {}, destination = nil)
9
+ @client = client
10
+ @method = method
11
+ @full_path = client.config.target_api_url + path
12
+ @payload = perform_payload(query)
13
+ @headers = headers
14
+ @destination = destination
15
+ @errors = []
16
+ end
17
+
18
+ def perform
19
+ process_request!
20
+ process_response!
21
+ end
22
+
23
+ private
24
+
25
+ def process_request!
26
+ return @response = client.connection[@full_path].delete if delete_request?
27
+ return @response = client.connection[@full_path].get(@payload) if get_request?
28
+
29
+ client.connection[@full_path].send(@method, @payload, @headers) { |response, _, _| @response = response }
30
+ rescue StandardError => error
31
+ client.log! error
32
+
33
+ @errors << "Something went wrong while proccessing request. Details - #{error.class}"
34
+ end
35
+
36
+ def process_response!
37
+ return fetch_errors if @errors.any?
38
+
39
+ begin
40
+ if @response
41
+ client.log! "args: #{@response.request.args}"
42
+
43
+ if @response.body.empty?
44
+ @response.code
45
+ else
46
+ doc = JSON.parse(@response.body)
47
+
48
+ client.log! "body: #{doc}"
49
+
50
+ data = fetch_response_data(doc)
51
+
52
+ @errors.any? ? fetch_errors : data
53
+ end
54
+ end
55
+ rescue StandardError => error
56
+ client.log! error
57
+
58
+ @errors << "Something went wrong while proccessing response. Details - #{error.class}"
59
+
60
+ fetch_errors
61
+ end
62
+ end
63
+
64
+ def perform_payload(query)
65
+ return query if query.is_a?(File)
66
+
67
+ get_request? ? { params: fetch_cleared_query(query) } : fetch_cleared_query(query).to_json
68
+ end
69
+
70
+ def download_file(url)
71
+ download = URI.parse(url).open
72
+ IO.copy_stream(download, @destination)
73
+
74
+ @destination
75
+ rescue StandardError => error
76
+ client.log! error
77
+
78
+ @errors << "Something went wrong while downloading file. Details - #{error.class}"
79
+ end
80
+
81
+ def fetch_errors
82
+ @errors.join(';')
83
+ end
84
+
85
+ def fetch_response_data(doc)
86
+ if doc['data'].is_a?(Hash) && doc['data']['url'] && doc['data']['url'].include?('response-content-disposition')
87
+ download_file(doc['data']['url'])
88
+ else
89
+ doc
90
+ end
91
+ end
92
+
93
+ def fetch_cleared_query(query)
94
+ case query
95
+ when Array
96
+ query.map do |el|
97
+ el.reject { |_, value| value.nil? }
98
+ end.reject(&:empty?)
99
+ when Hash
100
+ query.reject { |_, value| value.nil? }
101
+ else
102
+ query
103
+ end
104
+ end
105
+
106
+ def get_request?
107
+ @method.eql?(:get)
108
+ end
109
+
110
+ def delete_request?
111
+ @method.eql?(:delete)
112
+ end
113
+ end
114
+ end
115
+ end
data/lib/crowdin-api.rb CHANGED
@@ -1,130 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Libs
1
4
  require 'json'
5
+ require 'open-uri'
2
6
  require 'rest-client'
3
- # require 'byebug'
4
-
5
- require "crowdin-api/errors"
6
- require "crowdin-api/methods"
7
- require "crowdin-api/version"
8
-
9
-
10
- # The Crowdin::API library is used for interactions with a crowdin.com website.
11
- #
12
- # == Example
13
- #
14
- # require 'crowdin-api'
15
- # require 'logger'
16
- #
17
- # crowdin = Crowdin::API.new(:api_key => API_KEY, :project_id => PROJECT_ID)
18
- # crowdin.log = Logger.new($stderr)
19
- #
20
- module Crowdin
21
- class API
22
-
23
- class << self
24
- # Default logger for all Crowdin::API instances
25
- #
26
- # Crowdin::API.log = Logger.new($stderr)
27
- #
28
- attr_accessor :log
29
- end
30
-
31
- # Create a new API object using the given parameters.
32
- #
33
- # @param [String] api_key the authentication API key can be found on the project settings page
34
- # @param [String] project_id the project identifier.
35
- # @param [String] account_key the account API Key
36
- # @param [String] base_url the url of the Crowdin API
37
- #
38
- def initialize(options = {})
39
- @api_key = options.delete(:api_key)
40
- @project_id = options.delete(:project_id)
41
- @account_key = options.delete(:account_key)
42
- @base_url = options.delete(:base_url) || 'https://api.crowdin.com'
43
-
44
- @log = nil
45
-
46
- options = {
47
- :headers => {},
48
- :params => {},
49
- :timeout => nil,
50
- :open_timeout => nil,
51
- :key => @api_key,
52
- :'account-key' => @account_key,
53
- :json => true
54
- }.merge(options)
55
-
56
- options[:headers] = {
57
- 'Accept' => 'application/json',
58
- 'User-Agent' => "crowdin-rb/#{Crowdin::API::VERSION}",
59
- 'X-Ruby-Version' => RUBY_VERSION,
60
- 'X-Ruby-Platform' => RUBY_PLATFORM
61
- }.merge(options[:headers])
62
-
63
- options[:params] = {
64
- :key => @api_key,
65
- :'account-key' => @account_key,
66
- :json => true
67
- }.merge(options[:params])
68
-
69
- RestClient.proxy = ENV['http_proxy'] if ENV['http_proxy']
70
- @connection = RestClient::Resource.new(@base_url, options)
71
- end
72
-
73
- def request(params, &block)
74
- # Returns a query hash with non nil values.
75
- params[:query].reject! { |_, value| value.nil? } if params[:query]
76
-
77
- case params[:method]
78
- when :post
79
- query = @connection.options.merge(params[:query] || {})
80
- @connection[params[:path]].post(query) { |response, _, _|
81
- @response = response
82
- }
83
- when :get
84
- query = @connection.options[:params].merge(params[:query] || {})
85
- @connection[params[:path]].get(:params => query) { |response, _, _|
86
- @response = response
87
- }
88
- end
89
-
90
- log.debug("args: #{@response.args}") if log
91
-
92
- if @response.headers[:content_disposition]
93
- filename = params[:output] || @response.headers[:content_disposition][/attachment; filename="(.+?)"/, 1]
94
- body = @response.body
95
- file = open(filename, 'wb')
96
- file.write(body)
97
- file.close
98
- return true
99
- else
100
- doc = JSON.load(@response.body)
101
- log.debug("body: #{doc}") if log
102
-
103
- if doc.kind_of?(Hash) && doc['success'] == false
104
- code = doc['error']['code']
105
- message = doc['error']['message']
106
- error = Crowdin::API::Errors::Error.new(code, message)
107
- raise(error)
108
- else
109
- return doc
110
- end
111
- end
112
-
113
- end
114
-
115
- # The current logger. If no logger has been set Crowdin::API.log is used.
116
- #
117
- def log
118
- @log || Crowdin::API.log
119
- end
120
-
121
- # Sets the +logger+ used by this instance of Crowdin::API
122
- #
123
- def log= logger
124
- @log = logger
125
- end
126
-
127
- private
128
7
 
129
- end
130
- end
8
+ # Core modules
9
+ require 'crowdin-api/core/errors'
10
+ require 'crowdin-api/core/api_errors_raiser'
11
+ require 'crowdin-api/core/request'
12
+
13
+ # Api modules
14
+ require 'crowdin-api/api-resources/languages'
15
+ require 'crowdin-api/api-resources/projects'
16
+ require 'crowdin-api/api-resources/source_files'
17
+ require 'crowdin-api/api-resources/storages'
18
+ require 'crowdin-api/api-resources/translation_status'
19
+ require 'crowdin-api/api-resources/translations'
20
+ require 'crowdin-api/api-resources/workflows'
21
+ require 'crowdin-api/api-resources/source_strings'
22
+
23
+ # Client
24
+ require 'crowdin-api/client/version'
25
+ require 'crowdin-api/client/configuration'
26
+ require 'crowdin-api/client/client'