shotgun_api_ruby 0.0.8.4 → 0.1.0.1

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.
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