diff --git a/app/actions/app_revisions_update.rb b/app/actions/revisions_update.rb similarity index 94% rename from app/actions/app_revisions_update.rb rename to app/actions/revisions_update.rb index 55211fd8fbb..78907f9f6a1 100644 --- a/app/actions/app_revisions_update.rb +++ b/app/actions/revisions_update.rb @@ -1,5 +1,5 @@ module VCAP::CloudController - class AppRevisionsUpdate + class RevisionsUpdate class InvalidAppRevisions < StandardError end diff --git a/app/controllers/v3/app_revisions_controller.rb b/app/controllers/v3/app_revisions_controller.rb index 3e6a7a4b7c8..c3a07516483 100644 --- a/app/controllers/v3/app_revisions_controller.rb +++ b/app/controllers/v3/app_revisions_controller.rb @@ -1,8 +1,6 @@ require 'messages/app_revisions_list_message' require 'fetchers/app_fetcher' require 'fetchers/app_revisions_fetcher' -require 'messages/app_revisions_update_message' -require 'actions/app_revisions_update' require 'presenters/v3/revision_presenter' require 'controllers/v3/mixins/app_sub_resource' require 'presenters/v3/revision_environment_variables_presenter' @@ -26,46 +24,4 @@ def index message: message ) end - - def show - revision = fetch_revision(hashed_params[:revision_guid]) - render status: :ok, json: Presenters::V3::RevisionPresenter.new(revision) - end - - def update - message = AppRevisionsUpdateMessage.new(hashed_params[:body]) - unprocessable!(message.errors.full_messages) unless message.valid? - - revision = fetch_revision(hashed_params[:revision_guid], needs_write_permissions: true) - - revision = AppRevisionsUpdate.new.update(revision, message) - - render status: :ok, json: Presenters::V3::RevisionPresenter.new(revision) - end - - def show_environment_variables - app, space, org = AppFetcher.new.fetch(hashed_params[:guid]) - app_not_found! unless app && permission_queryer.can_read_from_space?(space.guid, org.guid) - unauthorized! unless permission_queryer.can_read_secrets_in_space?(space.guid, org.guid) - - revision = RevisionModel.find(guid: hashed_params[:revision_guid]) - resource_not_found!(:revision) unless revision && revision.app_guid == app.guid - - render status: :ok, json: Presenters::V3::RevisionEnvironmentVariablesPresenter.new(revision) - end - - private - - def fetch_revision(guid, needs_write_permissions: false) - revision = RevisionModel.find(guid: guid) - resource_not_found!(:revision) unless revision - - app = revision.app - space = app.space - org = space.organization - app_not_found! unless permission_queryer.can_read_from_space?(space.guid, org.guid) - unauthorized! if needs_write_permissions && !permission_queryer.can_write_to_space?(space.guid) - - revision - end end diff --git a/app/controllers/v3/revisions_controller.rb b/app/controllers/v3/revisions_controller.rb new file mode 100644 index 00000000000..9e5677e26d4 --- /dev/null +++ b/app/controllers/v3/revisions_controller.rb @@ -0,0 +1,43 @@ +require 'messages/revisions_update_message' +require 'actions/revisions_update' +require 'presenters/v3/revision_presenter' +require 'presenters/v3/revision_environment_variables_presenter' + +class RevisionsController < ApplicationController + def show + revision = fetch_revision(hashed_params[:revision_guid]) + render status: :ok, json: Presenters::V3::RevisionPresenter.new(revision) + end + + def update + message = RevisionsUpdateMessage.new(hashed_params[:body]) + unprocessable!(message.errors.full_messages) unless message.valid? + + revision = fetch_revision(hashed_params[:revision_guid], needs_write_permissions: true) + + revision = RevisionsUpdate.new.update(revision, message) + + render status: :ok, json: Presenters::V3::RevisionPresenter.new(revision) + end + + def show_environment_variables + revision = fetch_revision(hashed_params[:revision_guid], needs_secrets_read_permission: true) + render status: :ok, json: Presenters::V3::RevisionEnvironmentVariablesPresenter.new(revision) + end + + private + + def fetch_revision(guid, needs_write_permissions: false, needs_secrets_read_permission: false) + revision = RevisionModel.find(guid: guid) + resource_not_found!(:revision) unless revision + + app = revision.app + space = app.space + org = space.organization + resource_not_found!(:revision) unless permission_queryer.can_read_from_space?(space.guid, org.guid) + unauthorized! if needs_write_permissions && !permission_queryer.can_write_to_space?(space.guid) + unauthorized! if needs_secrets_read_permission && !permission_queryer.can_read_secrets_in_space?(space.guid, org.guid) + + revision + end +end diff --git a/app/messages/app_revisions_update_message.rb b/app/messages/revisions_update_message.rb similarity index 73% rename from app/messages/app_revisions_update_message.rb rename to app/messages/revisions_update_message.rb index 83b70fbec47..42c948be65c 100644 --- a/app/messages/app_revisions_update_message.rb +++ b/app/messages/revisions_update_message.rb @@ -1,7 +1,7 @@ require 'messages/metadata_base_message' module VCAP::CloudController - class AppRevisionsUpdateMessage < MetadataBaseMessage + class RevisionsUpdateMessage < MetadataBaseMessage register_allowed_keys [] validates_with NoAdditionalKeysValidator diff --git a/app/presenters/v3/revision_environment_variables_presenter.rb b/app/presenters/v3/revision_environment_variables_presenter.rb index 0f192817f53..00f686b2964 100644 --- a/app/presenters/v3/revision_environment_variables_presenter.rb +++ b/app/presenters/v3/revision_environment_variables_presenter.rb @@ -34,8 +34,8 @@ def build_links url_builder = VCAP::CloudController::Presenters::ApiUrlBuilder.new { - self: { href: url_builder.build_url(path: "/v3/apps/#{revision.app.guid}/revisions/#{revision.guid}/environment_variables") }, - revision: { href: url_builder.build_url(path: "/v3/apps/#{revision.app.guid}/revisions/#{revision.guid}") }, + self: { href: url_builder.build_url(path: "/v3/revisions/#{revision.guid}/environment_variables") }, + revision: { href: url_builder.build_url(path: "/v3/revisions/#{revision.guid}") }, app: { href: url_builder.build_url(path: "/v3/apps/#{revision.app.guid}") } } end diff --git a/config/routes.rb b/config/routes.rb index e05f11ea8cc..1c395159e0b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -28,9 +28,11 @@ # app revisions get '/apps/:guid/revisions', to: 'app_revisions#index' - get '/apps/:guid/revisions/:revision_guid/environment_variables', to: 'app_revisions#show_environment_variables' - patch '/revisions/:revision_guid', to: 'app_revisions#update' - get '/revisions/:revision_guid', to: 'app_revisions#show' + + # revisions + get '/revisions/:revision_guid/environment_variables', to: 'revisions#show_environment_variables' + patch '/revisions/:revision_guid', to: 'revisions#update' + get '/revisions/:revision_guid', to: 'revisions#show' # environment variables get '/apps/:guid/environment_variables', to: 'apps_v3#show_environment_variables' diff --git a/spec/request/revisions_spec.rb b/spec/request/revisions_spec.rb index 19fca2026ea..090885982b2 100644 --- a/spec/request/revisions_spec.rb +++ b/spec/request/revisions_spec.rb @@ -292,7 +292,7 @@ } it 'gets the environment variables for the revision' do - get "/v3/apps/#{app_model.reload.guid}/revisions/#{revision2.guid}/environment_variables", nil, user_header + get "/v3/revisions/#{revision2.guid}/environment_variables", nil, user_header expect(last_response.status).to eq(200), last_response.body parsed_response = MultiJson.load(last_response.body) @@ -302,8 +302,8 @@ 'key' => 'value' }, 'links' => { - 'self' => { 'href' => "#{link_prefix}/v3/apps/#{app_model.guid}/revisions/#{revision2.guid}/environment_variables" }, - 'revision' => { 'href' => "#{link_prefix}/v3/apps/#{app_model.guid}/revisions/#{revision2.guid}" }, + 'self' => { 'href' => "#{link_prefix}/v3/revisions/#{revision2.guid}/environment_variables" }, + 'revision' => { 'href' => "#{link_prefix}/v3/revisions/#{revision2.guid}" }, 'app' => { 'href' => "#{link_prefix}/v3/apps/#{app_model.guid}" }, } } diff --git a/spec/unit/actions/app_revisions_update_spec.rb b/spec/unit/actions/revisions_update_spec.rb similarity index 80% rename from spec/unit/actions/app_revisions_update_spec.rb rename to spec/unit/actions/revisions_update_spec.rb index b952a529f8b..fb683505e03 100644 --- a/spec/unit/actions/app_revisions_update_spec.rb +++ b/spec/unit/actions/revisions_update_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -require 'actions/app_revisions_update' +require 'actions/revisions_update' module VCAP::CloudController - RSpec.describe AppRevisionsUpdate do - subject(:revision_update) { AppRevisionsUpdate.new } + RSpec.describe RevisionsUpdate do + subject(:revision_update) { RevisionsUpdate.new } describe '#update' do let(:body) do @@ -19,7 +19,7 @@ module VCAP::CloudController } end let(:revision) { RevisionModel.make } - let(:message) { AppRevisionsUpdateMessage.new(body) } + let(:message) { RevisionsUpdateMessage.new(body) } it 'updates the revision metadata' do expect(message).to be_valid diff --git a/spec/unit/controllers/v3/app_revisions_controller_spec.rb b/spec/unit/controllers/v3/app_revisions_controller_spec.rb index 85e9727cd45..8bbd798f1f4 100644 --- a/spec/unit/controllers/v3/app_revisions_controller_spec.rb +++ b/spec/unit/controllers/v3/app_revisions_controller_spec.rb @@ -2,132 +2,6 @@ require 'permissions_spec_helper' RSpec.describe AppRevisionsController, type: :controller do - describe '#show' do - let!(:droplet) { VCAP::CloudController::DropletModel.make } - let!(:app_model) { VCAP::CloudController::AppModel.make(droplet: droplet) } - let!(:space) { app_model.space } - let(:user) { VCAP::CloudController::User.make } - let(:revision) { VCAP::CloudController::RevisionModel.make(app: app_model, version: 808, droplet_guid: droplet.guid) } - - before do - set_current_user(user) - allow_user_read_access_for(user, spaces: [space]) - allow_user_secret_access(user, space: space) - end - - it 'returns 200 and shows the revision' do - get :show, params: { revision_guid: revision.guid } - - expect(response.status).to eq(200) - expect(parsed_body).to be_a_response_like( - { - 'guid' => revision.guid, - 'version' => revision.version, - 'droplet' => { - 'guid' => droplet.guid - }, - 'relationships' => { - 'app' => { - 'data' => { - 'guid' => app_model.guid - } - } - }, - 'created_at' => iso8601, - 'updated_at' => iso8601, - 'links' => { - 'self' => { - 'href' => "#{link_prefix}/v3/revisions/#{revision.guid}" - }, - 'app' => { - 'href' => "#{link_prefix}/v3/apps/#{app_model.guid}" - } - }, - 'metadata' => { - 'labels' => {}, - 'annotations' => {} - } - } - ) - end - - it 'still shows the revision droplet_guid even after the droplet is deleted' do - droplet_guid = droplet.guid - droplet.delete - - get :show, params: { revision_guid: revision.guid } - - expect(response.status).to eq(200) - expect(parsed_body).to be_a_response_like( - { - 'guid' => revision.guid, - 'version' => revision.version, - 'droplet' => { - 'guid' => droplet_guid - }, - 'relationships' => { - 'app' => { - 'data' => { - 'guid' => app_model.guid - } - } - }, - 'created_at' => iso8601, - 'updated_at' => iso8601, - 'links' => { - 'self' => { - 'href' => "#{link_prefix}/v3/revisions/#{revision.guid}" - }, - 'app' => { - 'href' => "#{link_prefix}/v3/apps/#{app_model.guid}" - } - }, - 'metadata' => { - 'labels' => {}, - 'annotations' => {} - } - } - ) - end - - it 'raises an ApiError with a 404 code when the revision does not exist' do - get :show, params: { revision_guid: 'hahaha' } - - expect(response.status).to eq 404 - expect(response.body).to include 'ResourceNotFound' - end - - context 'permissions' do - context 'when the user does not have cc read scope' do - before do - set_current_user(VCAP::CloudController::User.make, scopes: []) - end - - it 'raises an ApiError with a 403 code' do - get :show, params: { revision_guid: revision.guid } - - expect(response.body).to include 'NotAuthorized' - expect(response.status).to eq 403 - end - end - - context 'when the user cannot read the app' do - let(:space) { app_model.space } - - before do - disallow_user_read_access(user, space: space) - end - - it 'returns a 404 ResourceNotFound error' do - get :show, params: { revision_guid: revision.guid } - - expect(response.status).to eq 404 - expect(response.body).to include 'ResourceNotFound' - end - end - end - end - describe '#index' do let!(:app_model) { VCAP::CloudController::AppModel.make } let!(:app_without_revisions) { VCAP::CloudController::AppModel.make(space: space) } @@ -205,233 +79,4 @@ end end end - - describe '#update' do - let!(:droplet) { VCAP::CloudController::DropletModel.make } - let!(:app_model) { VCAP::CloudController::AppModel.make(droplet: droplet) } - let!(:space) { app_model.space } - let(:user) { VCAP::CloudController::User.make } - let(:labels) do - { - fruit: 'pears', - truck: 'hino' - } - end - let(:annotations) do - { - potato: 'celandine', - beet: 'formanova', - } - end - let(:revision) { VCAP::CloudController::RevisionModel.make(app: app_model, version: 808, droplet_guid: droplet.guid) } - let!(:update_message) do - { - metadata: { - labels: { - fruit: 'passionfruit' - }, - annotations: { - potato: 'adora' - } - } - } - end - - before do - set_current_user(user) - allow_user_read_access_for(user, spaces: [space]) - allow_user_write_access(user, space: space) - - VCAP::CloudController::LabelsUpdate.update(revision, labels, VCAP::CloudController::RevisionLabelModel) - VCAP::CloudController::AnnotationsUpdate.update(revision, annotations, VCAP::CloudController::RevisionAnnotationModel) - end - - context 'when the user can modify the app' do - it 'returns a 200 and the updated revision' do - patch :update, params: { revision_guid: revision.guid }.merge(update_message), as: :json - - expect(response.status).to eq(200) - expect(parsed_body).to be_a_response_like( - { - 'guid' => revision.guid, - 'version' => revision.version, - 'droplet' => { - 'guid' => droplet.guid - }, - 'relationships' => { - 'app' => { - 'data' => { - 'guid' => app_model.guid - } - } - }, - 'created_at' => iso8601, - 'updated_at' => iso8601, - 'links' => { - 'self' => { - 'href' => "#{link_prefix}/v3/revisions/#{revision.guid}" - }, - 'app' => { - 'href' => "#{link_prefix}/v3/apps/#{app_model.guid}" - } - }, - 'metadata' => { - 'labels' => { 'fruit' => 'passionfruit', 'truck' => 'hino' }, - 'annotations' => { 'potato' => 'adora', 'beet' => 'formanova' } - } - } - ) - end - end - - context 'when the user sets metadata to null' do - let!(:update_message) do - { - metadata: { - labels: { - fruit: nil - }, - annotations: { - potato: nil - } - } - } - end - - it 'is removed' do - patch :update, params: { revision_guid: revision.guid }.merge(update_message), as: :json - - expect(response.status).to eq(200) - expect(parsed_body).to be_a_response_like( - { - 'guid' => revision.guid, - 'version' => revision.version, - 'droplet' => { - 'guid' => droplet.guid - }, - 'relationships' => { - 'app' => { - 'data' => { - 'guid' => app_model.guid - } - } - }, - 'created_at' => iso8601, - 'updated_at' => iso8601, - 'links' => { - 'self' => { - 'href' => "#{link_prefix}/v3/revisions/#{revision.guid}" - }, - 'app' => { - 'href' => "#{link_prefix}/v3/apps/#{app_model.guid}" - } - }, - 'metadata' => { - 'labels' => { 'truck' => 'hino' }, - 'annotations' => { 'beet' => 'formanova' } - } - } - ) - end - end - - context 'when the user cannot read from the space' do - before do - disallow_user_read_access(user, space: space) - end - - it 'returns a 404' do - patch :update, params: { revision_guid: revision.guid }.merge(update_message), as: :json - - expect(response.status).to eq(404) - end - end - - context 'when the user cannot modify the app' do - before do - disallow_user_write_access(user, space: space) - end - - it 'returns a 403' do - patch :update, params: { revision_guid: revision.guid }.merge(update_message), as: :json - - expect(response.status).to eq(403) - end - end - - context 'when the user gives bad metadata' do - let(:update_message) do - { - metadata: { - annotations: { - "": 'mashed', - "/potato": '.value.' - } - } - } - end - - it 'returns a 422' do - patch :update, params: { revision_guid: revision.guid }.merge(update_message), as: :json - - expect(response.status).to eq(422) - end - end - end - - describe '#show_environment_variables' do - let!(:droplet) { VCAP::CloudController::DropletModel.make } - let!(:app_model) { VCAP::CloudController::AppModel.make(droplet: droplet) } - let!(:space) { app_model.space } - let(:user) { VCAP::CloudController::User.make } - let(:revision) { VCAP::CloudController::RevisionModel.make( - app: app_model, - version: 808, - droplet_guid: droplet.guid, - environment_variables: { 'key' => 'value' }, - ) - } - - before do - set_current_user(user) - allow_user_read_access_for(user, spaces: [space]) - allow_user_secret_access(user, space: space) - end - - it 'returns 200 and shows environment_variables' do - get :show_environment_variables, params: { guid: app_model.guid, revision_guid: revision.guid } - - expect(response.status).to eq(200) - expect(parsed_body['var']).to eq({ 'key' => 'value' }) - end - - context 'when retrieving env variables for revision that do not exist' do - it '404s' do - get :show_environment_variables, params: { guid: app_model.guid, revision_guid: 'nonsense' } - expect(response.status).to eq(404) - end - end - - context 'when access to the space is restricted' do - before do - disallow_user_read_access(user, space: space) - end - - it '404s' do - get :show_environment_variables, params: { guid: app_model.guid, revision_guid: 'nonsense' } - expect(response.status).to eq(404) - end - end - - context 'when access is restricted' do - before do - disallow_user_secret_access(user, space: space) - end - - it '403s' do - get :show_environment_variables, params: { guid: app_model.guid, revision_guid: 'nonsense' } - expect(response.status).to eq(403) - end - end - end end diff --git a/spec/unit/controllers/v3/revisions_controller_spec.rb b/spec/unit/controllers/v3/revisions_controller_spec.rb new file mode 100644 index 00000000000..6da9822e448 --- /dev/null +++ b/spec/unit/controllers/v3/revisions_controller_spec.rb @@ -0,0 +1,359 @@ +require 'rails_helper' +require 'permissions_spec_helper' + +RSpec.describe RevisionsController, type: :controller do + describe '#show' do + let!(:droplet) { VCAP::CloudController::DropletModel.make } + let!(:app_model) { VCAP::CloudController::AppModel.make(droplet: droplet) } + let!(:space) { app_model.space } + let(:user) { VCAP::CloudController::User.make } + let(:revision) { VCAP::CloudController::RevisionModel.make(app: app_model, version: 808, droplet_guid: droplet.guid) } + + before do + set_current_user(user) + allow_user_read_access_for(user, spaces: [space]) + allow_user_secret_access(user, space: space) + end + + it 'returns 200 and shows the revision' do + get :show, params: { revision_guid: revision.guid } + + expect(response.status).to eq(200) + expect(parsed_body).to be_a_response_like( + { + 'guid' => revision.guid, + 'version' => revision.version, + 'droplet' => { + 'guid' => droplet.guid + }, + 'relationships' => { + 'app' => { + 'data' => { + 'guid' => app_model.guid + } + } + }, + 'created_at' => iso8601, + 'updated_at' => iso8601, + 'links' => { + 'self' => { + 'href' => "#{link_prefix}/v3/revisions/#{revision.guid}" + }, + 'app' => { + 'href' => "#{link_prefix}/v3/apps/#{app_model.guid}" + } + }, + 'metadata' => { + 'labels' => {}, + 'annotations' => {} + } + } + ) + end + + it 'still shows the revision droplet_guid even after the droplet is deleted' do + droplet_guid = droplet.guid + droplet.delete + + get :show, params: { revision_guid: revision.guid } + + expect(response.status).to eq(200) + expect(parsed_body).to be_a_response_like( + { + 'guid' => revision.guid, + 'version' => revision.version, + 'droplet' => { + 'guid' => droplet_guid + }, + 'relationships' => { + 'app' => { + 'data' => { + 'guid' => app_model.guid + } + } + }, + 'created_at' => iso8601, + 'updated_at' => iso8601, + 'links' => { + 'self' => { + 'href' => "#{link_prefix}/v3/revisions/#{revision.guid}" + }, + 'app' => { + 'href' => "#{link_prefix}/v3/apps/#{app_model.guid}" + } + }, + 'metadata' => { + 'labels' => {}, + 'annotations' => {} + } + } + ) + end + + it 'raises an ApiError with a 404 code when the revision does not exist' do + get :show, params: { revision_guid: 'hahaha' } + + expect(response.status).to eq 404 + expect(response.body).to include 'ResourceNotFound' + end + + context 'permissions' do + context 'when the user does not have cc read scope' do + before do + set_current_user(VCAP::CloudController::User.make, scopes: []) + end + + it 'raises an ApiError with a 403 code' do + get :show, params: { revision_guid: revision.guid } + + expect(response.body).to include 'NotAuthorized' + expect(response.status).to eq 403 + end + end + + context 'when the user cannot read the app' do + let(:space) { app_model.space } + + before do + disallow_user_read_access(user, space: space) + end + + it 'returns a 404 ResourceNotFound error' do + get :show, params: { revision_guid: revision.guid } + + expect(response.status).to eq 404 + expect(response.body).to include 'ResourceNotFound' + end + end + end + end + + describe '#update' do + let!(:droplet) { VCAP::CloudController::DropletModel.make } + let!(:app_model) { VCAP::CloudController::AppModel.make(droplet: droplet) } + let!(:space) { app_model.space } + let(:user) { VCAP::CloudController::User.make } + let(:labels) do + { + fruit: 'pears', + truck: 'hino' + } + end + let(:annotations) do + { + potato: 'celandine', + beet: 'formanova', + } + end + let(:revision) { VCAP::CloudController::RevisionModel.make(app: app_model, version: 808, droplet_guid: droplet.guid) } + let!(:update_message) do + { + metadata: { + labels: { + fruit: 'passionfruit' + }, + annotations: { + potato: 'adora' + } + } + } + end + + before do + set_current_user(user) + allow_user_read_access_for(user, spaces: [space]) + allow_user_write_access(user, space: space) + + VCAP::CloudController::LabelsUpdate.update(revision, labels, VCAP::CloudController::RevisionLabelModel) + VCAP::CloudController::AnnotationsUpdate.update(revision, annotations, VCAP::CloudController::RevisionAnnotationModel) + end + + context 'when the user can modify the app' do + it 'returns a 200 and the updated revision' do + patch :update, params: { revision_guid: revision.guid }.merge(update_message), as: :json + + expect(response.status).to eq(200) + expect(parsed_body).to be_a_response_like( + { + 'guid' => revision.guid, + 'version' => revision.version, + 'droplet' => { + 'guid' => droplet.guid + }, + 'relationships' => { + 'app' => { + 'data' => { + 'guid' => app_model.guid + } + } + }, + 'created_at' => iso8601, + 'updated_at' => iso8601, + 'links' => { + 'self' => { + 'href' => "#{link_prefix}/v3/revisions/#{revision.guid}" + }, + 'app' => { + 'href' => "#{link_prefix}/v3/apps/#{app_model.guid}" + } + }, + 'metadata' => { + 'labels' => { 'fruit' => 'passionfruit', 'truck' => 'hino' }, + 'annotations' => { 'potato' => 'adora', 'beet' => 'formanova' } + } + } + ) + end + end + + context 'when the user sets metadata to null' do + let!(:update_message) do + { + metadata: { + labels: { + fruit: nil + }, + annotations: { + potato: nil + } + } + } + end + + it 'is removed' do + patch :update, params: { revision_guid: revision.guid }.merge(update_message), as: :json + + expect(response.status).to eq(200) + expect(parsed_body).to be_a_response_like( + { + 'guid' => revision.guid, + 'version' => revision.version, + 'droplet' => { + 'guid' => droplet.guid + }, + 'relationships' => { + 'app' => { + 'data' => { + 'guid' => app_model.guid + } + } + }, + 'created_at' => iso8601, + 'updated_at' => iso8601, + 'links' => { + 'self' => { + 'href' => "#{link_prefix}/v3/revisions/#{revision.guid}" + }, + 'app' => { + 'href' => "#{link_prefix}/v3/apps/#{app_model.guid}" + } + }, + 'metadata' => { + 'labels' => { 'truck' => 'hino' }, + 'annotations' => { 'beet' => 'formanova' } + } + } + ) + end + end + + context 'when the user cannot read from the space' do + before do + disallow_user_read_access(user, space: space) + end + + it 'returns a 404' do + patch :update, params: { revision_guid: revision.guid }.merge(update_message), as: :json + + expect(response.status).to eq(404) + end + end + + context 'when the user cannot modify the app' do + before do + disallow_user_write_access(user, space: space) + end + + it 'returns a 403' do + patch :update, params: { revision_guid: revision.guid }.merge(update_message), as: :json + + expect(response.status).to eq(403) + end + end + + context 'when the user gives bad metadata' do + let(:update_message) do + { + metadata: { + annotations: { + "": 'mashed', + "/potato": '.value.' + } + } + } + end + + it 'returns a 422' do + patch :update, params: { revision_guid: revision.guid }.merge(update_message), as: :json + + expect(response.status).to eq(422) + end + end + end + + describe '#show_environment_variables' do + let!(:droplet) { VCAP::CloudController::DropletModel.make } + let!(:app_model) { VCAP::CloudController::AppModel.make(droplet: droplet) } + let!(:space) { app_model.space } + let(:user) { VCAP::CloudController::User.make } + let(:revision) { VCAP::CloudController::RevisionModel.make( + app: app_model, + version: 808, + droplet_guid: droplet.guid, + environment_variables: { 'key' => 'value' }, + ) + } + + before do + set_current_user(user) + allow_user_read_access_for(user, spaces: [space]) + allow_user_secret_access(user, space: space) + end + + it 'returns 200 and shows environment_variables' do + get :show_environment_variables, params: { revision_guid: revision.guid } + + expect(response.status).to eq(200) + expect(parsed_body['var']).to eq({ 'key' => 'value' }) + end + + context 'when retrieving env variables for revision that do not exist' do + it '404s' do + get :show_environment_variables, params: { revision_guid: 'nonsense' } + expect(response.status).to eq(404) + end + end + + context 'when access to the space is restricted' do + before do + disallow_user_read_access(user, space: space) + end + + it '404s' do + get :show_environment_variables, params: { revision_guid: 'nonsense' } + expect(response.status).to eq(404) + end + end + + context 'when access is restricted' do + before do + disallow_user_secret_access(user, space: space) + end + + it '403s' do + get :show_environment_variables, params: { revision_guid: revision.guid } + expect(response.status).to eq(403) + end + end + end +end diff --git a/spec/unit/messages/app_revisions_update_message_spec.rb b/spec/unit/messages/revisions_update_message_spec.rb similarity index 75% rename from spec/unit/messages/app_revisions_update_message_spec.rb rename to spec/unit/messages/revisions_update_message_spec.rb index 407e0f895a2..7c69145b5f8 100644 --- a/spec/unit/messages/app_revisions_update_message_spec.rb +++ b/spec/unit/messages/revisions_update_message_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' -require 'messages/app_revisions_update_message' +require 'messages/revisions_update_message' module VCAP::CloudController - RSpec.describe AppRevisionsUpdateMessage do + RSpec.describe RevisionsUpdateMessage do let(:body) do { "metadata": { @@ -19,21 +19,21 @@ module VCAP::CloudController describe 'validations' do it 'validates that there are not excess fields' do body['bogus'] = 'field' - message = AppRevisionsUpdateMessage.new(body) + message = RevisionsUpdateMessage.new(body) expect(message).to_not be_valid expect(message.errors.full_messages).to include("Unknown field(s): 'bogus'") end it 'validates metadata' do - message = AppRevisionsUpdateMessage.new(body) + message = RevisionsUpdateMessage.new(body) expect(message).to be_valid end it 'complains about bogus metadata fields' do newbody = body.merge({ "metadata": { "choppers": 3 } }) - message = AppRevisionsUpdateMessage.new(newbody) + message = RevisionsUpdateMessage.new(newbody) expect(message).not_to be_valid end diff --git a/spec/unit/presenters/v3/revision_environment_variables_presenter_spec.rb b/spec/unit/presenters/v3/revision_environment_variables_presenter_spec.rb index 80adb47ba35..27776b02aea 100644 --- a/spec/unit/presenters/v3/revision_environment_variables_presenter_spec.rb +++ b/spec/unit/presenters/v3/revision_environment_variables_presenter_spec.rb @@ -21,10 +21,10 @@ module VCAP::CloudController::Presenters::V3 }, links: { self: { - href: "#{link_prefix}/v3/apps/#{revision.app.guid}/revisions/#{revision.guid}/environment_variables", + href: "#{link_prefix}/v3/revisions/#{revision.guid}/environment_variables", }, revision: { - href: "#{link_prefix}/v3/apps/#{revision.app.guid}/revisions/#{revision.guid}", + href: "#{link_prefix}/v3/revisions/#{revision.guid}", }, app: { href: "#{link_prefix}/v3/apps/#{revision.app.guid}",