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
34a4563e
Commit
34a4563e
authored
Nov 11, 2021
by
Angelo Gulina
Committed by
Douglas Barbosa Alexandre
Nov 11, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Retrieve and use (current) subscription name for finalising the purchase
parent
1b58bdd5
Changes
23
Hide whitespace changes
Inline
Side-by-side
Showing
23 changed files
with
298 additions
and
122 deletions
+298
-122
ee/app/assets/javascripts/subscriptions/buy_addons_shared/components/checkout.vue
...s/subscriptions/buy_addons_shared/components/checkout.vue
+3
-3
ee/app/assets/javascripts/subscriptions/buy_addons_shared/constants.js
.../javascripts/subscriptions/buy_addons_shared/constants.js
+5
-0
ee/app/assets/javascripts/subscriptions/buy_addons_shared/utils.js
...sets/javascripts/subscriptions/buy_addons_shared/utils.js
+27
-9
ee/app/assets/javascripts/subscriptions/buy_minutes/components/app.vue
.../javascripts/subscriptions/buy_minutes/components/app.vue
+10
-3
ee/app/assets/javascripts/subscriptions/graphql/queries/state.query.graphql
...scripts/subscriptions/graphql/queries/state.query.graphql
+7
-1
ee/app/assets/javascripts/vue_shared/purchase_flow/components/checkout/confirm_order.vue
...hared/purchase_flow/components/checkout/confirm_order.vue
+5
-2
ee/app/controllers/subscriptions_controller.rb
ee/app/controllers/subscriptions_controller.rb
+5
-3
ee/app/helpers/subscriptions_helper.rb
ee/app/helpers/subscriptions_helper.rb
+2
-1
ee/app/services/gitlab_subscriptions/fetch_purchase_eligible_namespaces_service.rb
...bscriptions/fetch_purchase_eligible_namespaces_service.rb
+6
-2
ee/app/services/subscriptions/create_service.rb
ee/app/services/subscriptions/create_service.rb
+33
-10
ee/app/views/subscriptions/buy_minutes.html.haml
ee/app/views/subscriptions/buy_minutes.html.haml
+1
-1
ee/app/views/subscriptions/buy_storage.html.haml
ee/app/views/subscriptions/buy_storage.html.haml
+1
-1
ee/lib/gitlab/subscription_portal/clients/graphql.rb
ee/lib/gitlab/subscription_portal/clients/graphql.rb
+1
-0
ee/spec/fixtures/create_service_params.json
ee/spec/fixtures/create_service_params.json
+64
-29
ee/spec/frontend/subscriptions/buy_addons_shared/components/order_summary_spec.js
...ptions/buy_addons_shared/components/order_summary_spec.js
+0
-1
ee/spec/frontend/subscriptions/buy_minutes/components/app_spec.js
...frontend/subscriptions/buy_minutes/components/app_spec.js
+24
-4
ee/spec/frontend/subscriptions/buy_minutes/components/checkout/confirm_order_spec.js
...ons/buy_minutes/components/checkout/confirm_order_spec.js
+33
-34
ee/spec/frontend/subscriptions/buy_minutes/mock_data.js
ee/spec/frontend/subscriptions/buy_minutes/mock_data.js
+21
-6
ee/spec/helpers/subscriptions_helper_spec.rb
ee/spec/helpers/subscriptions_helper_spec.rb
+3
-1
ee/spec/lib/gitlab/subscription_portal/clients/graphql_spec.rb
...ec/lib/gitlab/subscription_portal/clients/graphql_spec.rb
+1
-0
ee/spec/services/gitlab_subscriptions/fetch_purchase_eligible_namespaces_service_spec.rb
...ptions/fetch_purchase_eligible_namespaces_service_spec.rb
+10
-8
ee/spec/services/subscriptions/create_service_spec.rb
ee/spec/services/subscriptions/create_service_spec.rb
+22
-1
ee/spec/support/shared_examples/views/subscription_shared_examples.rb
...ort/shared_examples/views/subscription_shared_examples.rb
+14
-2
No files found.
ee/app/assets/javascripts/subscriptions/buy_addons_shared/components/checkout.vue
View file @
34a4563e
...
@@ -16,15 +16,15 @@ export default {
...
@@ -16,15 +16,15 @@ export default {
},
},
},
},
mounted
()
{
mounted
()
{
this
.
updateSelectedPlan
Id
(
this
.
plan
.
id
);
this
.
updateSelectedPlan
(
this
.
plan
);
},
},
methods
:
{
methods
:
{
updateSelectedPlan
Id
(
planId
)
{
updateSelectedPlan
({
id
,
isAddon
}
)
{
this
.
$apollo
this
.
$apollo
.
mutate
({
.
mutate
({
mutation
:
updateState
,
mutation
:
updateState
,
variables
:
{
variables
:
{
input
:
{
selectedPlan
Id
:
planId
},
input
:
{
selectedPlan
:
{
id
,
isAddon
}
},
},
},
})
})
.
catch
((
error
)
=>
{
.
catch
((
error
)
=>
{
...
...
ee/app/assets/javascripts/subscriptions/buy_addons_shared/constants.js
View file @
34a4563e
...
@@ -8,6 +8,11 @@ export const planTags = {
...
@@ -8,6 +8,11 @@ export const planTags = {
/* eslint-enable @gitlab/require-i18n-strings */
/* eslint-enable @gitlab/require-i18n-strings */
export
const
CUSTOMERSDOT_CLIENT
=
'
customersDotClient
'
;
export
const
CUSTOMERSDOT_CLIENT
=
'
customersDotClient
'
;
export
const
GITLAB_CLIENT
=
'
gitlabClient
'
;
export
const
GITLAB_CLIENT
=
'
gitlabClient
'
;
export
const
CUSTOMER_TYPE
=
'
Customer
'
;
export
const
SUBSCRIPTION_TYPE
=
'
Subscription
'
;
export
const
NAMESPACE_TYPE
=
'
Namespace
'
;
export
const
PAYMENT_METHOD_TYPE
=
'
PaymentMethod
'
;
export
const
PLAN_TYPE
=
'
Plan
'
;
export
const
CI_MINUTES_PER_PACK
=
1000
;
export
const
CI_MINUTES_PER_PACK
=
1000
;
export
const
STORAGE_PER_PACK
=
10
;
export
const
STORAGE_PER_PACK
=
10
;
...
...
ee/app/assets/javascripts/subscriptions/buy_addons_shared/utils.js
View file @
34a4563e
import
{
CUSTOMER_TYPE
,
NAMESPACE_TYPE
,
SUBSCRIPTION_TYPE
,
PAYMENT_METHOD_TYPE
,
PLAN_TYPE
,
}
from
'
ee/subscriptions/buy_addons_shared/constants
'
;
import
{
STEPS
}
from
'
ee/subscriptions/constants
'
;
import
{
STEPS
}
from
'
ee/subscriptions/constants
'
;
import
stateQuery
from
'
ee/subscriptions/graphql/queries/state.query.graphql
'
;
import
stateQuery
from
'
ee/subscriptions/graphql/queries/state.query.graphql
'
;
import
{
convertObjectPropsToCamelCase
}
from
'
~/lib/utils/common_utils
'
;
import
{
convertObjectPropsToCamelCase
}
from
'
~/lib/utils/common_utils
'
;
...
@@ -11,26 +18,38 @@ function arrayToGraphqlArray(arr, typename) {
...
@@ -11,26 +18,38 @@ function arrayToGraphqlArray(arr, typename) {
}
}
export
function
writeInitialDataToApolloCache
(
apolloProvider
,
dataset
)
{
export
function
writeInitialDataToApolloCache
(
apolloProvider
,
dataset
)
{
const
{
groupData
,
namespaceId
,
redirectAfterSuccess
,
subscriptionQuantity
}
=
dataset
;
const
{
activeSubscriptionName
=
''
,
groupData
,
namespaceId
,
redirectAfterSuccess
,
subscriptionQuantity
,
}
=
dataset
;
// eslint-disable-next-line @gitlab/require-i18n-strings
const
eligibleNamespaces
=
arrayToGraphqlArray
(
JSON
.
parse
(
groupData
),
NAMESPACE_TYPE
);
const
eligibleNamespaces
=
arrayToGraphqlArray
(
JSON
.
parse
(
groupData
),
'
Namespace
'
);
const
quantity
=
subscriptionQuantity
||
1
;
const
quantity
=
subscriptionQuantity
||
1
;
apolloProvider
.
clients
.
defaultClient
.
cache
.
writeQuery
({
apolloProvider
.
clients
.
defaultClient
.
cache
.
writeQuery
({
query
:
stateQuery
,
query
:
stateQuery
,
data
:
{
data
:
{
activeSubscription
:
{
name
:
activeSubscriptionName
,
__typename
:
SUBSCRIPTION_TYPE
,
},
isNewUser
:
false
,
isNewUser
:
false
,
fullName
:
null
,
fullName
:
null
,
isSetupForCompany
:
false
,
isSetupForCompany
:
false
,
selectedPlanId
:
null
,
selectedPlan
:
{
id
:
null
,
isAddon
:
false
,
__typename
:
PLAN_TYPE
,
},
eligibleNamespaces
,
eligibleNamespaces
,
redirectAfterSuccess
,
redirectAfterSuccess
,
selectedNamespaceId
:
namespaceId
,
selectedNamespaceId
:
namespaceId
,
subscription
:
{
subscription
:
{
quantity
,
quantity
,
// eslint-disable-next-line @gitlab/require-i18n-strings
__typename
:
SUBSCRIPTION_TYPE
,
__typename
:
'
Subscription
'
,
},
},
paymentMethod
:
{
paymentMethod
:
{
id
:
null
,
id
:
null
,
...
@@ -38,7 +57,7 @@ export function writeInitialDataToApolloCache(apolloProvider, dataset) {
...
@@ -38,7 +57,7 @@ export function writeInitialDataToApolloCache(apolloProvider, dataset) {
creditCardExpirationYear
:
null
,
creditCardExpirationYear
:
null
,
creditCardType
:
null
,
creditCardType
:
null
,
creditCardMaskNumber
:
null
,
creditCardMaskNumber
:
null
,
__typename
:
'
PaymentMethod
'
,
__typename
:
PAYMENT_METHOD_TYPE
,
},
},
customer
:
{
customer
:
{
country
:
null
,
country
:
null
,
...
@@ -48,8 +67,7 @@ export function writeInitialDataToApolloCache(apolloProvider, dataset) {
...
@@ -48,8 +67,7 @@ export function writeInitialDataToApolloCache(apolloProvider, dataset) {
state
:
null
,
state
:
null
,
zipCode
:
null
,
zipCode
:
null
,
company
:
null
,
company
:
null
,
// eslint-disable-next-line @gitlab/require-i18n-strings
__typename
:
CUSTOMER_TYPE
,
__typename
:
'
Customer
'
,
},
},
activeStep
:
STEPS
[
0
],
activeStep
:
STEPS
[
0
],
stepList
:
STEPS
,
stepList
:
STEPS
,
...
...
ee/app/assets/javascripts/subscriptions/buy_minutes/components/app.vue
View file @
34a4563e
...
@@ -55,6 +55,14 @@ export default {
...
@@ -55,6 +55,14 @@ export default {
hasError
:
false
,
hasError
:
false
,
};
};
},
},
computed
:
{
plan
()
{
return
{
...
this
.
plans
[
0
],
isAddon
:
true
,
};
},
},
methods
:
{
methods
:
{
isQuantityValid
(
quantity
)
{
isQuantityValid
(
quantity
)
{
return
Number
.
isFinite
(
quantity
)
&&
quantity
>
0
;
return
Number
.
isFinite
(
quantity
)
&&
quantity
>
0
;
...
@@ -102,7 +110,6 @@ export default {
...
@@ -102,7 +110,6 @@ export default {
this
.
hasError
=
true
;
this
.
hasError
=
true
;
return
null
;
return
null
;
}
}
return
data
.
plans
;
return
data
.
plans
;
},
},
error
(
error
)
{
error
(
error
)
{
...
@@ -122,7 +129,7 @@ export default {
...
@@ -122,7 +129,7 @@ export default {
/>
/>
<step-order-app
v-else-if=
"!$apollo.loading"
>
<step-order-app
v-else-if=
"!$apollo.loading"
>
<template
#checkout
>
<template
#checkout
>
<checkout
:plan=
"plan
s[0]
"
>
<checkout
:plan=
"plan"
>
<template
#purchase-details
>
<template
#purchase-details
>
<addon-purchase-details
<addon-purchase-details
:product-label=
"$options.i18n.productLabel"
:product-label=
"$options.i18n.productLabel"
...
@@ -145,7 +152,7 @@ export default {
...
@@ -145,7 +152,7 @@ export default {
</checkout>
</checkout>
</template>
</template>
<
template
#order-summary
>
<
template
#order-summary
>
<order-summary
:plan=
"plan
s[0]
"
:title=
"$options.i18n.title"
>
<order-summary
:plan=
"plan"
:title=
"$options.i18n.title"
>
<template
#price-per-unit
="
{ price }">
<template
#price-per-unit
="
{ price }">
{{
pricePerUnitLabel
(
price
)
}}
{{
pricePerUnitLabel
(
price
)
}}
</
template
>
</
template
>
...
...
ee/app/assets/javascripts/subscriptions/graphql/queries/state.query.graphql
View file @
34a4563e
...
@@ -5,10 +5,16 @@ query State {
...
@@ -5,10 +5,16 @@ query State {
name
name
users
users
}
}
activeSubscription
@client
{
name
}
isNewUser
@client
isNewUser
@client
fullName
@client
fullName
@client
isSetupForCompany
@client
isSetupForCompany
@client
selectedPlanId
@client
selectedPlan
@client
{
id
isAddon
}
selectedNamespaceId
@client
selectedNamespaceId
@client
redirectAfterSuccess
@client
redirectAfterSuccess
@client
customer
@client
{
customer
@client
{
...
...
ee/app/assets/javascripts/vue_shared/purchase_flow/components/checkout/confirm_order.vue
View file @
34a4563e
...
@@ -35,11 +35,13 @@ export default {
...
@@ -35,11 +35,13 @@ export default {
},
},
update
(
data
)
{
update
(
data
)
{
const
{
customer
}
=
data
;
const
{
customer
}
=
data
;
const
{
name
}
=
data
.
activeSubscription
;
return
{
return
{
setup_for_company
:
data
.
isSetupForCompany
,
setup_for_company
:
data
.
isSetupForCompany
,
selected_group
:
data
.
selectedNamespaceId
,
selected_group
:
data
.
selectedNamespaceId
,
new_user
:
data
.
isNewUser
,
new_user
:
data
.
isNewUser
,
redirect_after_success
:
data
.
redirectAfterSuccess
,
redirect_after_success
:
data
.
redirectAfterSuccess
,
active_subscription
:
name
,
customer
:
{
customer
:
{
country
:
customer
.
country
,
country
:
customer
.
country
,
address_1
:
customer
.
address1
,
address_1
:
customer
.
address1
,
...
@@ -50,9 +52,10 @@ export default {
...
@@ -50,9 +52,10 @@ export default {
company
:
customer
.
company
,
company
:
customer
.
company
,
},
},
subscription
:
{
subscription
:
{
plan_id
:
data
.
selectedPlanId
,
payment_method_id
:
data
.
paymentMethod
.
id
,
quantity
:
data
.
subscription
.
quantity
,
quantity
:
data
.
subscription
.
quantity
,
is_addon
:
data
.
selectedPlan
.
isAddon
,
plan_id
:
data
.
selectedPlan
.
id
,
payment_method_id
:
data
.
paymentMethod
.
id
,
},
},
};
};
},
},
...
...
ee/app/controllers/subscriptions_controller.rb
View file @
34a4563e
...
@@ -36,11 +36,12 @@ class SubscriptionsController < ApplicationController
...
@@ -36,11 +36,12 @@ class SubscriptionsController < ApplicationController
return
render_404
unless
ci_minutes_plan_data
.
present?
return
render_404
unless
ci_minutes_plan_data
.
present?
# At the moment of this comment the account id is directly available to the view.
# At the moment of this comment the account id is directly available to the view.
# This might change in the future
given the intention to associate the account id to the namespace.
# This might change in the future given the intention to associate the account id to the namespace.
# See: https://gitlab.com/gitlab-org/gitlab/-/issues/338546#note_684762160
# See: https://gitlab.com/gitlab-org/gitlab/-/issues/338546#note_684762160
result
=
find_group
(
plan_id:
ci_minutes_plan_data
[
"id"
])
result
=
find_group
(
plan_id:
ci_minutes_plan_data
[
"id"
])
@group
=
result
[
:namespace
]
@group
=
result
[
:namespace
]
@account_id
=
result
[
:account_id
]
@account_id
=
result
[
:account_id
]
@active_subscription
=
result
[
:active_subscription
]
return
render_404
if
@group
.
nil?
return
render_404
if
@group
.
nil?
...
@@ -51,11 +52,12 @@ class SubscriptionsController < ApplicationController
...
@@ -51,11 +52,12 @@ class SubscriptionsController < ApplicationController
return
render_404
unless
storage_plan_data
.
present?
return
render_404
unless
storage_plan_data
.
present?
# At the moment of this comment the account id is directly available to the view.
# At the moment of this comment the account id is directly available to the view.
# This might change in the future
given the intention to associate the account id to the namespace.
# This might change in the future given the intention to associate the account id to the namespace.
# See: https://gitlab.com/gitlab-org/gitlab/-/issues/338546#note_684762160
# See: https://gitlab.com/gitlab-org/gitlab/-/issues/338546#note_684762160
result
=
find_group
(
plan_id:
storage_plan_data
[
"id"
])
result
=
find_group
(
plan_id:
storage_plan_data
[
"id"
])
@group
=
result
[
:namespace
]
@group
=
result
[
:namespace
]
@account_id
=
result
[
:account_id
]
@account_id
=
result
[
:account_id
]
@active_subscription
=
result
[
:active_subscription
]
return
render_404
if
@group
.
nil?
return
render_404
if
@group
.
nil?
...
@@ -110,7 +112,7 @@ class SubscriptionsController < ApplicationController
...
@@ -110,7 +112,7 @@ class SubscriptionsController < ApplicationController
end
end
def
subscription_params
def
subscription_params
params
.
require
(
:subscription
).
permit
(
:plan_id
,
:
payment_method_id
,
:quantity
,
:source
)
params
.
require
(
:subscription
).
permit
(
:plan_id
,
:
is_addon
,
:payment_method_id
,
:quantity
,
:source
).
merge
(
params
.
permit
(
:active_subscription
)
)
end
end
def
find_group
(
plan_id
:)
def
find_group
(
plan_id
:)
...
...
ee/app/helpers/subscriptions_helper.rb
View file @
34a4563e
...
@@ -16,8 +16,9 @@ module SubscriptionsHelper
...
@@ -16,8 +16,9 @@ module SubscriptionsHelper
}
}
end
end
def
buy_addon_data
(
group
,
account_id
,
anchor
,
purchased_product
)
def
buy_addon_data
(
group
,
account_id
,
a
ctive_subscription
,
a
nchor
,
purchased_product
)
{
{
active_subscription:
active_subscription
,
group_data:
[
present_group
(
group
,
account_id
)].
to_json
,
group_data:
[
present_group
(
group
,
account_id
)].
to_json
,
namespace_id:
params
[
:selected_group
],
namespace_id:
params
[
:selected_group
],
redirect_after_success:
group_usage_quotas_path
(
group
,
anchor:
anchor
,
purchased_product:
purchased_product
),
redirect_after_success:
group_usage_quotas_path
(
group
,
anchor:
anchor
,
purchased_product:
purchased_product
),
...
...
ee/app/services/gitlab_subscriptions/fetch_purchase_eligible_namespaces_service.rb
View file @
34a4563e
...
@@ -25,10 +25,14 @@ module GitlabSubscriptions
...
@@ -25,10 +25,14 @@ module GitlabSubscriptions
return
missing_plan_error
if
plan_id
.
nil?
&&
any_self_service_plan
.
nil?
return
missing_plan_error
if
plan_id
.
nil?
&&
any_self_service_plan
.
nil?
if
response
[
:success
]
&&
response
[
:data
]
if
response
[
:success
]
&&
response
[
:data
]
eligible_namespaces
=
response
[
:data
].
to_h
{
|
data
|
[
data
[
"id"
],
data
[
"accountId"
]]
}
eligible_namespaces
=
response
[
:data
].
to_h
{
|
data
|
[
data
[
"id"
],
[
data
[
"accountId"
],
data
[
'subscription'
]
]]
}
data
=
namespaces
.
each_with_object
([])
do
|
namespace
,
acc
|
data
=
namespaces
.
each_with_object
([])
do
|
namespace
,
acc
|
if
eligible_namespaces
.
include?
(
namespace
.
id
)
if
eligible_namespaces
.
include?
(
namespace
.
id
)
acc
<<
{
namespace:
namespace
,
account_id:
eligible_namespaces
[
namespace
.
id
]
}
acc
<<
{
namespace:
namespace
,
account_id:
eligible_namespaces
[
namespace
.
id
][
0
],
active_subscription:
eligible_namespaces
[
namespace
.
id
][
1
]
}
end
end
end
end
...
...
ee/app/services/subscriptions/create_service.rb
View file @
34a4563e
...
@@ -25,10 +25,7 @@ module Subscriptions
...
@@ -25,10 +25,7 @@ module Subscriptions
# We can't use an email from GL.com because it may differ from the billing email.
# We can't use an email from GL.com because it may differ from the billing email.
# Instead we use the email received from the CustomersDot as a billing email.
# Instead we use the email received from the CustomersDot as a billing email.
customer_data
=
response
.
with_indifferent_access
[
:data
][
:customer
]
customer_data
=
response
.
with_indifferent_access
[
:data
][
:customer
]
billing_email
=
customer_data
[
:email
]
response
=
create_subscription
(
customer_data
)
token
=
customer_data
[
:authentication_token
]
response
=
client
.
create_subscription
(
create_subscription_params
,
billing_email
,
token
)
OnboardingProgressService
.
new
(
@group
).
execute
(
action: :subscription_created
)
if
response
[
:success
]
OnboardingProgressService
.
new
(
@group
).
execute
(
action: :subscription_created
)
if
response
[
:success
]
...
@@ -73,15 +70,20 @@ module Subscriptions
...
@@ -73,15 +70,20 @@ module Subscriptions
}
}
end
end
def
create_subscription_params
def
create_subscription
(
customer_data
)
# When purchasing an add on, we don't want to send create_subscription_params
# in order to avoid amending the main product. Note that this will go away
# when fully transitioning the flow to GraphQL
create_params
=
add_on?
?
create_addon_params
:
create_subscription_params
billing_email
,
token
=
customer_data
.
values_at
(
:email
,
:authentication_token
)
client
.
create_subscription
(
create_params
,
billing_email
,
token
)
end
def
create_params
{
{
plan_id:
subscription_params
[
:plan_id
],
plan_id:
subscription_params
[
:plan_id
],
payment_method_id:
subscription_params
[
:payment_method_id
],
payment_method_id:
subscription_params
[
:payment_method_id
],
products:
{
main:
{
quantity:
subscription_params
[
:quantity
]
}
},
gl_namespace_id:
@group
.
id
,
gl_namespace_id:
@group
.
id
,
gl_namespace_name:
@group
.
name
,
gl_namespace_name:
@group
.
name
,
preview:
'false'
,
preview:
'false'
,
...
@@ -89,6 +91,27 @@ module Subscriptions
...
@@ -89,6 +91,27 @@ module Subscriptions
}
}
end
end
def
create_addon_params
{
active_subscription:
subscription_params
[
:active_subscription
],
quantity:
subscription_params
[
:quantity
]
}.
merge
(
create_params
)
end
def
create_subscription_params
{
products:
{
main:
{
quantity:
subscription_params
[
:quantity
]
}
}
}.
merge
(
create_params
)
end
def
add_on?
Gitlab
::
Utils
.
to_boolean
(
subscription_params
[
:is_addon
],
default:
false
)
end
def
country_code
(
country
)
def
country_code
(
country
)
World
.
alpha3_from_alpha2
(
country
)
World
.
alpha3_from_alpha2
(
country
)
end
end
...
...
ee/app/views/subscriptions/buy_minutes.html.haml
View file @
34a4563e
...
@@ -2,4 +2,4 @@
...
@@ -2,4 +2,4 @@
-
content_for
:page_specific_javascripts
do
-
content_for
:page_specific_javascripts
do
=
render
"layouts/one_trust"
=
render
"layouts/one_trust"
#js-buy-minutes
{
data:
buy_addon_data
(
@group
,
@account_id
,
'pipelines-quota-tab'
,
s_
(
'Checkout|CI minutes'
))
}
#js-buy-minutes
{
data:
buy_addon_data
(
@group
,
@account_id
,
@active_subscription
,
'pipelines-quota-tab'
,
s_
(
'Checkout|CI minutes'
))
}
ee/app/views/subscriptions/buy_storage.html.haml
View file @
34a4563e
...
@@ -2,4 +2,4 @@
...
@@ -2,4 +2,4 @@
-
content_for
:page_specific_javascripts
do
-
content_for
:page_specific_javascripts
do
=
render
"layouts/one_trust"
=
render
"layouts/one_trust"
#js-buy-storage
{
data:
buy_addon_data
(
@group
,
@account_id
,
'storage-quota-tab'
,
s_
(
'Checkout|a storage subscription'
))
}
#js-buy-storage
{
data:
buy_addon_data
(
@group
,
@account_id
,
@active_subscription
,
'storage-quota-tab'
,
s_
(
'Checkout|a storage subscription'
))
}
ee/lib/gitlab/subscription_portal/clients/graphql.rb
View file @
34a4563e
...
@@ -124,6 +124,7 @@ module Gitlab
...
@@ -124,6 +124,7 @@ module Gitlab
namespaceEligibility(customerUid: $customerUid, namespaces: $namespaces, planId: $planId, eligibleForPurchase: $eligibleForPurchase) {
namespaceEligibility(customerUid: $customerUid, namespaces: $namespaces, planId: $planId, eligibleForPurchase: $eligibleForPurchase) {
id
id
accountId: zuoraAccountId
accountId: zuoraAccountId
subscription { name }
}
}
}
}
GQL
GQL
...
...
ee/spec/fixtures/create_service_params.json
View file @
34a4563e
{
[
"customer"
:
{
{
"provider"
:
"gitlab"
,
"uid"
:
111
,
"credentials"
:
{
"token"
:
"foo_token"
},
"customer"
:
{
"customer"
:
{
"country"
:
"NLD"
,
"provider"
:
"gitlab"
,
"address_1"
:
"Address line 1"
,
"uid"
:
111
,
"address_2"
:
"Address line 2"
,
"credentials"
:
{
"city"
:
"City"
,
"token"
:
"foo_token"
"state"
:
"State"
,
},
"zip_code"
:
"Zip code"
,
"customer"
:
{
"company"
:
"My organization"
"country"
:
"NLD"
,
"address_1"
:
"Address line 1"
,
"address_2"
:
"Address line 2"
,
"city"
:
"City"
,
"state"
:
"State"
,
"zip_code"
:
"Zip code"
,
"company"
:
"My organization"
},
"info"
:
{
"first_name"
:
"First name"
,
"last_name"
:
"Last name"
,
"email"
:
"first.last@gitlab.com"
}
},
},
"info"
:
{
"subscription"
:
{
"first_name"
:
"First name"
,
"plan_id"
:
"Plan ID"
,
"last_name"
:
"Last name"
,
"payment_method_id"
:
"Payment method ID"
,
"email"
:
"first.last@gitlab.com"
"products"
:
{
"main"
:
{
"quantity"
:
123
}
},
"gl_namespace_id"
:
222
,
"gl_namespace_name"
:
"Group name"
,
"preview"
:
"false"
,
"source"
:
"some_source"
}
}
},
},
"subscription"
:
{
{
"plan_id"
:
"Plan ID"
,
"customer"
:
{
"payment_method_id"
:
"Payment method ID"
,
"provider"
:
"gitlab"
,
"products"
:
{
"uid"
:
111
,
"main"
:
{
"credentials"
:
{
"quantity"
:
123
"token"
:
"foo_token"
},
"customer"
:
{
"country"
:
"NLD"
,
"address_1"
:
"Address line 1"
,
"address_2"
:
"Address line 2"
,
"city"
:
"City"
,
"state"
:
"State"
,
"zip_code"
:
"Zip code"
,
"company"
:
"My organization"
},
"info"
:
{
"first_name"
:
"First name"
,
"last_name"
:
"Last name"
,
"email"
:
"first.last@gitlab.com"
}
}
},
},
"gl_namespace_id"
:
222
,
"subscription"
:
{
"gl_namespace_name"
:
"Group name"
,
"plan_id"
:
"Add-on Plan ID"
,
"preview"
:
"false"
,
"payment_method_id"
:
"Payment method ID"
,
"source"
:
"some_source"
"quantity"
:
111
,
"active_subscription"
:
"A-000000"
,
"gl_namespace_id"
:
222
,
"gl_namespace_name"
:
"Group name"
,
"preview"
:
"false"
,
"source"
:
"some_source"
}
}
}
}
]
ee/spec/frontend/subscriptions/buy_addons_shared/components/order_summary_spec.js
View file @
34a4563e
...
@@ -59,7 +59,6 @@ describe('Order Summary', () => {
...
@@ -59,7 +59,6 @@ describe('Order Summary', () => {
beforeEach
(()
=>
{
beforeEach
(()
=>
{
createComponent
({
createComponent
({
subscription
:
{
quantity
:
1
},
subscription
:
{
quantity
:
1
},
selectedPlanId
:
'
ciMinutesPackPlanId
'
,
});
});
});
});
...
...
ee/spec/frontend/subscriptions/buy_minutes/components/app_spec.js
View file @
34a4563e
import
{
GlEmptyState
}
from
'
@gitlab/ui
'
;
import
{
GlEmptyState
}
from
'
@gitlab/ui
'
;
import
{
createLocalVue
}
from
'
@vue/test-utils
'
;
import
{
createLocalVue
}
from
'
@vue/test-utils
'
;
import
VueApollo
from
'
vue-apollo
'
;
import
VueApollo
from
'
vue-apollo
'
;
import
{
pick
}
from
'
lodash
'
;
import
{
I18N_CI_MINUTES_TITLE
,
planTags
}
from
'
ee/subscriptions/buy_addons_shared/constants
'
;
import
Checkout
from
'
ee/subscriptions/buy_addons_shared/components/checkout.vue
'
;
import
Checkout
from
'
ee/subscriptions/buy_addons_shared/components/checkout.vue
'
;
import
AddonPurchaseDetails
from
'
ee/subscriptions/buy_addons_shared/components/checkout/addon_purchase_details.vue
'
;
import
AddonPurchaseDetails
from
'
ee/subscriptions/buy_addons_shared/components/checkout/addon_purchase_details.vue
'
;
import
OrderSummary
from
'
ee/subscriptions/buy_addons_shared/components/order_summary.vue
'
;
import
OrderSummary
from
'
ee/subscriptions/buy_addons_shared/components/order_summary.vue
'
;
...
@@ -9,8 +11,8 @@ import App from 'ee/subscriptions/buy_minutes/components/app.vue';
...
@@ -9,8 +11,8 @@ import App from 'ee/subscriptions/buy_minutes/components/app.vue';
import
StepOrderApp
from
'
ee/vue_shared/purchase_flow/components/step_order_app.vue
'
;
import
StepOrderApp
from
'
ee/vue_shared/purchase_flow/components/step_order_app.vue
'
;
import
{
shallowMountExtended
}
from
'
helpers/vue_test_utils_helper
'
;
import
{
shallowMountExtended
}
from
'
helpers/vue_test_utils_helper
'
;
import
waitForPromises
from
'
helpers/wait_for_promises
'
;
import
waitForPromises
from
'
helpers/wait_for_promises
'
;
import
{
planTags
}
from
'
../../../../../app/assets/javascripts/subscriptions/buy_addons_shared/constants
'
;
import
{
createMockApolloProvider
}
from
'
../spec_helper
'
;
import
{
createMockApolloProvider
}
from
'
../spec_helper
'
;
import
{
mockCiMinutesPlans
}
from
'
../mock_data
'
;
const
localVue
=
createLocalVue
();
const
localVue
=
createLocalVue
();
localVue
.
use
(
VueApollo
);
localVue
.
use
(
VueApollo
);
...
@@ -32,24 +34,42 @@ describe('App', () => {
...
@@ -32,24 +34,42 @@ describe('App', () => {
});
});
}
}
const
getCiMinutePlan
=
()
=>
pick
(
mockCiMinutesPlans
[
0
],
[
'
id
'
,
'
code
'
,
'
pricePerYear
'
,
'
name
'
]);
const
findCheckout
=
()
=>
wrapper
.
findComponent
(
Checkout
);
const
findOrderSummary
=
()
=>
wrapper
.
findComponent
(
OrderSummary
);
const
findPriceLabel
=
()
=>
wrapper
.
findByTestId
(
'
price-per-unit
'
);
const
findQuantityText
=
()
=>
wrapper
.
findByTestId
(
'
addon-quantity-text
'
);
const
findQuantityText
=
()
=>
wrapper
.
findByTestId
(
'
addon-quantity-text
'
);
const
findSummaryLabel
=
()
=>
wrapper
.
findByTestId
(
'
summary-label
'
);
const
findSummaryLabel
=
()
=>
wrapper
.
findByTestId
(
'
summary-label
'
);
const
findSummaryTotal
=
()
=>
wrapper
.
findByTestId
(
'
summary-total
'
);
const
findSummaryTotal
=
()
=>
wrapper
.
findByTestId
(
'
summary-total
'
);
const
findPriceLabel
=
()
=>
wrapper
.
findByTestId
(
'
price-per-unit
'
);
afterEach
(()
=>
{
afterEach
(()
=>
{
wrapper
.
destroy
();
wrapper
.
destroy
();
});
});
describe
(
'
when data is received
'
,
()
=>
{
describe
(
'
when data is received
'
,
()
=>
{
it
(
'
should display the StepOrderApp
'
,
async
()
=>
{
beforeEach
(
()
=>
{
const
mockApollo
=
createMockApolloProvider
();
const
mockApollo
=
createMockApolloProvider
();
wrapper
=
createComponent
(
mockApollo
);
wrapper
=
createComponent
(
mockApollo
);
await
waitForPromises
();
return
waitForPromises
();
});
it
(
'
should display the StepOrderApp
'
,
()
=>
{
expect
(
wrapper
.
findComponent
(
StepOrderApp
).
exists
()).
toBe
(
true
);
expect
(
wrapper
.
findComponent
(
StepOrderApp
).
exists
()).
toBe
(
true
);
expect
(
wrapper
.
findComponent
(
GlEmptyState
).
exists
()).
toBe
(
false
);
expect
(
wrapper
.
findComponent
(
GlEmptyState
).
exists
()).
toBe
(
false
);
});
});
it
(
'
provides the correct props to checkout
'
,
()
=>
{
expect
(
findCheckout
().
props
()).
toMatchObject
({
plan
:
{
...
getCiMinutePlan
,
isAddon
:
true
},
});
});
it
(
'
provides the correct props to order summary
'
,
()
=>
{
expect
(
findOrderSummary
().
props
()).
toMatchObject
({
plan
:
{
...
getCiMinutePlan
,
isAddon
:
true
},
title
:
I18N_CI_MINUTES_TITLE
,
});
});
});
});
describe
(
'
when data is not received
'
,
()
=>
{
describe
(
'
when data is not received
'
,
()
=>
{
...
...
ee/spec/frontend/subscriptions/buy_minutes/components/checkout/confirm_order_spec.js
View file @
34a4563e
...
@@ -6,7 +6,10 @@ import { STEPS } from 'ee/subscriptions/constants';
...
@@ -6,7 +6,10 @@ import { STEPS } from 'ee/subscriptions/constants';
import
stateQuery
from
'
ee/subscriptions/graphql/queries/state.query.graphql
'
;
import
stateQuery
from
'
ee/subscriptions/graphql/queries/state.query.graphql
'
;
import
ConfirmOrder
from
'
ee/vue_shared/purchase_flow/components/checkout/confirm_order.vue
'
;
import
ConfirmOrder
from
'
ee/vue_shared/purchase_flow/components/checkout/confirm_order.vue
'
;
import
{
GENERAL_ERROR_MESSAGE
}
from
'
ee/vue_shared/purchase_flow/constants
'
;
import
{
GENERAL_ERROR_MESSAGE
}
from
'
ee/vue_shared/purchase_flow/constants
'
;
import
{
stateData
as
initialStateData
}
from
'
ee_jest/subscriptions/buy_minutes/mock_data
'
;
import
{
stateData
as
initialStateData
,
subscriptionName
,
}
from
'
ee_jest/subscriptions/buy_minutes/mock_data
'
;
import
{
createMockApolloProvider
}
from
'
ee_jest/vue_shared/purchase_flow/spec_helper
'
;
import
{
createMockApolloProvider
}
from
'
ee_jest/vue_shared/purchase_flow/spec_helper
'
;
import
{
extendedWrapper
}
from
'
helpers/vue_test_utils_helper
'
;
import
{
extendedWrapper
}
from
'
helpers/vue_test_utils_helper
'
;
import
flash
from
'
~/flash
'
;
import
flash
from
'
~/flash
'
;
...
@@ -40,17 +43,13 @@ describe('Confirm Order', () => {
...
@@ -40,17 +43,13 @@ describe('Confirm Order', () => {
wrapper
.
destroy
();
wrapper
.
destroy
();
});
});
describe
(
'
Active
'
,
()
=>
{
describe
(
'
when rendering
'
,
()
=>
{
describe
(
'
when receiving proper step data
'
,
()
=>
{
describe
(
'
when receiving proper step data
'
,
()
=>
{
beforeEach
(()
=>
{
beforeEach
(()
=>
{
mockApolloProvider
=
createMockApolloProvider
(
STEPS
,
3
);
mockApolloProvider
=
createMockApolloProvider
(
STEPS
,
3
);
createComponent
({
apolloProvider
:
mockApolloProvider
});
createComponent
({
apolloProvider
:
mockApolloProvider
});
});
});
it
(
'
button should be visible
'
,
()
=>
{
expect
(
findConfirmButton
().
exists
()).
toBe
(
true
);
});
it
(
'
shows the text "Confirm purchase"
'
,
()
=>
{
it
(
'
shows the text "Confirm purchase"
'
,
()
=>
{
expect
(
findConfirmButton
().
text
()).
toBe
(
'
Confirm purchase
'
);
expect
(
findConfirmButton
().
text
()).
toBe
(
'
Confirm purchase
'
);
});
});
...
@@ -60,7 +59,7 @@ describe('Confirm Order', () => {
...
@@ -60,7 +59,7 @@ describe('Confirm Order', () => {
});
});
});
});
describe
(
'
Clicking the button
'
,
()
=>
{
describe
(
'
when confirming the order
'
,
()
=>
{
beforeEach
(()
=>
{
beforeEach
(()
=>
{
mockApolloProvider
=
createMockApolloProvider
([]);
mockApolloProvider
=
createMockApolloProvider
([]);
mockApolloProvider
.
clients
.
defaultClient
.
cache
.
writeQuery
({
mockApolloProvider
.
clients
.
defaultClient
.
cache
.
writeQuery
({
...
@@ -74,9 +73,10 @@ describe('Confirm Order', () => {
...
@@ -74,9 +73,10 @@ describe('Confirm Order', () => {
it
(
'
calls the confirmOrder API method with the correct params
'
,
()
=>
{
it
(
'
calls the confirmOrder API method with the correct params
'
,
()
=>
{
expect
(
Api
.
confirmOrder
).
toHaveBeenCalledTimes
(
1
);
expect
(
Api
.
confirmOrder
).
toHaveBeenCalledTimes
(
1
);
expect
(
Api
.
confirmOrder
.
mock
.
calls
[
0
][
0
]).
to
MatchObject
({
expect
(
Api
.
confirmOrder
.
mock
.
calls
[
0
][
0
]).
to
Equal
({
setup_for_company
:
true
,
setup_for_company
:
true
,
selected_group
:
'
30
'
,
selected_group
:
'
30
'
,
active_subscription
:
subscriptionName
,
new_user
:
false
,
new_user
:
false
,
redirect_after_success
:
'
/path/to/redirect/
'
,
redirect_after_success
:
'
/path/to/redirect/
'
,
customer
:
{
customer
:
{
...
@@ -90,6 +90,7 @@ describe('Confirm Order', () => {
...
@@ -90,6 +90,7 @@ describe('Confirm Order', () => {
},
},
subscription
:
{
subscription
:
{
plan_id
:
null
,
plan_id
:
null
,
is_addon
:
true
,
payment_method_id
:
null
,
payment_method_id
:
null
,
quantity
:
1
,
quantity
:
1
,
},
},
...
@@ -105,35 +106,33 @@ describe('Confirm Order', () => {
...
@@ -105,35 +106,33 @@ describe('Confirm Order', () => {
});
});
});
});
describe
(
'
Order confirmation
'
,
()
=>
{
describe
(
'
when confirming the purchase
'
,
()
=>
{
describe
(
'
when the confirmation succeeds
'
,
()
=>
{
const
location
=
'
group/location/path
'
;
const
location
=
'
group/location/path
'
;
beforeEach
(()
=>
{
beforeEach
(()
=>
{
mockApolloProvider
=
createMockApolloProvider
(
STEPS
,
3
);
mockApolloProvider
=
createMockApolloProvider
(
STEPS
,
3
);
createComponent
({
apolloProvider
:
mockApolloProvider
});
createComponent
({
apolloProvider
:
mockApolloProvider
});
});
});
it
(
'
should redirect to the location
'
,
async
()
=>
{
it
(
'
redirects to the location if it succeeds
'
,
async
()
=>
{
Api
.
confirmOrder
=
jest
.
fn
().
mockResolvedValueOnce
({
data
:
{
location
}
});
Api
.
confirmOrder
=
jest
.
fn
().
mockResolvedValueOnce
({
data
:
{
location
}
});
findConfirmButton
().
vm
.
$emit
(
'
click
'
);
findConfirmButton
().
vm
.
$emit
(
'
click
'
);
await
flushPromises
();
await
flushPromises
();
expect
(
UrlUtility
.
redirectTo
).
toHaveBeenCalledTimes
(
1
);
expect
(
UrlUtility
.
redirectTo
).
toHaveBeenCalledTimes
(
1
);
expect
(
UrlUtility
.
redirectTo
).
toHaveBeenCalledWith
(
location
);
expect
(
UrlUtility
.
redirectTo
).
toHaveBeenCalledWith
(
location
);
});
});
it
(
'
shows an error if it fails
'
,
async
()
=>
{
const
errors
=
'
an error
'
;
Api
.
confirmOrder
=
jest
.
fn
().
mockResolvedValueOnce
({
data
:
{
errors
}
});
findConfirmButton
().
vm
.
$emit
(
'
click
'
);
await
flushPromises
();
it
(
'
shows an error
'
,
async
()
=>
{
expect
(
flash
.
mock
.
calls
[
0
][
0
]).
toMatchObject
({
const
errors
=
'
an error
'
;
message
:
GENERAL_ERROR_MESSAGE
,
Api
.
confirmOrder
=
jest
.
fn
().
mockResolvedValueOnce
({
data
:
{
errors
}
});
captureError
:
true
,
findConfirmButton
().
vm
.
$emit
(
'
click
'
);
error
:
new
Error
(
JSON
.
stringify
(
errors
)),
await
flushPromises
();
expect
(
flash
.
mock
.
calls
[
0
][
0
]).
toMatchObject
({
message
:
GENERAL_ERROR_MESSAGE
,
captureError
:
true
,
error
:
new
Error
(
JSON
.
stringify
(
errors
)),
});
});
});
});
});
});
});
...
@@ -163,7 +162,7 @@ describe('Confirm Order', () => {
...
@@ -163,7 +162,7 @@ describe('Confirm Order', () => {
});
});
});
});
describe
(
'
I
nactive
'
,
()
=>
{
describe
(
'
when i
nactive
'
,
()
=>
{
it
(
'
does not show buttons
'
,
()
=>
{
it
(
'
does not show buttons
'
,
()
=>
{
mockApolloProvider
=
createMockApolloProvider
(
STEPS
,
1
);
mockApolloProvider
=
createMockApolloProvider
(
STEPS
,
1
);
createComponent
({
apolloProvider
:
mockApolloProvider
});
createComponent
({
apolloProvider
:
mockApolloProvider
});
...
...
ee/spec/frontend/subscriptions/buy_minutes/mock_data.js
View file @
34a4563e
import
{
STEPS
}
from
'
ee/subscriptions/constants
'
;
import
{
STEPS
}
from
'
ee/subscriptions/constants
'
;
import
{
CUSTOMER_TYPE
,
NAMESPACE_TYPE
,
PAYMENT_METHOD_TYPE
,
PLAN_TYPE
,
SUBSCRIPTION_TYPE
,
}
from
'
ee/subscriptions/buy_addons_shared/constants
'
;
export
const
accountId
=
'
111111111111
'
;
export
const
accountId
=
'
111111111111
'
;
export
const
subscriptionName
=
'
A-000000000
'
;
export
const
mockCiMinutesPlans
=
[
export
const
mockCiMinutesPlans
=
[
{
{
...
@@ -8,7 +16,7 @@ export const mockCiMinutesPlans = [
...
@@ -8,7 +16,7 @@ export const mockCiMinutesPlans = [
code
:
'
ci_minutes
'
,
code
:
'
ci_minutes
'
,
pricePerYear
:
10
,
pricePerYear
:
10
,
name
:
'
CI minutes pack
'
,
name
:
'
CI minutes pack
'
,
__typename
:
'
Plan
'
,
__typename
:
PLAN_TYPE
,
},
},
];
];
...
@@ -19,7 +27,7 @@ export const mockNamespaces = `
...
@@ -19,7 +27,7 @@ export const mockNamespaces = `
export
const
mockParsedNamespaces
=
JSON
.
parse
(
mockNamespaces
).
map
((
namespace
)
=>
({
export
const
mockParsedNamespaces
=
JSON
.
parse
(
mockNamespaces
).
map
((
namespace
)
=>
({
...
namespace
,
...
namespace
,
__typename
:
'
Namespace
'
,
__typename
:
NAMESPACE_TYPE
,
}));
}));
export
const
mockNewUser
=
'
false
'
;
export
const
mockNewUser
=
'
false
'
;
...
@@ -35,18 +43,25 @@ export const stateData = {
...
@@ -35,18 +43,25 @@ export const stateData = {
eligibleNamespaces
:
[],
eligibleNamespaces
:
[],
subscription
:
{
subscription
:
{
quantity
:
1
,
quantity
:
1
,
__typename
:
'
Subscription
'
,
__typename
:
SUBSCRIPTION_TYPE
,
},
activeSubscription
:
{
name
:
subscriptionName
,
__typename
:
SUBSCRIPTION_TYPE
,
},
},
redirectAfterSuccess
:
'
/path/to/redirect/
'
,
redirectAfterSuccess
:
'
/path/to/redirect/
'
,
selectedNamespaceId
:
'
30
'
,
selectedNamespaceId
:
'
30
'
,
selectedPlanId
:
null
,
selectedPlan
:
{
id
:
null
,
isAddon
:
true
,
},
paymentMethod
:
{
paymentMethod
:
{
id
:
null
,
id
:
null
,
creditCardExpirationMonth
:
null
,
creditCardExpirationMonth
:
null
,
creditCardExpirationYear
:
null
,
creditCardExpirationYear
:
null
,
creditCardType
:
null
,
creditCardType
:
null
,
creditCardMaskNumber
:
null
,
creditCardMaskNumber
:
null
,
__typename
:
'
PaymentMethod
'
,
__typename
:
PAYMENT_METHOD_TYPE
,
},
},
customer
:
{
customer
:
{
country
:
null
,
country
:
null
,
...
@@ -56,7 +71,7 @@ export const stateData = {
...
@@ -56,7 +71,7 @@ export const stateData = {
state
:
null
,
state
:
null
,
zipCode
:
null
,
zipCode
:
null
,
company
:
null
,
company
:
null
,
__typename
:
'
Customer
'
,
__typename
:
CUSTOMER_TYPE
,
},
},
fullName
:
'
Full Name
'
,
fullName
:
'
Full Name
'
,
isNewUser
:
false
,
isNewUser
:
false
,
...
...
ee/spec/helpers/subscriptions_helper_spec.rb
View file @
34a4563e
...
@@ -134,11 +134,12 @@ RSpec.describe SubscriptionsHelper do
...
@@ -134,11 +134,12 @@ RSpec.describe SubscriptionsHelper do
end
end
describe
'#buy_addon_data'
do
describe
'#buy_addon_data'
do
subject
(
:buy_addon_data
)
{
helper
.
buy_addon_data
(
group
,
account_id
,
anchor
,
purchased_product
)
}
subject
(
:buy_addon_data
)
{
helper
.
buy_addon_data
(
group
,
account_id
,
a
ctive_subscription
,
a
nchor
,
purchased_product
)
}
let_it_be
(
:group
)
{
create
(
:group
,
name:
'My Namespace'
)
}
let_it_be
(
:group
)
{
create
(
:group
,
name:
'My Namespace'
)
}
let_it_be
(
:user
)
{
create
(
:user
,
name:
'First Last'
)
}
let_it_be
(
:user
)
{
create
(
:user
,
name:
'First Last'
)
}
let_it_be
(
:account_id
)
{
'111111111111'
}
let_it_be
(
:account_id
)
{
'111111111111'
}
let_it_be
(
:active_subscription
)
{
{
name:
'S-000000000'
}
}
let
(
:anchor
)
{
'pipelines-quota-tab'
}
let
(
:anchor
)
{
'pipelines-quota-tab'
}
let
(
:purchased_product
)
{
'CI Minutes'
}
let
(
:purchased_product
)
{
'CI Minutes'
}
...
@@ -150,6 +151,7 @@ RSpec.describe SubscriptionsHelper do
...
@@ -150,6 +151,7 @@ RSpec.describe SubscriptionsHelper do
end
end
it
{
is_expected
.
to
include
(
namespace_id:
group
.
id
.
to_s
)
}
it
{
is_expected
.
to
include
(
namespace_id:
group
.
id
.
to_s
)
}
it
{
is_expected
.
to
include
(
active_subscription:
active_subscription
)
}
it
{
is_expected
.
to
include
(
source:
'some_source'
)
}
it
{
is_expected
.
to
include
(
source:
'some_source'
)
}
it
{
is_expected
.
to
include
(
group_data:
%Q{[{"id":
#{
group
.
id
}
,"account_id":"
#{
account_id
}
","name":"My Namespace","users":1,"guests":0}]}
)
}
it
{
is_expected
.
to
include
(
group_data:
%Q{[{"id":
#{
group
.
id
}
,"account_id":"
#{
account_id
}
","name":"My Namespace","users":1,"guests":0}]}
)
}
it
{
is_expected
.
to
include
(
redirect_after_success:
group_usage_quotas_path
(
group
,
anchor:
anchor
,
purchased_product:
purchased_product
))
}
it
{
is_expected
.
to
include
(
redirect_after_success:
group_usage_quotas_path
(
group
,
anchor:
anchor
,
purchased_product:
purchased_product
))
}
...
...
ee/spec/lib/gitlab/subscription_portal/clients/graphql_spec.rb
View file @
34a4563e
...
@@ -323,6 +323,7 @@ RSpec.describe Gitlab::SubscriptionPortal::Clients::Graphql do
...
@@ -323,6 +323,7 @@ RSpec.describe Gitlab::SubscriptionPortal::Clients::Graphql do
namespaceEligibility(customerUid: $customerUid, namespaces: $namespaces, planId: $planId, eligibleForPurchase: $eligibleForPurchase) {
namespaceEligibility(customerUid: $customerUid, namespaces: $namespaces, planId: $planId, eligibleForPurchase: $eligibleForPurchase) {
id
id
accountId: zuoraAccountId
accountId: zuoraAccountId
subscription { name }
}
}
}
}
GQL
GQL
...
...
ee/spec/services/gitlab_subscriptions/fetch_purchase_eligible_namespaces_service_spec.rb
View file @
34a4563e
...
@@ -67,13 +67,15 @@ RSpec.describe GitlabSubscriptions::FetchPurchaseEligibleNamespacesService do
...
@@ -67,13 +67,15 @@ RSpec.describe GitlabSubscriptions::FetchPurchaseEligibleNamespacesService do
end
end
context
'when all the namespaces are eligible'
do
context
'when all the namespaces are eligible'
do
let
(
:subscription_name
)
{
'S-000000'
}
before
do
before
do
allow
(
Gitlab
::
SubscriptionPortal
::
Client
)
allow
(
Gitlab
::
SubscriptionPortal
::
Client
)
.
to
receive
(
:filter_purchase_eligible_namespaces
)
.
to
receive
(
:filter_purchase_eligible_namespaces
)
.
with
(
user
,
[
namespace_1
,
namespace_2
],
plan_id:
'test'
,
any_self_service_plan:
nil
)
.
with
(
user
,
[
namespace_1
,
namespace_2
],
plan_id:
'test'
,
any_self_service_plan:
nil
)
.
and_return
(
success:
true
,
data:
[
.
and_return
(
success:
true
,
data:
[
{
'id'
=>
namespace_1
.
id
,
'accountId'
=>
nil
},
{
'id'
=>
namespace_1
.
id
,
'accountId'
=>
nil
,
'subscription'
=>
{
'name'
=>
subscription_name
}
},
{
'id'
=>
namespace_2
.
id
,
'accountId'
=>
nil
}
{
'id'
=>
namespace_2
.
id
,
'accountId'
=>
nil
,
'subscription'
=>
nil
}
])
])
end
end
...
@@ -83,8 +85,8 @@ RSpec.describe GitlabSubscriptions::FetchPurchaseEligibleNamespacesService do
...
@@ -83,8 +85,8 @@ RSpec.describe GitlabSubscriptions::FetchPurchaseEligibleNamespacesService do
expect
(
result
).
to
be_success
expect
(
result
).
to
be_success
expect
(
result
.
payload
).
to
match_array
[
expect
(
result
.
payload
).
to
match_array
[
namespace_result
(
namespace_1
,
nil
),
namespace_result
(
namespace_1
,
nil
,
{
'name'
=>
subscription_name
}
),
namespace_result
(
namespace_2
,
nil
)
namespace_result
(
namespace_2
,
nil
,
nil
)
]
]
end
end
end
end
...
@@ -105,7 +107,7 @@ RSpec.describe GitlabSubscriptions::FetchPurchaseEligibleNamespacesService do
...
@@ -105,7 +107,7 @@ RSpec.describe GitlabSubscriptions::FetchPurchaseEligibleNamespacesService do
expect
(
result
).
to
be_success
expect
(
result
).
to
be_success
expect
(
result
.
payload
).
to
match_array
[
expect
(
result
.
payload
).
to
match_array
[
namespace_result
(
namespace_1
,
account_id
)
namespace_result
(
namespace_1
,
account_id
,
nil
)
]
]
end
end
end
end
...
@@ -124,7 +126,7 @@ RSpec.describe GitlabSubscriptions::FetchPurchaseEligibleNamespacesService do
...
@@ -124,7 +126,7 @@ RSpec.describe GitlabSubscriptions::FetchPurchaseEligibleNamespacesService do
expect
(
result
).
to
be_success
expect
(
result
).
to
be_success
expect
(
result
.
payload
).
to
match_array
[
expect
(
result
.
payload
).
to
match_array
[
namespace_result
(
namespace_1
,
nil
)
namespace_result
(
namespace_1
,
nil
,
nil
)
]
]
end
end
end
end
...
@@ -132,7 +134,7 @@ RSpec.describe GitlabSubscriptions::FetchPurchaseEligibleNamespacesService do
...
@@ -132,7 +134,7 @@ RSpec.describe GitlabSubscriptions::FetchPurchaseEligibleNamespacesService do
private
private
def
namespace_result
(
namespace
,
account_id
)
def
namespace_result
(
namespace
,
account_id
,
active_subscription
)
{
namespace:
namespace
,
account_id:
account_id
}
{
namespace:
namespace
,
account_id:
account_id
,
active_subscription:
active_subscription
}
end
end
end
end
ee/spec/services/subscriptions/create_service_spec.rb
View file @
34a4563e
...
@@ -32,7 +32,7 @@ RSpec.describe Subscriptions::CreateService do
...
@@ -32,7 +32,7 @@ RSpec.describe Subscriptions::CreateService do
let_it_be
(
:customer_email
)
{
'first.last@gitlab.com'
}
let_it_be
(
:customer_email
)
{
'first.last@gitlab.com'
}
let_it_be
(
:client
)
{
Gitlab
::
SubscriptionPortal
::
Client
}
let_it_be
(
:client
)
{
Gitlab
::
SubscriptionPortal
::
Client
}
let_it_be
(
:create_service_params
)
{
Gitlab
::
Json
.
parse
(
fixture_file
(
'create_service_params.json'
,
dir:
'ee'
)).
deep_symbolize_keys
}
let_it_be
(
:create_service_params
)
{
Gitlab
::
Json
.
parse
(
fixture_file
(
'create_service_params.json'
,
dir:
'ee'
))
[
0
]
.
deep_symbolize_keys
}
describe
'#execute'
do
describe
'#execute'
do
before
do
before
do
...
@@ -123,6 +123,27 @@ RSpec.describe Subscriptions::CreateService do
...
@@ -123,6 +123,27 @@ RSpec.describe Subscriptions::CreateService do
execute
execute
end
end
context
'with add-on purchase'
do
let_it_be
(
:subscription_params
)
do
{
is_addon:
true
,
active_subscription:
'A-000000'
,
plan_id:
'Add-on Plan ID'
,
payment_method_id:
'Payment method ID'
,
quantity:
111
,
source:
'some_source'
}
end
it
'passes the correct parameters for creating a subscription'
do
create_service_addon_params
=
Gitlab
::
Json
.
parse
(
fixture_file
(
'create_service_params.json'
,
dir:
'ee'
))[
1
].
deep_symbolize_keys
expect
(
client
).
to
receive
(
:create_subscription
).
with
(
create_service_addon_params
[
:subscription
],
customer_email
,
'token'
)
execute
end
end
it_behaves_like
'records an onboarding progress action'
,
:subscription_created
do
it_behaves_like
'records an onboarding progress action'
,
:subscription_created
do
let
(
:namespace
)
{
group
}
let
(
:namespace
)
{
group
}
end
end
...
...
ee/spec/support/shared_examples/views/subscription_shared_examples.rb
View file @
34a4563e
...
@@ -21,8 +21,13 @@ RSpec.shared_examples_for 'subscription form data' do |js_selector|
...
@@ -21,8 +21,13 @@ RSpec.shared_examples_for 'subscription form data' do |js_selector|
end
end
RSpec
.
shared_examples_for
'buy minutes addon form data'
do
|
js_selector
|
RSpec
.
shared_examples_for
'buy minutes addon form data'
do
|
js_selector
|
let_it_be
(
:group
)
{
create
(
:group
)
}
let_it_be
(
:account_id
)
{
'111111111111'
}
let_it_be
(
:active_subscription
)
{
{
name:
'S-000000000'
}
}
before
do
before
do
allow
(
view
).
to
receive
(
:buy_addon_data
).
and_return
(
allow
(
view
).
to
receive
(
:buy_addon_data
).
with
(
@group
,
@account_id
,
@active_subscription
,
'pipelines-quota-tab'
,
s_
(
'Checkout|CI minutes'
)).
and_return
(
active_subscription:
active_subscription
,
group_data:
'[{"id":"ci_minutes_plan_id","code":"ci_minutes","price_per_year":10.0}]'
,
group_data:
'[{"id":"ci_minutes_plan_id","code":"ci_minutes","price_per_year":10.0}]'
,
namespace_id:
'1'
,
namespace_id:
'1'
,
plan_id:
'ci_minutes_plan_id'
,
plan_id:
'ci_minutes_plan_id'
,
...
@@ -33,6 +38,7 @@ RSpec.shared_examples_for 'buy minutes addon form data' do |js_selector|
...
@@ -33,6 +38,7 @@ RSpec.shared_examples_for 'buy minutes addon form data' do |js_selector|
subject
{
render
}
subject
{
render
}
it
{
is_expected
.
to
have_selector
(
"
#{
js_selector
}
[data-active-subscription-name='S-000000000']"
)
}
it
{
is_expected
.
to
have_selector
(
"
#{
js_selector
}
[data-group-data='[{
\"
id
\"
:
\"
ci_minutes_plan_id
\"
,
\"
code
\"
:
\"
ci_minutes
\"
,
\"
price_per_year
\"
:10.0}]']"
)
}
it
{
is_expected
.
to
have_selector
(
"
#{
js_selector
}
[data-group-data='[{
\"
id
\"
:
\"
ci_minutes_plan_id
\"
,
\"
code
\"
:
\"
ci_minutes
\"
,
\"
price_per_year
\"
:10.0}]']"
)
}
it
{
is_expected
.
to
have_selector
(
"
#{
js_selector
}
[data-plan-id='ci_minutes_plan_id']"
)
}
it
{
is_expected
.
to
have_selector
(
"
#{
js_selector
}
[data-plan-id='ci_minutes_plan_id']"
)
}
it
{
is_expected
.
to
have_selector
(
"
#{
js_selector
}
[data-namespace-id='1']"
)
}
it
{
is_expected
.
to
have_selector
(
"
#{
js_selector
}
[data-namespace-id='1']"
)
}
...
@@ -41,8 +47,13 @@ RSpec.shared_examples_for 'buy minutes addon form data' do |js_selector|
...
@@ -41,8 +47,13 @@ RSpec.shared_examples_for 'buy minutes addon form data' do |js_selector|
end
end
RSpec
.
shared_examples_for
'buy storage addon form data'
do
|
js_selector
|
RSpec
.
shared_examples_for
'buy storage addon form data'
do
|
js_selector
|
let_it_be
(
:group
)
{
create
(
:group
)
}
let_it_be
(
:account_id
)
{
'111111111111'
}
let_it_be
(
:active_subscription
)
{
{
name:
'S-000000000'
}
}
before
do
before
do
allow
(
view
).
to
receive
(
:buy_addon_data
).
and_return
(
allow
(
view
).
to
receive
(
:buy_addon_data
).
with
(
@group
,
@account_id
,
@active_subscription
,
'storage-quota-tab'
,
s_
(
'Checkout|a storage subscription'
)).
and_return
(
active_subscription:
active_subscription
,
group_data:
'[{"id":"storage_plan_id","code":"storage","price_per_year":10.0}]'
,
group_data:
'[{"id":"storage_plan_id","code":"storage","price_per_year":10.0}]'
,
namespace_id:
'2'
,
namespace_id:
'2'
,
plan_id:
'storage_plan_id'
,
plan_id:
'storage_plan_id'
,
...
@@ -53,6 +64,7 @@ RSpec.shared_examples_for 'buy storage addon form data' do |js_selector|
...
@@ -53,6 +64,7 @@ RSpec.shared_examples_for 'buy storage addon form data' do |js_selector|
subject
{
render
}
subject
{
render
}
it
{
is_expected
.
to
have_selector
(
"
#{
js_selector
}
[data-active-subscription-name='S-000000000']"
)
}
it
{
is_expected
.
to
have_selector
(
"
#{
js_selector
}
[data-group-data='[{
\"
id
\"
:
\"
storage_plan_id
\"
,
\"
code
\"
:
\"
storage
\"
,
\"
price_per_year
\"
:10.0}]']"
)
}
it
{
is_expected
.
to
have_selector
(
"
#{
js_selector
}
[data-group-data='[{
\"
id
\"
:
\"
storage_plan_id
\"
,
\"
code
\"
:
\"
storage
\"
,
\"
price_per_year
\"
:10.0}]']"
)
}
it
{
is_expected
.
to
have_selector
(
"
#{
js_selector
}
[data-plan-id='storage_plan_id']"
)
}
it
{
is_expected
.
to
have_selector
(
"
#{
js_selector
}
[data-plan-id='storage_plan_id']"
)
}
it
{
is_expected
.
to
have_selector
(
"
#{
js_selector
}
[data-namespace-id='2']"
)
}
it
{
is_expected
.
to
have_selector
(
"
#{
js_selector
}
[data-namespace-id='2']"
)
}
...
...
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