Skip to content

Commit

Permalink
Merge branch 'master' into enable-recommended-products
Browse files Browse the repository at this point in the history
  • Loading branch information
tmuntaner committed Apr 23, 2018
2 parents 98d1c7a + 2141a42 commit 77bc4b1
Show file tree
Hide file tree
Showing 7 changed files with 173 additions and 124 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -31,6 +31,7 @@ coverage/
config/secrets.yml.key
config/secrets.yml.enc
config/rmt.local.yml
config/system_uuid

.env
.tags*
2 changes: 1 addition & 1 deletion app/models/hw_info.rb
Expand Up @@ -5,7 +5,7 @@ class HwInfo < ApplicationRecord
before_validation :make_invalid_uuid_nil

# We store UUID as a downcased string. Please take that in account in finders
validates :uuid, uuid_format: true, uniqueness: { allow_nil: true }
validates :uuid, uuid_format: true
validates :system, uniqueness: true, presence: true

before_save -> { uuid.try(:downcase!) }
Expand Down
5 changes: 5 additions & 0 deletions db/migrate/20180420145408_remove_hw_info_uuid_index.rb
@@ -0,0 +1,5 @@
class RemoveHwInfoUuidIndex < ActiveRecord::Migration[5.1]
def change
remove_index :hw_infos, :uuid
end
end
3 changes: 1 addition & 2 deletions db/schema.rb
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 20180416124217) do
ActiveRecord::Schema.define(version: 20180420145408) do

create_table "activations", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
t.bigint "service_id", null: false
Expand Down Expand Up @@ -41,7 +41,6 @@
t.datetime "updated_at", null: false
t.index ["hypervisor"], name: "index_hw_infos_on_hypervisor"
t.index ["system_id"], name: "index_hw_infos_on_system_id", unique: true
t.index ["uuid"], name: "index_hw_infos_on_uuid", unique: true
end

create_table "product_predecessors", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
Expand Down
24 changes: 19 additions & 5 deletions lib/suse/connect/api.rb
Expand Up @@ -7,30 +7,32 @@ module Connect
class Api

class InvalidCredentialsError < StandardError; end
CONNECT_API_URL = 'https://scc.suse.com/connect'.freeze
UUID_FILE_LOCATION = File.expand_path('../../../config/system_uuid', __dir__).freeze

def initialize(username, password)
@username = username
@password = password
end

def list_orders
make_paginated_request(:get, 'https://scc.suse.com/connect/organizations/orders')
make_paginated_request(:get, "#{CONNECT_API_URL}/organizations/orders")
end

def list_products
make_paginated_request(:get, 'https://scc.suse.com/connect/organizations/products')
make_paginated_request(:get, "#{CONNECT_API_URL}/organizations/products")
end

def list_products_unscoped
make_paginated_request(:get, 'https://scc.suse.com/connect/organizations/products/unscoped')
make_paginated_request(:get, "#{CONNECT_API_URL}/organizations/products/unscoped")
end

def list_repositories
make_paginated_request(:get, 'https://scc.suse.com/connect/organizations/repositories')
make_paginated_request(:get, "#{CONNECT_API_URL}/organizations/repositories")
end

def list_subscriptions
make_paginated_request(:get, 'https://scc.suse.com/connect/organizations/subscriptions')
make_paginated_request(:get, "#{CONNECT_API_URL}/organizations/subscriptions")
end

protected
Expand All @@ -47,6 +49,7 @@ def make_request(method, url, options)
options[:userpwd] = "#{@username}:#{@password}" unless options[:userpwd]
options[:method] = method
options[:accept_encoding] = 'gzip, deflate'
options[:headers] = { 'RMT' => system_uuid }

response = RMT::HttpRequest.new(url, options).run
raise InvalidCredentialsError if (response.code == 401)
Expand Down Expand Up @@ -79,6 +82,17 @@ def make_paginated_request(method, url, options = {})
@entities
end

private

def system_uuid
@system_uuid ||= if File.exist?(UUID_FILE_LOCATION)
File.read(UUID_FILE_LOCATION)
else
uuid = SecureRandom.uuid
File.write(UUID_FILE_LOCATION, uuid)
uuid
end
end
end
end
end
1 change: 0 additions & 1 deletion spec/models/hw_info_spec.rb
Expand Up @@ -11,7 +11,6 @@

it 'enforces uniqueness' do
expect(hw_info).to validate_uniqueness_of(:system)
expect(hw_info).to validate_uniqueness_of(:uuid)
end

describe '.uuid' do
Expand Down
261 changes: 146 additions & 115 deletions spec/suse/connect/api_spec.rb
Expand Up @@ -2,125 +2,156 @@
require 'webmock/rspec'

RSpec.describe SUSE::Connect::Api do
before do
stub_request(:GET, 'http://example.org/api_method')
.with(headers: expected_request_headers)
.to_return(
status: 200,
body: response_data.to_json,
headers: {}
)

stub_request(:GET, 'http://example.org/api_method?page=1')
.with(headers: expected_request_headers)
.to_return(
status: 200,
body: [ response_data ].to_json,
headers: {
'Link' => '<http://example.org/api_method?page=2>; rel="next"'
}
)

stub_request(:GET, 'http://example.org/api_method?page=2')
.with(headers: expected_request_headers)
.to_return(
status: 200,
body: [ response_data ].to_json,
headers: {}
)

stub_request(:get, 'https://scc.suse.com/connect/organizations/orders?page=1')
.with(headers: expected_request_headers)
.to_return(
status: 200,
body: [ { endpoint: 'organizations/orders' } ].to_json,
headers: {}
)

stub_request(:get, 'https://scc.suse.com/connect/organizations/products?page=1')
.with(headers: expected_request_headers)
.to_return(
status: 200,
body: [ { endpoint: 'organizations/products' } ].to_json,
headers: {}
)

stub_request(:get, 'https://scc.suse.com/connect/organizations/products/unscoped?page=1')
.with(headers: expected_request_headers)
.to_return(
status: 200,
body: [ { endpoint: 'organizations/products/unscoped' } ].to_json,
headers: {}
)

stub_request(:get, 'https://scc.suse.com/connect/organizations/repositories?page=1')
.with(headers: expected_request_headers)
.to_return(
status: 200,
body: [ { endpoint: 'organizations/repositories' } ].to_json,
headers: {}
)

stub_request(:get, 'https://scc.suse.com/connect/organizations/subscriptions?page=1')
.with(headers: expected_request_headers)
.to_return(
status: 200,
body: [ { endpoint: 'organizations/subscriptions' } ].to_json,
headers: {}
)
end

let(:url) { 'http://example.com' }
let(:username) { 'scc_user' }
let(:password) { 'scc_password' }
let(:api_client) { described_class.new(username, password) }
let(:expected_request_headers) do
{
'Authorization' => 'Basic ' + Base64.encode64("#{username}:#{password}").strip,
'User-Agent' => "RMT/#{RMT::VERSION}"
}
end
let(:response_data) { { foo: 'bar' } }

describe '#make_single_request' do
subject { api_client.send(:make_single_request, 'GET', 'http://example.org/api_method') }

it { is_expected.to eq(response_data) }
end

describe '#make_paginated_request' do
subject { api_client.send(:make_paginated_request, 'GET', 'http://example.org/api_method') }

it { is_expected.to eq([response_data, response_data]) }
end

describe '#list_orders' do
subject { api_client.list_orders }

it { is_expected.to eq([ { endpoint: 'organizations/orders' } ]) }
let(:uuid) { 'test-uuid' }

describe '#system_uuid' do
subject(:method_call) { api_client.send(:system_uuid) }

context 'when system_uuid file exists' do
it 'reads a file' do
allow(File).to receive(:exist?).with(described_class::UUID_FILE_LOCATION).and_return(true)
expect(File).not_to receive(:write).with(described_class::UUID_FILE_LOCATION, uuid)
expect(File).to receive(:read).with(described_class::UUID_FILE_LOCATION).and_return(uuid)
expect(method_call).to be(uuid)
end
end

context 'when system_uuid file does not exist' do
it 'creates a file' do
allow(File).to receive(:exist?).with(described_class::UUID_FILE_LOCATION).and_return(false)
allow(SecureRandom).to receive(:uuid).and_return(uuid)

expect(File).to receive(:write).with(described_class::UUID_FILE_LOCATION, uuid).exactly(1).times
expect(File).not_to receive(:read).with(described_class::UUID_FILE_LOCATION)
expect(method_call).to be(uuid)
end
end
end

describe '#list_products' do
subject { api_client.list_products }

it { is_expected.to eq([ { endpoint: 'organizations/products' } ]) }
end

describe '#list_products_unscoped' do
subject { api_client.list_products_unscoped }

it { is_expected.to eq([ { endpoint: 'organizations/products/unscoped' } ]) }
end

describe '#list_repositories' do
subject { api_client.list_repositories }

it { is_expected.to eq([ { endpoint: 'organizations/repositories' } ]) }
end

describe '#list_subscriptions' do
subject { api_client.list_subscriptions }

it { is_expected.to eq([ { endpoint: 'organizations/subscriptions' } ]) }
context 'api requests' do
before do
allow_any_instance_of(described_class).to receive(:system_uuid).and_return(uuid)

stub_request(:GET, 'http://example.org/api_method')
.with(headers: expected_request_headers)
.to_return(
status: 200,
body: response_data.to_json,
headers: {}
)

stub_request(:GET, 'http://example.org/api_method?page=1')
.with(headers: expected_request_headers)
.to_return(
status: 200,
body: [ response_data ].to_json,
headers: {
'Link' => '<http://example.org/api_method?page=2>; rel="next"'
}
)

stub_request(:GET, 'http://example.org/api_method?page=2')
.with(headers: expected_request_headers)
.to_return(
status: 200,
body: [ response_data ].to_json,
headers: {}
)

stub_request(:get, 'https://scc.suse.com/connect/organizations/orders?page=1')
.with(headers: expected_request_headers)
.to_return(
status: 200,
body: [ { endpoint: 'organizations/orders' } ].to_json,
headers: {}
)

stub_request(:get, 'https://scc.suse.com/connect/organizations/products?page=1')
.with(headers: expected_request_headers)
.to_return(
status: 200,
body: [ { endpoint: 'organizations/products' } ].to_json,
headers: {}
)

stub_request(:get, 'https://scc.suse.com/connect/organizations/products/unscoped?page=1')
.with(headers: expected_request_headers)
.to_return(
status: 200,
body: [ { endpoint: 'organizations/products/unscoped' } ].to_json,
headers: {}
)

stub_request(:get, 'https://scc.suse.com/connect/organizations/repositories?page=1')
.with(headers: expected_request_headers)
.to_return(
status: 200,
body: [ { endpoint: 'organizations/repositories' } ].to_json,
headers: {}
)

stub_request(:get, 'https://scc.suse.com/connect/organizations/subscriptions?page=1')
.with(headers: expected_request_headers)
.to_return(
status: 200,
body: [ { endpoint: 'organizations/subscriptions' } ].to_json,
headers: {}
)
end

let(:url) { 'http://example.com' }
let(:expected_request_headers) do
{
'Authorization' => 'Basic ' + Base64.encode64("#{username}:#{password}").strip,
'User-Agent' => "RMT/#{RMT::VERSION}",
'RMT' => uuid
}
end
let(:response_data) { { foo: 'bar' } }

describe '#make_single_request' do
subject { api_client.send(:make_single_request, 'GET', 'http://example.org/api_method') }

it { is_expected.to eq(response_data) }
end

describe '#make_paginated_request' do
subject { api_client.send(:make_paginated_request, 'GET', 'http://example.org/api_method') }

it { is_expected.to eq([response_data, response_data]) }
end

describe '#list_orders' do
subject { api_client.list_orders }

it { is_expected.to eq([ { endpoint: 'organizations/orders' } ]) }
end

describe '#list_products' do
subject { api_client.list_products }

it { is_expected.to eq([ { endpoint: 'organizations/products' } ]) }
end

describe '#list_products_unscoped' do
subject { api_client.list_products_unscoped }

it { is_expected.to eq([ { endpoint: 'organizations/products/unscoped' } ]) }
end

describe '#list_repositories' do
subject { api_client.list_repositories }

it { is_expected.to eq([ { endpoint: 'organizations/repositories' } ]) }
end

describe '#list_subscriptions' do
subject { api_client.list_subscriptions }

it { is_expected.to eq([ { endpoint: 'organizations/subscriptions' } ]) }
end
end
end

0 comments on commit 77bc4b1

Please sign in to comment.