Commit 040a742e authored by Oswaldo Ferreira's avatar Oswaldo Ferreira

Add subscription_plan and caller_id to ApplicationContext

It adds new attributes subscription_plan and caller_id.

The subscription_plan can be "free", "bronze", "silver" or "gold",
these are exclusively fetched from the given namespace (or the
project if it's not given).

The caller_id can be the controller action name if it's a regular
Rails request, or the Grape route, when it's an API request.

These new attributes will also be logged at Sidekiq logs, along
the project, user and namespace.
parent 6ec84d45
......@@ -302,7 +302,7 @@ gem 'sentry-raven', '~> 2.9'
gem 'premailer-rails', '~> 1.10.3'
# LabKit: Tracing and Correlation
gem 'gitlab-labkit', '0.8.0'
gem 'gitlab-labkit', '0.9.1'
# I18n
gem 'ruby_parser', '~> 3.8', require: false
......
......@@ -367,7 +367,7 @@ GEM
github-markup (1.7.0)
gitlab-chronic (0.10.5)
numerizer (~> 0.2)
gitlab-labkit (0.8.0)
gitlab-labkit (0.9.1)
actionpack (>= 5.0.0, < 6.1.0)
activesupport (>= 5.0.0, < 6.1.0)
grpc (~> 1.19)
......@@ -1215,7 +1215,7 @@ DEPENDENCIES
gitaly (~> 1.81.0)
github-markup (~> 1.7.0)
gitlab-chronic (~> 0.10.5)
gitlab-labkit (= 0.8.0)
gitlab-labkit (= 0.9.1)
gitlab-license (~> 1.0)
gitlab-markup (~> 1.7.0)
gitlab-net-dns (~> 0.9.1)
......
......@@ -454,6 +454,7 @@ class ApplicationController < ActionController::Base
user: -> { auth_user },
project: -> { @project },
namespace: -> { @group },
caller_id: full_action_name,
&block)
end
......@@ -551,6 +552,10 @@ class ApplicationController < ActionController::Base
end
end
def full_action_name
"#{self.class.name}##{action_name}"
end
# A user requires a role and have the setup_for_company attribute set when they are part of the experimental signup
# flow (executed by the Growth team). Users are redirected to the welcome page when their role is required and the
# experiment is enabled for the current user.
......
---
title: Add extra fields to the application context
merge_request: 22792
author:
type: added
......@@ -371,6 +371,9 @@ module EE
end
def generate_subscription
return unless persisted?
return if ::Gitlab::Database.read_only?
create_gitlab_subscription(
plan_code: plan&.name,
trial: trial_active?,
......
......@@ -160,7 +160,7 @@ module EE
delegate :merge_pipelines_enabled, :merge_pipelines_enabled=, :merge_pipelines_enabled?, :merge_pipelines_were_disabled?, to: :ci_cd_settings
delegate :merge_trains_enabled?, to: :ci_cd_settings
delegate :actual_limits, to: :namespace, allow_nil: true
delegate :actual_limits, :actual_plan_name, to: :namespace, allow_nil: true
validates :repository_size_limit,
numericality: { only_integer: true, greater_than_or_equal_to: 0, allow_nil: true }
......
# frozen_string_literal: true
module EE
module Gitlab
module ApplicationContext
extend ::Gitlab::Utils::Override
override :to_lazy_hash
def to_lazy_hash
super.tap do |hash|
hash[:subscription_plan] = -> { subcription_plan_name } if include_namespace?
end
end
def subcription_plan_name
object = namespace || project
object&.actual_plan_name
end
end
end
end
......@@ -27,7 +27,7 @@ module EE
retry_optimistic_lock(pipeline) do
pipeline.drop!(:activity_limit_exceeded)
limit.log_error!(project_id: project.id, plan: project.namespace.actual_plan_name)
limit.log_error!(project_id: project.id, plan: project.actual_plan_name)
end
end
......
......@@ -27,7 +27,7 @@ module EE
retry_optimistic_lock(pipeline) do
pipeline.drop!(:job_activity_limit_exceeded)
limit.log_error!(project_id: project.id, plan: project.namespace.actual_plan_name)
limit.log_error!(project_id: project.id, plan: project.actual_plan_name)
end
end
......
......@@ -28,7 +28,7 @@ module EE
pipeline.drop!(:size_limit_exceeded)
end
limit.log_error!(project_id: project.id, plan: project.namespace.actual_plan_name)
limit.log_error!(project_id: project.id, plan: project.actual_plan_name)
error(limit.message)
end
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::ApplicationContext do
describe '#to_lazy_hash' do
let(:user) { build(:user) }
let(:project) { build(:project) }
let(:namespace) { create(:group) }
let(:subgroup) { create(:group, parent: namespace) }
def result(context)
context.to_lazy_hash.transform_values { |v| v.respond_to?(:call) ? v.call : v }
end
it 'correctly loads the expected values' do
context = described_class.new(namespace: -> { subgroup })
expect(result(context))
.to include(root_namespace: namespace.full_path,
subscription_plan: 'free')
end
it 'falls back to a projects namespace plan when a project is passed but no namespace' do
create(:gitlab_subscription, :silver, namespace: project.namespace)
context = described_class.new(project: project)
expect(result(context))
.to include(project: project.full_path,
root_namespace: project.full_path_components.first,
subscription_plan: 'silver')
end
end
context 'only include values for which an option was specified' do
using RSpec::Parameterized::TableSyntax
where(:provided_options, :expected_context_keys) do
[:user, :namespace, :project] | [:user, :project, :root_namespace, :subscription_plan]
[:user, :project] | [:user, :project, :root_namespace, :subscription_plan]
[:user, :namespace] | [:user, :root_namespace, :subscription_plan]
[:user] | [:user]
[:caller_id] | [:caller_id]
[] | []
end
with_them do
it do
# Build a hash that has all `provided_options` as keys, and `nil` as value
provided_values = provided_options.map { |key| [key, nil] }.to_h
context = described_class.new(provided_values)
expect(context.to_lazy_hash.keys).to contain_exactly(*expected_context_keys)
end
end
end
end
......@@ -54,4 +54,46 @@ describe Namespace do
expect(namespace.use_elasticsearch?).to eq(true)
end
end
describe '#actual_plan_name' do
let(:namespace) { create(:namespace, plan: :gold_plan) }
subject { namespace.actual_plan_name }
context 'when DB is read-only' do
before do
expect(Gitlab::Database).to receive(:read_only?) { true }
end
it 'returns free plan' do
is_expected.to eq('free')
end
it 'does not create a gitlab_subscription' do
expect { subject }.not_to change(GitlabSubscription, :count)
end
end
context 'when namespace is not persisted' do
let(:namespace) { build(:namespace, plan: :gold_plan) }
it 'returns free plan' do
is_expected.to eq('free')
end
it 'does not create a gitlab_subscription' do
expect { subject }.not_to change(GitlabSubscription, :count)
end
end
context 'when DB is not read-only' do
it 'returns gold plan' do
is_expected.to eq('gold')
end
it 'creates a gitlab_subscription' do
expect { subject }.to change(GitlabSubscription, :count).by(1)
end
end
end
end
......@@ -31,7 +31,7 @@ describe UpdateAllMirrorsWorker do
inner_context = nil
outer_context = nil
Gitlab::ApplicationContext.with_context(project: build_stubbed(:project)) do
Gitlab::ApplicationContext.with_context(project: build(:project)) do
outer_context = Labkit::Context.current.to_h
expect(worker).to receive(:schedule_mirrors!) do
......
......@@ -47,7 +47,8 @@ module API
Gitlab::ApplicationContext.push(
user: -> { current_user },
project: -> { @project },
namespace: -> { @group }
namespace: -> { @group },
caller_id: route.origin
)
end
......
......@@ -9,7 +9,8 @@ module API
before do
Gitlab::ApplicationContext.push(
user: -> { actor&.user },
project: -> { project }
project: -> { project },
caller_id: route.origin
)
end
......
......@@ -5,12 +5,13 @@ module Gitlab
class ApplicationContext
include Gitlab::Utils::LazyAttributes
Attribute = Struct.new(:name, :type)
Attribute = Struct.new(:name, :type, :evaluation)
APPLICATION_ATTRIBUTES = [
Attribute.new(:project, Project),
Attribute.new(:namespace, Namespace),
Attribute.new(:user, User)
Attribute.new(:user, User),
Attribute.new(:caller_id, String)
].freeze
def self.with_context(args, &block)
......@@ -37,6 +38,7 @@ module Gitlab
hash[:user] = -> { username } if set_values.include?(:user)
hash[:project] = -> { project_path } if set_values.include?(:project)
hash[:root_namespace] = -> { root_namespace_path } if include_namespace?
hash[:caller_id] = caller_id if set_values.include?(:caller_id)
end
end
......@@ -75,3 +77,5 @@ module Gitlab
end
end
end
Gitlab::ApplicationContext.prepend_if_ee('EE::Gitlab::ApplicationContext')
......@@ -924,7 +924,7 @@ describe ApplicationController do
end
it 'sets the group if it was available' do
group = build_stubbed(:group)
group = build(:group)
controller.instance_variable_set(:@group, group)
get :index, format: :json
......@@ -933,12 +933,18 @@ describe ApplicationController do
end
it 'sets the project if one was available' do
project = build_stubbed(:project)
project = build(:project)
controller.instance_variable_set(:@project, project)
get :index, format: :json
expect(json_response['meta.project']).to eq(project.full_path)
end
it 'sets the caller_id as controller#action' do
get :index, format: :json
expect(json_response['meta.caller_id']).to eq('AnonymousController#index')
end
end
end
......@@ -43,11 +43,11 @@ describe Gitlab::ApplicationContext do
describe '#to_lazy_hash' do
let(:user) { build(:user) }
let(:project) { build(:project) }
let(:namespace) { build(:group) }
let(:subgroup) { build(:group, parent: namespace) }
let(:namespace) { create(:group) }
let(:subgroup) { create(:group, parent: namespace) }
def result(context)
context.to_lazy_hash.transform_values { |v| v.call }
context.to_lazy_hash.transform_values { |v| v.respond_to?(:call) ? v.call : v }
end
it 'does not call the attributes until needed' do
......@@ -78,27 +78,5 @@ describe Gitlab::ApplicationContext do
expect(result(context))
.to include(project: project.full_path, root_namespace: project.full_path_components.first)
end
context 'only include values for which an option was specified' do
using RSpec::Parameterized::TableSyntax
where(:provided_options, :expected_context_keys) do
[:user, :namespace, :project] | [:user, :project, :root_namespace]
[:user, :project] | [:user, :project, :root_namespace]
[:user, :namespace] | [:user, :root_namespace]
[:user] | [:user]
[] | []
end
with_them do
it do
# Build a hash that has all `provided_options` as keys, and `nil` as value
provided_values = provided_options.map { |key| [key, nil] }.to_h
context = described_class.new(provided_values)
expect(context.to_lazy_hash.keys).to contain_exactly(*expected_context_keys)
end
end
end
end
end
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