shotgun_api_ruby 0.0.8.4 → 0.1.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/bin/prettirun ADDED
@@ -0,0 +1 @@
1
+ yarn prettier -c './**/*.rb'
data/bin/ruborun ADDED
@@ -0,0 +1 @@
1
+ bundle exec rubocop -P
@@ -1,20 +1,33 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ShotgunApiRuby
4
+ # Faraday middleware responsible for authentication with
5
+ # the shotgun site
4
6
  class Auth < Faraday::Middleware
7
+ # Validate auth parameters format
5
8
  module Validator
6
- def self.valid?(auth)
7
- (auth[:client_id] && auth[:client_secret]) ||
8
- (auth[:password] && auth[:username]) ||
9
- auth[:session_token] ||
10
- auth[:refresh_token]
9
+ # Validate auth parameters format
10
+ #
11
+ # @param []
12
+ def self.valid?(
13
+ client_id: nil,
14
+ client_secret: nil,
15
+ username: nil,
16
+ password: nil,
17
+ session_token: nil,
18
+ refresh_token: nil
19
+ )
20
+ (client_id && client_secret) || (password && username) ||
21
+ session_token || refresh_token
11
22
  end
12
23
  end
13
24
 
14
25
  def initialize(app = nil, options = {})
15
- raise "missing auth" unless options[:auth]
16
- raise "missing site_url" unless options[:site_url]
17
- raise "Auth not valid" unless Validator.valid?(options[:auth])
26
+ raise 'missing auth' unless options[:auth]
27
+ raise 'missing site_url' unless options[:site_url]
28
+ unless Validator.valid?(**options[:auth]&.transform_keys(&:to_sym))
29
+ raise 'Auth not valid'
30
+ end
18
31
 
19
32
  super(app)
20
33
 
@@ -27,19 +40,25 @@ module ShotgunApiRuby
27
40
  @refresh_token = options[:auth][:refresh_token]
28
41
  end
29
42
 
30
- attr_reader :client_id, :client_secret, :site_url, :username, :password, :session_token, :refresh_token
43
+ attr_reader :client_id,
44
+ :client_secret,
45
+ :site_url,
46
+ :username,
47
+ :password,
48
+ :session_token,
49
+ :refresh_token
31
50
 
32
51
  def auth_type
33
52
  @auth_type ||=
34
53
  begin
35
- if client_id
54
+ if refresh_token
55
+ 'refresh_token'
56
+ elsif client_id
36
57
  'client_credentials'
37
58
  elsif username
38
59
  'password'
39
60
  elsif session_token
40
61
  'session_token'
41
- elsif refresh_token
42
- 'refresh_token'
43
62
  end
44
63
  end
45
64
  end
@@ -56,16 +75,18 @@ module ShotgunApiRuby
56
75
  @auth_params ||=
57
76
  begin
58
77
  case auth_type
78
+ when 'refresh_token'
79
+ "refresh_token=#{refresh_token}&grant_type=refresh_token"
59
80
  when 'client_credentials'
60
- "client_id=#{client_id}&client_secret=#{client_secret}&grant_type=client_credentials"
81
+ "client_id=#{client_id}&client_secret=#{
82
+ client_secret
83
+ }&grant_type=client_credentials"
61
84
  when 'password'
62
85
  "username=#{username}&password=#{password}&grant_type=password"
63
86
  when 'session_token'
64
87
  "session_token=#{session_token}&grant_type=session_token"
65
- when 'refresh_token'
66
- "refresh_token=#{refresh_token}&grant_type=refresh_token"
67
88
  else
68
- raise "Not a valid/implemented auth type"
89
+ raise 'Not a valid/implemented auth type'
69
90
  end
70
91
  end
71
92
  end
@@ -81,8 +102,8 @@ module ShotgunApiRuby
81
102
  def sign_in
82
103
  resp =
83
104
  Faraday.post(auth_url) do |req|
84
- req.headers["Content-Type"] = 'application/x-www-form-urlencoded'
85
- req.headers["Accept"] = "application/json"
105
+ req.headers['Content-Type'] = 'application/x-www-form-urlencoded'
106
+ req.headers['Accept'] = 'application/json'
86
107
  end
87
108
  resp_body = JSON.parse(resp.body)
88
109
 
@@ -95,8 +116,8 @@ module ShotgunApiRuby
95
116
 
96
117
  def std_headers
97
118
  {
98
- "Accept" => "application/json",
99
- "Authorization" => "Bearer #{access_token}",
119
+ 'Accept' => 'application/json',
120
+ 'Authorization' => "Bearer #{access_token}",
100
121
  }
101
122
  end
102
123
  end
@@ -1,12 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ShotgunApiRuby
4
+ # Main class for connection.
5
+ #
6
+ # This should be only instanciated once to re-use tokens
4
7
  class Client
8
+ # Faraday connection
5
9
  attr_reader :connection
6
10
 
7
11
  def initialize(auth:, site_url: nil, shotgun_site: nil)
8
- raise "No site given" unless site_url || shotgun_site
9
- raise "auth param not valid" unless auth && Auth::Validator.valid?(auth)
12
+ raise 'No site given' unless site_url || shotgun_site
13
+ raise 'auth param not valid' unless auth && Auth::Validator.valid?(**auth)
10
14
 
11
15
  site_url ||= "https://#{shotgun_site}.shotgunstudio.com/api/v1"
12
16
  @connection =
@@ -16,14 +20,17 @@ module ShotgunApiRuby
16
20
  end
17
21
  end
18
22
 
23
+ # Access preferences APIs
19
24
  def preferences
20
25
  @preferences = Preferences.new(connection)
21
26
  end
22
27
 
28
+ # Access server_info APIs
23
29
  def server_info
24
30
  @server_info || ServerInfo.new(connection)
25
31
  end
26
32
 
33
+ # Access entities related APIs
27
34
  def entities(type)
28
35
  public_send(type)
29
36
  end
@@ -34,16 +41,19 @@ module ShotgunApiRuby
34
41
 
35
42
  def method_missing(name, *args, &block)
36
43
  if args.empty?
37
- name = formated_name(name)
38
- define_singleton_method(name) do
39
- if entities_client = instance_variable_get("@#{name}")
40
- entities_client
41
- else
42
- entities_client = entities_aux(name)
43
- instance_variable_set("@#{name}", entities_client)
44
+ fname = formated_name(name)
45
+ self
46
+ .class
47
+ .define_method(fname) do
48
+ if entities_client = instance_variable_get("@#{fname}")
49
+ entities_client
50
+ else
51
+ entities_client = entities_aux(fname)
52
+ instance_variable_set("@#{fname}", entities_client)
53
+ end
44
54
  end
45
- end
46
- send(name)
55
+ self.class.instance_eval { alias_method name, fname }
56
+ send(fname)
47
57
  else
48
58
  super
49
59
  end
@@ -26,7 +26,7 @@ module ShotgunApiRuby
26
26
  retired: retired,
27
27
  logical_operator: logical_operator,
28
28
  include_archived_projects: include_archived_projects,
29
- page_size: 1
29
+ page_size: 1,
30
30
  ).first
31
31
  end
32
32
 
@@ -43,13 +43,13 @@ module ShotgunApiRuby
43
43
  raise "Error while getting #{type}: #{resp_body['errors']}"
44
44
  end
45
45
 
46
- entity = resp_body["data"]
46
+ entity = resp_body['data']
47
47
  Entity.new(
48
48
  entity['type'],
49
49
  OpenStruct.new(entity['attributes']),
50
50
  entity['relationships'],
51
51
  entity['id'],
52
- entity['links']
52
+ entity['links'],
53
53
  )
54
54
  end
55
55
 
@@ -62,16 +62,18 @@ module ShotgunApiRuby
62
62
  resp_body = JSON.parse(resp.body)
63
63
 
64
64
  if resp.status >= 300
65
- raise "Error while creating #{type}# with #{attributes}: #{resp_body['errors']}"
65
+ raise "Error while creating #{type} with #{attributes}: #{
66
+ resp_body['errors']
67
+ }"
66
68
  end
67
69
 
68
- entity = resp_body["data"]
70
+ entity = resp_body['data']
69
71
  Entity.new(
70
72
  entity['type'],
71
73
  OpenStruct.new(entity['attributes']),
72
74
  entity['relationships'],
73
75
  entity['id'],
74
- entity['links']
76
+ entity['links'],
75
77
  )
76
78
  end
77
79
 
@@ -86,16 +88,18 @@ module ShotgunApiRuby
86
88
  resp_body = JSON.parse(resp.body)
87
89
 
88
90
  if resp.status >= 300
89
- raise "Error while updating #{type}##{id} with #{changes}: #{resp_body['errors']}"
91
+ raise "Error while updating #{type}##{id} with #{changes}: #{
92
+ resp_body['errors']
93
+ }"
90
94
  end
91
95
 
92
- entity = resp_body["data"]
96
+ entity = resp_body['data']
93
97
  Entity.new(
94
98
  entity['type'],
95
99
  OpenStruct.new(entity['attributes']),
96
100
  entity['relationships'],
97
101
  entity['id'],
98
- entity['links']
102
+ entity['links'],
99
103
  )
100
104
  end
101
105
 
@@ -114,8 +118,7 @@ module ShotgunApiRuby
114
118
  end
115
119
 
116
120
  def revive(id)
117
- resp =
118
- @connection.post("#{id}?revive=true")
121
+ resp = @connection.post("#{id}?revive=true")
119
122
 
120
123
  if resp.status >= 300
121
124
  resp_body = JSON.parse(resp.body)
@@ -135,16 +138,18 @@ module ShotgunApiRuby
135
138
  retired: nil,
136
139
  include_archived_projects: nil
137
140
  )
138
- if filter && !filters_are_simple?(filter)
139
- return search(
140
- fields: fields,
141
- logical_operator: logical_operator,
142
- sort: sort,
143
- filter: filter,
144
- page: page,
145
- page_size: page_size,
146
- retired: retired,
147
- include_archived_projects: include_archived_projects
141
+ if filter && !Params.filters_are_simple?(filter)
142
+ return(
143
+ search(
144
+ fields: fields,
145
+ logical_operator: logical_operator,
146
+ sort: sort,
147
+ filter: filter,
148
+ page: page,
149
+ page_size: page_size,
150
+ retired: retired,
151
+ include_archived_projects: include_archived_projects,
152
+ )
148
153
  )
149
154
  end
150
155
 
@@ -163,13 +168,13 @@ module ShotgunApiRuby
163
168
  raise "Error while getting #{type}: #{resp_body['errors']}"
164
169
  end
165
170
 
166
- resp_body["data"].map do |entity|
171
+ resp_body['data'].map do |entity|
167
172
  Entity.new(
168
173
  entity['type'],
169
174
  OpenStruct.new(entity['attributes']),
170
175
  entity['relationships'],
171
176
  entity['id'],
172
- entity['links']
177
+ entity['links'],
173
178
  )
174
179
  end
175
180
  end
@@ -184,16 +189,18 @@ module ShotgunApiRuby
184
189
  retired: nil,
185
190
  include_archived_projects: nil
186
191
  )
187
- if filter.nil? || filters_are_simple?(filter)
188
- return all(
189
- fields: fields,
190
- logical_operator: logical_operator,
191
- sort: sort,
192
- filter: filter,
193
- page: page,
194
- page_size: page_size,
195
- retired: retired,
196
- include_archived_projects: include_archived_projects
192
+ if filter.nil? || Params.filters_are_simple?(filter)
193
+ return(
194
+ all(
195
+ fields: fields,
196
+ logical_operator: logical_operator,
197
+ sort: sort,
198
+ filter: filter,
199
+ page: page,
200
+ page_size: page_size,
201
+ retired: retired,
202
+ include_archived_projects: include_archived_projects,
203
+ )
197
204
  )
198
205
  end
199
206
  params = Params.new
@@ -202,25 +209,21 @@ module ShotgunApiRuby
202
209
  params.add_sort(sort)
203
210
  params.add_page(page, page_size)
204
211
  params.add_options(retired, include_archived_projects)
205
- new_filter = {}
206
- if filter.is_a?(Hash)
207
- new_filter[:conditions] =
208
- (filter[:conditions] || translate_complex_to_sg_filters(filter))
209
- new_filter[:logical_operator] = filter[:logical_operator] || filter['logical_operator'] || logical_operator
210
- else
211
- new_filter[:conditions] = filter
212
- new_filter[:logical_operator] = logical_operator
213
- end
214
- filter = new_filter
212
+ params.add_filter(filter, logical_operator)
213
+
214
+ # In search: The name is filters and not filter
215
+ params[:filters] = params[:filter]
216
+ params.delete(:filter)
215
217
 
216
218
  resp =
217
219
  @connection.post('_search', params) do |req|
218
- if filter.is_a? Array
219
- req.headers["Content-Type"] = 'application/vnd+shotgun.api3_array+json'
220
- else
221
- req.headers['Content-Type'] = 'application/vnd+shotgun.api3_hash+json'
222
- end
223
- req.body = params.to_h.merge(filters: filter).to_json
220
+ req.headers['Content-Type'] =
221
+ if params[:filters].is_a? Array
222
+ 'application/vnd+shotgun.api3_array+json'
223
+ else
224
+ 'application/vnd+shotgun.api3_hash+json'
225
+ end
226
+ req.body = params.to_h.to_json
224
227
  end
225
228
  resp_body = JSON.parse(resp.body)
226
229
 
@@ -228,13 +231,13 @@ module ShotgunApiRuby
228
231
  raise "Error while getting #{type}: #{resp_body['errors']}"
229
232
  end
230
233
 
231
- resp_body["data"].map do |entity|
234
+ resp_body['data'].map do |entity|
232
235
  Entity.new(
233
236
  entity['type'],
234
237
  OpenStruct.new(entity['attributes']),
235
238
  entity['relationships'],
236
239
  entity['id'],
237
- entity['links']
240
+ entity['links'],
238
241
  )
239
242
  end
240
243
  end
@@ -250,47 +253,5 @@ module ShotgunApiRuby
250
253
  def fields
251
254
  schema_client.fields
252
255
  end
253
-
254
- private
255
-
256
- def filters_are_simple?(filters)
257
- return false if filters.is_a? Array
258
-
259
- filters.values.all? do |filter_val|
260
- (filter_val.is_a?(Integer) || filter_val.is_a?(String) || filter_val.is_a?(Symbol)) ||
261
- (filter_val.is_a?(Array) && filter_val.all?{ |val|
262
- val.is_a?(String) || val.is_a?(Symbol) || val.is_a?(Integer)
263
- } )
264
- end
265
- end
266
-
267
- def translate_complex_to_sg_filters(filters)
268
- # We don't know how to translate anything but hashes
269
- return filters if !filters.is_a?(Hash)
270
-
271
- filters.each.with_object([]) do |item, result|
272
- field, value = item
273
- case value
274
- when String, Symbol, Integer, Float
275
- result << [field, "is", value]
276
- when Hash
277
- value.each do |subfield, subvalue|
278
- sanitized_subfield = "#{field.capitalize}.#{subfield}" unless subfield.to_s.include?(".")
279
- case subvalue
280
- when String, Symbol, Integer, Float
281
- result << ["#{field}.#{sanitized_subfield}", "is", subvalue]
282
- when Array
283
- result << ["#{field}.#{sanitized_subfield}", "in", subvalue]
284
- else
285
- raise "This case is too complex to auto-translate. Please use shotgun query syntax."
286
- end
287
- end
288
- when Array
289
- result << [field, "in", value]
290
- else
291
- raise "This case is too complex to auto-translate. Please use shotgun query syntax."
292
- end
293
- end
294
- end
295
256
  end
296
257
  end
@@ -3,12 +3,17 @@
3
3
  module ShotgunApiRuby
4
4
  class Entities
5
5
  class Params < Hash
6
+ class TooComplexFiltersError < StandardError
7
+ end
8
+
6
9
  def add_sort(sort)
7
10
  return unless sort
8
11
 
9
12
  self[:sort] =
10
13
  if sort.is_a?(Hash)
11
- sort.map{ |field, direction| "#{direction.to_s.start_with?('desc') ? '-' : ''}#{field}" }.join(',')
14
+ sort.map do |field, direction|
15
+ "#{direction.to_s.start_with?('desc') ? '-' : ''}#{field}"
16
+ end.join(',')
12
17
  else
13
18
  [sort].flatten.join(',')
14
19
  end
@@ -26,11 +31,7 @@ module ShotgunApiRuby
26
31
 
27
32
  def add_fields(fields)
28
33
  self[:fields] =
29
- if fields
30
- [fields].flatten.join(',')
31
- else
32
- "*"
33
- end
34
+ fields && !fields.empty? ? [fields].flatten.join(',') : '*'
34
35
  end
35
36
 
36
37
  def add_options(return_only, include_archived_projects)
@@ -42,16 +43,50 @@ module ShotgunApiRuby
42
43
  }
43
44
  end
44
45
 
45
- def add_filter(filters)
46
+ def add_filter(filters, logical_operator = 'and')
46
47
  return unless filters
47
48
 
48
- # filter
49
- self['filter'] = translate_simple_filter_to_sg(filters)
49
+ self[:filter] =
50
+ if (self.class.filters_are_simple?(filters))
51
+ translate_simple_filters_to_sg(filters)
52
+ elsif filters.is_a? Hash
53
+ {
54
+ conditions:
55
+ filters[:conditions] || filters['conditions'] ||
56
+ translate_complex_filters_to_sg(filters),
57
+ logical_operator:
58
+ filters[:logical_operator] || filters['logical_operator'] ||
59
+ logical_operator,
60
+ }
61
+ else
62
+ { conditions: filters, logical_operator: logical_operator }
63
+ end
64
+ end
65
+
66
+ def self.filters_are_simple?(filters)
67
+ return false if filters.is_a? Array
68
+
69
+ if filters.is_a?(Hash) &&
70
+ (filters[:conditions] || filters['conditions'])
71
+ return false
72
+ end
73
+
74
+ filters.values.all? do |filter_val|
75
+ (
76
+ filter_val.is_a?(Integer) || filter_val.is_a?(String) ||
77
+ filter_val.is_a?(Symbol)
78
+ ) ||
79
+ (
80
+ filter_val.is_a?(Array) && filter_val.all? do |val|
81
+ val.is_a?(String) || val.is_a?(Symbol) || val.is_a?(Integer)
82
+ end
83
+ )
84
+ end
50
85
  end
51
86
 
52
87
  private
53
88
 
54
- def translate_simple_filter_to_sg(filters)
89
+ def translate_simple_filters_to_sg(filters)
55
90
  filters.map do |field, value|
56
91
  [
57
92
  field.to_s,
@@ -59,6 +94,44 @@ module ShotgunApiRuby
59
94
  ]
60
95
  end.to_h
61
96
  end
97
+
98
+ def translate_complex_filters_to_sg(filters)
99
+ # We don't know how to translate anything but hashes
100
+ return filters if !filters.is_a?(Hash)
101
+
102
+ filters
103
+ .each
104
+ .with_object([]) do |item, result|
105
+ field, value = item
106
+ case value
107
+ when String, Symbol, Integer, Float
108
+ result << [field.to_s, 'is', value]
109
+ when Hash
110
+ value.each do |subfield, subvalue|
111
+ sanitized_subfield =
112
+ if !subfield.to_s.include?('.')
113
+ "#{field.capitalize}.#{subfield}"
114
+ else
115
+ subfield
116
+ end
117
+ case subvalue
118
+ when String, Symbol, Integer, Float
119
+ result << ["#{field}.#{sanitized_subfield}", 'is', subvalue]
120
+ when Array
121
+ result << ["#{field}.#{sanitized_subfield}", 'in', subvalue]
122
+ else
123
+ raise TooComplexFiltersError,
124
+ 'This case is too complex to auto-translate. Please use shotgun query syntax.'
125
+ end
126
+ end
127
+ when Array
128
+ result << [field.to_s, 'in', value]
129
+ else
130
+ raise TooComplexFiltersError,
131
+ 'This case is too complex to auto-translate. Please use shotgun query syntax.'
132
+ end
133
+ end
134
+ end
62
135
  end
63
136
  end
64
137
  end