smartring 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +53 -0
- data/bin/bundle +103 -0
- data/bin/byebug +29 -0
- data/bin/coderay +29 -0
- data/bin/dotenv +29 -0
- data/bin/httparty +29 -0
- data/bin/listen +29 -0
- data/bin/nokogiri +29 -0
- data/bin/pry +29 -0
- data/bin/rackup +29 -0
- data/bin/rake +29 -0
- data/bin/rerun +29 -0
- data/bin/rubocop +29 -0
- data/bin/ruby-parse +29 -0
- data/bin/ruby-rewrite +29 -0
- data/bin/safe_yaml +29 -0
- data/bin/smartring +29 -0
- data/exe/smartring +7 -0
- data/lib/smartling/auth.rb +61 -0
- data/lib/smartling/client.rb +36 -0
- data/lib/smartling/contexts.rb +76 -0
- data/lib/smartling/files.rb +50 -0
- data/lib/smartling/strings.rb +31 -0
- data/lib/smartling/verbs.rb +17 -0
- data/lib/smartling.rb +14 -0
- data/lib/smartring.rb +3 -0
- data/test/smartling/auth_test.rb +24 -0
- data/test/smartling/client_test.rb +33 -0
- data/test/smartling/contexts_test.rb +119 -0
- data/test/smartling/files_test.rb +78 -0
- data/test/smartling/strings_test.rb +26 -0
- data/test/smartling_test.rb +16 -0
- data/test/test_helper.rb +26 -0
- metadata +319 -0
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'httmultiparty'
|
4
|
+
require 'hipsterhash'
|
5
|
+
|
6
|
+
module Smartling
|
7
|
+
# Methods for authenticating a Smartling user with the API.
|
8
|
+
module Auth
|
9
|
+
attr_reader :user_id, :user_secret, :access_token, :refresh_token
|
10
|
+
|
11
|
+
def authenticate
|
12
|
+
refresh_token! if access_token&.expired? && refresh_token&.valid?
|
13
|
+
authenticate! unless access_token&.valid?
|
14
|
+
token = access_token.to_s
|
15
|
+
return token unless block_given?
|
16
|
+
yield token
|
17
|
+
end
|
18
|
+
|
19
|
+
public
|
20
|
+
|
21
|
+
def refresh_token!
|
22
|
+
path = '/auth-api/v2/authenticate/refresh'
|
23
|
+
headers = { 'Content-Type' => 'application/json' }
|
24
|
+
payload = { refreshToken: refresh_token.to_s }.to_json
|
25
|
+
resp = self.class.post(path, headers: headers, query: payload)
|
26
|
+
resp = HipsterHash[resp.parsed_response].response
|
27
|
+
raise(Failed, resp) unless resp.code == 'SUCCESS'
|
28
|
+
self.tokens = resp.data
|
29
|
+
true
|
30
|
+
end
|
31
|
+
|
32
|
+
def authenticate!
|
33
|
+
path = '/auth-api/v2/authenticate'
|
34
|
+
payload = { userIdentifier: user_id, userSecret: user_secret }.to_json
|
35
|
+
headers = { 'Content-Type' => 'application/json' }
|
36
|
+
resp = self.class.post(path, headers: headers, body: payload)
|
37
|
+
resp = HipsterHash[resp.parsed_response].response
|
38
|
+
raise(Failed, resp) unless resp.code == 'SUCCESS'
|
39
|
+
self.tokens = resp.data
|
40
|
+
true
|
41
|
+
end
|
42
|
+
|
43
|
+
def tokens=(data)
|
44
|
+
@access_token = Token.new(data.accessToken, data.expiresIn)
|
45
|
+
@refresh_token = Token.new(data.refreshToken, data.refreshExpiresIn)
|
46
|
+
end
|
47
|
+
|
48
|
+
Token = Class.new do
|
49
|
+
def initialize(token, expires_in)
|
50
|
+
@token = token
|
51
|
+
@expires_at = Time.now + expires_in
|
52
|
+
end
|
53
|
+
define_method(:expired?) { @expires_at < Time.now }
|
54
|
+
define_method(:valid?) { !expired? }
|
55
|
+
define_method(:to_s) { @token.to_s }
|
56
|
+
end
|
57
|
+
|
58
|
+
Error = Class.new(Exception)
|
59
|
+
Failed = Class.new(Error)
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'smartling/auth'
|
5
|
+
require 'smartling/files'
|
6
|
+
require 'smartling/contexts'
|
7
|
+
require 'smartling/strings'
|
8
|
+
require 'smartling/verbs'
|
9
|
+
require 'hipsterhash'
|
10
|
+
require 'forwardable'
|
11
|
+
|
12
|
+
module Smartling
|
13
|
+
# A fairly generic Smartling REST API client.
|
14
|
+
class Client
|
15
|
+
include HTTMultiParty
|
16
|
+
include Auth
|
17
|
+
include Files
|
18
|
+
include Strings
|
19
|
+
include Contexts
|
20
|
+
include Verbs
|
21
|
+
|
22
|
+
attr_accessor :project_id
|
23
|
+
|
24
|
+
base_uri 'https://api.smartling.com'
|
25
|
+
headers 'Accept' => 'application/json'
|
26
|
+
raise_on [404, 401, 500]
|
27
|
+
|
28
|
+
def initialize(user_id: ENV.fetch('SMARTLING_USER_ID'),
|
29
|
+
user_secret: ENV.fetch('SMARTLING_USER_SECRET'),
|
30
|
+
project_id: ENV.fetch('SMARTLING_PROJECT_ID', nil))
|
31
|
+
@user_id = user_id
|
32
|
+
@user_secret = user_secret
|
33
|
+
@project_id = project_id
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Smartling
|
4
|
+
# Methods for dealing wth the Smartling Contexts API
|
5
|
+
module Contexts
|
6
|
+
def contexts(project_id: @project_id, name_filter: nil, offset: nil,
|
7
|
+
type: nil)
|
8
|
+
path = "/context-api/v2/projects/#{project_id}/contexts"
|
9
|
+
query = {}
|
10
|
+
query[:nameFilter] = name_filter unless name_filter.nil?
|
11
|
+
query[:offset] = Integer(offset) unless offset.nil?
|
12
|
+
query[:type] = type.to_s.upcase unless type.nil?
|
13
|
+
get(path, query: query)
|
14
|
+
end
|
15
|
+
|
16
|
+
def context(project_id: @project_id, context_uid:)
|
17
|
+
path = "/context-api/v2/projects/#{project_id}/contexts/#{context_uid}"
|
18
|
+
get(path)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Uploads a new context. Content must quack like an UploadIO.
|
22
|
+
def upload_context(project_id: @project_id, content:, name: nil)
|
23
|
+
path = "/context-api/v2/projects/#{project_id}/contexts"
|
24
|
+
raise(InvalidContent, content) unless Contexts.valid_content?(content)
|
25
|
+
body = { content: content }
|
26
|
+
body[:name] = name unless name.nil?
|
27
|
+
post(path, body: body)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Uploads a new context. Content must quack like an UploadIO.
|
31
|
+
def upload_context_and_match(project_id: @project_id, content:,
|
32
|
+
match_params: nil, name: nil)
|
33
|
+
path = "/context-api/v2/projects/#{project_id}/contexts"
|
34
|
+
path += '/upload-and-match-async'
|
35
|
+
raise(InvalidContent, content) unless Contexts.valid_content?(content)
|
36
|
+
body = { content: content }
|
37
|
+
body[:name] = name unless name.nil?
|
38
|
+
body[:matchParams] = match_params unless match_params.nil?
|
39
|
+
post(path, body: body)
|
40
|
+
end
|
41
|
+
|
42
|
+
def download_context(project_id: @project_id, context_uid:)
|
43
|
+
path = "/context-api/v2/projects/#{project_id}/contexts"
|
44
|
+
path += "/#{context_uid}/content"
|
45
|
+
get(path)
|
46
|
+
end
|
47
|
+
|
48
|
+
def delete_context(project_id: @project_id, context_uid:)
|
49
|
+
path = "/context-api/v2/projects/#{project_id}/contexts/#{context_uid}"
|
50
|
+
delete(path)
|
51
|
+
end
|
52
|
+
|
53
|
+
# POSTs to the /match/async endpoint
|
54
|
+
def match_context(project_id: @project_id, context_uid:, hashcodes: [])
|
55
|
+
path = "/context-api/v2/projects/#{project_id}/contexts/#{context_uid}"
|
56
|
+
path += '/match/async'
|
57
|
+
query = { stringHashcodes: hashcodes }.to_json
|
58
|
+
headers = { 'Content-Type' => 'application/json' }
|
59
|
+
post(path, query: query, headers: headers)
|
60
|
+
end
|
61
|
+
|
62
|
+
# GETs the results of an async match
|
63
|
+
def context_matches(project_id: @project_id, match_id:)
|
64
|
+
path = "/context-api/v2/projects/#{project_id}/match/#{match_id}"
|
65
|
+
get(path)
|
66
|
+
end
|
67
|
+
|
68
|
+
InvalidContent = Class.new(ArgumentError)
|
69
|
+
|
70
|
+
def self.valid_content?(content)
|
71
|
+
%i[read content_type original_filename].all? do |required_method|
|
72
|
+
content.respond_to?(required_method)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'time'
|
4
|
+
require 'net/http/post/multipart'
|
5
|
+
|
6
|
+
module Smartling
|
7
|
+
# Methods for using the Smartling files API
|
8
|
+
module Files
|
9
|
+
def files(project_id: @project_id)
|
10
|
+
path = "/files-api/v2/projects/#{project_id}/files/list"
|
11
|
+
get(path)
|
12
|
+
end
|
13
|
+
|
14
|
+
def file(project_id: @project_id, file_uri:, locale: nil)
|
15
|
+
path = ["/files-api/v2/projects/#{project_id}"]
|
16
|
+
path << "locales/#{locale}" unless locale.nil?
|
17
|
+
path << 'file/status'
|
18
|
+
path = path.join('/')
|
19
|
+
get(path, query: { fileUri: file_uri })
|
20
|
+
end
|
21
|
+
|
22
|
+
def delete_file(project_id: @project_id, file_uri:)
|
23
|
+
path = "/files-api/v2/projects/#{project_id}/file/delete"
|
24
|
+
post(path, body: { fileUri: file_uri })
|
25
|
+
end
|
26
|
+
|
27
|
+
def upload_file(project_id: @project_id, file:, file_uri:, file_type:,
|
28
|
+
callback: nil, authorize: nil, locales_to_authorize: nil,
|
29
|
+
smartling: {})
|
30
|
+
raise(InvalidFile, file) unless Files.valid_file?(file)
|
31
|
+
path = "/files-api/v2/projects/#{project_id}/file"
|
32
|
+
body = { file: file, fileUri: file_uri, fileType: file_type }
|
33
|
+
body[:authorize] = authorize unless authorize.nil?
|
34
|
+
body[:callback] = callback unless callback.nil?
|
35
|
+
unless locales_to_authorize.nil?
|
36
|
+
body[:localeIdsToAuthorize] = locales_to_authorize
|
37
|
+
end
|
38
|
+
smartling.each { |k, v| body["smartling.#{k}"] = v }
|
39
|
+
post(path, body: body)
|
40
|
+
end
|
41
|
+
|
42
|
+
InvalidFile = Class.new(ArgumentError)
|
43
|
+
|
44
|
+
def self.valid_file?(content)
|
45
|
+
%i[read content_type original_filename].all? do |required_method|
|
46
|
+
content.respond_to?(required_method)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Smartling
|
4
|
+
# Methods for the Smartling strings API
|
5
|
+
module Strings
|
6
|
+
def source_strings(project_id: @project_id, file_uri: nil, hashcodes: nil,
|
7
|
+
limit: nil, offset: nil)
|
8
|
+
path = "/strings-api/v2/projects/#{project_id}/source-strings"
|
9
|
+
query = {}
|
10
|
+
query[:fileUri] = file_uri unless file_uri.nil?
|
11
|
+
query[:stringHashcodes] = hashcodes unless hashcodes.nil?
|
12
|
+
query[:limit] = Integer(limit) unless limit.nil?
|
13
|
+
query[:offset] = Integer(offset) unless limit.nil?
|
14
|
+
get(path, query: query)
|
15
|
+
end
|
16
|
+
|
17
|
+
def translations(project_id: @project_id, file_uri: nil, hashcodes: nil,
|
18
|
+
limit: nil, offset: nil, retrieval_type: nil,
|
19
|
+
target_locale_id: nil)
|
20
|
+
path = "/strings-api/v2/projects/#{project_id}/translations"
|
21
|
+
query = {}
|
22
|
+
query[:fileUri] = file_uri unless file_uri.nil?
|
23
|
+
query[:hashcodes] = hashcodes unless hashcodes.nil?
|
24
|
+
query[:retrievalType] = retrieval_type unless retrieval_type.nil?
|
25
|
+
query[:limit] = Integer(limit) unless limit.nil?
|
26
|
+
query[:offset] = Integer(offset) unless limit.nil?
|
27
|
+
query[:targetLocaleId] = target_locale_id unless target_locale_id.nil?
|
28
|
+
get(path, query: query)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Smartling
|
5
|
+
module Verbs
|
6
|
+
%i[get put post patch delete].each do |verb|
|
7
|
+
define_method(verb) do |path, options = {}|
|
8
|
+
authenticate do |token|
|
9
|
+
(options[:headers] ||= {})[:Authorization] = "Bearer #{token}"
|
10
|
+
resp = self.class.send(verb, path, options).parsed_response
|
11
|
+
return resp unless resp.is_a?(Hash)
|
12
|
+
HipsterHash[resp].response
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/smartling.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'smartling/client'
|
4
|
+
|
5
|
+
# Smartling contains a client and helpers for working with the Smartling
|
6
|
+
# files, contexts, strings and authentication API
|
7
|
+
module Smartling
|
8
|
+
class << self
|
9
|
+
# Gets a new Client
|
10
|
+
def new(*args)
|
11
|
+
Client.new(*args)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/smartring.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
require 'hipsterhash'
|
5
|
+
require 'httmultiparty'
|
6
|
+
require 'smartling/client'
|
7
|
+
|
8
|
+
describe Smartling::Auth do
|
9
|
+
describe 'authenticate (first time)' do
|
10
|
+
it 'POSTs to the authentication endpoint', :vcr do
|
11
|
+
client = Smartling::Client.new
|
12
|
+
client.authenticate!
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe 'authenticate (twice)' do
|
17
|
+
it 'POSTs to the authentication endpoint', :vcr do
|
18
|
+
client = Smartling::Client.new
|
19
|
+
client.authenticate!
|
20
|
+
client.refresh_token!
|
21
|
+
client.authenticate!
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
require 'smartling/client'
|
5
|
+
|
6
|
+
describe Smartling::Client, :vcr do
|
7
|
+
let(:options) { {} }
|
8
|
+
let(:client) { Smartling::Client.new(options) }
|
9
|
+
|
10
|
+
it('knows context') { assert client.is_a?(Smartling::Contexts) }
|
11
|
+
it('knows about files') { assert client.is_a?(Smartling::Files) }
|
12
|
+
it('knows about strings') { assert client.is_a?(Smartling::Strings) }
|
13
|
+
it('knows how to authenticate') { assert client.is_a?(Smartling::Auth) }
|
14
|
+
|
15
|
+
it 'can be created with options' do
|
16
|
+
c = Smartling::Client.new(project_id: 3)
|
17
|
+
refute_nil c.user_id
|
18
|
+
refute_nil c.user_secret
|
19
|
+
assert_equal 3, c.project_id
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'is cool with bullshit' do
|
23
|
+
path = '/no/such/path'
|
24
|
+
assert_raises { Smartling::Client.new.get(path) }
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'explains when credentials are borked' do
|
28
|
+
path = '/auth-api/v2/authorize'
|
29
|
+
assert_raises do
|
30
|
+
Smartling::Client.new(user_id: 'no', user_secret: 'way').get(path)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
require 'smartling/contexts'
|
5
|
+
|
6
|
+
describe Smartling::Contexts do
|
7
|
+
let(:smartling) do
|
8
|
+
Class.new(Minitest::Mock) do
|
9
|
+
include Smartling::Contexts
|
10
|
+
end.new
|
11
|
+
end
|
12
|
+
|
13
|
+
after { smartling.verify }
|
14
|
+
|
15
|
+
describe 'contexts' do
|
16
|
+
it 'lists contexts the items of the smartling contexts result' do
|
17
|
+
smartling.expect(:get, nil) do |path, query:|
|
18
|
+
assert_equal path, '/context-api/v2/projects/1/contexts'
|
19
|
+
assert_equal 'n', query.fetch(:nameFilter)
|
20
|
+
assert_equal 1, query.fetch(:offset)
|
21
|
+
assert_equal 'HTML', query.fetch(:type)
|
22
|
+
end
|
23
|
+
smartling.contexts(project_id: 1, name_filter: 'n',
|
24
|
+
offset: 1, type: 'HTML')
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe 'context' do
|
29
|
+
it 'gets the /context with the right param' do
|
30
|
+
smartling.expect(:get, nil) do |path|
|
31
|
+
assert_equal '/context-api/v2/projects/1/contexts/x', path
|
32
|
+
end
|
33
|
+
smartling.context(project_id: 1, context_uid: 'x')
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe 'download_context' do
|
38
|
+
it 'gets the /context with the right param' do
|
39
|
+
smartling.expect(:get, nil) do |path|
|
40
|
+
assert_equal '/context-api/v2/projects/1/contexts/x/content', path
|
41
|
+
end
|
42
|
+
smartling.download_context(project_id: 1, context_uid: 'x')
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe 'delete_context' do
|
47
|
+
it 'deletes the context at the right endpoing' do
|
48
|
+
smartling.expect(:delete, nil) do |path|
|
49
|
+
assert_equal '/context-api/v2/projects/1/contexts/x', path
|
50
|
+
end
|
51
|
+
smartling.delete_context(project_id: 1, context_uid: 'x')
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe 'upload_context' do
|
56
|
+
it 'posts the right body to the right endpoint' do
|
57
|
+
smartling.expect(:post, nil) do |path, body:|
|
58
|
+
assert_equal '/context-api/v2/projects/1/contexts', path
|
59
|
+
assert_equal 'n', body.fetch(:name)
|
60
|
+
assert_equal '<i>Hi</i>', body.fetch(:content).read
|
61
|
+
assert_equal 'text/html', body.fetch(:content).content_type
|
62
|
+
assert_equal 'n', body.fetch(:name)
|
63
|
+
end
|
64
|
+
content = UploadIO.new(StringIO.new('<i>Hi</i>'), 'text/html')
|
65
|
+
smartling.upload_context(project_id: 1, content: content, name: 'n')
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'prints a helpful message if the contextType is unsupported' do
|
69
|
+
assert_raises Smartling::Contexts::InvalidContent do
|
70
|
+
smartling.upload_context(project_id: 1, content: 'not a file')
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe 'upload_context_and_match' do
|
76
|
+
it 'posts the right body to the right endpoint' do
|
77
|
+
smartling.expect(:post, nil) do |path, body:|
|
78
|
+
expected = '/context-api/v2/projects/1/contexts/'
|
79
|
+
expected += 'upload-and-match-async'
|
80
|
+
assert_equal expected, path
|
81
|
+
assert_equal 'n', body.fetch(:name)
|
82
|
+
assert_equal '<i>Hi</i>', body.fetch(:content).read
|
83
|
+
assert_equal 'text/html', body.fetch(:content).content_type
|
84
|
+
assert_equal %w[1 2 3], body.fetch(:matchParams)
|
85
|
+
assert_equal 'n', body.fetch(:name)
|
86
|
+
end
|
87
|
+
content = UploadIO.new(StringIO.new('<i>Hi</i>'), 'text/html')
|
88
|
+
smartling.upload_context_and_match(project_id: 1, content: content,
|
89
|
+
match_params: %w[1 2 3], name: 'n')
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'prints a helpful message if the contextType is unsupported' do
|
93
|
+
assert_raises Smartling::Contexts::InvalidContent do
|
94
|
+
smartling.upload_context(project_id: 1, content: 'not a file')
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe 'async context matching' do
|
100
|
+
it 'posts to the right endpoint with the right params' do
|
101
|
+
smartling.expect(:post, nil) do |path, query:, headers:|
|
102
|
+
assert_equal '/context-api/v2/projects/1/contexts/x/match/async', path
|
103
|
+
assert_equal %w[x], JSON.parse(query)['stringHashcodes']
|
104
|
+
assert_equal 'application/json', headers['Content-Type']
|
105
|
+
end
|
106
|
+
smartling.match_context(project_id: 1, context_uid: 'x',
|
107
|
+
hashcodes: %w[x])
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe 'retrieving match results' do
|
112
|
+
it 'get the right endpoint' do
|
113
|
+
smartling.expect(:get, nil) do |p|
|
114
|
+
assert_equal '/context-api/v2/projects/1/match/8', p
|
115
|
+
end
|
116
|
+
smartling.context_matches(project_id: 1, match_id: 8)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
require 'smartling/files'
|
5
|
+
|
6
|
+
describe Smartling::Files do
|
7
|
+
let(:smartling) do
|
8
|
+
Class.new(Minitest::Mock) do
|
9
|
+
include Smartling::Files
|
10
|
+
end.new
|
11
|
+
end
|
12
|
+
|
13
|
+
after { smartling.verify }
|
14
|
+
|
15
|
+
describe 'files' do
|
16
|
+
it 'lists files the items of the smartling files result' do
|
17
|
+
smartling.expect(:get, nil, ['/files-api/v2/projects/1/files/list'])
|
18
|
+
smartling.files(project_id: 1)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe 'file' do
|
23
|
+
it 'gets the /file/status endpoint with the right param' do
|
24
|
+
smartling.expect(:get, nil) do |path, query:|
|
25
|
+
assert_match %r{files-api/v2/projects/1/file/status$}, path
|
26
|
+
assert_equal({ fileUri: 'x' }, query)
|
27
|
+
end
|
28
|
+
smartling.file(project_id: 1, file_uri: 'x')
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe 'file with locale' do
|
33
|
+
it 'gets the /locale/x/file/status endpoint with the right params' do
|
34
|
+
smartling.expect(:get, nil) do |path, query:|
|
35
|
+
assert_match %r{files-api/v2/projects/1/locales/es/file/status$}, path
|
36
|
+
assert_equal({ fileUri: 'x' }, query)
|
37
|
+
end
|
38
|
+
smartling.file(project_id: 1, file_uri: 'x', locale: 'es')
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe 'delete_file' do
|
43
|
+
it 'posts the right body to the right endpoint' do
|
44
|
+
smartling.expect(:post, nil) do |path, body:|
|
45
|
+
assert_match %r{files-api/v2/projects/1/file/delete$}, path,
|
46
|
+
assert_equal({ fileUri: 'x' }, body)
|
47
|
+
end
|
48
|
+
smartling.delete_file(project_id: 1, file_uri: 'x')
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe 'upload_file' do
|
53
|
+
it 'posts the right body to the right endpoint' do
|
54
|
+
smartling.expect(:post, nil) do |path, body:|
|
55
|
+
assert_equal '/files-api/v2/projects/1/file', path
|
56
|
+
assert_equal 'x', body.fetch(:fileUri)
|
57
|
+
assert_equal 'json', body.fetch(:fileType)
|
58
|
+
assert_equal '"hello"', body.fetch(:file).read
|
59
|
+
assert_equal 'application/json', body.fetch(:file).content_type
|
60
|
+
assert_equal 'cb', body.fetch(:callback)
|
61
|
+
assert_equal %w[a b], body.fetch(:localeIdsToAuthorize)
|
62
|
+
assert_equal 'bar', body.fetch('smartling.foo')
|
63
|
+
assert_equal false, body.fetch(:authorize)
|
64
|
+
end
|
65
|
+
file = UploadIO.new(StringIO.new('"hello"'), 'application/json')
|
66
|
+
smartling.upload_file(project_id: 1, file: file, file_uri: 'x',
|
67
|
+
file_type: 'json', callback: 'cb',
|
68
|
+
locales_to_authorize: %w[a b],
|
69
|
+
smartling: { foo: 'bar' }, authorize: false)
|
70
|
+
end
|
71
|
+
it 'throws an exception if the file is not a file' do
|
72
|
+
assert_raises Smartling::Files::InvalidFile do
|
73
|
+
smartling.upload_file(project_id: 1, file: 'hi', file_uri: 'x',
|
74
|
+
file_type: 'bullshit')
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
require 'smartling/strings'
|
5
|
+
|
6
|
+
describe Smartling::Strings do
|
7
|
+
let(:smartling) do
|
8
|
+
Class.new(Minitest::Mock) do
|
9
|
+
include Smartling::Strings
|
10
|
+
end.new
|
11
|
+
end
|
12
|
+
|
13
|
+
describe 'source_strings' do
|
14
|
+
it 'lists files the items of the smartling files result' do
|
15
|
+
smartling.expect(:get, nil) do |path, query:|
|
16
|
+
assert_equal path, '/strings-api/v2/projects/1/source-strings'
|
17
|
+
assert_equal 'x', query.fetch(:fileUri)
|
18
|
+
assert_equal ['a'], query.fetch(:stringHashcodes)
|
19
|
+
assert_equal 99, query.fetch(:limit)
|
20
|
+
assert_equal 2, query.fetch(:offset)
|
21
|
+
end
|
22
|
+
smartling.source_strings(project_id: 1, file_uri: 'x', hashcodes: ['a'],
|
23
|
+
limit: 99, offset: 2)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
require 'smartling'
|
5
|
+
|
6
|
+
describe Smartling do
|
7
|
+
it 'can be "instantiated"' do
|
8
|
+
assert_instance_of Smartling::Client, Smartling.new
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'handles things gracefully', :vcr do
|
12
|
+
client = Smartling::Client.new
|
13
|
+
assert_instance_of HipsterHash, client.files
|
14
|
+
assert_instance_of HipsterHash, client.translations(file_uri: 'boo')
|
15
|
+
end
|
16
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift File.expand_path('../lib', __dir__)
|
4
|
+
|
5
|
+
require 'simplecov'
|
6
|
+
SimpleCov.start
|
7
|
+
|
8
|
+
require 'minitest/autorun'
|
9
|
+
|
10
|
+
require 'dotenv'
|
11
|
+
Dotenv.load '.env.local', '.env.test'
|
12
|
+
|
13
|
+
require 'vcr'
|
14
|
+
require 'cgi'
|
15
|
+
VCR.configure do |vcr|
|
16
|
+
vcr.hook_into :webmock
|
17
|
+
vcr.cassette_library_dir = 'test/cassettes'
|
18
|
+
vcr.filter_sensitive_data('SMARTLING_PROJECT_ID') { ENV.fetch('SMARTLING_PROJECT_ID') }
|
19
|
+
vcr.filter_sensitive_data('SMARTLING_USER_ID') { ENV.fetch('SMARTLING_USER_ID') }
|
20
|
+
vcr.filter_sensitive_data('SMARTLING_USER_SECRET') { ENV.fetch('SMARTLING_USER_SECRET') }
|
21
|
+
end
|
22
|
+
|
23
|
+
require 'minitest-vcr'
|
24
|
+
MinitestVcr::Spec.configure!
|
25
|
+
|
26
|
+
require 'pry'
|