Commit fc50dc31 authored by Sean McGivern's avatar Sean McGivern

Merge branch 'rd-add-support-for-gl-com-trials' into 'master'

Update billing section to support GitLab.com trials

See merge request gitlab-org/gitlab-ee!6252
parents 7abbde6e 75e1faf2
...@@ -1752,6 +1752,7 @@ ActiveRecord::Schema.define(version: 20180702181530) do ...@@ -1752,6 +1752,7 @@ ActiveRecord::Schema.define(version: 20180702181530) do
t.integer "plan_id" t.integer "plan_id"
t.integer "project_creation_level" t.integer "project_creation_level"
t.string "runners_token" t.string "runners_token"
t.datetime_with_timezone "trial_ends_on"
end end
add_index "namespaces", ["created_at"], name: "index_namespaces_on_created_at", using: :btree add_index "namespaces", ["created_at"], name: "index_namespaces_on_created_at", using: :btree
......
...@@ -22,4 +22,8 @@ module BillingPlansHelper ...@@ -22,4 +22,8 @@ module BillingPlansHelper
button_tag link_text, class: 'btn disabled' button_tag link_text, class: 'btn disabled'
end end
end end
def new_gitlab_com_trial_url
"#{EE::SUBSCRIPTIONS_URL}/trials/new?gl_com=true"
end
end end
...@@ -180,6 +180,23 @@ module EE ...@@ -180,6 +180,23 @@ module EE
end end
end end
def eligible_for_trial?
::Gitlab.com? &&
parent_id.nil? &&
trial_ends_on.blank? &&
[EARLY_ADOPTER_PLAN, FREE_PLAN].include?(actual_plan_name)
end
def trial_active?
trial_ends_on.present? && trial_ends_on >= Date.today
end
def trial_expired?
trial_ends_on.present? &&
trial_ends_on < Date.today &&
actual_plan_name == FREE_PLAN
end
private private
def validate_plan_name def validate_plan_name
......
...@@ -20,5 +20,8 @@ ...@@ -20,5 +20,8 @@
%p= s_("BillingPlans|To manage the plan for this group, visit the billing section of %{parent_billing_page_link}.").html_safe % { parent_billing_page_link: parent_billing_page_link } %p= s_("BillingPlans|To manage the plan for this group, visit the billing section of %{parent_billing_page_link}.").html_safe % { parent_billing_page_link: parent_billing_page_link }
= link_to s_("BillingPlans|Manage plan"), group_billings_path(parent_group), class: 'btn btn-success' = link_to s_("BillingPlans|Manage plan"), group_billings_path(parent_group), class: 'btn btn-success'
- else - else
- faq_link = link_to s_("BillingPlans|frequently asked questions"), "https://about.gitlab.com/gitlab-com/#faq" = render 'shared/billings/trial_status', namespace: namespace
= s_("BillingPlans|Learn more about each plan by reading our %{faq_link}.").html_safe % { faq_link: faq_link }
- if namespace.eligible_for_trial?
- namespace_billing_url = namespace.is_a?(Group) ? group_billings_url(namespace) : profile_billings_url
%p= link_to 'Start your free trial', new_gitlab_com_trial_url, class: 'btn btn-primary btn-inverted'
- faq_link = link_to s_("BillingPlans|frequently asked questions"), "https://about.gitlab.com/gitlab-com/#faq"
- features_link = link_to s_("BillingPlans|features"), "https://about.gitlab.com/features"
- learn_more_text = s_("BillingPlans|Learn more about each plan by reading our %{faq_link}.").html_safe % { faq_link: faq_link }
- if namespace.eligible_for_trial?
= s_("BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Gold.").html_safe % { faq_link: faq_link }
- elsif namespace.trial_active?
= s_("BillingPlans|Your Gold trial will <strong>expire after %{expiration_date}</strong>. You can learn more about GitLab.com Gold by reading about our %{features_link}.").html_safe % { expiration_date: namespace.trial_ends_on, features_link: features_link }
- elsif namespace.trial_expired?
= s_("BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}").html_safe % { expiration_date: namespace.trial_ends_on, learn_more_text: learn_more_text }
- else
= learn_more_text
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddTrialEndsOnToNamespaces < ActiveRecord::Migration
DOWNTIME = false
def change
add_column :namespaces, :trial_ends_on, :datetime_with_timezone
end
end
...@@ -69,6 +69,7 @@ module EE ...@@ -69,6 +69,7 @@ module EE
extend ActiveSupport::Concern extend ActiveSupport::Concern
prepended do prepended do
expose :trial_ends_on
expose :shared_runners_minutes_limit, if: ->(_, options) { options[:current_user]&.admin? } expose :shared_runners_minutes_limit, if: ->(_, options) { options[:current_user]&.admin? }
expose :plan, if: ->(namespace, opts) { ::Ability.allowed?(opts[:current_user], :admin_namespace, namespace) } do |namespace, _| expose :plan, if: ->(namespace, opts) { ::Ability.allowed?(opts[:current_user], :admin_namespace, namespace) } do |namespace, _|
namespace.plan&.name namespace.plan&.name
......
module EE
module API
module Namespaces
extend ActiveSupport::Concern
prepended do
resource :namespaces do
desc 'Update a namespace' do
success Entities::Namespace
end
params do
optional :plan, type: String, desc: "Namespace or Group plan"
optional :shared_runners_minutes_limit, type: Integer, desc: "Pipeline minutes quota for this namespace"
optional :trial_ends_on, type: Date, desc: "Trial expiration date"
end
put ':id' do
authenticated_as_admin!
namespace = find_namespace(params[:id])
trial_ends_on = params[:trial_ends_on]
break not_found!('Namespace') unless namespace
break bad_request!("Invalid trial expiration date") if trial_ends_on&.past?
if namespace.update(declared_params)
present namespace, with: ::API::Entities::Namespace, current_user: current_user
else
render_validation_error!(namespace)
end
end
end
end
end
end
end
require 'spec_helper'
describe API::Namespaces do
let(:admin) { create(:admin) }
let(:user) { create(:user) }
let!(:group1) { create(:group) }
let!(:group2) { create(:group, :nested) }
describe "GET /namespaces" do
context "when authenticated as admin" do
it "returns correct attributes" do
get api("/namespaces", admin)
group_kind_json_response = json_response.find { |resource| resource['kind'] == 'group' }
user_kind_json_response = json_response.find { |resource| resource['kind'] == 'user' }
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(group_kind_json_response.keys).to contain_exactly('id', 'kind', 'name', 'path', 'full_path',
'parent_id', 'members_count_with_descendants',
'plan', 'shared_runners_minutes_limit',
'trial_ends_on')
expect(user_kind_json_response.keys).to contain_exactly('id', 'kind', 'name', 'path', 'full_path',
'parent_id', 'plan', 'shared_runners_minutes_limit',
'trial_ends_on')
end
end
context "when authenticated as a regular user" do
it "returns correct attributes when user can admin group" do
group1.add_owner(user)
get api("/namespaces", user)
owned_group_response = json_response.find { |resource| resource['id'] == group1.id }
expect(owned_group_response.keys).to contain_exactly('id', 'kind', 'name', 'path', 'full_path',
'plan', 'parent_id', 'members_count_with_descendants',
'trial_ends_on')
end
it "returns correct attributes when user cannot admin group" do
group1.add_guest(user)
get api("/namespaces", user)
guest_group_response = json_response.find { |resource| resource['id'] == group1.id }
expect(guest_group_response.keys).to contain_exactly('id', 'kind', 'name', 'path', 'full_path', 'parent_id',
'trial_ends_on')
end
end
end
describe 'PUT /namespaces/:id' do
before do
create(:silver_plan)
end
context 'when authenticated as admin' do
it 'updates namespace using full_path' do
put api("/namespaces/#{group1.full_path}", admin), plan: 'silver', shared_runners_minutes_limit: 9001
expect(response).to have_gitlab_http_status(200)
expect(json_response['plan']).to eq('silver')
expect(json_response['shared_runners_minutes_limit']).to eq(9001)
end
it 'updates namespace using id' do
put api("/namespaces/#{group1.id}", admin), plan: 'silver', shared_runners_minutes_limit: 9001
expect(response).to have_gitlab_http_status(200)
expect(json_response['plan']).to eq('silver')
expect(json_response['shared_runners_minutes_limit']).to eq(9001)
end
context 'setting the trial expiration date' do
context 'when the attr has a future date' do
it 'updates the trial expiration date' do
date = 30.days.from_now.to_date
put api("/namespaces/#{group1.id}", admin), trial_ends_on: date
expect(response).to have_gitlab_http_status(200)
expect(json_response['trial_ends_on']).to eq(date.to_s)
end
end
context 'when the attr has an old date' do
it 'returns 400' do
put api("/namespaces/#{group1.id}", admin), trial_ends_on: 2.days.ago.to_date
expect(response).to have_gitlab_http_status(400)
expect(json_response['trial_ends_on']).to eq(nil)
end
end
end
end
context 'when not authenticated as admin' do
it 'retuns 403' do
put api("/namespaces/#{group1.id}", user), plan: 'silver'
expect(response).to have_gitlab_http_status(403)
end
end
context 'when namespace not found' do
it 'returns 404' do
put api("/namespaces/12345", admin), plan: 'silver'
expect(response).to have_gitlab_http_status(404)
expect(json_response).to eq('message' => '404 Namespace Not Found')
end
end
context 'when invalid params' do
it 'returns validation error' do
put api("/namespaces/#{group1.id}", admin), plan: 'unknown'
expect(response).to have_gitlab_http_status(400)
expect(json_response['message']).to eq('plan' => ['is not included in the list'])
end
end
end
end
...@@ -4,6 +4,8 @@ module API ...@@ -4,6 +4,8 @@ module API
before { authenticate! } before { authenticate! }
prepend EE::API::Namespaces
resource :namespaces do resource :namespaces do
desc 'Get a namespaces list' do desc 'Get a namespaces list' do
success Entities::Namespace success Entities::Namespace
...@@ -20,27 +22,6 @@ module API ...@@ -20,27 +22,6 @@ module API
present paginate(namespaces), with: Entities::Namespace, current_user: current_user present paginate(namespaces), with: Entities::Namespace, current_user: current_user
end end
desc 'Update a namespace' do
success Entities::Namespace
end
params do
optional :plan, type: String, desc: "Namespace or Group plan"
optional :shared_runners_minutes_limit, type: Integer, desc: "Pipeline minutes quota for this namespace"
end
put ':id' do
authenticated_as_admin!
namespace = find_namespace(params[:id])
break not_found!('Namespace') unless namespace
if namespace.update(declared_params)
present namespace, with: Entities::Namespace, current_user: current_user
else
render_validation_error!(namespace)
end
end
desc 'Get a namespace by ID' do desc 'Get a namespace by ID' do
success Entities::Namespace success Entities::Namespace
end end
......
...@@ -823,6 +823,9 @@ msgstr "" ...@@ -823,6 +823,9 @@ msgstr ""
msgid "BillingPlans|Downgrade" msgid "BillingPlans|Downgrade"
msgstr "" msgstr ""
msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}, or start a free 30-day trial of GitLab.com Gold."
msgstr ""
msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}." msgid "BillingPlans|Learn more about each plan by reading our %{faq_link}."
msgstr "" msgstr ""
...@@ -847,6 +850,15 @@ msgstr "" ...@@ -847,6 +850,15 @@ msgstr ""
msgid "BillingPlans|You are currently on the %{plan_link} plan." msgid "BillingPlans|You are currently on the %{plan_link} plan."
msgstr "" msgstr ""
msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
msgstr ""
msgid "BillingPlans|Your Gold trial will <strong>expire after %{expiration_date}</strong>. You can learn more about GitLab.com Gold by reading about our %{features_link}."
msgstr ""
msgid "BillingPlans|features"
msgstr ""
msgid "BillingPlans|frequently asked questions" msgid "BillingPlans|frequently asked questions"
msgstr "" msgstr ""
......
...@@ -23,12 +23,10 @@ describe API::Namespaces do ...@@ -23,12 +23,10 @@ describe API::Namespaces do
expect(response).to have_gitlab_http_status(200) expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers expect(response).to include_pagination_headers
expect(group_kind_json_response.keys).to contain_exactly('id', 'kind', 'name', 'path', 'full_path', expect(group_kind_json_response.keys).to include('id', 'kind', 'name', 'path', 'full_path',
'parent_id', 'members_count_with_descendants', 'parent_id', 'members_count_with_descendants')
'plan', 'shared_runners_minutes_limit')
expect(user_kind_json_response.keys).to contain_exactly('id', 'kind', 'name', 'path', 'full_path', expect(user_kind_json_response.keys).to include('id', 'kind', 'name', 'path', 'full_path', 'parent_id')
'parent_id', 'plan', 'shared_runners_minutes_limit')
end end
it "admin: returns an array of all namespaces" do it "admin: returns an array of all namespaces" do
...@@ -60,8 +58,8 @@ describe API::Namespaces do ...@@ -60,8 +58,8 @@ describe API::Namespaces do
owned_group_response = json_response.find { |resource| resource['id'] == group1.id } owned_group_response = json_response.find { |resource| resource['id'] == group1.id }
expect(owned_group_response.keys).to contain_exactly('id', 'kind', 'name', 'path', 'full_path', expect(owned_group_response.keys).to include('id', 'kind', 'name', 'path', 'full_path',
'plan', 'parent_id', 'members_count_with_descendants') 'parent_id', 'members_count_with_descendants')
end end
it "returns correct attributes when user cannot admin group" do it "returns correct attributes when user cannot admin group" do
...@@ -71,7 +69,7 @@ describe API::Namespaces do ...@@ -71,7 +69,7 @@ describe API::Namespaces do
guest_group_response = json_response.find { |resource| resource['id'] == group1.id } guest_group_response = json_response.find { |resource| resource['id'] == group1.id }
expect(guest_group_response.keys).to contain_exactly('id', 'kind', 'name', 'path', 'full_path', 'parent_id') expect(guest_group_response.keys).to include('id', 'kind', 'name', 'path', 'full_path', 'parent_id')
end end
it "user: returns an array of namespaces" do it "user: returns an array of namespaces" do
...@@ -94,56 +92,6 @@ describe API::Namespaces do ...@@ -94,56 +92,6 @@ describe API::Namespaces do
end end
end end
describe 'PUT /namespaces/:id' do
before do
create(:silver_plan)
end
context 'when authenticated as admin' do
it 'updates namespace using full_path' do
put api("/namespaces/#{group1.full_path}", admin), plan: 'silver', shared_runners_minutes_limit: 9001
expect(response).to have_gitlab_http_status(200)
expect(json_response['plan']).to eq('silver')
expect(json_response['shared_runners_minutes_limit']).to eq(9001)
end
it 'updates namespace using id' do
put api("/namespaces/#{group1.id}", admin), plan: 'silver', shared_runners_minutes_limit: 9001
expect(response).to have_gitlab_http_status(200)
expect(json_response['plan']).to eq('silver')
expect(json_response['shared_runners_minutes_limit']).to eq(9001)
end
end
context 'when not authenticated as admin' do
it 'retuns 403' do
put api("/namespaces/#{group1.id}", user), plan: 'silver'
expect(response).to have_gitlab_http_status(403)
end
end
context 'when namespace not found' do
it 'returns 404' do
put api("/namespaces/12345", admin), plan: 'silver'
expect(response).to have_gitlab_http_status(404)
expect(json_response).to eq('message' => '404 Namespace Not Found')
end
end
context 'when invalid params' do
it 'returns validation error' do
put api("/namespaces/#{group1.id}", admin), plan: 'unknown'
expect(response).to have_gitlab_http_status(400)
expect(json_response['message']).to eq('plan' => ['is not included in the list'])
end
end
end
describe 'GET /namespaces/:id' do describe 'GET /namespaces/:id' do
let(:owned_group) { group1 } let(:owned_group) { group1 }
let(:user2) { create(:user) } let(:user2) { create(:user) }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment