Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple organization support #46

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 2 additions & 22 deletions lib/fabricio/authorization/authorization_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
require 'fabricio/services/organization_service'

AUTH_API_URL = 'https://fabric.io/oauth/token'
ORGANIZATION_API_URL = 'https://fabric.io/api/v2/organizations'

module Fabricio
module Authorization
Expand Down Expand Up @@ -46,8 +45,7 @@ def perform_authorization(username, password, client_id, client_secret)
if auth_data['access_token'] == nil
raise StandardError.new("Incorrect authorization response: #{auth_data}")
end
organization_id = obtain_organization_id(auth_data)
Session.new(auth_data, organization_id)
Session.new(auth_data)
end

# Initiates a session refresh network request
Expand All @@ -71,7 +69,7 @@ def perform_refresh_token_request(session)
if result['access_token'] == nil
raise StandardError.new("Incorrect authorization response: #{auth_data}")
end
Session.new(result, session.organization_id)
Session.new(result)
end

# Makes a request to OAuth API and obtains access and refresh tokens.
Expand Down Expand Up @@ -99,24 +97,6 @@ def obtain_auth_data(username, password, client_id, client_secret)
end
JSON.parse(response.body)
end

# Makes a request to fetch current organization identifier.
# This identifier is used in most other API requests, so we store it in the session.
#
# @param auth_data [Hash] A set of authorization tokens
# @option options [String] :access_token OAuth access token
# @option options [String] :refresh_token OAuth refresh token
# @return [String]
def obtain_organization_id(auth_data)
conn = Faraday.new(:url => ORGANIZATION_API_URL) do |faraday|
faraday.adapter Faraday.default_adapter
end

response = conn.get do |req|
req.headers['Authorization'] = "Bearer #{auth_data['access_token']}"
end
JSON.parse(response.body).first['id']
end
end
end
end
6 changes: 2 additions & 4 deletions lib/fabricio/authorization/session.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,17 @@ module Fabricio
module Authorization
# This class is a data structure that holds tokens and identifiers necessary for making API requests.
class Session
attr_reader :access_token, :refresh_token, :organization_id
attr_reader :access_token, :refresh_token

# Initializes a new Session object
#
# @param attributes [Hash] Hash containing access and refresh tokens
# @option options [String] :access_token OAuth access token
# @option options [String] :refresh_token OAuth refresh token
# @param organization_id [String]
# @return [Fabricio::Authorization::Session]
def initialize(attributes = {}, organization_id = '')
def initialize(attributes = {})
@access_token = attributes['access_token']
@refresh_token = attributes['refresh_token']
@organization_id = organization_id
end
end
end
Expand Down
8 changes: 5 additions & 3 deletions lib/fabricio/client/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
require 'fabricio/authorization/session'
require 'fabricio/authorization/memory_session_storage'
require 'fabricio/networking/network_client'
require 'fabricio/networking/organization_id_provider'

module Fabricio
# The main object of the gem. It's used to initiate all data requests.
Expand Down Expand Up @@ -54,10 +55,11 @@ def initialize(options =
@auth_client = Fabricio::Authorization::AuthorizationClient.new
session = obtain_session
network_client = Fabricio::Networking::NetworkClient.new(@auth_client, @session_storage)
organization_id_provider = Fabricio::Networking::OrganizationIdProvider.new(lambda { return @app_service.all })
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not 100% happy with this approach, but it's the easiest way I found to break the circular reference between OrganizationIdProvider and AppService without giving up constructor injection (the provider needs the service to lazily request the list of applications, and the service needs the provider to build some URLs via its model factory).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks ok to me


@organization_service ||= Fabricio::Service::OrganizationService.new(session, network_client)
@app_service ||= Fabricio::Service::AppService.new(session, network_client)
@build_service ||= Fabricio::Service::BuildService.new(session, network_client)
@organization_service ||= Fabricio::Service::OrganizationService.new(network_client)
@app_service ||= Fabricio::Service::AppService.new(organization_id_provider, network_client)
@build_service ||= Fabricio::Service::BuildService.new(organization_id_provider, network_client)
end

# We use `method_missing` approach instead of explicit methods.
Expand Down
3 changes: 2 additions & 1 deletion lib/fabricio/models/app.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ module Fabricio
module Model
# This model represents an application
class App < AbstractModel
attr_reader :id, :name, :bundle_id, :created_at, :platform, :icon_url
attr_reader :id, :name, :bundle_id, :created_at, :platform, :icon_url, :organization_id

# Returns an App model object
#
Expand All @@ -17,6 +17,7 @@ def initialize(attributes)
@created_at = attributes['created_at']
@platform = attributes['platform']
@icon_url = attributes['icon_url']
@organization_id = attributes['organization_id']
@json = attributes
end

Expand Down
55 changes: 28 additions & 27 deletions lib/fabricio/networking/app_request_model_factory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ class AppRequestModelFactory
FABRIC_ORGANIZATIONS_ENDPOINT = '/organizations'
FABRIC_PROJECTS_ENDPOINT = '/projects'

# Initializes a new AppRequestModelFactory object
#
# @param organization_id_provider [Fabricio::Networking::OrganizationIdProvider]
# @return [Fabricio::Networking::AppRequestModelFactory]
def initialize(organization_id_provider)
@organization_id_provider = organization_id_provider
end

# Returns a request model for obtaining the list of all apps
#
# @return [Fabricio::Networking::RequestModel]
Expand Down Expand Up @@ -43,11 +51,10 @@ def get_app_request_model(app_id)

# Returns a request model for obtaining the count of active users at the current moment
#
# @param session [Fabricio::Authorization::Session]
# @param app_id [String]
# @return [Fabricio::Networking::RequestModel]
def active_now_request_model(session, app_id)
path = growth_analytics_endpoint(session, app_id, 'active_now')
def active_now_request_model(app_id)
path = growth_analytics_endpoint(app_id, 'active_now')
model = Fabricio::Networking::RequestModel.new do |config|
config.type = :GET
config.base_url = FABRIC_API_URL
Expand All @@ -58,13 +65,12 @@ def active_now_request_model(session, app_id)

# Returns a request model for obtaining the count of daily new users
#
# @param session [Fabricio::Authorization::Session]
# @param app_id [String]
# @param start_time [String] Timestamp of the start date
# @param end_time [String] Timestamp of the end date
# @return [Fabricio::Networking::RequestModel]
def daily_new_request_model(session, app_id, start_time, end_time)
path = growth_analytics_endpoint(session, app_id, 'daily_new')
def daily_new_request_model(app_id, start_time, end_time)
path = growth_analytics_endpoint(app_id, 'daily_new')
params = time_range_params(start_time, end_time)
model = Fabricio::Networking::RequestModel.new do |config|
config.type = :GET
Expand All @@ -77,14 +83,13 @@ def daily_new_request_model(session, app_id, start_time, end_time)

# Returns a request model for obtaining the count of daily active users
#
# @param session [Fabricio::Authorization::Session]
# @param app_id [String]
# @param start_time [String] Timestamp of the start date
# @param end_time [String] Timestamp of the end date
# @param build [String] The version of the build. E.g. '4.0.1 (38)'
# @return [Fabricio::Networking::RequestModel]
def daily_active_request_model(session, app_id, start_time, end_time, build)
path = growth_analytics_endpoint(session, app_id, 'daily_active')
def daily_active_request_model(app_id, start_time, end_time, build)
path = growth_analytics_endpoint(app_id, 'daily_active')
params = time_range_params(start_time, end_time)
params['build'] = build
model = Fabricio::Networking::RequestModel.new do |config|
Expand All @@ -98,14 +103,13 @@ def daily_active_request_model(session, app_id, start_time, end_time, build)

# Returns a request model for obtaining the count of weekly active users
#
# @param session [Fabricio::Authorization::Session]
# @param app_id [String]
# @param start_time [String] Timestamp of the start date
# @param end_time [String] Timestamp of the end date
# @param build [String] The version of the build. E.g. '4.0.1 (38)'
# @return [Fabricio::Networking::RequestModel]
def weekly_active_request_model(session, app_id, start_time, end_time, build)
path = growth_analytics_endpoint(session, app_id, 'weekly_active')
def weekly_active_request_model(app_id, start_time, end_time, build)
path = growth_analytics_endpoint(app_id, 'weekly_active')
params = time_range_params(start_time, end_time)
params['build'] = build
model = Fabricio::Networking::RequestModel.new do |config|
Expand All @@ -119,14 +123,13 @@ def weekly_active_request_model(session, app_id, start_time, end_time, build)

# Returns a request model for obtaining the count of monhtly active users
#
# @param session [Fabricio::Authorization::Session]
# @param app_id [String]
# @param start_time [String] Timestamp of the start date
# @param end_time [String] Timestamp of the end date
# @param build [String] The version of the build. E.g. '4.0.1 (38)'
# @return [Fabricio::Networking::RequestModel]
def monthly_active_request_model(session, app_id, start_time, end_time, build)
path = growth_analytics_endpoint(session, app_id, 'monthly_active')
def monthly_active_request_model(app_id, start_time, end_time, build)
path = growth_analytics_endpoint(app_id, 'monthly_active')
params = time_range_params(start_time, end_time)
params['build'] = build
model = Fabricio::Networking::RequestModel.new do |config|
Expand All @@ -140,14 +143,13 @@ def monthly_active_request_model(session, app_id, start_time, end_time, build)

# Returns a request model for obtaining the count of sessions
#
# @param session [Fabricio::Authorization::Session]
# @param app_id [String]
# @param start_time [String] Timestamp of the start date
# @param end_time [String] Timestamp of the end date
# @param build [String] The version of the build. E.g. '4.0.1 (38)'
# @return [Fabricio::Networking::RequestModel]
def total_sessions_request_model(session, app_id, start_time, end_time, build)
path = growth_analytics_endpoint(session, app_id, 'total_sessions_scalar')
def total_sessions_request_model(app_id, start_time, end_time, build)
path = growth_analytics_endpoint(app_id, 'total_sessions_scalar')
params = {
'start' => start_time,
'end' => end_time,
Expand Down Expand Up @@ -357,21 +359,20 @@ def issue_session_endpoint(app_id, issue_id, session_id)

# Returns an API path to some growth analytic endpoint
#
# @param session [Fabricio::Authorization::Session]
# @param app_id [String]
# @param name [String]
# @return [String]
def growth_analytics_endpoint(session, app_id, name)
"#{FABRIC_API_PATH}#{org_app_endpoint(session, app_id)}/growth_analytics/#{name}.json"
def growth_analytics_endpoint(app_id, name)
"#{FABRIC_API_PATH}#{org_app_endpoint(app_id)}/growth_analytics/#{name}.json"
end

# Returns an API path to organization endpoint
#
# @param session [Fabricio::Authorization::Session]
# @param app_id [String]
# @return [String]
def org_app_endpoint(session, app_id)
"#{org_endpoint(session)}/#{app_endpoint(app_id)}"
def org_app_endpoint(app_id)
organization_id = @organization_id_provider.get(app_id)
"#{org_endpoint(organization_id)}/#{app_endpoint(app_id)}"
end

# Returns an API path to app endpoint
Expand All @@ -384,10 +385,10 @@ def app_endpoint(app_id)

# Returns an API path to app endpoint
#
# @param session [Fabricio::Authorization::Session]
# @param organization_id [String]
# @return [String]
def org_endpoint(session)
"#{FABRIC_ORGANIZATIONS_ENDPOINT}/#{session.organization_id}"
def org_endpoint(organization_id)
"#{FABRIC_ORGANIZATIONS_ENDPOINT}/#{organization_id}"
end

# Returns an API path to app endpoint
Expand Down
35 changes: 20 additions & 15 deletions lib/fabricio/networking/build_request_model_factory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,20 @@ class BuildRequestModelFactory
FABRIC_APPS_ENDPOINT = '/apps'
FABRIC_ORGANIZATIONS_ENDPOINT = '/organizations'

# Initializes a new BuildRequestModelFactory object
#
# @param organization_id_provider [Fabricio::Networking::OrganizationIdProvider]
# @return [Fabricio::Networking::BuildRequestModelFactory]
def initialize(organization_id_provider)
@organization_id_provider = organization_id_provider
end

# Returns a request model for obtaining the list of all builds for a specific app
#
# @param session [Fabricio::Authorization::Session]
# @param app_id [String]
# @return [Fabricio::Networking::RequestModel]
def all_builds_request_model(session, app_id)
path = "#{FABRIC_API_PATH}#{org_app_endpoint(session, app_id)}/beta_distribution/releases"
def all_builds_request_model(app_id)
path = "#{FABRIC_API_PATH}#{org_app_endpoint(app_id)}/beta_distribution/releases"
model = Fabricio::Networking::RequestModel.new do |config|
config.type = :GET
config.base_url = FABRIC_API_URL
Expand All @@ -28,13 +35,12 @@ def all_builds_request_model(session, app_id)

# Returns a request model for obtaining a specific build for a specific app
#
# @param session [Fabricio::Authorization::Session]
# @param app_id [String]
# @param version [String] The version number. E.g. '4.0.0'
# @param build_number [String] The build number. E.g. '48'
# @return [Fabricio::Networking::RequestModel]
def get_build_request_model(session, app_id, version, build_number)
path = "#{FABRIC_API_PATH}#{org_app_endpoint(session, app_id)}/beta_distribution/releases"
def get_build_request_model(app_id, version, build_number)
path = "#{FABRIC_API_PATH}#{org_app_endpoint(app_id)}/beta_distribution/releases"
params = {
'app[display_version]' => version,
'app[build_version]' => build_number
Expand All @@ -50,13 +56,12 @@ def get_build_request_model(session, app_id, version, build_number)

# Returns a request model for obtaining an array of top versions for a given app
#
# @param session [Fabricio::Authorization::Session]
# @param app_id [String]
# @param start_time [String] Timestamp of the start date
# @param end_time [String] Timestamp of the end date
# @return [Fabricio::Networking::RequestModel]
def top_versions_request_model(session, app_id, start_time, end_time)
path = "#{FABRIC_API_PATH}#{org_app_endpoint(session, app_id)}/growth_analytics/top_builds"
def top_versions_request_model(app_id, start_time, end_time)
path = "#{FABRIC_API_PATH}#{org_app_endpoint(app_id)}/growth_analytics/top_builds"
params = {
'app_id' => app_id,
'start' => start_time,
Expand All @@ -83,19 +88,19 @@ def app_endpoint(app_id)

# Returns an API path to app endpoint
#
# @param session [Fabricio::Authorization::Session]
# @param organization_id [String]
# @return [String]
def org_endpoint(session)
"#{FABRIC_ORGANIZATIONS_ENDPOINT}/#{session.organization_id}"
def org_endpoint(organization_id)
"#{FABRIC_ORGANIZATIONS_ENDPOINT}/#{organization_id}"
end

# Returns an API path to organization endpoint
#
# @param session [Fabricio::Authorization::Session]
# @param app_id [String]
# @return [String]
def org_app_endpoint(session, app_id)
"#{org_endpoint(session)}#{app_endpoint(app_id)}"
def org_app_endpoint(app_id)
organization_id = @organization_id_provider.get(app_id)
"#{org_endpoint(organization_id)}#{app_endpoint(app_id)}"
end

end
Expand Down
41 changes: 41 additions & 0 deletions lib/fabricio/networking/organization_id_provider.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
require 'fabricio/models/app'

module Fabricio
module Networking
# Maps app ids to organization ids
class OrganizationIdProvider

# Initializes a new OrganizationIdProvider object
#
# @param app_list_provider A lambda that takes no arguments and returns [Array<Fabricio::Model::App>]
# @return [Fabricio::Networking::OrganizationIdProvider]
def initialize(app_list_provider)
@app_list_provider = app_list_provider
@app_list = nil
end

# Returns the organization id of the given app id
#
# @param app_id [String] Application identifier
# @return [String] The organization identifier to which it belongs
def get(app_id)
app_list = obtain_app_list
candidate = app_list.select do |app|
app.id == app_id
end
raise "Could not find application with id #{app_id}" unless candidate.size > 0
candidate.first.organization_id
end

private def obtain_app_list()
app_list = @app_list
if !app_list
app_list = @app_list_provider.call
@app_list = app_list
end

app_list
end
end
end
end
Loading