Skip to content

Commit

Permalink
Merge pull request #72 from SUSE/sles15_product_tree_support
Browse files Browse the repository at this point in the history
SLE15 product tree support -- 4702
  • Loading branch information
ikapelyukhin committed Jan 23, 2018
2 parents be789dd + 629adfd commit 0ad46d8
Show file tree
Hide file tree
Showing 12 changed files with 509 additions and 43 deletions.
20 changes: 16 additions & 4 deletions app/models/product.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,21 @@ class Product < ApplicationRecord
class_name: 'ProductsExtensionsAssociation',
foreign_key: :product_id

has_many :extensions,
through: :extension_products_associations,
source: :extension
has_many :extensions, -> { distinct },
through: :extension_products_associations,
source: :extension do
def for_root_product(root_product)
where('products_extensions.root_product_id = %s', root_product.id)
end
end

has_many :mirrored_extensions, -> { mirrored },
through: :extension_products_associations,
source: :extension
source: :extension do
def for_root_product(root_product)
where('products_extensions.root_product_id = %s', root_product.id)
end
end

has_and_belongs_to_many :predecessors, class_name: 'Product', join_table: :product_predecessors,
association_foreign_key: :predecessor_id
Expand Down Expand Up @@ -69,6 +77,10 @@ def change_repositories_mirroring!(conditions, mirroring_enabled)
repositories.where(conditions).update_all(mirroring_enabled: mirroring_enabled)
end

def recommended_for?(root_product)
product_extensions_associations.where(recommended: true).where(root_product: root_product).present?
end

def service
Service.find_or_create_by(product_id: id)
end
Expand Down
4 changes: 4 additions & 0 deletions app/models/products_extensions_association.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ class ProductsExtensionsAssociation < ApplicationRecord

self.table_name = 'products_extensions'
belongs_to :product, class_name: 'Product', foreign_key: :product_id
belongs_to :root_product, class_name: 'Product', foreign_key: :root_product_id
belongs_to :extension, class_name: 'Product', foreign_key: :extension_id

validates :product_id, :extension_id, :root_product_id, presence: true
validates :product_id, uniqueness: { scope: %i[extension_id root_product_id] }

end
20 changes: 17 additions & 3 deletions app/serializers/v3/product_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ def repositories
end

attributes :id, :name, :identifier, :former_identifier, :version, :release_type, :arch,
:friendly_name, :product_class, :cpe, :free, :description, :eula_url, :repositories, :product_type, :extensions
:friendly_name, :product_class, :cpe, :free, :description, :eula_url, :repositories, :product_type,
:extensions, :recommended, :available

def extensions
object.mirrored_extensions.map do |extension|
::V3::ProductSerializer.new(extension, base_url: base_url).attributes
object.extensions.for_root_product(root_product).map do |extension|
::V3::ProductSerializer.new(extension, base_url: base_url, root_product: root_product).attributes
end
end

Expand All @@ -37,4 +38,17 @@ def free
true
end

def root_product
@instance_options[:root_product] ||= object
end

def recommended
object.recommended_for? root_product
end

# This attribute is added by SMT as well, indicating whether the product is mirrored or not
def available
object.mirror?
end

end
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
class AddRootAndRecommendedToProductsExtensions < ActiveRecord::Migration[5.1]

def change
add_column :products_extensions, :recommended, :boolean
add_column :products_extensions, :root_product_id, :integer
add_index :products_extensions, %i[product_id extension_id root_product_id],
unique: true, name: 'index_products_extensions_on_product_extension_root'

reversible do |dir|
dir.up do
ProductsExtensionsAssociation.find_each.each do |pa|
base = pa.product
pa.root_product = base.bases.present? ? base.bases.first : base
pa.recommended = false
pa.save!
end
change_column_null(:products_extensions, :root_product_id, false)
end
end
end

end
3 changes: 3 additions & 0 deletions db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,10 @@
create_table "products_extensions", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
t.bigint "product_id", null: false
t.bigint "extension_id", null: false
t.boolean "recommended"
t.integer "root_product_id", null: false
t.index ["extension_id"], name: "index_products_extensions_on_extension_id"
t.index ["product_id", "extension_id", "root_product_id"], name: "index_products_extensions_on_product_extension_root", unique: true
t.index ["product_id"], name: "index_products_extensions_on_product_id"
end

Expand Down
51 changes: 28 additions & 23 deletions lib/rmt/scc.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,7 @@ def sync
@logger.info('Updating products')
data = scc_api_client.list_products
data.each do |item|
@logger.debug("Adding product #{item[:identifier]}/#{item[:version]}#{(item[:arch]) ? '/' + item[:arch] : ''}")
product = create_product(item)
create_service(item, product)
create_product(item) if (item[:product_type] == 'base')
end

@logger.info('Updating repositories')
Expand Down Expand Up @@ -80,8 +78,7 @@ def import(path)
data = JSON.parse(File.read(File.join(path, 'organizations_products_scoped.json')), symbolize_names: true)
data.each do |item|
@logger.debug("Adding product #{item[:identifier]}/#{item[:version]}#{(item[:arch]) ? '/' + item[:arch] : ''}")
product = create_product(item)
create_service(item, product)
create_product(item)
end

@logger.info('Updating repositories')
Expand All @@ -101,17 +98,8 @@ def import(path)

protected

def create_product(item)
extensions = []

item[:extensions].each do |ext_item|
extension = Product.find_or_create_by(id: ext_item[:id])
extension.attributes = ext_item.select { |k, _| extension.attributes.keys.member?(k.to_s) }
extension.save!

create_service(ext_item, extension)
extensions << extension
end
def create_product(item, root_product_id = nil, base_product = nil, recommended = false)
@logger.debug("Adding product #{item[:identifier]}/#{item[:version]}#{(item[:arch]) ? '/' + item[:arch] : ''}")

product = Product.find_or_create_by(id: item[:id])
product.attributes = item.select { |k, _| product.attributes.keys.member?(k.to_s) }
Expand All @@ -122,14 +110,23 @@ def create_product(item)
ProductPredecessorAssociation.create(product_id: product.id, predecessor_id: predecessor_id)
end

extensions.each do |extension|
association = ProductsExtensionsAssociation.new
association.product_id = product.id
association.extension_id = extension.id
association.save!
create_service(item, product)

if root_product_id
ProductsExtensionsAssociation.create(
product_id: base_product,
extension_id: product.id,
root_product_id: root_product_id,
recommended: recommended
)
else
root_product_id = product.id
ProductsExtensionsAssociation.where(root_product_id: root_product_id).destroy_all
end

product
item[:extensions].each do |ext_item|
create_product(ext_item, root_product_id, product.id, ext_item[:recommended])
end
end

def create_service(item, product)
Expand All @@ -143,7 +140,15 @@ def create_service(item, product)
def update_auth_token(item)
uri = URI(item[:url])
auth_token = uri.query
Repository.by_id(item[:id]).update! auth_token: auth_token

# Sometimes the extension is available, but a base product is not, e.g.:
# sle-hae/11.3/s390x available without base product for s390x
# In this case no repository data was added in create_product -- can't update those repos.
begin
Repository.find_by!(scc_id: item[:id]).update! auth_token: auth_token
rescue ActiveRecord::RecordNotFound
@logger.debug("Repository #{item[:id]} is not available")
end
end

def create_subscription(item)
Expand Down
14 changes: 9 additions & 5 deletions spec/factories/products.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,17 @@
transient do
base_products []
predecessor nil
root_product nil
recommended false
end

after :create do |product, evaluator|
evaluator.base_products.each do |base_product|
product.product_extensions_associations << ProductsExtensionsAssociation.create(product: base_product)
product.product_extensions_associations << ProductsExtensionsAssociation.create(
product: base_product,
root_product: evaluator.root_product || base_product,
recommended: evaluator.recommended
)
end

product.predecessors << evaluator.predecessor if evaluator.predecessor
Expand All @@ -38,17 +44,15 @@
trait :with_extensions do
after :create do |product, _evaluator|
5.times do
extension = create :product, :extension
product.extensions << extension
create(:product, :extension, base_products: [product])
end
end
end

trait :with_mirrored_extensions do
after :create do |product, _evaluator|
5.times do
extension = create :product, :extension, :with_mirrored_repositories
product.extensions << extension
create(:product, :extension, :with_mirrored_repositories, base_products: [product])
end
end
end
Expand Down

0 comments on commit 0ad46d8

Please sign in to comment.