Skip to content
This repository was archived by the owner on Sep 30, 2024. It is now read-only.

Commit c37d944

Browse files
Gate Batch Change UI based on license (#52113)
Closes #49714 - Uses new batch change licensing info exposed on the JS Context (from #52044) - Hides Batch Change nav item on OSS - Shows Get Started page for dotcom & enterprise customers without a Batch Changes license ## Test plan - Run `sg start oss` - observe missing batch changes nav item (route is inaccessible too) - Run `sg start dotcom` - observe batch changes page, only the `Get started` content displays (subrotues are inaccessible) - Mock enterprise with limited batch changes - observe limited batch change warning once executing a SSBC --------- Co-authored-by: Kelli Rockwell <kelli@sourcegraph.com>
1 parent b672ff9 commit c37d944

File tree

10 files changed

+207
-124
lines changed

10 files changed

+207
-124
lines changed

client/shared/src/testing/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
export interface BatchChangesLicenseInfo {
2+
unrestricted: boolean
3+
maxNumChangesets: number
4+
}
5+
6+
const BATCH_CHANGES_LIMITED_LICENSE: BatchChangesLicenseInfo = {
7+
maxNumChangesets: 10,
8+
unrestricted: false,
9+
}
10+
11+
const BATCH_CHANGES_FULL_LICENSE: BatchChangesLicenseInfo = {
12+
maxNumChangesets: -1,
13+
unrestricted: true,
14+
}
15+
16+
/**
17+
*
18+
* @param type a small subset of the batch changes license types
19+
* mocked here to change the window context license info for batches UI gating
20+
*
21+
* returns void
22+
*/
23+
export const updateJSContextBatchChangesLicense = (type: 'none' | 'limited' | 'full'): void => {
24+
const license =
25+
type === 'full' ? BATCH_CHANGES_FULL_LICENSE : type === 'limited' ? BATCH_CHANGES_LIMITED_LICENSE : undefined
26+
27+
window.context.licenseInfo = window.context.licenseInfo
28+
? {
29+
...window.context.licenseInfo,
30+
batchChanges: license,
31+
}
32+
: {
33+
currentPlan: 'team-0',
34+
batchChanges: license,
35+
}
36+
}

client/web/src/enterprise/batches/list/BatchChangeListPage.story.tsx

Lines changed: 135 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { WildcardMockLink, MATCH_ANY_PARAMETERS } from 'wildcard-mock-link'
44
import { getDocumentNode } from '@sourcegraph/http-client'
55
import { EMPTY_SETTINGS_CASCADE } from '@sourcegraph/shared/src/settings/settings'
66
import { MockedTestProvider } from '@sourcegraph/shared/src/testing/apollo'
7+
import { updateJSContextBatchChangesLicense } from '@sourcegraph/shared/src/testing/batches'
78

89
import { WebStory } from '../../../components/WebStory'
910
import { GlobalChangesetsStatsResult } from '../../../graphql-operations'
@@ -18,8 +19,8 @@ import { BatchChangeListPage } from './BatchChangeListPage'
1819
import {
1920
BATCH_CHANGES_BY_NAMESPACE_RESULT,
2021
BATCH_CHANGES_RESULT,
21-
getLicenseAndUsageInfoResult,
2222
NO_BATCH_CHANGES_RESULT,
23+
getLicenseAndUsageInfoResult,
2324
} from './testData'
2425

2526
const decorator: DecoratorFn = story => <div className="p-3 container">{story()}</div>
@@ -89,23 +90,27 @@ interface Args {
8990
isApp: boolean
9091
}
9192

92-
export const ListOfBatchChanges: Story<Args> = args => (
93-
<WebStory>
94-
{props => (
95-
<MockedTestProvider link={buildMocks()}>
96-
<BatchChangeListPage
97-
{...props}
98-
headingElement="h1"
99-
canCreate={args.canCreate || "You don't have permission to create batch changes"}
100-
settingsCascade={EMPTY_SETTINGS_CASCADE}
101-
isSourcegraphDotCom={args.isDotCom}
102-
isSourcegraphApp={args.isApp}
103-
authenticatedUser={null}
104-
/>
105-
</MockedTestProvider>
106-
)}
107-
</WebStory>
108-
)
93+
export const ListOfBatchChanges: Story<Args> = args => {
94+
updateJSContextBatchChangesLicense('full')
95+
96+
return (
97+
<WebStory>
98+
{props => (
99+
<MockedTestProvider link={buildMocks()}>
100+
<BatchChangeListPage
101+
{...props}
102+
headingElement="h1"
103+
canCreate={args.canCreate || "You don't have permission to create batch changes"}
104+
settingsCascade={EMPTY_SETTINGS_CASCADE}
105+
isSourcegraphDotCom={args.isDotCom}
106+
isSourcegraphApp={args.isApp}
107+
authenticatedUser={null}
108+
/>
109+
</MockedTestProvider>
110+
)}
111+
</WebStory>
112+
)
113+
}
109114
ListOfBatchChanges.argTypes = {
110115
canCreate: {
111116
name: 'can create batch changes',
@@ -126,109 +131,129 @@ ListOfBatchChanges.argTypes = {
126131

127132
ListOfBatchChanges.storyName = 'List of batch changes'
128133

129-
export const ListOfBatchChangesSpecificNamespace: Story = () => (
130-
<WebStory>
131-
{props => (
132-
<MockedTestProvider link={MOCKS_FOR_NAMESPACE}>
133-
<BatchChangeListPage
134-
{...props}
135-
headingElement="h1"
136-
canCreate={true}
137-
namespaceID="test-12345"
138-
settingsCascade={EMPTY_SETTINGS_CASCADE}
139-
isSourcegraphDotCom={false}
140-
isSourcegraphApp={false}
141-
authenticatedUser={null}
142-
/>
143-
</MockedTestProvider>
144-
)}
145-
</WebStory>
146-
)
134+
export const ListOfBatchChangesSpecificNamespace: Story = () => {
135+
updateJSContextBatchChangesLicense('full')
136+
137+
return (
138+
<WebStory>
139+
{props => (
140+
<MockedTestProvider link={MOCKS_FOR_NAMESPACE}>
141+
<BatchChangeListPage
142+
{...props}
143+
headingElement="h1"
144+
canCreate={true}
145+
namespaceID="test-12345"
146+
settingsCascade={EMPTY_SETTINGS_CASCADE}
147+
isSourcegraphDotCom={false}
148+
isSourcegraphApp={false}
149+
authenticatedUser={null}
150+
/>
151+
</MockedTestProvider>
152+
)}
153+
</WebStory>
154+
)
155+
}
147156

148157
ListOfBatchChangesSpecificNamespace.storyName = 'List of batch changes, for a specific namespace'
149158

150-
export const ListOfBatchChangesServerSideExecutionEnabled: Story = () => (
151-
<WebStory>
152-
{props => (
153-
<MockedTestProvider link={buildMocks()}>
154-
<BatchChangeListPage
155-
{...props}
156-
headingElement="h1"
157-
canCreate={true}
158-
settingsCascade={{
159-
...EMPTY_SETTINGS_CASCADE,
160-
final: {
161-
experimentalFeatures: { batchChangesExecution: true },
162-
},
163-
}}
164-
isSourcegraphDotCom={false}
165-
isSourcegraphApp={false}
166-
authenticatedUser={null}
167-
/>
168-
</MockedTestProvider>
169-
)}
170-
</WebStory>
171-
)
159+
export const ListOfBatchChangesServerSideExecutionEnabled: Story = () => {
160+
updateJSContextBatchChangesLicense('full')
161+
162+
return (
163+
<WebStory>
164+
{props => (
165+
<MockedTestProvider link={buildMocks()}>
166+
<BatchChangeListPage
167+
{...props}
168+
headingElement="h1"
169+
canCreate={true}
170+
settingsCascade={{
171+
...EMPTY_SETTINGS_CASCADE,
172+
final: {
173+
experimentalFeatures: { batchChangesExecution: true },
174+
},
175+
}}
176+
isSourcegraphDotCom={false}
177+
isSourcegraphApp={false}
178+
authenticatedUser={null}
179+
/>
180+
</MockedTestProvider>
181+
)}
182+
</WebStory>
183+
)
184+
}
172185

173186
ListOfBatchChangesServerSideExecutionEnabled.storyName = 'List of batch changes, server-side execution enabled'
174187

175-
export const LicensingNotEnforced: Story = () => (
176-
<WebStory>
177-
{props => (
178-
<MockedTestProvider link={buildMocks(false)}>
179-
<BatchChangeListPage
180-
{...props}
181-
headingElement="h1"
182-
canCreate={true}
183-
settingsCascade={EMPTY_SETTINGS_CASCADE}
184-
isSourcegraphDotCom={false}
185-
isSourcegraphApp={false}
186-
authenticatedUser={null}
187-
/>
188-
</MockedTestProvider>
189-
)}
190-
</WebStory>
191-
)
188+
export const LicensingNotEnforced: Story = () => {
189+
updateJSContextBatchChangesLicense('limited')
190+
191+
return (
192+
<WebStory>
193+
{props => (
194+
<MockedTestProvider link={buildMocks(false)}>
195+
<BatchChangeListPage
196+
{...props}
197+
headingElement="h1"
198+
canCreate={true}
199+
settingsCascade={EMPTY_SETTINGS_CASCADE}
200+
isSourcegraphDotCom={false}
201+
isSourcegraphApp={false}
202+
authenticatedUser={null}
203+
/>
204+
</MockedTestProvider>
205+
)}
206+
</WebStory>
207+
)
208+
}
192209

193210
LicensingNotEnforced.storyName = 'Licensing not enforced'
194211

195-
export const NoBatchChanges: Story = () => (
196-
<WebStory>
197-
{props => (
198-
<MockedTestProvider link={buildMocks(true, false)}>
199-
<BatchChangeListPage
200-
{...props}
201-
headingElement="h1"
202-
canCreate={true}
203-
settingsCascade={EMPTY_SETTINGS_CASCADE}
204-
isSourcegraphDotCom={false}
205-
isSourcegraphApp={false}
206-
authenticatedUser={null}
207-
/>
208-
</MockedTestProvider>
209-
)}
210-
</WebStory>
211-
)
212+
export const NoBatchChanges: Story = () => {
213+
updateJSContextBatchChangesLicense('full')
214+
215+
return (
216+
<WebStory>
217+
{props => (
218+
<MockedTestProvider link={buildMocks(true, false)}>
219+
<BatchChangeListPage
220+
{...props}
221+
headingElement="h1"
222+
canCreate={true}
223+
settingsCascade={EMPTY_SETTINGS_CASCADE}
224+
isSourcegraphDotCom={false}
225+
isSourcegraphApp={false}
226+
authenticatedUser={null}
227+
/>
228+
</MockedTestProvider>
229+
)}
230+
</WebStory>
231+
)
232+
}
212233

213234
NoBatchChanges.storyName = 'No batch changes'
214235

215-
export const AllBatchChangesTabEmpty: Story = () => (
216-
<WebStory>
217-
{props => (
218-
<MockedTestProvider link={buildMocks(true, true, false)}>
219-
<BatchChangeListPage
220-
{...props}
221-
headingElement="h1"
222-
canCreate={true}
223-
openTab="batchChanges"
224-
settingsCascade={EMPTY_SETTINGS_CASCADE}
225-
isSourcegraphDotCom={false}
226-
isSourcegraphApp={false}
227-
authenticatedUser={null}
228-
/>
229-
</MockedTestProvider>
230-
)}
231-
</WebStory>
232-
)
236+
export const AllBatchChangesTabEmpty: Story = () => {
237+
updateJSContextBatchChangesLicense('full')
238+
239+
return (
240+
<WebStory>
241+
{props => (
242+
<MockedTestProvider link={buildMocks(true, true, false)}>
243+
<BatchChangeListPage
244+
{...props}
245+
headingElement="h1"
246+
canCreate={true}
247+
openTab="batchChanges"
248+
settingsCascade={EMPTY_SETTINGS_CASCADE}
249+
isSourcegraphDotCom={false}
250+
isSourcegraphApp={false}
251+
authenticatedUser={null}
252+
/>
253+
</MockedTestProvider>
254+
)}
255+
</WebStory>
256+
)
257+
}
233258

234259
AllBatchChangesTabEmpty.storyName = 'All batch changes tab empty'

client/web/src/enterprise/batches/list/BatchChangeListPage.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,12 @@ export const BatchChangeListPage: React.FunctionComponent<React.PropsWithChildre
8686
useEffect(() => telemetryService.logViewEvent('BatchChangesListPage'), [telemetryService])
8787

8888
const isExecutionEnabled = isBatchChangesExecutionEnabled(settingsCascade)
89+
const isBatchChangesLicensed = !!window.context.licenseInfo?.batchChanges?.unrestricted
90+
const canUseBatchChanges = !!window.context.licenseInfo?.batchChanges
8991

9092
const { selectedFilters, setSelectedFilters, availableFilters } = useBatchChangeListFilters({ isExecutionEnabled })
9193
const [selectedTab, setSelectedTab] = useState<SelectedTab>(
92-
openTab ?? (isSourcegraphDotCom ? 'gettingStarted' : 'batchChanges')
94+
openTab ?? (isSourcegraphDotCom || !canUseBatchChanges ? 'gettingStarted' : 'batchChanges')
9395
)
9496

9597
// We keep state to track to the last total count of batch changes in the connection
@@ -196,8 +198,8 @@ export const BatchChangeListPage: React.FunctionComponent<React.PropsWithChildre
196198
</strong>
197199
</LimitedAccessBanner>
198200
)}
199-
<BatchChangesListIntro isLicensed={licenseAndUsageInfo?.batchChanges || licenseAndUsageInfo?.campaigns} />
200-
{!isSourcegraphDotCom && (
201+
<BatchChangesListIntro isLicensed={isBatchChangesLicensed} />
202+
{!isSourcegraphDotCom && canUseBatchChanges && (
201203
<BatchChangeListTabHeader selectedTab={selectedTab} setSelectedTab={setSelectedTab} />
202204
)}
203205
{selectedTab === 'gettingStarted' && (

client/web/src/enterprise/batches/list/BatchChangesListIntro.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ const BatchChangesUnlicensedAlert: React.FunctionComponent<React.PropsWithChildr
4949
<H4>Batch changes trial</H4>
5050
<Text>
5151
Batch changes is a paid feature of Sourcegraph. All users can create sample batch changes with
52-
up to five changesets without a license.
52+
up to ten changesets without a license.
5353
</Text>
5454
<Text className="mb-0">
5555
<Link to="https://about.sourcegraph.com/contact/sales/">Contact sales</Link> to obtain a trial

client/web/src/integration/jscontext.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,13 @@ export const createJsContext = ({ sourcegraphBaseUrl }: { sourcegraphBaseUrl: st
3939
emailEnabled: false,
4040
experimentalFeatures: {},
4141
isAuthenticatedUser: true,
42+
licenseInfo: {
43+
currentPlan: 'team-0',
44+
batchChanges: {
45+
maxNumChangesets: -1,
46+
unrestricted: true,
47+
},
48+
},
4249
needServerRestart: false,
4350
needsSiteInit: false,
4451
needsRepositoryConfiguration: false,

client/web/src/jscontext.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { AuthenticatedUser } from '@sourcegraph/shared/src/auth'
22
import { SiteConfiguration } from '@sourcegraph/shared/src/schema/site.schema'
3+
import { BatchChangesLicenseInfo } from '@sourcegraph/shared/src/testing/batches'
34

45
import { TemporarySettingsResult } from './graphql-operations'
56

@@ -255,6 +256,7 @@ export interface SourcegraphContext extends Pick<Required<SiteConfiguration>, 'e
255256
codeScaleLimit?: string
256257
codeScaleCloseToLimit?: boolean
257258
codeScaleExceededLimit?: boolean
259+
batchChanges?: BatchChangesLicenseInfo
258260
knownLicenseTags?: string[]
259261
}
260262

0 commit comments

Comments
 (0)