Skip to content

Commit ead0ab4

Browse files
committed
Add licenseXService
1 parent 31ae8eb commit ead0ab4

File tree

4 files changed

+135
-0
lines changed

4 files changed

+135
-0
lines changed

site/src/api/types.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,17 @@ export interface ReconnectingPTYRequest {
1414
export type WorkspaceBuildTransition = "start" | "stop" | "delete"
1515

1616
export type Message = { message: string }
17+
18+
export type LicensePermission = "audit" | "createUser" | "createOrg"
19+
20+
export type LicenseFeatures = Record<LicensePermission, {
21+
entitled: boolean
22+
enabled: boolean
23+
limit?: number
24+
actual?: number
25+
}>
26+
27+
export type LicenseData = {
28+
features: LicenseFeatures
29+
warnings: string[]
30+
}

site/src/xServices/StateContext.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { useNavigate } from "react-router"
44
import { ActorRefFrom } from "xstate"
55
import { authMachine } from "./auth/authXService"
66
import { buildInfoMachine } from "./buildInfo/buildInfoXService"
7+
import { licenseMachine } from "./license/licenseXService"
78
import { siteRolesMachine } from "./roles/siteRolesXService"
89
import { usersMachine } from "./users/usersXService"
910

@@ -12,6 +13,7 @@ interface XServiceContextType {
1213
buildInfoXService: ActorRefFrom<typeof buildInfoMachine>
1314
usersXService: ActorRefFrom<typeof usersMachine>
1415
siteRolesXService: ActorRefFrom<typeof siteRolesMachine>
16+
licenseXService: ActorRefFrom<typeof licenseMachine>
1517
}
1618

1719
/**
@@ -39,6 +41,7 @@ export const XServiceProvider: React.FC = ({ children }) => {
3941
usersMachine.withConfig({ actions: { redirectToUsersPage } }),
4042
),
4143
siteRolesXService: useInterpret(siteRolesMachine),
44+
licenseXService: useInterpret(licenseMachine)
4245
}}
4346
>
4447
{children}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { State } from "xstate"
2+
import { LicensePermission } from "../../api/types"
3+
import { LicenseContext, LicenseEvent } from "./licenseXService"
4+
type LicenseState = State<LicenseContext, LicenseEvent>
5+
6+
export const selectLicenseVisibility = (state: LicenseState): Record<LicensePermission, boolean> => {
7+
const features = state.context.licenseData.features
8+
const featureNames = Object.keys(features) as LicensePermission[]
9+
const visibilityPairs = featureNames.map((feature: LicensePermission) => {
10+
return [feature, features[feature].enabled]
11+
})
12+
return Object.fromEntries(visibilityPairs)
13+
}
14+
15+
export const selectLicenseEntitlement = (state: LicenseState): Record<LicensePermission, boolean> => {
16+
const features = state.context.licenseData.features
17+
const featureNames = Object.keys(features) as LicensePermission[]
18+
const permissionPairs = featureNames.map((feature: LicensePermission) => {
19+
const { entitled, limit, actual } = features[feature]
20+
const limitCompliant = limit && actual && limit >= actual
21+
return [feature, entitled && limitCompliant]
22+
})
23+
return Object.fromEntries(permissionPairs)
24+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import { assign, createMachine } from "xstate"
2+
import * as API from "../../api/api"
3+
import { LicenseData } from "../../api/types"
4+
5+
export const Language = {
6+
getLicenseError: "Error getting license information.",
7+
}
8+
9+
/* deserves more thought but this is one way to handle unlicensed cases */
10+
const defaultLicenseData = {
11+
warnings: [],
12+
features: {
13+
audit: {
14+
enabled: false,
15+
entitled: false
16+
},
17+
createUser: {
18+
enabled: true,
19+
entitled: true,
20+
limit: 0
21+
},
22+
createOrg: {
23+
enabled: false,
24+
entitled: false
25+
}
26+
}
27+
}
28+
29+
export type LicenseContext = {
30+
licenseData: LicenseData
31+
getLicenseError?: Error | unknown
32+
}
33+
34+
export type LicenseEvent = {
35+
type: "GET_LICENSE_DATA"
36+
}
37+
38+
export const licenseMachine = createMachine(
39+
{
40+
id: "licenseMachine",
41+
initial: "idle",
42+
schema: {
43+
context: {} as LicenseContext,
44+
events: {} as LicenseEvent,
45+
services: {
46+
getLicenseData: {
47+
data: {} as LicenseData,
48+
},
49+
},
50+
},
51+
tsTypes: {} as import("./licenseXService.typegen").Typegen0,
52+
context: {
53+
licenseData: defaultLicenseData
54+
},
55+
states: {
56+
idle: {
57+
on: {
58+
GET_LICENSE_DATA: "gettingLicenseData",
59+
},
60+
},
61+
gettingLicenseData: {
62+
entry: "clearGetLicenseError",
63+
invoke: {
64+
id: "getLicenseData",
65+
src: "getLicenseData",
66+
onDone: {
67+
target: "idle",
68+
actions: ["assignLicenseData"],
69+
},
70+
onError: {
71+
target: "idle",
72+
actions: ["assignGetLicenseError"],
73+
},
74+
},
75+
},
76+
},
77+
},
78+
{
79+
actions: {
80+
assignLicenseData: assign({
81+
licenseData: (_, event) => event.data,
82+
}),
83+
assignGetLicenseError: assign({
84+
getLicenseError: (_, event) => event.data,
85+
}),
86+
clearGetLicenseError: assign({
87+
getLicenseError: (_) => undefined,
88+
}),
89+
},
90+
services: {
91+
getLicenseData: () => API.getLicenseData(),
92+
},
93+
},
94+
)

0 commit comments

Comments
 (0)