Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
a9ac7c25
Commit
a9ac7c25
authored
Oct 14, 2020
by
Andrei Stoicescu
Committed by
Miguel Rincon
Oct 14, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add billable seats to VueX state
- add a VueX module that handles billable seats
parent
803d87fe
Changes
16
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
390 additions
and
7 deletions
+390
-7
app/assets/javascripts/api.js
app/assets/javascripts/api.js
+21
-0
ee/app/assets/javascripts/billings/constants.js
ee/app/assets/javascripts/billings/constants.js
+5
-0
ee/app/assets/javascripts/billings/stores/index.js
ee/app/assets/javascripts/billings/stores/index.js
+2
-0
ee/app/assets/javascripts/billings/stores/modules/seats/actions.js
...sets/javascripts/billings/stores/modules/seats/actions.js
+28
-0
ee/app/assets/javascripts/billings/stores/modules/seats/index.js
...assets/javascripts/billings/stores/modules/seats/index.js
+10
-0
ee/app/assets/javascripts/billings/stores/modules/seats/mutation_types.js
...vascripts/billings/stores/modules/seats/mutation_types.js
+5
-0
ee/app/assets/javascripts/billings/stores/modules/seats/mutations.js
...ts/javascripts/billings/stores/modules/seats/mutations.js
+33
-0
ee/app/assets/javascripts/billings/stores/modules/seats/state.js
...assets/javascripts/billings/stores/modules/seats/state.js
+9
-0
ee/spec/frontend/api_spec.js
ee/spec/frontend/api_spec.js
+20
-0
ee/spec/frontend/billings/components/subscription_table_spec.js
...c/frontend/billings/components/subscription_table_spec.js
+1
-1
ee/spec/frontend/billings/mock_data.js
ee/spec/frontend/billings/mock_data.js
+44
-1
ee/spec/frontend/billings/store/modules/seats/actions_spec.js
...pec/frontend/billings/store/modules/seats/actions_spec.js
+136
-0
ee/spec/frontend/billings/store/modules/seats/mutations_spec.js
...c/frontend/billings/store/modules/seats/mutations_spec.js
+68
-0
ee/spec/frontend/billings/store/modules/subscription/actions_spec.js
...ntend/billings/store/modules/subscription/actions_spec.js
+1
-1
ee/spec/frontend/billings/store/modules/subscription/mutations_spec.js
...end/billings/store/modules/subscription/mutations_spec.js
+4
-4
locale/gitlab.pot
locale/gitlab.pot
+3
-0
No files found.
app/assets/javascripts/api.js
View file @
a9ac7c25
...
...
@@ -68,6 +68,7 @@ const Api = {
usageDataIncrementUniqueUsersPath
:
'
/api/:version/usage_data/increment_unique_users
'
,
featureFlagUserLists
:
'
/api/:version/projects/:id/feature_flags_user_lists
'
,
featureFlagUserList
:
'
/api/:version/projects/:id/feature_flags_user_lists/:list_iid
'
,
billableGroupMembersPath
:
'
/api/:version/groups/:id/billable_members
'
,
group
(
groupId
,
callback
=
()
=>
{})
{
const
url
=
Api
.
buildUrl
(
Api
.
groupPath
).
replace
(
'
:id
'
,
groupId
);
...
...
@@ -756,6 +757,26 @@ const Api = {
return
axios
.
delete
(
url
);
},
fetchBillableGroupMembersList
(
namespaceId
,
options
=
{},
callback
=
()
=>
{})
{
const
url
=
Api
.
buildUrl
(
this
.
billableGroupMembersPath
).
replace
(
'
:id
'
,
namespaceId
);
const
defaults
=
{
per_page
:
DEFAULT_PER_PAGE
,
page
:
1
,
};
return
axios
.
get
(
url
,
{
params
:
{
...
defaults
,
...
options
,
},
})
.
then
(({
data
,
headers
})
=>
{
callback
(
data
);
return
{
data
,
headers
};
});
},
};
export
default
Api
;
ee/app/assets/javascripts/billings/constants.js
View file @
a9ac7c25
export
const
TABLE_TYPE_DEFAULT
=
'
default
'
;
export
const
TABLE_TYPE_FREE
=
'
free
'
;
export
const
TABLE_TYPE_TRIAL
=
'
trial
'
;
// Billable Seats HTTP headers
export
const
HEADER_TOTAL_ENTRIES
=
'
x-total
'
;
export
const
HEADER_PAGE_NUMBER
=
'
x-page
'
;
export
const
HEADER_ITEMS_PER_PAGE
=
'
x-per-page
'
;
ee/app/assets/javascripts/billings/stores/index.js
View file @
a9ac7c25
import
Vue
from
'
vue
'
;
import
Vuex
from
'
vuex
'
;
import
subscription
from
'
./modules/subscription/index
'
;
import
seats
from
'
./modules/seats/index
'
;
Vue
.
use
(
Vuex
);
...
...
@@ -8,5 +9,6 @@ export default () =>
new
Vuex
.
Store
({
modules
:
{
subscription
,
seats
,
},
});
ee/app/assets/javascripts/billings/stores/modules/seats/actions.js
0 → 100644
View file @
a9ac7c25
import
Api
from
'
~/api
'
;
import
*
as
types
from
'
./mutation_types
'
;
import
createFlash
from
'
~/flash
'
;
import
{
s__
}
from
'
~/locale
'
;
export
const
setNamespaceId
=
({
commit
},
namespaceId
)
=>
{
commit
(
types
.
SET_NAMESPACE_ID
,
namespaceId
);
};
export
const
fetchBillableMembersList
=
({
dispatch
,
state
},
page
)
=>
{
dispatch
(
'
requestBillableMembersList
'
);
return
Api
.
fetchBillableGroupMembersList
(
state
.
namespaceId
,
{
page
})
.
then
(
data
=>
dispatch
(
'
receiveBillableMembersListSuccess
'
,
data
))
.
catch
(()
=>
dispatch
(
'
receiveBillableMembersListError
'
));
};
export
const
requestBillableMembersList
=
({
commit
})
=>
commit
(
types
.
REQUEST_BILLABLE_MEMBERS
);
export
const
receiveBillableMembersListSuccess
=
({
commit
},
response
)
=>
commit
(
types
.
RECEIVE_BILLABLE_MEMBERS_SUCCESS
,
response
);
export
const
receiveBillableMembersListError
=
({
commit
})
=>
{
createFlash
({
message
:
s__
(
'
Billing|An error occurred while loading billable members list
'
),
});
commit
(
types
.
RECEIVE_BILLABLE_MEMBERS_ERROR
);
};
ee/app/assets/javascripts/billings/stores/modules/seats/index.js
0 → 100644
View file @
a9ac7c25
import
*
as
actions
from
'
./actions
'
;
import
mutations
from
'
./mutations
'
;
import
state
from
'
./state
'
;
export
default
{
namespaced
:
true
,
actions
,
mutations
,
state
,
};
ee/app/assets/javascripts/billings/stores/modules/seats/mutation_types.js
0 → 100644
View file @
a9ac7c25
export
const
SET_NAMESPACE_ID
=
'
SET_NAMESPACE_ID
'
;
export
const
REQUEST_BILLABLE_MEMBERS
=
'
REQUEST_BILLABLE_MEMBERS
'
;
export
const
RECEIVE_BILLABLE_MEMBERS_SUCCESS
=
'
RECEIVE_BILLABLE_MEMBERS_SUCCESS
'
;
export
const
RECEIVE_BILLABLE_MEMBERS_ERROR
=
'
RECEIVE_BILLABLE_MEMBERS_ERROR
'
;
ee/app/assets/javascripts/billings/stores/modules/seats/mutations.js
0 → 100644
View file @
a9ac7c25
import
*
as
types
from
'
./mutation_types
'
;
import
{
HEADER_TOTAL_ENTRIES
,
HEADER_PAGE_NUMBER
,
HEADER_ITEMS_PER_PAGE
,
}
from
'
../../../constants
'
;
export
default
{
[
types
.
SET_NAMESPACE_ID
](
state
,
payload
)
{
state
.
namespaceId
=
payload
;
},
[
types
.
REQUEST_BILLABLE_MEMBERS
](
state
)
{
state
.
isLoading
=
true
;
state
.
hasError
=
false
;
},
[
types
.
RECEIVE_BILLABLE_MEMBERS_SUCCESS
](
state
,
payload
)
{
const
{
data
,
headers
}
=
payload
;
state
.
members
=
data
;
state
.
total
=
headers
[
HEADER_TOTAL_ENTRIES
];
state
.
page
=
headers
[
HEADER_PAGE_NUMBER
];
state
.
perPage
=
headers
[
HEADER_ITEMS_PER_PAGE
];
state
.
isLoading
=
false
;
},
[
types
.
RECEIVE_BILLABLE_MEMBERS_ERROR
](
state
)
{
state
.
isLoading
=
false
;
state
.
hasError
=
true
;
},
};
ee/app/assets/javascripts/billings/stores/modules/seats/state.js
0 → 100644
View file @
a9ac7c25
export
default
()
=>
({
isLoading
:
false
,
hasError
:
false
,
namespaceId
:
null
,
members
:
[],
total
:
null
,
page
:
null
,
perPage
:
null
,
});
ee/spec/frontend/api_spec.js
View file @
a9ac7c25
...
...
@@ -869,4 +869,24 @@ describe('Api', () => {
});
});
});
describe
(
'
Billable members list
'
,
()
=>
{
let
expectedUrl
;
let
namespaceId
;
beforeEach
(()
=>
{
namespaceId
=
1000
;
expectedUrl
=
`
${
dummyUrlRoot
}
/api/
${
dummyApiVersion
}
/groups/
${
namespaceId
}
/billable_members`
;
});
describe
(
'
fetchBillableGroupMembersList
'
,
()
=>
{
it
(
'
GETs the right url
'
,
()
=>
{
mock
.
onGet
(
expectedUrl
).
replyOnce
(
httpStatus
.
OK
,
[]);
return
Api
.
fetchBillableGroupMembersList
(
namespaceId
).
then
(({
data
})
=>
{
expect
(
data
).
toEqual
([]);
});
});
});
});
});
ee/spec/frontend/billings/components/subscription_table_spec.js
View file @
a9ac7c25
...
...
@@ -5,7 +5,7 @@ import createStore from 'ee/billings/stores';
import
*
as
types
from
'
ee/billings/stores/modules/subscription/mutation_types
'
;
import
SubscriptionTable
from
'
ee/billings/components/subscription_table.vue
'
;
import
SubscriptionTableRow
from
'
ee/billings/components/subscription_table_row.vue
'
;
import
mockDataSubscription
from
'
../mock_data
'
;
import
{
mockDataSubscription
}
from
'
../mock_data
'
;
const
TEST_NAMESPACE_NAME
=
'
GitLab.com
'
;
const
CUSTOMER_PORTAL_URL
=
'
https://customers.gitlab.com/subscriptions
'
;
...
...
ee/spec/frontend/billings/mock_data.js
View file @
a9ac7c25
export
default
{
import
{
HEADER_TOTAL_ENTRIES
,
HEADER_PAGE_NUMBER
,
HEADER_ITEMS_PER_PAGE
,
}
from
'
ee/billings/constants
'
;
export
const
mockDataSubscription
=
{
gold
:
{
plan
:
{
name
:
'
Gold
'
,
...
...
@@ -58,3 +64,40 @@ export default {
},
},
};
export
const
mockDataSeats
=
{
data
:
[
{
id
:
1
,
name
:
'
Administrator
'
,
username
:
'
root
'
,
state
:
'
active
'
,
avatar_url
:
'
https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80
\
u0026d=identicon
'
,
web_url
:
'
http://192.168.1.209:3001/root
'
,
},
{
id
:
3
,
name
:
'
Agustin Walker
'
,
username
:
'
lester.orn
'
,
state
:
'
active
'
,
avatar_url
:
'
https://www.gravatar.com/avatar/772352aed294c4b3e6f236b0624764b6?s=80
\
u0026d=identicon
'
,
web_url
:
'
http://192.168.1.209:3001/lester.orn
'
,
},
{
id
:
5
,
name
:
'
Joella Miller
'
,
username
:
'
era
'
,
state
:
'
active
'
,
avatar_url
:
'
https://www.gravatar.com/avatar/8b306a0c173657865f6a5a6c7120b408?s=80
\
u0026d=identicon
'
,
web_url
:
'
http://192.168.1.209:3001/era
'
,
},
],
headers
:
{
[
HEADER_TOTAL_ENTRIES
]:
'
3
'
,
[
HEADER_PAGE_NUMBER
]:
'
1
'
,
[
HEADER_ITEMS_PER_PAGE
]:
'
1
'
,
},
};
ee/spec/frontend/billings/store/modules/seats/actions_spec.js
0 → 100644
View file @
a9ac7c25
import
MockAdapter
from
'
axios-mock-adapter
'
;
import
testAction
from
'
helpers/vuex_action_helper
'
;
import
state
from
'
ee/billings/stores/modules/seats/state
'
;
import
*
as
types
from
'
ee/billings/stores/modules/seats/mutation_types
'
;
import
*
as
actions
from
'
ee/billings/stores/modules/seats/actions
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
createFlash
from
'
~/flash
'
;
import
{
mockDataSeats
}
from
'
../../../mock_data
'
;
jest
.
mock
(
'
~/flash
'
);
describe
(
'
seats actions
'
,
()
=>
{
let
mockedState
;
let
mock
;
beforeEach
(()
=>
{
mockedState
=
state
();
mock
=
new
MockAdapter
(
axios
);
});
afterEach
(()
=>
{
mock
.
restore
();
createFlash
.
mockClear
();
});
describe
(
'
setNamespaceId
'
,
()
=>
{
it
(
'
should commit the correct mutuation
'
,
()
=>
{
const
namespaceId
=
1
;
testAction
(
actions
.
setNamespaceId
,
namespaceId
,
mockedState
,
[
{
type
:
types
.
SET_NAMESPACE_ID
,
payload
:
namespaceId
,
},
],
[],
);
});
});
describe
(
'
fetchBillableMembersList
'
,
()
=>
{
beforeEach
(()
=>
{
gon
.
api_version
=
'
v4
'
;
mockedState
.
namespaceId
=
1
;
});
describe
(
'
on success
'
,
()
=>
{
beforeEach
(()
=>
{
mock
.
onGet
(
'
/api/v4/groups/1/billable_members
'
)
.
replyOnce
(
200
,
mockDataSeats
.
data
,
mockDataSeats
.
headers
);
});
it
(
'
should dispatch the request and success actions
'
,
()
=>
{
testAction
(
actions
.
fetchBillableMembersList
,
{},
mockedState
,
[],
[
{
type
:
'
requestBillableMembersList
'
},
{
type
:
'
receiveBillableMembersListSuccess
'
,
payload
:
mockDataSeats
,
},
],
);
});
});
describe
(
'
on error
'
,
()
=>
{
beforeEach
(()
=>
{
mock
.
onGet
(
'
/api/v4/groups/1/billable_members
'
).
replyOnce
(
404
,
{});
});
it
(
'
should dispatch the request and error actions
'
,
()
=>
{
testAction
(
actions
.
fetchBillableMembersList
,
{},
mockedState
,
[],
[{
type
:
'
requestBillableMembersList
'
},
{
type
:
'
receiveBillableMembersListError
'
}],
);
});
});
});
describe
(
'
requestBillableMembersList
'
,
()
=>
{
it
(
'
should commit the request mutation
'
,
()
=>
{
testAction
(
actions
.
requestBillableMembersList
,
{},
state
,
[{
type
:
types
.
REQUEST_BILLABLE_MEMBERS
}],
[],
);
});
});
describe
(
'
receiveBillableMembersListSuccess
'
,
()
=>
{
it
(
'
should commit the success mutation
'
,
()
=>
{
testAction
(
actions
.
receiveBillableMembersListSuccess
,
mockDataSeats
,
mockedState
,
[
{
type
:
types
.
RECEIVE_BILLABLE_MEMBERS_SUCCESS
,
payload
:
mockDataSeats
,
},
],
[],
);
});
});
describe
(
'
receiveBillableMembersListError
'
,
()
=>
{
it
(
'
should commit the error mutation
'
,
done
=>
{
testAction
(
actions
.
receiveBillableMembersListError
,
{},
mockedState
,
[{
type
:
types
.
RECEIVE_BILLABLE_MEMBERS_ERROR
}],
[],
()
=>
{
expect
(
createFlash
).
toHaveBeenCalled
();
done
();
},
);
});
});
});
ee/spec/frontend/billings/store/modules/seats/mutations_spec.js
0 → 100644
View file @
a9ac7c25
import
createState
from
'
ee/billings/stores/modules/seats/state
'
;
import
*
as
types
from
'
ee/billings/stores/modules/seats/mutation_types
'
;
import
mutations
from
'
ee/billings/stores/modules/seats/mutations
'
;
import
{
mockDataSeats
}
from
'
../../../mock_data
'
;
describe
(
'
EE billings seats module mutations
'
,
()
=>
{
let
state
;
beforeEach
(()
=>
{
state
=
createState
();
});
describe
(
types
.
SET_NAMESPACE_ID
,
()
=>
{
it
(
'
sets namespaceId
'
,
()
=>
{
const
expectedNamespaceId
=
'
test
'
;
expect
(
state
.
namespaceId
).
toBeNull
();
mutations
[
types
.
SET_NAMESPACE_ID
](
state
,
expectedNamespaceId
);
expect
(
state
.
namespaceId
).
toEqual
(
expectedNamespaceId
);
});
});
describe
(
types
.
REQUEST_BILLABLE_MEMBERS
,
()
=>
{
beforeEach
(()
=>
{
mutations
[
types
.
REQUEST_BILLABLE_MEMBERS
](
state
);
});
it
(
'
sets isLoading to true
'
,
()
=>
{
expect
(
state
.
isLoading
).
toBeTruthy
();
});
it
(
'
sets hasError to false
'
,
()
=>
{
expect
(
state
.
hasError
).
toBeFalsy
();
});
});
describe
(
types
.
RECEIVE_BILLABLE_MEMBERS_SUCCESS
,
()
=>
{
beforeEach
(()
=>
{
mutations
[
types
.
RECEIVE_BILLABLE_MEMBERS_SUCCESS
](
state
,
mockDataSeats
);
});
it
(
'
sets state as expected
'
,
()
=>
{
expect
(
state
.
total
).
toBe
(
'
3
'
);
expect
(
state
.
page
).
toBe
(
'
1
'
);
expect
(
state
.
perPage
).
toBe
(
'
1
'
);
});
it
(
'
sets isLoading to false
'
,
()
=>
{
expect
(
state
.
isLoading
).
toBeFalsy
();
});
});
describe
(
types
.
RECEIVE_BILLABLE_MEMBERS_ERROR
,
()
=>
{
beforeEach
(()
=>
{
mutations
[
types
.
RECEIVE_BILLABLE_MEMBERS_ERROR
](
state
);
});
it
(
'
sets isLoading to false
'
,
()
=>
{
expect
(
state
.
isLoading
).
toBeFalsy
();
});
it
(
'
sets hasError to true
'
,
()
=>
{
expect
(
state
.
hasError
).
toBeTruthy
();
});
});
});
ee/spec/frontend/billings/store/modules/subscription/actions_spec.js
View file @
a9ac7c25
...
...
@@ -6,7 +6,7 @@ import * as types from 'ee/billings/stores/modules/subscription/mutation_types';
import
*
as
actions
from
'
ee/billings/stores/modules/subscription/actions
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
mockDataSubscription
from
'
../../../mock_data
'
;
import
{
mockDataSubscription
}
from
'
../../../mock_data
'
;
describe
(
'
subscription actions
'
,
()
=>
{
let
mockedState
;
...
...
ee/spec/frontend/billings/store/modules/subscription/mutations_spec.js
View file @
a9ac7c25
...
...
@@ -3,7 +3,7 @@ import * as types from 'ee/billings/stores/modules/subscription/mutation_types';
import
mutations
from
'
ee/billings/stores/modules/subscription/mutations
'
;
import
{
TABLE_TYPE_DEFAULT
,
TABLE_TYPE_FREE
,
TABLE_TYPE_TRIAL
}
from
'
ee/billings/constants
'
;
import
{
convertObjectPropsToCamelCase
}
from
'
~/lib/utils/common_utils
'
;
import
mockSubscriptionData
from
'
../../../mock_data
'
;
import
{
mockDataSubscription
}
from
'
../../../mock_data
'
;
describe
(
'
EE billings subscription module mutations
'
,
()
=>
{
let
state
;
...
...
@@ -52,9 +52,9 @@ describe('EE billings subscription module mutations', () => {
describe
.
each
`
desc | subscription | tableKey
${
'
with Gold subscription
'
}
|
${
mock
SubscriptionData
.
gold
}
|
${
TABLE_TYPE_DEFAULT
}
${
'
with Free plan
'
}
|
${
mock
SubscriptionData
.
free
}
|
${
TABLE_TYPE_FREE
}
${
'
with Gold trial
'
}
|
${
mock
SubscriptionData
.
trial
}
|
${
TABLE_TYPE_TRIAL
}
${
'
with Gold subscription
'
}
|
${
mock
DataSubscription
.
gold
}
|
${
TABLE_TYPE_DEFAULT
}
${
'
with Free plan
'
}
|
${
mock
DataSubscription
.
free
}
|
${
TABLE_TYPE_FREE
}
${
'
with Gold trial
'
}
|
${
mock
DataSubscription
.
trial
}
|
${
TABLE_TYPE_TRIAL
}
`
(
'
$desc
'
,
({
subscription
,
tableKey
})
=>
{
beforeEach
(()
=>
{
state
.
isLoading
=
true
;
...
...
locale/gitlab.pot
View file @
a9ac7c25
...
...
@@ -4131,6 +4131,9 @@ msgstr ""
msgid "BillingPlan|Upgrade"
msgstr ""
msgid "Billing|An error occurred while loading billable members list"
msgstr ""
msgid "Bitbucket Server Import"
msgstr ""
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment