shotgun_api_ruby 0.0.8 → 0.1.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/Rakefile CHANGED
@@ -1,8 +1,3 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "bundler/gem_tasks"
4
- require "rspec/core/rake_task"
5
-
6
- RSpec::Core::RakeTask.new(:spec)
7
-
8
- task default: :spec
3
+ require 'bundler/gem_tasks'
data/bin/console CHANGED
@@ -1,17 +1,26 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require "bundler/setup"
5
- require "shotgun_api_ruby"
4
+ require 'bundler/setup'
5
+ require 'shotgun_api_ruby'
6
6
 
7
7
  require 'dotenv/load'
8
8
 
9
9
  if ENV['SITE_NAME'] && ENV['SCRIPT_NAME'] && ENV['SCRIPT_KEY']
10
- $cl = ShotgunApiRuby.new(
11
- shotgun_site: ENV['SITE_NAME'],
12
- auth: { client_id: ENV['SCRIPT_NAME'], client_secret: ENV['SCRIPT_KEY'] }
13
- )
10
+ $cl =
11
+ ShotgunApiRuby.new(
12
+ shotgun_site: ENV['SITE_NAME'],
13
+ auth: { client_id: ENV['SCRIPT_NAME'], client_secret: ENV['SCRIPT_KEY'] },
14
+ )
14
15
  end
16
+ if ENV['SITE_URL'] && ENV['SCRIPT_NAME'] && ENV['SCRIPT_KEY']
17
+ $cl =
18
+ ShotgunApiRuby.new(
19
+ site_url: ENV['SITE_URL'],
20
+ auth: { client_id: ENV['SCRIPT_NAME'], client_secret: ENV['SCRIPT_KEY'] },
21
+ )
22
+ end
23
+
15
24
  # You can add fixtures and/or initialization code here to make experimenting
16
25
  # with your gem easier. You can also use a different console, if you like.
17
26
 
@@ -19,5 +28,5 @@ end
19
28
  # require "pry"
20
29
  # Pry.start
21
30
 
22
- require "pry"
31
+ require 'pry'
23
32
  Pry.start(__FILE__)
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,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # zeitwerk will take care of auto loading files based on their name :)
4
- require "zeitwerk"
4
+ require 'zeitwerk'
5
5
  require 'active_support/core_ext/string/inflections'
6
6
  require 'ostruct'
7
- require "faraday"
7
+ require 'faraday'
8
8
  require 'json'
9
9
 
10
10
  loader = Zeitwerk::Loader.for_gem
@@ -15,3 +15,5 @@ module ShotgunApiRuby
15
15
  Client.new(**args)
16
16
  end
17
17
  end
18
+
19
+ loader.eager_load
@@ -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,17 +43,17 @@ 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
 
56
- def create(**attributes)
56
+ def create(attributes)
57
57
  resp =
58
58
  @connection.post('', attributes.to_json) do |req|
59
59
  req.headers['Content-Type'] = 'application/json'
@@ -62,20 +62,22 @@ 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
 
78
- def update(id, **changes)
80
+ def update(id, changes)
79
81
  return find(id) if changes.empty?
80
82
 
81
83
  resp =
@@ -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] if 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
@@ -251,46 +254,28 @@ module ShotgunApiRuby
251
254
  schema_client.fields
252
255
  end
253
256
 
254
- private
255
-
256
- def filters_are_simple?(filters)
257
- return false if filters.is_a? Array
257
+ def summary_client
258
+ @summary_client ||= Summarize.new(connection, type, @base_url_prefix)
259
+ end
258
260
 
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
261
+ def count(filter: nil, logical_operator: 'and')
262
+ summary_client.count(filter: filter, logical_operator: logical_operator)
265
263
  end
266
264
 
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
265
+ def summarize(
266
+ filter: nil,
267
+ grouping: nil,
268
+ summary_fields: nil,
269
+ logical_operator: 'and',
270
+ include_archived_projects: nil
271
+ )
272
+ summary_client.summarize(
273
+ filter: filter,
274
+ grouping: grouping,
275
+ summary_fields: summary_fields,
276
+ logical_operator: logical_operator,
277
+ include_archived_projects: include_archived_projects,
278
+ )
294
279
  end
295
280
  end
296
281
  end