From eb40c9e96d58423276e3995adde0a4d4692386ec Mon Sep 17 00:00:00 2001 From: Alexander Demichev Date: Thu, 5 Oct 2017 15:13:17 +0200 Subject: [PATCH] add flavors actions to api --- app/controllers/api/providers_controller.rb | 1 + app/controllers/api/subcollections/flavors.rb | 31 ++++ config/api.yml | 25 ++- spec/requests/flavors_spec.rb | 171 ++++++++++++++++++ 4 files changed, 227 insertions(+), 1 deletion(-) create mode 100644 app/controllers/api/subcollections/flavors.rb create mode 100644 spec/requests/flavors_spec.rb diff --git a/app/controllers/api/providers_controller.rb b/app/controllers/api/providers_controller.rb index 4cfdf54553..2ecb6a1e23 100644 --- a/app/controllers/api/providers_controller.rb +++ b/app/controllers/api/providers_controller.rb @@ -19,6 +19,7 @@ class ProvidersController < BaseController include Subcollections::LoadBalancers include Subcollections::SecurityGroups include Subcollections::Vms + include Subcollections::Flavors def create_resource(type, _id, data = {}) assert_id_not_specified(data, type) diff --git a/app/controllers/api/subcollections/flavors.rb b/app/controllers/api/subcollections/flavors.rb new file mode 100644 index 0000000000..be194646c6 --- /dev/null +++ b/app/controllers/api/subcollections/flavors.rb @@ -0,0 +1,31 @@ +module Api + module Subcollections + module Flavors + def flavors_query_resource(object) + object.flavors + end + + def flavors_create_resource(parent, _type, _id, data) + task_id = Flavor.create_flavor_queue(User.current_user.id, parent, data) + action_result(true, 'Creating Flavor', :task_id => task_id) + rescue => err + action_result(false, err.to_s) + end + + def delete_resource_flavors(_parent, type, id, _data) + flavor = resource_search(id, type, collection_class(type)) + task_id = flavor.delete_flavor_queue(User.current_user.id) + action_result(true, "Deleting #{flavor_ident(flavor)}", :task_id => task_id) + rescue => err + action_result(false, err.to_s) + end + alias flavors_delete_resource delete_resource_flavors + + private + + def flavor_ident(flavor) + "Flavor id:#{flavor.id} name: '#{flavor.name}'" + end + end + end +end diff --git a/config/api.yml b/config/api.yml index ac110b9e71..ded6d48bc7 100644 --- a/config/api.yml +++ b/config/api.yml @@ -864,7 +864,8 @@ :identifier: flavor :options: - :collection - :verbs: *gp + - :subcollection + :verbs: *gpd :klass: Flavor :collection_actions: :get: @@ -874,9 +875,30 @@ - :name: query :identifier: flavor_show_list :resource_actions: + :get: + - :name: read + :identifier: flavor_show + :subcollection_actions: + :get: + - :name: read + :identifier: flavor_show_list + :post: + - :name: delete + :identifier: flavor_delete + - :name: query + :identifier: flavor_show_list + - :name: create + :identifier: flavor_create + :subresource_actions: :get: - :name: read :identifier: flavor_show + :post: + - :name: delete + :identifier: flavor_delete + :delete: + - :name: delete + :identifier: flavor_delete :floating_ips: :description: Floating IPs :identifier: floating_ip @@ -1655,6 +1677,7 @@ - :load_balancers - :security_groups - :vms + - :flavors :collection_actions: :get: - :name: read diff --git a/spec/requests/flavors_spec.rb b/spec/requests/flavors_spec.rb new file mode 100644 index 0000000000..b5db4b3ce6 --- /dev/null +++ b/spec/requests/flavors_spec.rb @@ -0,0 +1,171 @@ +RSpec.describe "Flavors API" do + describe "as a subcollection of providers" do + describe "GET /api/providers/:c_id/flavors" do + it "can list the flavors of a provider" do + api_basic_authorize(action_identifier(:flavors, :read, :subcollection_actions, :get)) + ems = FactoryGirl.create(:ems_cloud) + flavor = FactoryGirl.create(:flavor, :ext_management_system => ems) + + get(api_provider_flavors_url(nil, ems)) + + expected = { + "count" => 1, + "name" => "flavors", + "resources" => [ + {"href" => api_provider_flavor_url(nil, ems, flavor)} + ] + } + expect(response.parsed_body).to include(expected) + expect(response).to have_http_status(:ok) + end + + it "will not list flavors unless authorized" do + api_basic_authorize + ems = FactoryGirl.create(:ems_cloud) + FactoryGirl.create(:flavor, :ext_management_system => ems) + + get(api_provider_flavors_url(nil, ems)) + + expect(response).to have_http_status(:forbidden) + end + end + + describe "GET /api/providers/:c_id/flavors/:id" do + it "can show a provider's flavor" do + api_basic_authorize(action_identifier(:flavors, :read, :subresource_actions, :get)) + ems = FactoryGirl.create(:ems_cloud) + flavor = FactoryGirl.create(:flavor, :ext_management_system => ems) + + get(api_provider_flavor_url(nil, ems, flavor)) + + expected = { + "href" => api_provider_flavor_url(nil, ems, flavor), + "id" => flavor.id.to_s + } + expect(response.parsed_body).to include(expected) + expect(response).to have_http_status(:ok) + end + + it "will not show a flavor unless authorized" do + api_basic_authorize + ems = FactoryGirl.create(:ems_cloud) + flavor = FactoryGirl.create(:flavor, :ext_management_system => ems) + + get(api_provider_flavor_url(nil, ems, flavor)) + + expect(response).to have_http_status(:forbidden) + end + end + + describe "POST /api/providers/:c_id/flavors" do + it "can queue the creation of a flavors" do + api_basic_authorize(action_identifier(:flavors, :create, :subcollection_actions)) + ems = FactoryGirl.create(:ems_cloud) + + post(api_provider_flavors_url(nil, ems), :params => { :name => "test-flavor" }) + + expected = { + "results" => [ + a_hash_including( + "success" => true, + "message" => "Creating Flavor", + "task_id" => anything, + "task_href" => a_string_matching(api_tasks_url) + ) + ] + } + expect(response.parsed_body).to include(expected) + expect(response).to have_http_status(:ok) + end + + it "will not create a flavor unless authorized" do + api_basic_authorize + ems = FactoryGirl.create(:ems_cloud) + + post(api_provider_flavors_url(nil, ems), :params => { :name => "test-flavor" }) + + expect(response).to have_http_status(:forbidden) + end + end + + describe "POST /api/providers/:c_id/flavors/:s_id with delete action" do + it "can queue a flavor for deletion" do + api_basic_authorize(action_identifier(:flavors, :delete, :subresource_actions)) + + ems = FactoryGirl.create(:ems_cloud) + flavor = FactoryGirl.create(:flavor, :ext_management_system => ems) + + post(api_provider_flavor_url(nil, ems, flavor), :params => { :action => "delete" }) + + expected = { + "message" => "Deleting Flavor id:#{flavor.id} name: '#{flavor.name}'", + "success" => true, + "task_href" => a_string_matching(api_tasks_url), + "task_id" => anything + } + expect(response.parsed_body).to include(expected) + expect(response).to have_http_status(:ok) + end + + it "will not delete a flavor unless authorized" do + api_basic_authorize + ems = FactoryGirl.create(:ems_cloud) + flavor = FactoryGirl.create(:flavor, :ext_management_system => ems) + + post(api_provider_flavor_url(nil, ems, flavor), :params => { :action => "delete" }) + + expect(response).to have_http_status(:forbidden) + end + end + + describe "POST /api/providers/:c_id/flavors/ with delete action" do + it "can delete multiple flavors" do + ems = FactoryGirl.create(:ems_cloud) + flavor1, flavor2 = FactoryGirl.create_list(:flavor, 2) + + api_basic_authorize(action_identifier(:flavors, :delete, :subresource_actions)) + + post(api_provider_flavors_url(nil, ems), :params => { :action => "delete", :resources => [{:id => flavor1.id}, + {:id => flavor2.id}] }) + + expect(response).to have_http_status(:ok) + end + + it "forbids multiple flavor deletion without an appropriate role" do + ems = FactoryGirl.create(:ems_cloud) + flavor1, flavor2 = FactoryGirl.create_list(:flavor, 2) + + api_basic_authorize + + post(api_provider_flavors_url(nil, ems), :params => { :action => "delete", :resources => [{:id => flavor1.id}, + {:id => flavor2.id}] }) + + expect(response).to have_http_status(:forbidden) + end + end + + describe "DELETE /api/providers/:c_id/flavors/:s_id" do + it "can delete a flavor" do + ems = FactoryGirl.create(:ems_cloud) + flavor = FactoryGirl.create(:flavor, :ext_management_system => ems) + + api_basic_authorize(action_identifier(:flavors, :delete, :subresource_actions, :delete)) + + delete(api_provider_flavor_url(nil, ems, flavor)) + + expect(response).to have_http_status(:no_content) + end + + it "will not delete a flavor unless authorized" do + ems = FactoryGirl.create(:ems_cloud) + flavor = FactoryGirl.create(:flavor, :ext_management_system => ems) + + api_basic_authorize + + delete(api_provider_flavor_url(nil, ems, flavor)) + + expect(response).to have_http_status(:forbidden) + end + end + end +end