diff --git a/.rubocop.yml b/.rubocop.yml index 1828876dd..b79b85ffa 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -11,6 +11,9 @@ Style/ClassAndModuleChildren: Metrics/LineLength: Max: 100 + Exclude: + - spec/**/*.rb + - examples/**/*.rb Metrics/MethodLength: Max: 25 @@ -18,3 +21,11 @@ Metrics/MethodLength: Metrics/AbcSize: Max: 25 +Metrics/BlockLength: + Max: 100 + Exclude: + - spec/pdc/**/*.rb + - examples/**/*.rb + +Naming/PredicateName: + Enabled: false diff --git a/lib/pdc/base.rb b/lib/pdc/base.rb index 1988c9444..d11a56b63 100644 --- a/lib/pdc/base.rb +++ b/lib/pdc/base.rb @@ -7,6 +7,7 @@ class Base include PDC::Resource::Attributes include PDC::Resource::Scopes include PDC::Resource::RestApi + include PDC::Resource::Associations scope :page, ->(value) { where(page: value) } scope :page_size, ->(value) { where(page_size: value) } diff --git a/lib/pdc/resource.rb b/lib/pdc/resource.rb index df80da235..b14847864 100644 --- a/lib/pdc/resource.rb +++ b/lib/pdc/resource.rb @@ -10,3 +10,4 @@ require 'pdc/resource/relation' require 'pdc/resource/rest_api' require 'pdc/resource/wip' +require 'pdc/resource/associations' diff --git a/lib/pdc/resource/associations.rb b/lib/pdc/resource/associations.rb new file mode 100644 index 000000000..d365593cc --- /dev/null +++ b/lib/pdc/resource/associations.rb @@ -0,0 +1,44 @@ +require 'pdc/resource/associations/has_many' +require 'pdc/resource/associations/association' +require 'pdc/resource/associations/builder' +require 'pdc/resource/associations/has_one' + +module PDC::Resource + module Associations + extend ActiveSupport::Concern + + included do + class_attribute :associations + self.associations = {}.freeze + end + + module ClassMethods + def has_many(name, options = {}) + create_association(name, HasMany, options) + + define_method "#{name.to_s.singularize}_ids=" do |ids| + attributes[name] = [] + ids.reject(&:blank?).each { |id| association(name).build(id: id) } + end + + define_method "#{name.to_s.singularize}_ids" do + association(name).map(&:id) + end + end + + def has_one(name, options = {}) + create_association(name, HasOne, options) + + define_method "build_#{name}" do |attributes = nil| + association(name).build(attributes) + end + end + + private + + def create_association(name, type, options) + self.associations = associations.merge(name => Builder.new(self, name, type, options)) + end + end + end +end diff --git a/lib/pdc/resource/associations/association.rb b/lib/pdc/resource/associations/association.rb new file mode 100644 index 000000000..ce7a5de4e --- /dev/null +++ b/lib/pdc/resource/associations/association.rb @@ -0,0 +1,25 @@ +require 'pdc/resource/relation' + +module PDC::Resource + module Associations + class Association < Relation + attr_reader :parent, :name + + def initialize(klass, parent, name, options = {}) + super(klass, options) + @parent = parent + @name = name + end + + def load + find_one! # Override for plural associations that return an association object + end + + private + + def foreign_key + (@options[:foreign_key] || "#{parent.class.model_name.element}_id").to_sym + end + end + end +end diff --git a/lib/pdc/resource/associations/builder.rb b/lib/pdc/resource/associations/builder.rb new file mode 100644 index 000000000..e4a91d9f8 --- /dev/null +++ b/lib/pdc/resource/associations/builder.rb @@ -0,0 +1,44 @@ +require 'active_support/dependencies' + +module PDC::Resource + module Associations + class Builder + def initialize(parent_class, name, type, options = {}) + @parent_class = parent_class + @name = name + @type = type + @options = options + end + + def build(parent) + @type.new(klass, parent, @name, @options) + end + + def klass + @klass ||= custom_class || compute_class(@name) + end + + private + + def custom_class + @options[:class_name].constantize if @options[:class_name] + end + + # https://github.com/rails/rails/blob/70ac072976c8cc6f013f0df3777e54ccae3f4f8c/activerecord/lib/active_record/inheritance.rb#L132-L150 + def compute_class(type_name) + parent_name = @parent_class.to_s + type_name = type_name.to_s.classify + + candidates = [] + parent_name.scan(/::|$/) { candidates.unshift "#{$`}::#{type_name}" } + candidates << type_name + + candidates.each do |candidate| + constant = ActiveSupport::Dependencies.safe_constantize(candidate) + return constant if candidate == constant.to_s + end + raise NameError.new("uninitialized constant #{candidates.first}", candidates.first) + end + end + end +end diff --git a/lib/pdc/resource/associations/has_many.rb b/lib/pdc/resource/associations/has_many.rb new file mode 100644 index 000000000..88855a4c5 --- /dev/null +++ b/lib/pdc/resource/associations/has_many.rb @@ -0,0 +1,23 @@ +require 'pdc/resource/associations/association' + +module PDC::Resource + module Associations + class HasMany < Association + def initialize(*args) + super + @options.reverse_merge!(uri: "#{parent_path}/:#{foreign_key}/#{@name}/(:#{primary_key})") + @params[foreign_key] = parent.id + end + + def load + self + end + + private + + def parent_path + parent.class.model_name.element.pluralize + end + end + end +end diff --git a/lib/pdc/resource/associations/has_one.rb b/lib/pdc/resource/associations/has_one.rb new file mode 100644 index 000000000..64ba9cd03 --- /dev/null +++ b/lib/pdc/resource/associations/has_one.rb @@ -0,0 +1,29 @@ +module PDC::Resource + module Associations + class HasOne < Association + def initialize(*args) + super + @options.reverse_merge!(uri: "#{parent.class.model_name.plural}/:#{foreign_key}/#{@name}") + collect_params + end + + def collect_params + p_mapping = parent.class.mapping + s_foreign_key = foreign_key.to_s + if s_foreign_key.include? '/' + s_foreign_key.split('/').each do |fk| + p_mapping_value = p_mapping[fk.to_sym] + @params[fk] = + if p_mapping && p_mapping_value + parent.attributes[p_mapping_value.to_sym] + else + parent.attributes[fk] + end + end + else + @params[foreign_key] = parent.id + end + end + end + end +end diff --git a/lib/pdc/resource/attributes.rb b/lib/pdc/resource/attributes.rb index 964c39b51..9c28fffc1 100644 --- a/lib/pdc/resource/attributes.rb +++ b/lib/pdc/resource/attributes.rb @@ -90,7 +90,8 @@ def use_setters(attributes) end def method_missing(name, *args, &block) - if attribute?(name) then attribute(name) + if association?(name) then association(name).load + elsif attribute?(name) then attribute(name) elsif predicate?(name) then predicate(name) elsif setter?(name) then set_attribute(name, args.first) else super @@ -98,7 +99,15 @@ def method_missing(name, *args, &block) end def respond_to_missing?(name, include_private = false) - attribute?(name) || predicate?(name) || setter?(name) || super + association?(name) || attribute?(name) || predicate?(name) || setter?(name) || super + end + + def association?(name) + associations.key?(name) + end + + def association(name) + associations[name].build(self) end def attribute?(name) diff --git a/lib/pdc/resource/identity.rb b/lib/pdc/resource/identity.rb index e2188d6ee..edd8c2244 100644 --- a/lib/pdc/resource/identity.rb +++ b/lib/pdc/resource/identity.rb @@ -24,6 +24,14 @@ def resource_path @resource_path ||= model_name.collection.sub(%r{^pdc\/}, '').tr('_', '-') end + def mapping + @mapping || {} + end + + def mapping=(mapping = {}) + @mapping = mapping + end + private def default_uri diff --git a/lib/pdc/resource/relation.rb b/lib/pdc/resource/relation.rb index 75db78d68..88b508ad3 100644 --- a/lib/pdc/resource/relation.rb +++ b/lib/pdc/resource/relation.rb @@ -12,6 +12,7 @@ class Relation attr_reader :klass attr_writer :params + delegate :to_ary, :[], :any?, :empty?, :last, :size, :metadata, to: :contents! alias all to_a diff --git a/spec/fixtures/vcr/array_like_behavior.yml b/spec/fixtures/vcr/array_like_behavior.yml new file mode 100644 index 000000000..1276267c6 --- /dev/null +++ b/spec/fixtures/vcr/array_like_behavior.yml @@ -0,0 +1,141 @@ +--- +http_interactions: +- request: + method: get + uri: https://pdc.host.dev.eng.pek2.redhat.com/rest_api/v1/releases/?product_version=ceph-1 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Faraday v0.9.2 + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Date: + - Wed, 03 Jan 2018 08:14:47 GMT + Server: + - Apache/2.4.6 (Red Hat Enterprise Linux) OpenSSL/1.0.1e-fips mod_auth_kerb/5.4 + mod_wsgi/3.4 Python/2.7.5 + Expires: + - Wed, 03 Jan 2018 08:15:17 GMT + Vary: + - Accept,Cookie + Last-Modified: + - Tue, 26 Sep 2017 08:04:24 GMT + Allow: + - GET, POST, PUT, PATCH, HEAD, OPTIONS + Cache-Control: + - max-age=30 + X-Frame-Options: + - SAMEORIGIN + Transfer-Encoding: + - chunked + Content-Type: + - application/json + body: + encoding: UTF-8 + string: '{"count":2,"next":null,"previous":null,"results":[{"release_id":"ceph-1.3@rhel-7","short":"ceph","version":"1.3","name":"Red + Hat Ceph Storage","base_product":"rhel-7","active":true,"product_version":"ceph-1","release_type":"ga","compose_set":[],"integrated_with":null,"sigkey":null,"allow_buildroot_push":true,"allowed_debuginfo_services":[],"allowed_push_targets":[],"bugzilla":null,"dist_git":null,"brew":null,"product_pages":null,"errata":null},{"release_id":"ceph-1.3-updates@rhel-7","short":"ceph","version":"1.3","name":"Red + Hat Ceph Storage","base_product":"rhel-7","active":true,"product_version":"ceph-1","release_type":"updates","compose_set":[],"integrated_with":null,"sigkey":null,"allow_buildroot_push":false,"allowed_debuginfo_services":[],"allowed_push_targets":[],"bugzilla":null,"dist_git":null,"brew":null,"product_pages":null,"errata":null}]}' + http_version: + recorded_at: Wed, 03 Jan 2018 08:14:48 GMT +- request: + method: get + uri: https://pdc.host.dev.eng.pek2.redhat.com/rest_api/v1/releases/?product_version=ceph-1 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Faraday v0.9.2 + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Date: + - Wed, 03 Jan 2018 08:14:48 GMT + Server: + - Apache/2.4.6 (Red Hat Enterprise Linux) OpenSSL/1.0.1e-fips mod_auth_kerb/5.4 + mod_wsgi/3.4 Python/2.7.5 + Expires: + - Wed, 03 Jan 2018 08:15:18 GMT + Vary: + - Accept,Cookie + Last-Modified: + - Tue, 26 Sep 2017 08:04:24 GMT + Allow: + - GET, POST, PUT, PATCH, HEAD, OPTIONS + Cache-Control: + - max-age=30 + X-Frame-Options: + - SAMEORIGIN + Transfer-Encoding: + - chunked + Content-Type: + - application/json + body: + encoding: UTF-8 + string: '{"count":2,"next":null,"previous":null,"results":[{"release_id":"ceph-1.3@rhel-7","short":"ceph","version":"1.3","name":"Red + Hat Ceph Storage","base_product":"rhel-7","active":true,"product_version":"ceph-1","release_type":"ga","compose_set":[],"integrated_with":null,"sigkey":null,"allow_buildroot_push":true,"allowed_debuginfo_services":[],"allowed_push_targets":[],"bugzilla":null,"dist_git":null,"brew":null,"product_pages":null,"errata":null},{"release_id":"ceph-1.3-updates@rhel-7","short":"ceph","version":"1.3","name":"Red + Hat Ceph Storage","base_product":"rhel-7","active":true,"product_version":"ceph-1","release_type":"updates","compose_set":[],"integrated_with":null,"sigkey":null,"allow_buildroot_push":false,"allowed_debuginfo_services":[],"allowed_push_targets":[],"bugzilla":null,"dist_git":null,"brew":null,"product_pages":null,"errata":null}]}' + http_version: + recorded_at: Wed, 03 Jan 2018 08:14:48 GMT +- request: + method: get + uri: https://pdc.host.dev.eng.pek2.redhat.com/rest_api/v1/releases/?product_version=ceph-1 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Faraday v0.9.2 + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Date: + - Wed, 03 Jan 2018 08:14:48 GMT + Server: + - Apache/2.4.6 (Red Hat Enterprise Linux) OpenSSL/1.0.1e-fips mod_auth_kerb/5.4 + mod_wsgi/3.4 Python/2.7.5 + Expires: + - Wed, 03 Jan 2018 08:15:18 GMT + Vary: + - Accept,Cookie + Last-Modified: + - Tue, 26 Sep 2017 08:04:24 GMT + Allow: + - GET, POST, PUT, PATCH, HEAD, OPTIONS + Cache-Control: + - max-age=30 + X-Frame-Options: + - SAMEORIGIN + Transfer-Encoding: + - chunked + Content-Type: + - application/json + body: + encoding: UTF-8 + string: '{"count":2,"next":null,"previous":null,"results":[{"release_id":"ceph-1.3@rhel-7","short":"ceph","version":"1.3","name":"Red + Hat Ceph Storage","base_product":"rhel-7","active":true,"product_version":"ceph-1","release_type":"ga","compose_set":[],"integrated_with":null,"sigkey":null,"allow_buildroot_push":true,"allowed_debuginfo_services":[],"allowed_push_targets":[],"bugzilla":null,"dist_git":null,"brew":null,"product_pages":null,"errata":null},{"release_id":"ceph-1.3-updates@rhel-7","short":"ceph","version":"1.3","name":"Red + Hat Ceph Storage","base_product":"rhel-7","active":true,"product_version":"ceph-1","release_type":"updates","compose_set":[],"integrated_with":null,"sigkey":null,"allow_buildroot_push":false,"allowed_debuginfo_services":[],"allowed_push_targets":[],"bugzilla":null,"dist_git":null,"brew":null,"product_pages":null,"errata":null}]}' + http_version: + recorded_at: Wed, 03 Jan 2018 08:14:48 GMT +recorded_with: VCR 3.0.3 diff --git a/spec/fixtures/vcr/find_on_has_many_association.yml b/spec/fixtures/vcr/find_on_has_many_association.yml new file mode 100644 index 000000000..d7255cf05 --- /dev/null +++ b/spec/fixtures/vcr/find_on_has_many_association.yml @@ -0,0 +1,48 @@ +--- +http_interactions: +- request: + method: get + uri: https://pdc.host.dev.eng.pek2.redhat.com/rest_api/v1/releases/?product_version=dp-1&release_id=dp-1.0 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Faraday v0.9.2 + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Date: + - Wed, 03 Jan 2018 08:14:47 GMT + Server: + - Apache/2.4.6 (Red Hat Enterprise Linux) OpenSSL/1.0.1e-fips mod_auth_kerb/5.4 + mod_wsgi/3.4 Python/2.7.5 + Expires: + - Wed, 03 Jan 2018 08:15:17 GMT + Vary: + - Accept,Cookie + Last-Modified: + - Tue, 26 Sep 2017 08:04:24 GMT + Allow: + - GET, POST, PUT, PATCH, HEAD, OPTIONS + Cache-Control: + - max-age=30 + X-Frame-Options: + - SAMEORIGIN + Transfer-Encoding: + - chunked + Content-Type: + - application/json + body: + encoding: UTF-8 + string: '{"count":1,"next":null,"previous":null,"results":[{"release_id":"dp-1.0","short":"dp","version":"1.0","name":"Dummy + Product","base_product":null,"active":true,"product_version":"dp-1","release_type":"ga","compose_set":[],"integrated_with":null,"sigkey":null,"allow_buildroot_push":false,"allowed_debuginfo_services":[],"allowed_push_targets":[],"bugzilla":null,"dist_git":null,"brew":null,"product_pages":null,"errata":null}]}' + http_version: + recorded_at: Wed, 03 Jan 2018 08:14:47 GMT +recorded_with: VCR 3.0.3 diff --git a/spec/fixtures/vcr/has_many_association.yml b/spec/fixtures/vcr/has_many_association.yml new file mode 100644 index 000000000..3d3a0a669 --- /dev/null +++ b/spec/fixtures/vcr/has_many_association.yml @@ -0,0 +1,48 @@ +--- +http_interactions: +- request: + method: get + uri: https://pdc.host.dev.eng.pek2.redhat.com/rest_api/v1/releases/?product_version=dp-1 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Faraday v0.9.2 + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Date: + - Wed, 03 Jan 2018 08:14:47 GMT + Server: + - Apache/2.4.6 (Red Hat Enterprise Linux) OpenSSL/1.0.1e-fips mod_auth_kerb/5.4 + mod_wsgi/3.4 Python/2.7.5 + Expires: + - Wed, 03 Jan 2018 08:15:17 GMT + Vary: + - Accept,Cookie + Last-Modified: + - Tue, 26 Sep 2017 08:04:24 GMT + Allow: + - GET, POST, PUT, PATCH, HEAD, OPTIONS + Cache-Control: + - max-age=30 + X-Frame-Options: + - SAMEORIGIN + Transfer-Encoding: + - chunked + Content-Type: + - application/json + body: + encoding: UTF-8 + string: '{"count":1,"next":null,"previous":null,"results":[{"release_id":"dp-1.0","short":"dp","version":"1.0","name":"Dummy + Product","base_product":null,"active":true,"product_version":"dp-1","release_type":"ga","compose_set":[],"integrated_with":null,"sigkey":null,"allow_buildroot_push":false,"allowed_debuginfo_services":[],"allowed_push_targets":[],"bugzilla":null,"dist_git":null,"brew":null,"product_pages":null,"errata":null}]}' + http_version: + recorded_at: Wed, 03 Jan 2018 08:14:47 GMT +recorded_with: VCR 3.0.3 diff --git a/spec/fixtures/vcr/has_one_assocation.yml b/spec/fixtures/vcr/has_one_assocation.yml new file mode 100644 index 000000000..d74e21b02 --- /dev/null +++ b/spec/fixtures/vcr/has_one_assocation.yml @@ -0,0 +1,47 @@ +--- +http_interactions: +- request: + method: get + uri: https://pdc.host.dev.eng.pek2.redhat.com/rest_api/v1/variant-cpes/?release=dp-1.0&variant_uid=Client + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Faraday v0.9.2 + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Date: + - Thu, 11 Jan 2018 13:34:52 GMT + Server: + - Apache/2.4.6 (Red Hat Enterprise Linux) OpenSSL/1.0.1e-fips mod_auth_kerb/5.4 + mod_wsgi/3.4 Python/2.7.5 + Expires: + - Thu, 11 Jan 2018 13:35:24 GMT + Vary: + - Accept,Cookie + Last-Modified: + - Thu, 11 Jan 2018 13:34:54 GMT + Allow: + - GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS + Cache-Control: + - max-age=30 + X-Frame-Options: + - SAMEORIGIN + Transfer-Encoding: + - chunked + Content-Type: + - application/json + body: + encoding: UTF-8 + string: '{"count":1,"next":null,"previous":null,"results":[{"id":5,"release":"dp-1.0","variant_uid":"Client","cpe":"cpe:/asdff"}]}' + http_version: + recorded_at: Thu, 11 Jan 2018 13:34:54 GMT +recorded_with: VCR 3.0.3 diff --git a/spec/fixtures/vcr/scopes_on_associations.yml b/spec/fixtures/vcr/scopes_on_associations.yml new file mode 100644 index 000000000..d7255cf05 --- /dev/null +++ b/spec/fixtures/vcr/scopes_on_associations.yml @@ -0,0 +1,48 @@ +--- +http_interactions: +- request: + method: get + uri: https://pdc.host.dev.eng.pek2.redhat.com/rest_api/v1/releases/?product_version=dp-1&release_id=dp-1.0 + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Faraday v0.9.2 + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + response: + status: + code: 200 + message: OK + headers: + Date: + - Wed, 03 Jan 2018 08:14:47 GMT + Server: + - Apache/2.4.6 (Red Hat Enterprise Linux) OpenSSL/1.0.1e-fips mod_auth_kerb/5.4 + mod_wsgi/3.4 Python/2.7.5 + Expires: + - Wed, 03 Jan 2018 08:15:17 GMT + Vary: + - Accept,Cookie + Last-Modified: + - Tue, 26 Sep 2017 08:04:24 GMT + Allow: + - GET, POST, PUT, PATCH, HEAD, OPTIONS + Cache-Control: + - max-age=30 + X-Frame-Options: + - SAMEORIGIN + Transfer-Encoding: + - chunked + Content-Type: + - application/json + body: + encoding: UTF-8 + string: '{"count":1,"next":null,"previous":null,"results":[{"release_id":"dp-1.0","short":"dp","version":"1.0","name":"Dummy + Product","base_product":null,"active":true,"product_version":"dp-1","release_type":"ga","compose_set":[],"integrated_with":null,"sigkey":null,"allow_buildroot_push":false,"allowed_debuginfo_services":[],"allowed_push_targets":[],"bugzilla":null,"dist_git":null,"brew":null,"product_pages":null,"errata":null}]}' + http_version: + recorded_at: Wed, 03 Jan 2018 08:14:47 GMT +recorded_with: VCR 3.0.3 diff --git a/spec/pdc/resource/associations_spec.rb b/spec/pdc/resource/associations_spec.rb new file mode 100644 index 000000000..ae1dcf220 --- /dev/null +++ b/spec/pdc/resource/associations_spec.rb @@ -0,0 +1,114 @@ +require 'spec_helper' + +describe PDC::Resource::Associations do + subject { PDC::Resource::Associations } + + before do + VCR.insert_cassette fixture_name + end + + after do + VCR.eject_cassette + end + + let(:site) { 'https://pdc.host.dev.eng.pek2.redhat.com' } + + let(:releases_url1) do + URI.join(site, PDC.config.api_root, 'v1/releases/?product_version=ceph-1&release_id=ceph-1.3@rhel-7') + end + + let(:releases_url2) do + URI.join(site, PDC.config.api_root, 'v1/releases/?product_version=ceph-1') + end + + describe 'association' do + it 'association independence' do + assert_kind_of subject::HasMany, ProductVersion.new.releases + assert_raises NoMethodError do + ProductVersion.new.non_existings + end + end + end + + describe 'has many' do + it 'has many association' do + releases = ProductVersion.new(product_version_id: 'dp-1').releases.to_a + assert_equal 'dp-1.0', releases.first.release_id + end + end + + describe 'find has many' do + it 'find on has many association' do + release = ProductVersion.new(product_version_id: 'dp-1').releases.find('dp-1.0') + assert_equal 'dp-1.0', release.release_id + end + end + + describe 'scope' do + it 'scopes on associations' do + releases = ProductVersion.new(product_version_id: 'dp-1').releases.where(release_id: 'dp-1.0').to_a + assert_equal 'dp-1.0', releases.first.release_id + end + end + + describe 'array like' do + it 'array like behavior' do + product_version = ProductVersion.new(product_version_id: 'ceph-1') + assert_equal %w[ceph-1.3@rhel-7 ceph-1.3-updates@rhel-7], product_version.releases[0..1].map(&:release_id) + assert_equal 'ceph-1.3@rhel-7', product_version.releases.first.release_id + assert_equal 'ceph-1.3-updates@rhel-7', product_version.releases.last.release_id + end + end + + describe 'cache' do + it 'cached result for associations' do + endpoint1 = stub_request(:get, releases_url1)\ + .to_return_json('data': + [{ 'release_id': 'ceph-1.3@rhel-7', 'short': 'ceph', + 'version': '1.3', 'name': 'Red Hat Ceph Storage', + 'base_product': 'rhel-7', 'active': true, + 'product_version': 'ceph-1', 'release_type': 'ga', + 'compose_set': [], 'integrated_with': 'null', 'sigkey': 'null', + 'allow_buildroot_push': true, 'allowed_debuginfo_services': [], + 'allowed_push_targets': [], 'bugzilla': 'null', + 'dist_git': 'null', 'brew': 'null', 'product_pages': 'null', + 'errata': 'null' }]) + endpoint2 = stub_request(:get, releases_url2)\ + .to_return_json('data': + [{ 'release_id': 'ceph-1.3@rhel-7', 'short': 'ceph', + 'version': '1.3', 'name': 'Red Hat Ceph Storage', + 'base_product': 'rhel-7', 'active': true, + 'product_version': 'ceph-1', 'release_type': 'ga', + 'compose_set': [], 'integrated_with': 'null', 'sigkey': 'null', + 'allow_buildroot_push': true, 'allowed_debuginfo_services': [], + 'allowed_push_targets': [], 'bugzilla': 'null', + 'dist_git': 'null', 'brew': 'null', 'product_pages': 'null', + 'errata': 'null' }, + { 'release_id': 'ceph-1.3-updates@rhel-7', 'short': 'ceph', + 'version': '1.3', 'name': 'Red Hat Ceph Storage', + 'base_product': 'rhel-7', 'active': true, + 'product_version': 'ceph-1', 'release_type': 'updates', + 'compose_set': [], 'integrated_with': 'null', 'sigkey': 'null', + 'allow_buildroot_push': false, 'allowed_debuginfo_services': [], + 'allowed_push_targets': [], 'bugzilla': 'null', + 'dist_git': 'null', 'brew': 'null', 'product_pages': 'null', + 'errata': 'null' }]) + + product_version = ProductVersion.new(product_version_id: 'ceph-1') + releases = product_version.releases.where(release_id: 'ceph-1.3@rhel-7') + releases.any? + releases.to_a + assert_requested endpoint1, times: 1 + + product_version.releases.to_a + assert_requested endpoint2, times: 1 + end + end + + describe 'has_one' do + it 'has_one assocation' do + variant_cpe = ReleaseVariant.new(release: 'dp-1.0', uid: 'Client').variant_cpe + assert_equal 'cpe:/asdff', variant_cpe.cpe + end + end +end diff --git a/spec/support/fixtures.rb b/spec/support/fixtures.rb index 67b6139b1..bd036f32a 100644 --- a/spec/support/fixtures.rb +++ b/spec/support/fixtures.rb @@ -28,6 +28,7 @@ class ModelWithIdentity include PDC::Resource::Attributes include PDC::Resource::Identity include PDC::Resource::Scopes + include PDC::Resource::Associations end class CustomPrimaryKeyModel @@ -37,6 +38,7 @@ class CustomPrimaryKeyModel include PDC::Resource::Attributes include PDC::Resource::Identity include PDC::Resource::Scopes + include PDC::Resource::Associations self.primary_key = :foobar end @@ -48,6 +50,7 @@ class ModelBase include PDC::Resource::Identity include PDC::Resource::Attributes include PDC::Resource::Scopes + include PDC::Resource::Associations end class Model < ModelBase; end @@ -60,6 +63,7 @@ class Foobar include PDC::Resource::Identity include PDC::Resource::Attributes include PDC::Resource::Scopes + include PDC::Resource::Associations end end end @@ -114,3 +118,32 @@ class CustomParserModel < Base attribute :age, parser: FixNumParser end end + +module Fixtures + class Association < PDC::Base + # stub the connection + SITE = 'https://pdc.host.dev.eng.pek2.redhat.com/'.freeze + self.connection = Faraday.new(url: SITE) do |faraday| + faraday.request :json + faraday.use PDC::Response::Parser + faraday.adapter Faraday.default_adapter + end + end + + class ProductVersion < Association + has_many :releases, uri: 'rest_api/v1/releases/?product_version=:product_version_id' + end + + class Release < Association + self.primary_key = :release_id + end + + class ReleaseVariant < Association + has_one :variant_cpe, uri: 'rest_api/v1/variant-cpes/?release=:release&variant_uid=:uid', foreign_key: 'release/variant_uid' + self.mapping = { variant_uid: 'uid' } + end + + class VariantCpe < Association + self.primary_key = :id + end +end