app_store_connect 0.6.0 → 0.7.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 78c441ea7e16744eee415f8b65fb1389436e28e1880a3b4687d60d67ed4e3388
4
- data.tar.gz: dc044031de07e8c2d7c5a543e5613a609d73d4fdfa753a3db71fec4370903b60
3
+ metadata.gz: 6bece2d6691fbb4f254b53de5001324dfe4d10840c8c0cf82a073f0260bde177
4
+ data.tar.gz: d45d001147d7694579c6c838a52fa376607d83cb82b82bc2481193f763cf4ed7
5
5
  SHA512:
6
- metadata.gz: b1361415a42d7ca66c345a6c43d97cf4d684c5f6b3aded20b148aeb1c769ceb58ae4a13a72b1a3fa8b9a228ac03158a47d952431d763450659c3595db750cae4
7
- data.tar.gz: 3672cc89f06df46185e84577309f9f5817f88e62bfe7b168a5eb67dabc9097361692627133719bab761a91ba8b3f4f1f5a9cb28e3c3c82340f7640a8b6253555
6
+ metadata.gz: 0e9cff4c10632c74fa8bc63a04b3cd24af7be3ee04bc0d9914da6e0fae3d421a6a802d429c7adc43ad1c8cf20761c296575041e73c78ab38ee2aa2bb0c4cf0b7
7
+ data.tar.gz: 8301b52487821ab284c8f0c6ccbdd87b1fc3361337239aec2569da1ae4bc8a9f894ebdda28bb849a2ad42403ab840f7586f19c012ef156c6cc611db48333379a
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- app_store_connect (0.6.0)
4
+ app_store_connect (0.7.0)
5
5
  activesupport (~> 5.2.3)
6
6
  httparty (~> 0.16)
7
7
  jwt (~> 2.1)
data/README.md CHANGED
@@ -23,15 +23,17 @@ Or install it yourself as:
23
23
  ## Usage
24
24
 
25
25
  ```ruby
26
- client = AppStoreConnect::Client.new(
26
+ AppStoreConnect.config = {
27
27
  issuer_id: issuer_id,
28
28
  key_id: key_id,
29
29
  private_key: private_key
30
- )
30
+ }
31
31
 
32
- client.apps
33
- client.app('1234')
34
- client.builds('1234')
32
+ app_store_connect = AppStoreConnect::Client.new
33
+
34
+ app_store_connect.apps
35
+ app_store_connect.app('1234')
36
+ app_store_connect.builds('1234')
35
37
  ```
36
38
 
37
39
  ## Development
data/bin/console CHANGED
@@ -5,4 +5,4 @@ require 'bundler/setup'
5
5
  require 'app_store_connect'
6
6
  require 'pry'
7
7
 
8
- Pry.start
8
+ Pry.start(AppStoreConnect::Client.new)
@@ -1,19 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative './bundle_id_create_request/data'
4
-
5
3
  module AppStoreConnect
6
- class BundleIdCreateRequest
7
- attr_reader :data
8
-
9
- def initialize(*args)
10
- @data = Data.new(*args)
11
- end
4
+ class BundleIdCreateRequest < CreateRequest
5
+ data do
6
+ type 'bundleIds'
12
7
 
13
- def to_h
14
- {
15
- data: data.to_h
16
- }
8
+ attributes do
9
+ property :identifier, required: true
10
+ property :name, required: true
11
+ property :platform, required: true
12
+ property :seed_id
13
+ end
17
14
  end
18
15
  end
19
16
  end
@@ -2,75 +2,131 @@
2
2
 
3
3
  require 'active_support/all'
4
4
 
5
+ require 'app_store_connect/web_service_endpoint'
6
+
5
7
  module AppStoreConnect
6
- class Client
7
- ENDPOINT = 'https://api.appstoreconnect.apple.com/v1'
8
+ class Client # rubocop:disable Metrics/ClassLength
9
+ def initialize(**kwargs)
10
+ @options = options(**kwargs)
8
11
 
9
- def initialize(key_id:, issuer_id:, private_key:)
10
12
  @authorization = Authorization.new(
11
- private_key: private_key,
12
- key_id: key_id,
13
- issuer_id: issuer_id
13
+ private_key: @options[:private_key],
14
+ key_id: @options[:key_id],
15
+ issuer_id: @options[:issuer_id]
14
16
  )
17
+ @web_service_endpoints_by_name ||= begin
18
+ AppStoreConnect::Config::API['web_service_endpoints'].dup.map do |config|
19
+ [config.fetch('alias').to_sym, WebServiceEndpoint.new(**config.deep_symbolize_keys)]
20
+ end.to_h
21
+ end
15
22
  end
16
23
 
17
- def apps
18
- get('apps')
24
+ def respond_to_missing?(method_name, include_private = false)
25
+ web_service_endpoint_names.include?(method_name) || super
19
26
  end
20
27
 
21
- def app(id)
22
- get("apps/#{id}")
23
- end
28
+ def method_missing(method_name, *kwargs)
29
+ super unless web_service_endpoint_names.include?(method_name)
24
30
 
25
- def builds(app_id)
26
- get("apps/#{app_id}/builds")
27
- end
31
+ web_service_endpoint = web_service_endpoint_by(name: method_name)
28
32
 
29
- def build(app_id, build_id)
30
- get("apps/#{app_id}/builds/#{build_id}")
33
+ call(web_service_endpoint, *kwargs)
31
34
  end
32
35
 
33
- def invite_user(first_name:, last_name:, email:, roles:)
34
- invitation = UserInvitationCreateRequest.new(first_name, last_name, email, roles)
36
+ private
35
37
 
36
- post('userInvitations', invitation.body.to_json)
38
+ def web_service_endpoint_names
39
+ @web_service_endpoints_by_name.keys
37
40
  end
38
41
 
39
- def create_bundle_id(*args)
40
- request = BundleIdCreateRequest.new(*args)
42
+ def call(web_service_endpoint, **kwargs)
43
+ case web_service_endpoint.http_method
44
+ when :get
45
+ get(web_service_endpoint, **kwargs)
46
+ when :post
47
+ post(web_service_endpoint, **kwargs)
48
+ else
49
+ raise "invalid http method: #{web_service_endpoint.http_method}"
50
+ end
51
+ end
41
52
 
42
- post('bundleIds', body(request))
53
+ def build_url(web_service_endpoint, **kwargs)
54
+ web_service_endpoint
55
+ .url
56
+ .gsub(/(\{(\w+)\})/) { kwargs.fetch(Regexp.last_match(2).to_sym) }
43
57
  end
44
58
 
45
- def users(limit: 200)
46
- get('users', query_params: { 'limit' => limit })
59
+ def url_parameter_names(web_service_endpoint)
60
+ web_service_endpoint
61
+ .url
62
+ .scan(/(\{(\w+)\})/)
63
+ .map { |_, n| n.to_sym }
47
64
  end
48
65
 
49
- def user_invitations
50
- get('userInvitations')
66
+ def web_service_endpoint_by(name:)
67
+ @web_service_endpoints_by_name[name]
51
68
  end
52
69
 
53
- private
70
+ def env_options
71
+ {}.tap do |hash|
72
+ ENV.each do |key, value|
73
+ match = key.match(/APP_STORE_CONNECT_(?<name>[A-Z_]+)/)
74
+
75
+ next unless match
76
+
77
+ hash[match[:name].downcase.to_sym] = value
78
+ end
79
+ end
80
+ end
54
81
 
55
- def body(request)
82
+ def options(**kwargs)
83
+ AppStoreConnect.config.merge(kwargs.merge(env_options)).tap do |options|
84
+ %i[key_id issuer_id private_key].each do |key|
85
+ raise ArgumentError, "missing #{key}" unless options.keys.include?(key)
86
+ end
87
+ end
88
+ end
89
+
90
+ def build_body(request)
56
91
  request
57
- .to_hash
92
+ .to_h
58
93
  .deep_transform_keys { |k| k.to_s.camelize(:lower) }
59
94
  .to_json
60
95
  end
61
96
 
62
- def get(path, query_params: {})
63
- response = HTTParty.get("#{ENDPOINT}/#{path}", headers: headers, query: query_params)
97
+ def build_query(web_service_endpoint, **kwargs)
98
+ query_parameters = kwargs.dup.tap do |hash|
99
+ url_parameter_names(web_service_endpoint).each do |name|
100
+ hash.delete(name.to_sym)
101
+ end
102
+ end
103
+
104
+ query_parameters.to_query
105
+ end
106
+
107
+ def get(web_service_endpoint, **kwargs)
108
+ url = build_url(web_service_endpoint, **kwargs)
109
+ query = build_query(web_service_endpoint, **kwargs)
110
+
111
+ response = execute(:get, url, headers: headers, query: query)
64
112
 
65
113
  response['data']
66
114
  end
67
115
 
68
- def post(path, body)
69
- response = HTTParty.post("#{ENDPOINT}/#{path}", headers: headers, body: body)
116
+ def post(web_service_endpoint, **kwargs)
117
+ url = build_url(web_service_endpoint, **kwargs)
118
+ request = "AppStoreConnect::#{web_service_endpoint.http_body_type}".constantize.new(**kwargs)
119
+ body = build_body(request)
120
+
121
+ response = execute(:post, url, headers: headers, body: body)
70
122
 
71
123
  response
72
124
  end
73
125
 
126
+ def execute(http_method, url, **options)
127
+ HTTParty.send(http_method, url, options)
128
+ end
129
+
74
130
  def headers
75
131
  {
76
132
  'Authorization' => "Bearer #{@authorization.token}",
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AppStoreConnect
4
+ class CreateRequest
5
+ include Object::Data
6
+
7
+ def initialize(**kwargs)
8
+ @data = Data.new(kwargs)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/concern'
4
+
5
+ module AppStoreConnect
6
+ module Object
7
+ module Attributes
8
+ extend ActiveSupport::Concern
9
+
10
+ class_methods do
11
+ def attributes(&block)
12
+ self::Attributes.class_eval(&block)
13
+ end
14
+ end
15
+
16
+ included do
17
+ attr_reader :attributes
18
+
19
+ klass = Class.new do |attributes|
20
+ include Object::Properties
21
+
22
+ attributes.define_method :initialize do |**kwargs|
23
+ self.class.properties.each do |name, options|
24
+ raise ArgumentError, "#{name} required" if options[:required] && !kwargs[name]
25
+
26
+ value = kwargs.fetch(name, options[:default])
27
+
28
+ instance_variable_set("@#{name}", value)
29
+ end
30
+ end
31
+
32
+ def to_h
33
+ {}.tap do |hash|
34
+ self.class.properties.keys.each do |name|
35
+ value = instance_variable_get("@#{name}")
36
+
37
+ hash[name] = value unless value.nil?
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ const_set('Attributes', klass)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/concern'
4
+
5
+ module AppStoreConnect
6
+ module Object
7
+ module Data
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ attr_reader :data
12
+
13
+ klass = Class.new do |data|
14
+ include Object::Attributes
15
+ include Object::Type
16
+
17
+ data.define_method :initialize do |**kwargs|
18
+ instance_variable_set('@attributes', data::Attributes.new(kwargs))
19
+ end
20
+
21
+ def to_h
22
+ {
23
+ attributes: attributes.to_h,
24
+ type: type
25
+ }
26
+ end
27
+ end
28
+
29
+ const_set('Data', klass)
30
+
31
+ def to_h
32
+ {
33
+ data: data.to_h
34
+ }
35
+ end
36
+ end
37
+
38
+ class_methods do
39
+ def data(&block)
40
+ self::Data.class_eval(&block)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/concern'
4
+
5
+ module AppStoreConnect
6
+ module Object
7
+ module Properties
8
+ extend ActiveSupport::Concern
9
+
10
+ class_methods do
11
+ def properties
12
+ @properties ||= {}
13
+ end
14
+
15
+ def property(name, options = {})
16
+ properties[name] = options
17
+
18
+ attr_accessor name.to_sym
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/concern'
4
+
5
+ module AppStoreConnect
6
+ module Object
7
+ module Type
8
+ extend ActiveSupport::Concern
9
+
10
+ class_methods do
11
+ def type(type)
12
+ @type = type
13
+
14
+ const_set('TYPE', type)
15
+ end
16
+ end
17
+
18
+ included do
19
+ def type
20
+ self.class.instance_variable_get('@type')
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -2,12 +2,20 @@
2
2
 
3
3
  module AppStoreConnect
4
4
  class UserInvitationCreateRequest < Struct.new(:first_name, :last_name, :email, :roles)
5
+ def initialize(kwargs)
6
+ super(kwargs[:first_name], kwargs[:last_name], kwargs[:email], kwargs[:roles])
7
+ end
8
+
5
9
  def body
6
10
  { 'data' =>
7
11
  { 'type' => 'userInvitations', 'attributes' =>
8
12
  { 'firstName' => first_name, 'lastName' => last_name, 'email' => email, 'roles' => roles, 'allAppsVisible' => true, 'provisioningAllowed' => true } } }
9
13
  end
10
14
 
15
+ def to_h
16
+ body
17
+ end
18
+
11
19
  def to_s
12
20
  body.to_json
13
21
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AppStoreConnect
4
- VERSION = '0.6.0'
4
+ VERSION = '0.7.0'
5
5
  end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AppStoreConnect
4
+ class WebServiceEndpoint
5
+ def initialize(**options)
6
+ @options = options
7
+ end
8
+
9
+ def http_method
10
+ @options[:http_method].to_sym
11
+ end
12
+
13
+ def http_body_type
14
+ @options[:http_body_type]
15
+ end
16
+
17
+ def url
18
+ @options[:url]
19
+ end
20
+ end
21
+ end
@@ -5,6 +5,12 @@ require 'httparty'
5
5
 
6
6
  require 'app_store_connect/authorization'
7
7
  require 'app_store_connect/parser'
8
+
9
+ require 'app_store_connect/object/type'
10
+ require 'app_store_connect/object/attributes'
11
+ require 'app_store_connect/object/properties'
12
+ require 'app_store_connect/object/data'
13
+ require 'app_store_connect/create_request'
8
14
  require 'app_store_connect/client'
9
15
  require 'app_store_connect/bundle_id_create_request'
10
16
  require 'app_store_connect/user_invitation_create_request'
@@ -21,4 +27,12 @@ module AppStoreConnect
21
27
  Factory.register('enum', Factory::Builder::Enum)
22
28
 
23
29
  Parser.parse!(Config::API)
30
+
31
+ @config = {}
32
+
33
+ class << self
34
+ attr_reader :config
35
+
36
+ attr_writer :config
37
+ end
24
38
  end
data/lib/config/api.json CHANGED
@@ -1,4 +1,48 @@
1
1
  {
2
+ "web_service_endpoints": [
3
+ {
4
+ "alias": "apps",
5
+ "http_method": "get",
6
+ "url": "https://api.appstoreconnect.apple.com/v1/apps"
7
+ },
8
+ {
9
+ "alias": "app",
10
+ "http_method": "get",
11
+ "url": "https://api.appstoreconnect.apple.com/v1/apps/{id}"
12
+ },
13
+ {
14
+ "alias": "builds",
15
+ "http_method": "get",
16
+ "url": "https://api.appstoreconnect.apple.com/v1/apps/{id}/builds"
17
+ },
18
+ {
19
+ "alias": "build",
20
+ "http_method": "get",
21
+ "url": "https://api.appstoreconnect.apple.com/v1/builds/{id}"
22
+ },
23
+ {
24
+ "alias": "invite_user",
25
+ "url": "https://api.appstoreconnect.apple.com/v1/userInvitations",
26
+ "http_body_type": "UserInvitationCreateRequest",
27
+ "http_method": "post"
28
+ },
29
+ {
30
+ "alias": "create_bundle_id",
31
+ "url": "https://api.appstoreconnect.apple.com/v1/bundleIds",
32
+ "http_body_type": "BundleIdCreateRequest",
33
+ "http_method": "post"
34
+ },
35
+ {
36
+ "alias": "users",
37
+ "url": "https://api.appstoreconnect.apple.com/v1/users",
38
+ "http_method": "get"
39
+ },
40
+ {
41
+ "alias": "user_invitations",
42
+ "http_method": "get",
43
+ "url": "https://api.appstoreconnect.apple.com/v1/userInvitations"
44
+ }
45
+ ],
2
46
  "Type": {
3
47
  "CapabilityType": {
4
48
  "type": "enum",
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: app_store_connect
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kyle Decot
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-07-06 00:00:00.000000000 Z
11
+ date: 2019-08-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -219,17 +219,21 @@ files:
219
219
  - lib/app_store_connect.rb
220
220
  - lib/app_store_connect/authorization.rb
221
221
  - lib/app_store_connect/bundle_id_create_request.rb
222
- - lib/app_store_connect/bundle_id_create_request/data.rb
223
- - lib/app_store_connect/bundle_id_create_request/data/attributes.rb
224
222
  - lib/app_store_connect/client.rb
225
223
  - lib/app_store_connect/config.rb
224
+ - lib/app_store_connect/create_request.rb
226
225
  - lib/app_store_connect/factory.rb
227
226
  - lib/app_store_connect/factory/builder/enum.rb
227
+ - lib/app_store_connect/object/attributes.rb
228
+ - lib/app_store_connect/object/data.rb
229
+ - lib/app_store_connect/object/properties.rb
230
+ - lib/app_store_connect/object/type.rb
228
231
  - lib/app_store_connect/parser.rb
229
232
  - lib/app_store_connect/type.rb
230
233
  - lib/app_store_connect/type/enum.rb
231
234
  - lib/app_store_connect/user_invitation_create_request.rb
232
235
  - lib/app_store_connect/version.rb
236
+ - lib/app_store_connect/web_service_endpoint.rb
233
237
  - lib/config/api.json
234
238
  homepage: https://github.com/kyledecot/app_store_connect
235
239
  licenses:
@@ -1,28 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module AppStoreConnect
4
- class BundleIdCreateRequest
5
- class Data
6
- class Attributes
7
- attr_accessor :identifier, :name, :platform, :seed_id
8
-
9
- def initialize(identifier:, name:, platform:, seed_id: nil)
10
- self.identifier = identifier
11
- self.name = name
12
- self.platform = platform
13
- self.seed_id = seed_id
14
- end
15
-
16
- def to_h
17
- {
18
- identifier: identifier,
19
- name: name,
20
- platform: platform
21
- }.tap do |hash|
22
- hash[:seed_id] = seed_id unless seed_id.nil?
23
- end
24
- end
25
- end
26
- end
27
- end
28
- end
@@ -1,28 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative './data/attributes'
4
-
5
- module AppStoreConnect
6
- class BundleIdCreateRequest
7
- class Data
8
- TYPE = 'bundleIds'
9
-
10
- attr_reader :attributes
11
-
12
- def initialize(*args)
13
- @attributes = Attributes.new(*args)
14
- end
15
-
16
- def type
17
- TYPE
18
- end
19
-
20
- def to_h
21
- {
22
- attributes: attributes.to_h,
23
- type: type
24
- }
25
- end
26
- end
27
- end
28
- end