shotgrid_api_ruby 0.1.2

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.
@@ -0,0 +1,182 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShotgridApiRuby
4
+ class Entities
5
+ class Params < Hash
6
+ class TooComplexFiltersError < StandardError
7
+ end
8
+
9
+ def add_sort(sort)
10
+ return unless sort
11
+
12
+ self[:sort] =
13
+ if sort.is_a?(Hash)
14
+ sort.map do |field, direction|
15
+ "#{direction.to_s.start_with?('desc') ? '-' : ''}#{field}"
16
+ end.join(',')
17
+ else
18
+ [sort].flatten.join(',')
19
+ end
20
+ end
21
+
22
+ def add_page(page, page_size)
23
+ return unless page || page_size
24
+
25
+ page = page.to_i if page
26
+ page_size = page_size.to_i if page_size
27
+
28
+ page = 1 if page && page < 1
29
+ self[:page] = { size: page_size || 20, number: page || 1 }
30
+ end
31
+
32
+ def add_fields(fields)
33
+ self[:fields] =
34
+ fields && !fields.empty? ? [fields].flatten.join(',') : '*'
35
+ end
36
+
37
+ def add_options(return_only, include_archived_projects)
38
+ return if return_only.nil? && include_archived_projects.nil?
39
+
40
+ self[:options] = {
41
+ return_only: return_only ? 'retired' : 'active',
42
+ include_archived_projects: !!include_archived_projects,
43
+ }
44
+ end
45
+
46
+ def add_filter(filters, logical_operator = 'and')
47
+ return unless filters
48
+
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 add_grouping(grouping)
67
+ return unless grouping
68
+
69
+ if grouping.is_a? Array
70
+ self[:grouping] = grouping
71
+ return
72
+ end
73
+
74
+ self[:grouping] =
75
+ grouping
76
+ .each
77
+ .with_object([]) do |(key, options), result|
78
+ if options.is_a? Hash
79
+ result << {
80
+ field: key.to_s,
81
+ type:
82
+ options[:type]&.to_s || options['type']&.to_s || 'exact',
83
+ direction:
84
+ options[:direction]&.to_s || options['direction']&.to_s ||
85
+ 'asc',
86
+ }
87
+ else
88
+ result << {
89
+ field: key.to_s,
90
+ type: 'exact',
91
+ direction: options.to_s,
92
+ }
93
+ end
94
+ end
95
+ end
96
+
97
+ def add_summary_fields(summary_fields)
98
+ return unless summary_fields
99
+
100
+ if summary_fields.is_a? Array
101
+ self[:summary_fields] = summary_fields
102
+ return
103
+ end
104
+
105
+ if summary_fields.is_a? Hash
106
+ self[:summary_fields] =
107
+ summary_fields.map { |k, v| { field: k.to_s, type: v.to_s } }
108
+ end
109
+ end
110
+
111
+ def self.filters_are_simple?(filters)
112
+ return false if filters.is_a? Array
113
+
114
+ if filters.is_a?(Hash) &&
115
+ (filters[:conditions] || filters['conditions'])
116
+ return false
117
+ end
118
+
119
+ filters.values.all? do |filter_val|
120
+ (
121
+ filter_val.is_a?(Integer) || filter_val.is_a?(String) ||
122
+ filter_val.is_a?(Symbol)
123
+ ) ||
124
+ (
125
+ filter_val.is_a?(Array) && filter_val.all? do |val|
126
+ val.is_a?(String) || val.is_a?(Symbol) || val.is_a?(Integer)
127
+ end
128
+ )
129
+ end
130
+ end
131
+
132
+ private
133
+
134
+ def translate_simple_filters_to_sg(filters)
135
+ filters.map do |field, value|
136
+ [
137
+ field.to_s,
138
+ value.is_a?(Array) ? value.map(&:to_s).join(',') : value.to_s,
139
+ ]
140
+ end.to_h
141
+ end
142
+
143
+ def translate_complex_filters_to_sg(filters)
144
+ # We don't know how to translate anything but hashes
145
+ return filters if !filters.is_a?(Hash)
146
+
147
+ filters
148
+ .each
149
+ .with_object([]) do |item, result|
150
+ field, value = item
151
+ case value
152
+ when String, Symbol, Integer, Float
153
+ result << [field.to_s, 'is', value]
154
+ when Hash
155
+ value.each do |subfield, subvalue|
156
+ sanitized_subfield =
157
+ if !subfield.to_s.include?('.')
158
+ "#{field.capitalize}.#{subfield}"
159
+ else
160
+ subfield
161
+ end
162
+ case subvalue
163
+ when String, Symbol, Integer, Float
164
+ result << ["#{field}.#{sanitized_subfield}", 'is', subvalue]
165
+ when Array
166
+ result << ["#{field}.#{sanitized_subfield}", 'in', subvalue]
167
+ else
168
+ raise TooComplexFiltersError,
169
+ 'This case is too complex to auto-translate. Please use shotgrid query syntax.'
170
+ end
171
+ end
172
+ when Array
173
+ result << [field.to_s, 'in', value]
174
+ else
175
+ raise TooComplexFiltersError,
176
+ 'This case is too complex to auto-translate. Please use shotgrid query syntax.'
177
+ end
178
+ end
179
+ end
180
+ end
181
+ end
182
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShotgridApiRuby
4
+ class Entities
5
+ class Schema
6
+ def initialize(connection, type, base_url_prefix)
7
+ @connection = connection.dup
8
+ @type = type
9
+ @connection.url_prefix = "#{base_url_prefix}/schema/#{type}"
10
+ end
11
+ attr_reader :type, :connection
12
+
13
+ def read
14
+ resp = @connection.get('')
15
+
16
+ if resp.status >= 300
17
+ raise "Error while read schema for #{type}: #{resp.body}"
18
+ end
19
+
20
+ resp_body = JSON.parse(resp.body)
21
+
22
+ OpenStruct.new(resp_body['data'].transform_values { |v| v['value'] })
23
+ end
24
+
25
+ def fields
26
+ resp = @connection.get('fields')
27
+ resp_body = JSON.parse(resp.body)
28
+
29
+ if resp.status >= 300
30
+ raise "Error while read schema fields for #{type}: #{resp.body}"
31
+ end
32
+
33
+ OpenStruct.new(
34
+ resp_body['data'].transform_values do |value|
35
+ OpenStruct.new(
36
+ value.transform_values { |attribute| attribute['value'] }.merge(
37
+ properties:
38
+ OpenStruct.new(
39
+ value['properties'].transform_values do |prop|
40
+ prop['value']
41
+ end,
42
+ ),
43
+ ),
44
+ )
45
+ end,
46
+ )
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,64 @@
1
+ module ShotgridApiRuby
2
+ class Entities
3
+ class Summarize
4
+ Summary = Struct.new(:summaries, :groups)
5
+
6
+ def initialize(connection, type, base_url_prefix)
7
+ @connection = connection.dup
8
+ @type = type
9
+ @connection.url_prefix = "#{base_url_prefix}/entity/#{type}/_summarize"
10
+ end
11
+ attr_reader :type, :connection
12
+
13
+ def count(filter: nil, logical_operator: 'and')
14
+ result =
15
+ summarize(
16
+ filter: filter,
17
+ logical_operator: logical_operator,
18
+ summary_fields: [{ type: :record_count, field: 'id' }],
19
+ )
20
+ result.summaries&.[]('id') || 0
21
+ end
22
+
23
+ def summarize(
24
+ filter: nil,
25
+ grouping: nil,
26
+ summary_fields: nil,
27
+ logical_operator: 'and',
28
+ include_archived_projects: nil
29
+ )
30
+ params = Params.new
31
+
32
+ params.add_filter(filter, logical_operator)
33
+
34
+ params[:filters] = params[:filter] if params[:filter]
35
+ params.delete(:filter)
36
+
37
+ params.add_grouping(grouping)
38
+ params.add_summary_fields(summary_fields)
39
+ params.add_options(nil, include_archived_projects)
40
+
41
+ resp =
42
+ @connection.post('', params) do |req|
43
+ req.headers['Content-Type'] =
44
+ if params[:filters].is_a? Array
45
+ 'application/vnd+shotgun.api3_array+json'
46
+ else
47
+ 'application/vnd+shotgun.api3_hash+json'
48
+ end
49
+ req.body = params.to_h.to_json
50
+ end
51
+ resp_body = JSON.parse(resp.body)
52
+
53
+ if resp.status >= 300
54
+ raise "Error while getting summarize for #{type}: #{resp_body['errors']}"
55
+ end
56
+
57
+ Summary.new(
58
+ resp_body['data']['summaries'],
59
+ resp_body['data']&.[]('groups'),
60
+ )
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShotgridApiRuby
4
+ # Represent each entity returned by Shotgrid
5
+ Entity =
6
+ Struct.new(:type, :attributes, :relationships, :id, :links) do
7
+ def method_missing(name, *args, &block)
8
+ if attributes.respond_to?(name)
9
+ define_singleton_method(name) { attributes.public_send(name) }
10
+ public_send(name)
11
+ else
12
+ super
13
+ end
14
+ end
15
+
16
+ def respond_to_missing?(name, _private_methods = false)
17
+ attributes.respond_to?(name) || super
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShotgridApiRuby
4
+ class Preferences
5
+ def initialize(connection)
6
+ @connection = connection.dup
7
+ @connection.url_prefix = "#{@connection.url_prefix}/preferences"
8
+ end
9
+
10
+ attr_reader :connection
11
+
12
+ def all
13
+ resp = @connection.get
14
+ resp_body = JSON.parse(resp.body)
15
+
16
+ if resp.status >= 300
17
+ raise "Error while getting server preferences: #{resp_body['errors']}"
18
+ end
19
+
20
+ data = resp_body['data']
21
+ OpenStruct.new(data)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShotgridApiRuby
4
+ class ServerInfo
5
+ def initialize(connection)
6
+ @connection = connection
7
+ end
8
+
9
+ attr_reader :connection
10
+
11
+ def get
12
+ resp = @connection.get
13
+ resp_body = JSON.parse(resp.body)
14
+
15
+ if resp.status >= 300
16
+ raise "Error while getting server infos: #{resp_body['errors']}"
17
+ end
18
+
19
+ data = resp_body['data']
20
+ OpenStruct.new(data)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ShotgridApiRuby
4
+ VERSION = '0.1.2'
5
+ end
data/package.json ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "shotgrid_api_ruby",
3
+ "version": "1.0.0",
4
+ "main": "index.js",
5
+ "repository": "git@github.com:shotgunsoftware/shotgrid_api_ruby.git",
6
+ "author": "Denis <Zaratan> Pasin <zaratan@hey.com>",
7
+ "license": "MIT",
8
+ "devDependencies": {
9
+ "@prettier/plugin-ruby": "^1.3.0",
10
+ "prettier": "^2.2.1"
11
+ }
12
+ }
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'shotgrid_api_ruby/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'shotgrid_api_ruby'
9
+ spec.version = ShotgridApiRuby::VERSION
10
+ spec.authors = ['Denis <Zaratan> Pasin']
11
+ spec.email = ['denis.pasin@autodesk.com']
12
+
13
+ spec.summary = 'Gem to interact easily with Shotgrid REST api.'
14
+ spec.description =
15
+ "Gem to facilitate the interaction with Shotgrid's REST API."
16
+ spec.homepage = 'https://github.com/shotgunsoftware/shotgrid_api_ruby'
17
+ spec.license = 'MIT'
18
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.5.0')
19
+
20
+ spec.metadata['homepage_uri'] = spec.homepage
21
+ spec.metadata['source_code_uri'] =
22
+ 'https://github.com/shotgunsoftware/shotgrid_api_ruby'
23
+ spec.metadata['changelog_uri'] =
24
+ 'https://github.com/shotgunsoftware/shotgrid_api_ruby/blob/main/CHANGELOG.md'
25
+
26
+ # Specify which files should be added to the gem when it is released.
27
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
28
+ spec.files =
29
+ Dir.chdir(File.expand_path(__dir__)) do
30
+ `git ls-files -z`.split("\x0").reject do |f|
31
+ f.match(%r{^(test|spec|features)/})
32
+ end
33
+ end
34
+ spec.require_paths = ['lib']
35
+
36
+ spec.add_dependency 'activesupport'
37
+ spec.add_dependency 'faraday', '~> 1'
38
+ spec.add_dependency 'zeitwerk', '~> 2.2'
39
+
40
+ spec.add_development_dependency 'bundler'
41
+ spec.add_development_dependency 'bundler-audit'
42
+ spec.add_development_dependency 'dotenv'
43
+ spec.add_development_dependency 'faker', '> 1.8'
44
+ spec.add_development_dependency 'guard-rspec', '> 4.7'
45
+ spec.add_development_dependency 'overcommit'
46
+ spec.add_development_dependency 'prettier'
47
+ spec.add_development_dependency 'pry'
48
+ spec.add_development_dependency 'rake'
49
+ spec.add_development_dependency 'rspec', '~> 3.0'
50
+ spec.add_development_dependency 'rspec_in_context', '> 1'
51
+ spec.add_development_dependency 'rubocop'
52
+ spec.add_development_dependency 'rubocop-faker'
53
+ spec.add_development_dependency 'rubocop-performance'
54
+ spec.add_development_dependency 'simplecov', '> 0.16'
55
+ spec.add_development_dependency 'solargraph'
56
+ spec.add_development_dependency 'timecop'
57
+ spec.add_development_dependency 'vcr'
58
+ spec.add_development_dependency 'yard'
59
+ end