shotgrid_api_ruby 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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