From aba1ac872d78b47eed2940d0135f55fe28072a60 Mon Sep 17 00:00:00 2001 From: Drew Bomhof Date: Thu, 20 Dec 2018 17:20:49 -0500 Subject: [PATCH 1/2] Added delete route for portfolio_items --- app/controllers/api/v0/admins_controller.rb | 5 ++ public/doc/swagger-2.yaml | 17 +++++ spec/requests/portfolio_items_request_spec.rb | 32 +++++++++ spec/requests/portfolio_items_spec.rb | 70 ++++++++++++------- 4 files changed, 100 insertions(+), 24 deletions(-) create mode 100644 spec/requests/portfolio_items_request_spec.rb diff --git a/app/controllers/api/v0/admins_controller.rb b/app/controllers/api/v0/admins_controller.rb index 6d239fcb9..0751c6646 100644 --- a/app/controllers/api/v0/admins_controller.rb +++ b/app/controllers/api/v0/admins_controller.rb @@ -34,6 +34,11 @@ def add_to_order render json: AddToOrder.new(params).process.to_hash end + def destroy_portfolio_item + PortfolioItem.find(params.require(:portfolio_item_id)).destroy + head :no_content + end + private def portfolio_item_params params.permit(:service_offering_ref) diff --git a/public/doc/swagger-2.yaml b/public/doc/swagger-2.yaml index cb1c42038..de1592eb7 100644 --- a/public/doc/swagger-2.yaml +++ b/public/doc/swagger-2.yaml @@ -298,6 +298,23 @@ paths: description: Service Offering not found '422': $ref: '#/responses/InvalidEntity' + '/portfolio_items/{portfolio_item_id}': + delete: + tags: + - admins + summary: Delete an existing portfolio item + operationId: destroyPortfolioItem + description: | + Deletes the portfolio item id passed in as the param. + produces: + - application/json + parameters: + - $ref: '#/parameters/PortfolioItemID' + responses: + 204: + description: Portfolio Item deleted + 404: + description: Portfolio Item not Found '/portfolio_items/{portfolio_item_id}/service_plans': get: tags: diff --git a/spec/requests/portfolio_items_request_spec.rb b/spec/requests/portfolio_items_request_spec.rb new file mode 100644 index 000000000..6487ab838 --- /dev/null +++ b/spec/requests/portfolio_items_request_spec.rb @@ -0,0 +1,32 @@ +describe "PortfolioItemRequests", :type => :request do + include ServiceSpecHelper + + let(:service_offering_ref) { "998" } + let(:order) { create(:order) } + let(:portfolio_item) { create(:portfolio_item, :service_offering_ref => service_offering_ref) } + let(:svc_object) { instance_double("ServiceCatalog::ServicePlans") } + let(:plans) { [{}, {}] } + let(:topo_ex) { ServiceCatalog::TopologyError.new("kaboom") } + + before do + allow(ServiceCatalog::ServicePlans).to receive(:new).with(portfolio_item.id.to_s).and_return(svc_object) + end + + it "fetches plans" do + allow(svc_object).to receive(:process).and_return(svc_object) + allow(svc_object).to receive(:items).and_return(plans) + + get "/api/v0.0/portfolio_items/#{portfolio_item.id}/service_plans" + + expect(JSON.parse(response.body).count).to eq(2) + expect(response.content_type).to eq("application/json") + expect(response).to have_http_status(:ok) + end + + it "raises error" do + allow(svc_object).to receive(:process).and_raise(topo_ex) + + get "/api/v0.0/portfolio_items/#{portfolio_item.id}/service_plans" + expect(response).to have_http_status(:internal_server_error) + end +end diff --git a/spec/requests/portfolio_items_spec.rb b/spec/requests/portfolio_items_spec.rb index ec8524ebd..609cab39e 100644 --- a/spec/requests/portfolio_items_spec.rb +++ b/spec/requests/portfolio_items_spec.rb @@ -1,32 +1,54 @@ -describe "PortfolioItemRequests", :type => :request do - include ServiceSpecHelper - - let(:service_offering_ref) { "998" } - let(:order) { create(:order) } - let(:portfolio_item) { create(:portfolio_item, :service_offering_ref => service_offering_ref) } - let(:svc_object) { instance_double("ServiceCatalog::ServicePlans") } - let(:plans) { [{}, {}] } - let(:topo_ex) { ServiceCatalog::TopologyError.new("kaboom") } - - before do - allow(ServiceCatalog::ServicePlans).to receive(:new).with(portfolio_item.id.to_s).and_return(svc_object) - end +describe 'PortfolioItems API' do + include RequestSpecHelper + + let!(:portfolio_item) { create(:portfolio_item) } + let(:portfolio_item_id) { portfolio_item.id } + let(:tenant) { create(:tenant, :with_external_tenant) } + + # Encoded Header: { 'identity' => { 'is_org_admin':false, 'org_id':111 } } + let(:user_encode_key_with_tenant) { { 'x-rh-auth-identity': 'eyJpZGVudGl0eSI6eyJpc19vcmdfYWRtaW4iOmZhbHNlLCJvcmdfaWQiOiIxMTEifX0=' } } + # Encoded Header: { 'identity' => { 'is_org_admin':true, 'org_id':111 } } + let(:admin_encode_key_with_tenant) { { 'x-rh-auth-identity': 'eyJpZGVudGl0eSI6eyJpc19vcmdfYWRtaW4iOnRydWUsIm9yZ19pZCI6MTExfX0=' } } - it "fetches plans" do - allow(svc_object).to receive(:process).and_return(svc_object) - allow(svc_object).to receive(:items).and_return(plans) + %w(admin user).each do |tag| + describe "GET #{tag} tagged /portfolio_items" do + before do + get "/portfolio_items", headers: send("#{tag}_encode_key_with_tenant") + end - get "/api/v0.0/portfolio_items/#{portfolio_item.id}/service_plans" + context 'when portfolios exist' do + it 'returns status code 200' do + expect(response).to have_http_status(200) + end - expect(JSON.parse(response.body).count).to eq(2) - expect(response.content_type).to eq("application/json") - expect(response).to have_http_status(:ok) + it 'returns all portfolio requests' do + expect(json.size).to eq(1) + end + end + end end - it "raises error" do - allow(svc_object).to receive(:process).and_raise(topo_ex) + describe 'admin tagged /portfolio_items', :type => :routing do + let(:valid_attributes) { { name: 'rspec 1', description: 'rspec 1 description' } } + context 'with wrong header' do + it 'returns a 404' do + expect(:post => "/portfolio_items").not_to be_routable + end + end + end + + describe 'POST admin tagged /portfolio_items' do + let(:valid_attributes) { { name: 'rspec 1', description: 'rspec 1 description', service_offering_ref: '10' } } + context 'when portfolio attributes are valid' do + before { post "/portfolio_items", params: valid_attributes, headers: admin_encode_key_with_tenant } + + it 'returns status code 200' do + expect(response).to have_http_status(200) + end - get "/api/v0.0/portfolio_items/#{portfolio_item.id}/service_plans" - expect(response).to have_http_status(:internal_server_error) + it 'returns the new portfolio' do + expect(json['name']).to eq valid_attributes[:name] + end + end end end From 1f030550a6e533540f1c2adcb69c94c6ec0562db Mon Sep 17 00:00:00 2001 From: Drew Bomhof Date: Fri, 4 Jan 2019 17:56:33 -0500 Subject: [PATCH 2/2] Add portfolio_item specs --- config/routes.rb | 1 + spec/requests/portfolio_items_spec.rb | 26 ++++++++-- .../service_catalog/service_offering_spec.rb | 18 +------ spec/support/lib/topological_inventory.rb | 24 +++++++++ .../service_catalog/service_offering.json | 1 + .../service_catalog/service_offering.rb | 51 +++++++++++++++++++ 6 files changed, 100 insertions(+), 21 deletions(-) create mode 100644 spec/support/lib/topological_inventory.rb create mode 100644 spec/support/service_catalog/service_offering.json create mode 100644 spec/support/service_catalog/service_offering.rb diff --git a/config/routes.rb b/config/routes.rb index 0c8027ae3..b69accf84 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -36,6 +36,7 @@ def add_swagger_route(http_method, path, opts = {}) add_swagger_route 'GET', '/portfolios/{portfolio_id}', :controller_name => 'admins', :action_name => 'fetch_portfolio_with_id' add_swagger_route 'PATCH', '/portfolios/{portfolio_id}', :controller_name => 'admins', :action_name => 'edit_portfolio' add_swagger_route 'DELETE', '/portfolios/{portfolio_id}', :controller_name => 'admins', :action_name => 'destroy_portfolio' + add_swagger_route 'DELETE', '/portfolio_items/{portfolio_item_id}', :controller_name => 'admins', :action_name => 'destroy_portfolio_item' add_swagger_route 'GET', '/orders/{order_id}/items/{order_item_id}', :controller_name => 'admins', :action_name => 'list_order_item' add_swagger_route 'GET', '/orders/{order_id}/items', :controller_name => 'admins', :action_name => 'list_order_items' add_swagger_route 'GET', '/orders', :controller_name => 'admins', :action_name => 'list_orders' diff --git a/spec/requests/portfolio_items_spec.rb b/spec/requests/portfolio_items_spec.rb index 609cab39e..7f88d8ce1 100644 --- a/spec/requests/portfolio_items_spec.rb +++ b/spec/requests/portfolio_items_spec.rb @@ -1,5 +1,6 @@ describe 'PortfolioItems API' do include RequestSpecHelper + include ServiceSpecHelper let!(:portfolio_item) { create(:portfolio_item) } let(:portfolio_item_id) { portfolio_item.id } @@ -13,7 +14,7 @@ %w(admin user).each do |tag| describe "GET #{tag} tagged /portfolio_items" do before do - get "/portfolio_items", headers: send("#{tag}_encode_key_with_tenant") + get "#{api}/portfolio_items", headers: send("#{tag}_encode_key_with_tenant") end context 'when portfolios exist' do @@ -32,15 +33,32 @@ let(:valid_attributes) { { name: 'rspec 1', description: 'rspec 1 description' } } context 'with wrong header' do it 'returns a 404' do - expect(:post => "/portfolio_items").not_to be_routable + pending("Will work again when headers are checked") + expect(:post => "#{api}/portfolio_items").not_to be_routable + end + end + end + + describe 'DELETE admin tagged /portfolio_items/:portfolio_item_id' do + let(:valid_attributes) { { :name => 'PatchPortfolio', :description => 'description for patched portfolio' } } + + context 'when :portfolio_item_id is valid' do + before do + delete "#{api}/portfolio_items/#{portfolio_item_id}", :headers => admin_headers, :params => valid_attributes + end + + it 'deletes the record' do + expect(response).to have_http_status(204) end end end describe 'POST admin tagged /portfolio_items' do - let(:valid_attributes) { { name: 'rspec 1', description: 'rspec 1 description', service_offering_ref: '10' } } + let(:valid_attributes) { { name: 'rh-mediawiki-apb', description: 'Mediawiki apb implementation', service_offering_ref: '21' } } context 'when portfolio attributes are valid' do - before { post "/portfolio_items", params: valid_attributes, headers: admin_encode_key_with_tenant } + before do + post "#{api}/portfolio_items", params: valid_attributes, headers: admin_encode_key_with_tenant + end it 'returns status code 200' do expect(response).to have_http_status(200) diff --git a/spec/services/service_catalog/service_offering_spec.rb b/spec/services/service_catalog/service_offering_spec.rb index 2b37a7e6d..0862f3b62 100644 --- a/spec/services/service_catalog/service_offering_spec.rb +++ b/spec/services/service_catalog/service_offering_spec.rb @@ -10,30 +10,14 @@ [{:@name => name}, {:@description => description}, {:@service_offering_ref => service_offering_ref}] end - let(:topology_service_offering) do - TopologicalInventoryApiClient::ServiceOffering.new('name' => name, - 'id' => service_offering_ref, - 'description' => description, - 'source_ref' => '123', - 'extra' => {}, - 'source_id' => '45') - end - - let(:ti_class) { class_double("TopologicalInventory").as_stubbed_const(:transfer_nested_constants => true) } - - before do - allow(ti_class).to receive(:call).and_yield(api_instance) - end it "#{described_class}#find" do expect(described_class).to receive(:new).and_return(service_offering) - expect(api_instance).to receive(:show_service_offering).with(service_offering_ref).and_return(topology_service_offering) described_class.find(service_offering_ref) end it "#show" do - expect(api_instance).to receive(:show_service_offering).with(service_offering_ref).and_return(topology_service_offering) - service_offering.show(service_offering_ref) + expect(service_offering.show(service_offering_ref)).to be_a ServiceCatalog::ServiceOffering end it "#to_normalized_params" do diff --git a/spec/support/lib/topological_inventory.rb b/spec/support/lib/topological_inventory.rb new file mode 100644 index 000000000..81fbf781d --- /dev/null +++ b/spec/support/lib/topological_inventory.rb @@ -0,0 +1,24 @@ +class TopologicalInventory + def self.api + Thread.current[:api_instance] ||= raw_api + end + + def self.call + pass_thru_headers + yield api + rescue TopologicalInventoryApiClient::ApiError => err + Rails.logger.error("TopologicalInventoryApiClient::ApiError #{err.message} ") + raise ServiceCatalog::TopologyError, err.message + end + + private_class_method def self.raw_api + TopologicalInventoryApiClient.configure do |config| + config.host = 'localhost' + config.scheme = 'http' + end + end + + private_class_method def self.pass_thru_headers + {} + end +end diff --git a/spec/support/service_catalog/service_offering.json b/spec/support/service_catalog/service_offering.json new file mode 100644 index 000000000..8075f148a --- /dev/null +++ b/spec/support/service_catalog/service_offering.json @@ -0,0 +1 @@ +[{"id":"21","name":"rh-mediawiki-apb","description":"Mediawiki apb implementation"},{"id":"153","name":"s2i-fuse71-karaf-camel-rest-sql","description":"Camel example using Rest DSL with SQL Database in Karaf container. This example demonstrates how to use SQL via JDBC along with Camel's REST DSL to expose a RESTful API. The OpenShift MySQL container image should already be installed and running on your OpenShift installation, one simple way to run a MySQL service is following the documentation of the OpenShift MySQL container image related to the mysql-ephemeral template.."}] diff --git a/spec/support/service_catalog/service_offering.rb b/spec/support/service_catalog/service_offering.rb new file mode 100644 index 000000000..5153ada3b --- /dev/null +++ b/spec/support/service_catalog/service_offering.rb @@ -0,0 +1,51 @@ +module ServiceCatalog + class ServiceOffering + def initialize + @name = nil + @description = nil + @service_offering_ref = nil + end + + def self.find(id) + new.show(id) + end + + def show(id) + @service_offering_ref = id + obj = nil + TopologicalInventory.call do |api_instance| + obj = JSON.parse(File.read("#{Rails.root}/spec/support/service_catalog/service_offering.json")) + end + resp = obj.select { |x| x['id'] == id } + @normalized = resp.first + self + end + + def to_normalized_params + hashy = instance_variables.each_with_object({}) do |var, hash| + next if var == :@normalized + hash[var.to_s.delete("@")] = instance_variable_get(var) + end + if @normalized + @normalized["service_offering_ref"] = @normalized["id"] + @normalized + else + hashy.compact + end + end + + private + + def apply_instance_vars(obj) + uniq_ivars(obj).each do |ivar| + value = obj.instance_variable_get(ivar) + instance_variable_set(ivar, value) + end + self + end + + def uniq_ivars(object) + instance_variables & object.instance_variables + end + end +end