crowdin-api 0.5.0 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
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 +162 -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 +118 -0
  31. data/lib/crowdin-api.rb +23 -126
  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 +112 -40
  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,162 @@
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(build_id = nil, destination = 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 = {}, destination = nil, 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
+ destination
156
+ )
157
+
158
+ request.perform
159
+ end
160
+ end
161
+ end
162
+ 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.1'
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,118 @@
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
+ destination = @destination || download.meta['content-disposition']
73
+ .match(/filename=("?)(.+)\1/)[2]
74
+
75
+ IO.copy_stream(download, destination)
76
+
77
+ destination
78
+ rescue StandardError => error
79
+ client.log! error
80
+
81
+ @errors << "Something went wrong while downloading file. Details - #{error.class}"
82
+ end
83
+
84
+ def fetch_errors
85
+ @errors.join(';')
86
+ end
87
+
88
+ def fetch_response_data(doc)
89
+ if doc['data'].is_a?(Hash) && doc['data']['url'] && doc['data']['url'].include?('response-content-disposition')
90
+ download_file(doc['data']['url'])
91
+ else
92
+ doc
93
+ end
94
+ end
95
+
96
+ def fetch_cleared_query(query)
97
+ case query
98
+ when Array
99
+ query.map do |el|
100
+ el.reject { |_, value| value.nil? }
101
+ end.reject(&:empty?)
102
+ when Hash
103
+ query.reject { |_, value| value.nil? }
104
+ else
105
+ query
106
+ end
107
+ end
108
+
109
+ def get_request?
110
+ @method.eql?(:get)
111
+ end
112
+
113
+ def delete_request?
114
+ @method.eql?(:delete)
115
+ end
116
+ end
117
+ end
118
+ end
data/lib/crowdin-api.rb CHANGED
@@ -1,129 +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
- :key => @api_key,
51
- :'account-key' => @account_key,
52
- :json => true
53
- }.merge(options)
54
-
55
- options[:headers] = {
56
- 'Accept' => 'application/json',
57
- 'User-Agent' => "crowdin-rb/#{Crowdin::API::VERSION}",
58
- 'X-Ruby-Version' => RUBY_VERSION,
59
- 'X-Ruby-Platform' => RUBY_PLATFORM
60
- }.merge(options[:headers])
61
-
62
- options[:params] = {
63
- :key => @api_key,
64
- :'account-key' => @account_key,
65
- :json => true
66
- }.merge(options[:params])
67
-
68
- RestClient.proxy = ENV['http_proxy'] if ENV['http_proxy']
69
- @connection = RestClient::Resource.new(@base_url, options)
70
- end
71
-
72
- def request(params, &block)
73
- # Returns a query hash with non nil values.
74
- params[:query].reject! { |_, value| value.nil? } if params[:query]
75
-
76
- case params[:method]
77
- when :post
78
- query = @connection.options.merge(params[:query] || {})
79
- @connection[params[:path]].post(query) { |response, _, _|
80
- @response = response
81
- }
82
- when :get
83
- query = @connection.options[:params].merge(params[:query] || {})
84
- @connection[params[:path]].get(:params => query) { |response, _, _|
85
- @response = response
86
- }
87
- end
88
-
89
- log.debug("args: #{@response.request.args}") if log
90
-
91
- if @response.headers[:content_disposition]
92
- filename = params[:output] || @response.headers[:content_disposition][/attachment; filename="(.+?)"/, 1]
93
- body = @response.body
94
- file = open(filename, 'wb')
95
- file.write(body)
96
- file.close
97
- return true
98
- else
99
- doc = JSON.load(@response.body)
100
- log.debug("body: #{doc}") if log
101
-
102
- if doc.kind_of?(Hash) && doc['success'] == false
103
- code = doc['error']['code']
104
- message = doc['error']['message']
105
- error = Crowdin::API::Errors::Error.new(code, message)
106
- raise(error)
107
- else
108
- return doc
109
- end
110
- end
111
-
112
- end
113
-
114
- # The current logger. If no logger has been set Crowdin::API.log is used.
115
- #
116
- def log
117
- @log || Crowdin::API.log
118
- end
119
-
120
- # Sets the +logger+ used by this instance of Crowdin::API
121
- #
122
- def log= logger
123
- @log = logger
124
- end
125
-
126
- private
127
7
 
128
- end
129
- 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'