Skip to content
Snippets Groups Projects
Commit d1d63b17 authored by Nicolò Mezzopera's avatar Nicolò Mezzopera
Browse files

Merge branch '238607-remove-feature-flag-licenses_app-and-code-that-is-enabled-by-it' into 'master'

Remove feature flag licenses_app and code that is enabled by it

Closes #238607

See merge request gitlab-org/gitlab!42520
parents 9c4ea521 5434772f
No related branches found
No related tags found
No related merge requests found
Showing
with 18 additions and 1166 deletions
export default () => ({
licenses: [],
deleteQueue: [],
isLoadingLicenses: false,
licensesPath: '',
deleteLicensePath: '',
newLicensePath: '',
downloadLicensePath: '',
currentActiveUserCount: null,
});
import mountInstanceLicenseApp from 'ee/licenses';
document.addEventListener('DOMContentLoaded', () => {
const mountElement = document.getElementById('instance-license-mount-element');
mountInstanceLicenseApp(mountElement);
});
Loading
Loading
@@ -5,64 +5,3 @@
color: $gray-600;
}
}
.license-card-body {
overflow-x: scroll;
@include media-breakpoint-up(lg) {
overflow-x: hidden;
}
}
.license-table {
min-width: max-content;
.license-row:first-child .license-cell {
border-top: 0;
}
.license-cell:last-child {
border-right: 0;
}
}
.license-cell {
border-color: $gray-100;
border-style: solid;
border-width: 1px 1px 0 0;
flex-basis: 0;
.title {
color: $gray-500;
line-height: $gl-line-height;
}
.value {
color: $gray-900;
&.number {
font-size: 1.25rem;
}
}
}
.license-header-cell {
flex-basis: initial;
width: $license-header-cell-width;
.title {
color: $gray-900;
}
}
.skeleton-license-card {
.skeleton-bar {
max-height: $gl-line-height;
.skeleton-line-1,
.skeleton-line-1::after {
height: 100%;
width: 100%;
}
}
}
Loading
Loading
@@ -10,10 +10,6 @@ module LicenseHelper
License.current&.current_active_users_count || active_user_count
end
 
def guest_user_count
active_user_count - User.active.excluding_guests.count
end
def maximum_user_count
License.current&.maximum_user_count || 0
end
Loading
Loading
@@ -53,22 +49,6 @@ module LicenseHelper
!Gitlab::CurrentSettings.should_check_namespace_plan? && show_promotions? && show_callout?('promote_advanced_search_dismissed') && !License.feature_available?(:elastic_search)
end
 
def license_app_data
{ data: { active_user_count: active_user_count,
guest_user_count: guest_user_count,
licenses_path: api_licenses_url,
delete_license_path: api_license_url(id: ':id'),
new_license_path: new_admin_license_path, download_license_path: download_admin_license_path } }
end
def api_licenses_url
expose_url(api_v4_licenses_path)
end
def api_license_url(args)
expose_url(api_v4_license_path(args))
end
extend self
 
private
Loading
Loading
- page_title _('License')
 
- if Feature.enabled?(:licenses_app)
#instance-license-mount-element{ license_app_data }
- else
%h3.page-title
= _('Your License')
- if @license&.trial?
= render 'upload_buy_license'
- else
= link_to _('Upload New License'), new_admin_license_path, class: 'btn btn-success float-right', data: { qa_selector: 'license_upload_link' }
%h3.page-title
= _('Your License')
- if @license&.trial?
= render 'upload_buy_license'
- else
= link_to _('Upload New License'), new_admin_license_path, class: 'btn btn-success float-right', data: { qa_selector: 'license_upload_link' }
 
%hr
%hr
 
- if License.future_dated_only?
.gl-alert.gl-alert-info
= sprite_icon('information-o', css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
.gl-alert-body
%h4.gl-alert-title= _('You do not have an active license')
= _('You have a license that activates at a future date. Please see the License History table below.')
- if License.future_dated_only?
.gl-alert.gl-alert-info
= sprite_icon('information-o', css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
.gl-alert-body
%h4.gl-alert-title= _('You do not have an active license')
= _('You have a license that activates at a future date. Please see the License History table below.')
 
- if @license.present?
= render 'info'
= render 'breakdown'
- if @license.present?
= render 'info'
= render 'breakdown'
 
- if @licenses.present?
= render 'license_history'
- if @licenses.present?
= render 'license_history'
---
name: licenses_app
introduced_by_url:
rollout_issue_url:
group:
type: development
default_enabled: false
Loading
Loading
@@ -6,7 +6,6 @@ RSpec.describe "Admin uploads license", :js do
let_it_be(:admin) { create(:admin) }
 
before do
stub_feature_flags(licenses_app: false)
sign_in(admin)
end
 
Loading
Loading
Loading
Loading
@@ -6,7 +6,6 @@ RSpec.describe "Admin views license" do
let_it_be(:admin) { create(:admin) }
 
before do
stub_feature_flags(licenses_app: false)
sign_in(admin)
allow_any_instance_of(Gitlab::ExpiringSubscriptionMessage).to receive(:grace_period_effective_from).and_return(Date.today - 45.days)
end
Loading
Loading
# frozen_string_literal: true
require "spec_helper"
RSpec.describe "Licenses app", :js do
let(:admin) { create(:admin) }
let!(:licenses) do
[
create(:license, data: build(:gitlab_license, restrictions: { active_user_count: 2000 }).export),
create(:license, data: build(:gitlab_license, expires_at: Date.today - 10, restrictions: { active_user_count: 2000, plan: 'ultimate' }).export)
]
end
def visit_page
visit(admin_license_path)
find('.js-license-table', match: :first)
end
def assert_usage_row(row, license)
header, seats_in_license, seats_in_use, historical_max, overage = row.find_all('.license-cell').to_a
expect(header).to have_content 'Usage'
expect(seats_in_license).to have_content 'Seats in license'
expect(seats_in_license).to have_content license.restrictions[:active_user_count]
expect(seats_in_use).to have_content 'Seats currently in use'
if license.exclude_guests_from_active_count?
expect(seats_in_use).to have_content User.active.excluding_guests.count
else
expect(seats_in_use).to have_content User.active.count
end
expect(historical_max).to have_content 'Max seats used'
expect(historical_max).to have_content license.historical_max
expect(overage).to have_content 'Users outside of license'
expect(overage).to have_content license.overage
end
def assert_validity_row(row, license)
header, starts_at, expires_at, created_at = row.find_all('.license-cell').to_a
expect(header).to have_content 'Validity'
expect(starts_at).to have_content 'Start date'
expect(starts_at).to have_content license.starts_at.strftime('%B %-d, %Y')
expect(expires_at).to have_content 'End date'
expect(expires_at).to have_content license.expires_at.strftime('%B %-d, %Y')
if license.expired?
expect(expires_at).to have_content 'Expired'
else
expect(expires_at).not_to have_content 'Expired'
end
expect(created_at).to have_content 'Uploaded on'
expect(created_at).to have_content license.created_at.strftime('%B %-d, %Y')
end
def assert_registration_row(row, license)
header, name, email, company = row.find_all('.license-cell').to_a
expect(header).to have_content 'Registration'
expect(name).to have_content 'Licensed to'
expect(name).to have_content license.licensee['Name'] || 'Unknown'
expect(email).to have_content 'Email address'
expect(email).to have_content license.licensee['Email'] || 'Unknown'
expect(company).to have_content 'Company'
expect(company).to have_content license.licensee['Company'] || 'Unknown'
end
def assert_license_card(card, license)
top_row, middle_row, bottom_row = card.find_all('.license-row').to_a
assert_usage_row(top_row, license)
assert_validity_row(middle_row, license)
assert_registration_row(bottom_row, license)
end
before do
stub_feature_flags(licenses_app: true)
sign_in(admin)
end
it 'renders a list of licenses' do
visit_page
licenses.each_with_index do |license, index|
assert_license_card(find_all('.license-table')[index], licenses.reverse[index])
end
end
it 'deletes a license' do
visit_page
license_card = find('.license-card', match: :first)
current_id = License.current.id
license_card.find('.js-manage-license').click
page.accept_alert 'Are you sure you want to permanently delete this license?' do
license_card.find('.js-delete-license').click
end
expect(license_card).not_to have_selector('.license-card-loading')
expect(License.find_by(id: current_id)).to be_nil
end
end
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`InstanceCardsList renders a list of license cards 1`] = `
<div>
<div
class="d-flex justify-content-between align-items-center"
>
<h4>
Instance license
</h4>
<gl-button-stub
category="primary"
class="my-3 js-add-license"
href="/newLicensePath"
icon=""
size="medium"
variant="success"
>
Add license
</gl-button-stub>
</div>
<ul
class="license-list list-unstyled"
>
<li>
<license-card-stub
iscurrentlicense="true"
license="[object Object]"
/>
</li>
<li>
<license-card-stub
license="[object Object]"
/>
</li>
</ul>
</div>
`;
exports[`InstanceCardsList renders a message when there are no licenses 1`] = `
<div>
<div
class="d-flex justify-content-between align-items-center"
>
<h4>
Instance license
</h4>
<gl-button-stub
category="primary"
class="my-3 js-add-license"
href="/newLicensePath"
icon=""
size="medium"
variant="success"
>
Add license
</gl-button-stub>
</div>
<ul
class="license-list list-unstyled"
>
<li>
<strong>
No licenses found.
</strong>
</li>
</ul>
</div>
`;
exports[`InstanceCardsList renders a skeleton loading card if loading licenses 1`] = `
<div>
<div
class="d-flex justify-content-between align-items-center"
>
<h4>
Instance license
</h4>
<gl-button-stub
category="primary"
class="my-3 js-add-license"
href="/newLicensePath"
icon=""
size="medium"
variant="success"
>
Add license
</gl-button-stub>
</div>
<ul
class="license-list list-unstyled"
>
<li>
<skeleton-license-card-stub />
</li>
</ul>
</div>
`;
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`LicenseCardBody renders a license card body 1`] = `
<div
class="card-body license-card-body p-0"
>
<div
class="license-table js-license-table"
>
<div
class="license-row d-flex"
>
<header-cell-stub
icon="monitor"
title="Usage"
/>
<cell-stub
isflexible="true"
title="Seats in license"
value="10"
/>
<info-cell-stub
popovercontent="Users with a Guest role or those who don't belong to any projects or groups don't count towards seats in use."
title="Seats currently in use"
value="2"
/>
<info-cell-stub
popovercontent="This is the maximum number of users that have existed at the same time since the license started. This is the minimum number of seats you will need to buy when you renew your license."
title="Max seats used"
value="20"
/>
<info-cell-stub
popovercontent="GitLab allows you to continue using your license even if you exceed the number of seats you purchased. You will be required to pay for these seats when you renew your license."
title="Users outside of license"
value="5"
/>
</div>
<div
class="license-row d-flex"
>
<header-cell-stub
icon="calendar"
title="Validity"
/>
<date-cell-stub
datenow="2017/10/10"
title="Start date"
value="2013/10/10"
/>
<date-cell-stub
datenow="2017/10/10"
isexpirable="true"
title="End date"
value="2015/10/10"
/>
<date-cell-stub
datenow="2017/10/10"
title="Uploaded on"
/>
</div>
<div
class="license-row d-flex"
>
<header-cell-stub
icon="user"
title="Registration"
/>
<cell-stub
isflexible="true"
title="Licensed to"
value="Jon Dough"
/>
<cell-stub
isflexible="true"
title="Email address"
value="email@address.tanuki"
/>
<cell-stub
isflexible="true"
title="Company"
value="TanukiVille"
/>
</div>
</div>
</div>
`;
exports[`LicenseCardBody renders a license card body without free user info for non-ultimate licenses 1`] = `
<div
class="card-body license-card-body p-0"
>
<div
class="license-table js-license-table"
>
<div
class="license-row d-flex"
>
<header-cell-stub
icon="monitor"
title="Usage"
/>
<cell-stub
isflexible="true"
title="Seats in license"
value="10"
/>
<cell-stub
isflexible="true"
popover-content="Users with a Guest role or those who don't belong to any projects or groups don't count towards seats in use."
title="Seats currently in use"
value="10"
/>
<info-cell-stub
popovercontent="This is the maximum number of users that have existed at the same time since the license started. This is the minimum number of seats you will need to buy when you renew your license."
title="Max seats used"
value="20"
/>
<info-cell-stub
popovercontent="GitLab allows you to continue using your license even if you exceed the number of seats you purchased. You will be required to pay for these seats when you renew your license."
title="Users outside of license"
value="5"
/>
</div>
<div
class="license-row d-flex"
>
<header-cell-stub
icon="calendar"
title="Validity"
/>
<date-cell-stub
datenow="2017/10/10"
title="Start date"
value="2013/10/10"
/>
<date-cell-stub
datenow="2017/10/10"
isexpirable="true"
title="End date"
value="2015/10/10"
/>
<date-cell-stub
datenow="2017/10/10"
title="Uploaded on"
/>
</div>
<div
class="license-row d-flex"
>
<header-cell-stub
icon="user"
title="Registration"
/>
<cell-stub
isflexible="true"
title="Licensed to"
value="Jon Dough"
/>
<cell-stub
isflexible="true"
title="Email address"
value="email@address.tanuki"
/>
<cell-stub
isflexible="true"
title="Company"
value="TanukiVille"
/>
</div>
</div>
</div>
`;
exports[`LicenseCardBody renders a loading state if isRemoving 1`] = `
<div
class="card-body license-card-body p-0"
>
<div
class="p-5 d-flex justify-content-center align-items-center license-card-loading"
>
<gl-icon-stub
name="spinner"
size="16"
/>
<span
class="ml-2"
>
Removing license…
</span>
</div>
</div>
`;
exports[`LicenseCardBody renders fallback licensee values 1`] = `
<div
class="card-body license-card-body p-0"
licensee="[object Object]"
>
<div
class="license-table js-license-table"
>
<div
class="license-row d-flex"
>
<header-cell-stub
icon="monitor"
title="Usage"
/>
<cell-stub
isflexible="true"
title="Seats in license"
value="10"
/>
<info-cell-stub
popovercontent="Users with a Guest role or those who don't belong to any projects or groups don't count towards seats in use."
title="Seats currently in use"
value="2"
/>
<info-cell-stub
popovercontent="This is the maximum number of users that have existed at the same time since the license started. This is the minimum number of seats you will need to buy when you renew your license."
title="Max seats used"
value="20"
/>
<info-cell-stub
popovercontent="GitLab allows you to continue using your license even if you exceed the number of seats you purchased. You will be required to pay for these seats when you renew your license."
title="Users outside of license"
value="5"
/>
</div>
<div
class="license-row d-flex"
>
<header-cell-stub
icon="calendar"
title="Validity"
/>
<date-cell-stub
datenow="2017/10/10"
title="Start date"
value="2013/10/10"
/>
<date-cell-stub
datenow="2017/10/10"
isexpirable="true"
title="End date"
value="2015/10/10"
/>
<date-cell-stub
datenow="2017/10/10"
title="Uploaded on"
/>
</div>
<div
class="license-row d-flex"
>
<header-cell-stub
icon="user"
title="Registration"
/>
<cell-stub
isflexible="true"
title="Licensed to"
value="Jon Dough"
/>
<cell-stub
isflexible="true"
title="Email address"
value="email@address.tanuki"
/>
<cell-stub
isflexible="true"
title="Company"
value="TanukiVille"
/>
</div>
</div>
</div>
`;
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`LicenseCard renders license card with a delete button and license body 1`] = `
<div
class="card license-card mb-5"
>
<div
class="card-header"
>
<div
class="d-flex justify-content-between align-items-center"
>
<h4>
GitLab Enterprise Edition Super duper
</h4>
<gl-dropdown-stub
category="tertiary"
class="js-manage-license"
headertext=""
right=""
size="medium"
text="Manage"
variant="default"
>
<!---->
<gl-dropdown-item-stub
avatarurl=""
class="js-delete-license text-danger"
iconcolor=""
iconname=""
iconrightname=""
secondarytext=""
>
Delete license
</gl-dropdown-item-stub>
</gl-dropdown-stub>
</div>
</div>
<license-card-body-stub
activeusercount="10"
guestusercount="8"
license="[object Object]"
/>
</div>
`;
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`SkeletonLicenseCard renders a skeleton license card 1`] = `
<div
class="card license-card skeleton-license-card"
>
<div
class="card-header d-flex justify-content-between align-items-center py-3"
>
<gl-skeleton-loading-stub
class="w-75 skeleton-bar"
lines="1"
/>
</div>
<div
class="card-body p-0"
>
<div
class="license-table"
>
<div
class="license-row d-flex"
>
<skeleton-header-cell-stub />
<skeleton-cell-stub />
<skeleton-cell-stub />
<skeleton-cell-stub />
<skeleton-cell-stub />
</div>
<div
class="license-row d-flex"
>
<skeleton-header-cell-stub />
<skeleton-cell-stub />
<skeleton-cell-stub />
<skeleton-cell-stub />
</div>
<div
class="license-row d-flex"
>
<skeleton-header-cell-stub />
<skeleton-cell-stub />
<skeleton-cell-stub />
<skeleton-cell-stub />
</div>
</div>
</div>
</div>
`;
import { shallowMount } from '@vue/test-utils';
import LicenseCardBody from 'ee/licenses/components/cards/license_card_body.vue';
describe('LicenseCardBody', () => {
let wrapper;
const defaultProps = {
license: {
plan: 'ultimate',
userLimit: 10,
historicalMax: 20,
overage: 5,
startsAt: '2013/10/10',
expiresAt: '2015/10/10',
licensee: {
Name: 'Jon Dough',
Email: 'email@address.tanuki',
Company: 'TanukiVille',
},
},
isRemoving: false,
activeUserCount: 10,
guestUserCount: 8,
};
function createComponent(props = {}) {
let propsData = props;
propsData.license = { ...defaultProps.license, ...(props.license || {}) };
propsData = { ...defaultProps, ...props };
wrapper = shallowMount(LicenseCardBody, {
propsData,
});
}
beforeEach(() => {
jest.spyOn(global.Date.prototype, 'toString').mockReturnValue('2017/10/10');
});
afterEach(() => {
if (wrapper) wrapper.destroy();
global.Date.prototype.toString.mockRestore();
});
it('renders a license card body', () => {
createComponent();
expect(wrapper.element).toMatchSnapshot();
});
it('renders a license card body without free user info for non-ultimate licenses', () => {
createComponent({ license: { plan: 'premium' } });
expect(wrapper.element).toMatchSnapshot();
});
it('renders a loading state if isRemoving', () => {
createComponent({ isRemoving: true });
expect(wrapper.element).toMatchSnapshot();
});
it('renders fallback licensee values', () => {
createComponent({ licensee: {} });
expect(wrapper.element).toMatchSnapshot();
});
});
import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
import { LicenseCard } from 'ee/licenses/components/cards';
describe('LicenseCard', () => {
let wrapper;
let actions;
const defaultProps = {
license: {
id: 1,
plan: 'super duper',
},
isCurrentLicense: false,
};
const defaultState = {
activeUserCount: 10,
guestUserCount: 8,
deleteQueue: [],
downloadLicensePath: '/downloadLicensePath',
};
const localVue = createLocalVue();
localVue.use(Vuex);
function createStore(newState) {
const state = { ...defaultState, ...newState };
actions = { fetchDeleteLicense: jest.fn() };
return new Vuex.Store({ state, actions });
}
function createComponent(state, props) {
const propsData = { ...defaultProps, ...props };
wrapper = shallowMount(LicenseCard, {
store: createStore(state),
propsData,
localVue,
});
}
afterEach(() => {
if (wrapper) wrapper.destroy();
});
it('renders license card with a delete button and license body', () => {
createComponent();
expect(wrapper.element).toMatchSnapshot();
});
});
import { shallowMount } from '@vue/test-utils';
import { SkeletonLicenseCard } from 'ee/licenses/components/cards';
describe('SkeletonLicenseCard', () => {
let wrapper;
function createComponent() {
wrapper = shallowMount(SkeletonLicenseCard);
}
afterEach(() => {
if (wrapper) wrapper.destroy();
});
it('renders a skeleton license card', () => {
createComponent();
expect(wrapper.element).toMatchSnapshot();
});
});
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Cell renders a number value and title through props 1`] = `
<div
class="license-cell p-3 text-nowrap flex-shrink-0 flex-grow-1"
>
<span
class="title d-flex align-items-center justify-content-start"
>
<span>
title
</span>
</span>
<div
class="value mt-2 number"
>
<span>
100
</span>
</div>
</div>
`;
exports[`Cell renders a string value and title through props 1`] = `
<div
class="license-cell p-3 text-nowrap flex-shrink-0 flex-grow-1"
>
<span
class="title d-flex align-items-center justify-content-start"
>
<span>
title
</span>
</span>
<div
class="value mt-2"
>
<span>
value
</span>
</div>
</div>
`;
exports[`Cell renders an inflexible variant 1`] = `
<div
class="license-cell p-3 text-nowrap flex-shrink-0"
>
<span
class="title d-flex align-items-center justify-content-start"
>
<span>
title
</span>
</span>
<div
class="value mt-2"
>
<span>
value
</span>
</div>
</div>
`;
exports[`Cell renders value and title slots that override props 1`] = `
<div
class="license-cell p-3 text-nowrap flex-shrink-0 flex-grow-1"
>
<span
class="title d-flex align-items-center justify-content-start"
>
<h1>
tanuki
</h1>
</span>
<div
class="value mt-2"
>
<marquee>
party
</marquee>
</div>
</div>
`;
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`DateCell renders a date value that represents a date in words and title through props 1`] = `
<cell-stub
isflexible="true"
title="title"
>
<div
class=""
>
March 6, 2018
<!---->
</div>
</cell-stub>
`;
exports[`DateCell renders a fallback value if isExpirable and no value 1`] = `
<cell-stub
isflexible="true"
title="title"
value="Never"
>
<!---->
</cell-stub>
`;
exports[`DateCell renders a string value that represents a date in words and title through props 1`] = `
<cell-stub
isflexible="true"
title="title"
>
<div
class=""
>
October 24, 2018
<!---->
</div>
</cell-stub>
`;
exports[`DateCell renders an expired warning if isExpirable and date value is before now 1`] = `
<cell-stub
isflexible="true"
title="title"
value="Never"
>
<div
class="text-danger"
>
October 24, 2018
<span>
- Expired
</span>
</div>
</cell-stub>
`;
exports[`DateCell renders date value with no warning if isExpirable and date value is after now 1`] = `
<cell-stub
isflexible="true"
title="title"
value="Never"
>
<div
class=""
>
October 24, 2018
<!---->
</div>
</cell-stub>
`;
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`InfoCell renders a number value 1`] = `
<cell-stub
class="license-info-cell"
isflexible="true"
value="100"
>
<template>
<span
class="mr-2 text"
>
title
</span>
<button
class="btn-link information-target"
type="button"
>
<gl-icon-stub
class="icon d-block"
name="information"
size="16"
/>
</button>
<gl-popover-stub
content="popoverContent"
cssclasses=""
placement="bottom"
triggers="hover"
/>
</template>
</cell-stub>
`;
exports[`InfoCell renders a title and string value with an info popover through props 1`] = `
<cell-stub
class="license-info-cell"
isflexible="true"
value="value"
>
<template>
<span
class="mr-2 text"
>
title
</span>
<button
class="btn-link information-target"
type="button"
>
<gl-icon-stub
class="icon d-block"
name="information"
size="16"
/>
</button>
<gl-popover-stub
content="popoverContent"
cssclasses=""
placement="bottom"
triggers="hover"
/>
</template>
</cell-stub>
`;
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`SkeletonCell renders a skeleton cell with a title and value loading bar 1`] = `
<cell-stub
isflexible="true"
>
<gl-skeleton-loading-stub
class="w-75 skeleton-bar"
lines="1"
/>
<gl-skeleton-loading-stub
class="w-50 skeleton-bar"
lines="1"
/>
</cell-stub>
`;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment