From 190a18e668e9d26ca524a55e88e14bc077935d15 Mon Sep 17 00:00:00 2001 From: michel Date: Wed, 12 Jun 2024 08:53:26 +0200 Subject: [PATCH 01/24] license update proposal --- .../src/generateLicense/generateLicense.ts | 22 ++++++-- .../licenseUpdateTimestamps.ts | 8 +++ .../useLicenseVerifier/useLicenseVerifier.ts | 1 + packages/x-license/src/utils/licenseScope.ts | 2 + packages/x-license/src/utils/licenseStatus.ts | 1 + .../src/verifyLicense/verifyLicense.ts | 50 ++++++++++++++++++- 6 files changed, 79 insertions(+), 5 deletions(-) create mode 100644 packages/x-license/src/generateLicense/licenseUpdateTimestamps.ts diff --git a/packages/x-license/src/generateLicense/generateLicense.ts b/packages/x-license/src/generateLicense/generateLicense.ts index 9ff33495f713..191c151c09c1 100644 --- a/packages/x-license/src/generateLicense/generateLicense.ts +++ b/packages/x-license/src/generateLicense/generateLicense.ts @@ -1,3 +1,4 @@ +import { LICENSE_UPDATE_TIMESTAMPS } from '@mui/x-license/generateLicense/licenseUpdateTimestamps'; import { md5 } from '../encoding/md5'; import { base64Encode } from '../encoding/base64'; import { LICENSE_SCOPES, LicenseScope } from '../utils/licenseScope'; @@ -8,6 +9,7 @@ const licenseVersion = '2'; export interface LicenseDetails { orderNumber: string; expiryDate: Date; + purchaseDate?: Date; scope: LicenseScope; licensingModel: LicensingModel; } @@ -21,9 +23,23 @@ function getClearLicenseString(details: LicenseDetails) { throw new Error('MUI X: Invalid licensing model'); } - return `O=${details.orderNumber},E=${details.expiryDate.getTime()},S=${details.scope},LM=${ - details.licensingModel - },KV=${licenseVersion}`; + if (!details.purchaseDate || new Date().getTime() < LICENSE_UPDATE_TIMESTAMPS['2024-07']) { + throw new Error('MUI X: Licenses generated without a purchaseDate are not supported'); + } + + const parts = [ + `O=${details.orderNumber}`, + `E=${details.expiryDate.getTime()}`, + `S=${details.scope}`, + `LM=${details.licensingModel}`, + `KV=${licenseVersion}`, + ]; + + if (details.purchaseDate) { + parts.splice(1, 0, `P=${details.purchaseDate.getTime()}`); + } + + return parts.join(','); } export function generateLicense(details: LicenseDetails) { diff --git a/packages/x-license/src/generateLicense/licenseUpdateTimestamps.ts b/packages/x-license/src/generateLicense/licenseUpdateTimestamps.ts new file mode 100644 index 000000000000..ec5ee68bb456 --- /dev/null +++ b/packages/x-license/src/generateLicense/licenseUpdateTimestamps.ts @@ -0,0 +1,8 @@ +// according to this section on the licensing notion page: +// https://www.notion.so/mui-org/mui-x-License-validation-after-Pro-plan-with-no-cap-91fa2d16a1eb4c58825f332654196c1a?pvs=4#d26e7747aa1341d299eac49145d57edb +export const LICENSE_UPDATE_TIMESTAMPS = { + // 2024-06-20: to include charts-pro and tree-view-pro, but allow for legacy licenses + '2024-06': new Date(2024, 5, 20, 0, 0, 0, 0).getTime(), + // 2024-07-20: to fully support charts-pro and tree-view-pro on all licenses generated after this date + '2024-07': new Date(2024, 6, 20, 0, 0, 0, 0).getTime(), +}; diff --git a/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.ts b/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.ts index 84151d4acc32..55faeb3bac63 100644 --- a/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.ts +++ b/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.ts @@ -56,6 +56,7 @@ export function useLicenseVerifier( releaseInfo, licenseKey, acceptedScopes, + packageName, }); const fullPackageName = `@mui/${packageName}`; diff --git a/packages/x-license/src/utils/licenseScope.ts b/packages/x-license/src/utils/licenseScope.ts index 74e27de02161..2d6e61c8541e 100644 --- a/packages/x-license/src/utils/licenseScope.ts +++ b/packages/x-license/src/utils/licenseScope.ts @@ -1,3 +1,5 @@ export const LICENSE_SCOPES = ['pro', 'premium'] as const; +export const PRODUCT_SCOPES = ['data-grid', 'date-pickers', 'charts', 'tree-view'] as const; export type LicenseScope = (typeof LICENSE_SCOPES)[number]; +export type ProductScope = (typeof PRODUCT_SCOPES)[number]; diff --git a/packages/x-license/src/utils/licenseStatus.ts b/packages/x-license/src/utils/licenseStatus.ts index 77a27e1d7357..2eef5361da6e 100644 --- a/packages/x-license/src/utils/licenseStatus.ts +++ b/packages/x-license/src/utils/licenseStatus.ts @@ -7,6 +7,7 @@ export enum LICENSE_STATUS { ExpiredVersion = 'ExpiredVersion', Valid = 'Valid', OutOfScope = 'OutOfScope', + ProductScope = 'ProductScope', } export type LicenseStatus = keyof typeof LICENSE_STATUS; diff --git a/packages/x-license/src/verifyLicense/verifyLicense.ts b/packages/x-license/src/verifyLicense/verifyLicense.ts index b280b196dd7f..bdfa65abbe5c 100644 --- a/packages/x-license/src/verifyLicense/verifyLicense.ts +++ b/packages/x-license/src/verifyLicense/verifyLicense.ts @@ -1,7 +1,8 @@ +import { LICENSE_UPDATE_TIMESTAMPS } from '@mui/x-license/generateLicense/licenseUpdateTimestamps'; import { base64Decode, base64Encode } from '../encoding/base64'; import { md5 } from '../encoding/md5'; import { LICENSE_STATUS, LicenseStatus } from '../utils/licenseStatus'; -import { LicenseScope, LICENSE_SCOPES } from '../utils/licenseScope'; +import { LicenseScope, LICENSE_SCOPES, ProductScope } from '../utils/licenseScope'; import { LicensingModel, LICENSING_MODELS } from '../utils/licensingModel'; const getDefaultReleaseDate = () => { @@ -21,6 +22,7 @@ interface MuiLicense { licensingModel: LicensingModel | null; scope: LicenseScope | null; expiryTimestamp: number | null; + purchaseTimestamp?: number | null; } /** @@ -45,7 +47,8 @@ const decodeLicenseVersion1 = (license: string): MuiLicense => { }; /** - * Format: O=${orderNumber},E=${expiryTimestamp},S=${scope},LM=${licensingModel},KV=2`; + * Format: O=${orderNumber}[,P=${purchaseTimestamp}],E=${expiryTimestamp},S=${scope},LM=${licensingModel},KV=2`; + * purchaseTimestamp is optional. */ const decodeLicenseVersion2 = (license: string): MuiLicense => { const licenseInfo: MuiLicense = { @@ -73,6 +76,13 @@ const decodeLicenseVersion2 = (license: string): MuiLicense => { licenseInfo.expiryTimestamp = expiryTimestamp; } } + + if (key === 'P') { + const purchaseTimestamp = parseInt(value, 10); + if (purchaseTimestamp && !Number.isNaN(purchaseTimestamp)) { + licenseInfo.purchaseTimestamp = purchaseTimestamp; + } + } }); return licenseInfo; @@ -95,14 +105,22 @@ const decodeLicense = (encodedLicense: string): MuiLicense | null => { return null; }; +const extractProductScope = (packageName: string): ProductScope | null => { + const regex = /x-(.*?)(-pro|-premium)?$/; + const match = packageName.match(regex); + return match ? (match[1] as ProductScope) : null; +}; + export function verifyLicense({ releaseInfo, licenseKey, acceptedScopes, + packageName, }: { releaseInfo: string; licenseKey: string | undefined; acceptedScopes: LicenseScope[]; + packageName: string; }): { status: LicenseStatus; meta?: any } { if (!releaseInfo) { throw new Error('MUI X: The release information is missing. Not able to validate license.'); @@ -136,6 +154,11 @@ export function verifyLicense({ return { status: LICENSE_STATUS.Invalid }; } + if (license.purchaseTimestamp && license.purchaseTimestamp > license.expiryTimestamp) { + console.error('MUI X: Error checking license. Purchase timestamp cannot be later than expiry!'); + return { status: LICENSE_STATUS.Invalid }; + } + if (license.licensingModel === 'perpetual' || process.env.NODE_ENV === 'production') { const pkgTimestamp = parseInt(base64Decode(releaseInfo), 10); if (Number.isNaN(pkgTimestamp)) { @@ -173,5 +196,28 @@ export function verifyLicense({ return { status: LICENSE_STATUS.OutOfScope }; } + if (packageName) { + const productScope = extractProductScope(packageName); + + switch (productScope) { + case 'charts': + case 'tree-view': + if ( + license.purchaseTimestamp && + license.purchaseTimestamp >= LICENSE_UPDATE_TIMESTAMPS['2024-06'] + ) { + // NEW LICENSE + // therefore usage of charts-pro and tree-view-pro is allowed + break; + } + + return { status: LICENSE_STATUS.ProductScope }; + case 'data-grid': + case 'date-pickers': + default: + break; + } + } + return { status: LICENSE_STATUS.Valid }; } From 17060167930abd92dbd75146c6b536e3704852c4 Mon Sep 17 00:00:00 2001 From: michel Date: Thu, 13 Jun 2024 10:09:36 +0200 Subject: [PATCH 02/24] Revert "license update proposal" This reverts commit 190a18e668e9d26ca524a55e88e14bc077935d15. --- .../src/generateLicense/generateLicense.ts | 22 ++------ .../licenseUpdateTimestamps.ts | 8 --- .../useLicenseVerifier/useLicenseVerifier.ts | 1 - packages/x-license/src/utils/licenseScope.ts | 2 - packages/x-license/src/utils/licenseStatus.ts | 1 - .../src/verifyLicense/verifyLicense.ts | 50 +------------------ 6 files changed, 5 insertions(+), 79 deletions(-) delete mode 100644 packages/x-license/src/generateLicense/licenseUpdateTimestamps.ts diff --git a/packages/x-license/src/generateLicense/generateLicense.ts b/packages/x-license/src/generateLicense/generateLicense.ts index 191c151c09c1..9ff33495f713 100644 --- a/packages/x-license/src/generateLicense/generateLicense.ts +++ b/packages/x-license/src/generateLicense/generateLicense.ts @@ -1,4 +1,3 @@ -import { LICENSE_UPDATE_TIMESTAMPS } from '@mui/x-license/generateLicense/licenseUpdateTimestamps'; import { md5 } from '../encoding/md5'; import { base64Encode } from '../encoding/base64'; import { LICENSE_SCOPES, LicenseScope } from '../utils/licenseScope'; @@ -9,7 +8,6 @@ const licenseVersion = '2'; export interface LicenseDetails { orderNumber: string; expiryDate: Date; - purchaseDate?: Date; scope: LicenseScope; licensingModel: LicensingModel; } @@ -23,23 +21,9 @@ function getClearLicenseString(details: LicenseDetails) { throw new Error('MUI X: Invalid licensing model'); } - if (!details.purchaseDate || new Date().getTime() < LICENSE_UPDATE_TIMESTAMPS['2024-07']) { - throw new Error('MUI X: Licenses generated without a purchaseDate are not supported'); - } - - const parts = [ - `O=${details.orderNumber}`, - `E=${details.expiryDate.getTime()}`, - `S=${details.scope}`, - `LM=${details.licensingModel}`, - `KV=${licenseVersion}`, - ]; - - if (details.purchaseDate) { - parts.splice(1, 0, `P=${details.purchaseDate.getTime()}`); - } - - return parts.join(','); + return `O=${details.orderNumber},E=${details.expiryDate.getTime()},S=${details.scope},LM=${ + details.licensingModel + },KV=${licenseVersion}`; } export function generateLicense(details: LicenseDetails) { diff --git a/packages/x-license/src/generateLicense/licenseUpdateTimestamps.ts b/packages/x-license/src/generateLicense/licenseUpdateTimestamps.ts deleted file mode 100644 index ec5ee68bb456..000000000000 --- a/packages/x-license/src/generateLicense/licenseUpdateTimestamps.ts +++ /dev/null @@ -1,8 +0,0 @@ -// according to this section on the licensing notion page: -// https://www.notion.so/mui-org/mui-x-License-validation-after-Pro-plan-with-no-cap-91fa2d16a1eb4c58825f332654196c1a?pvs=4#d26e7747aa1341d299eac49145d57edb -export const LICENSE_UPDATE_TIMESTAMPS = { - // 2024-06-20: to include charts-pro and tree-view-pro, but allow for legacy licenses - '2024-06': new Date(2024, 5, 20, 0, 0, 0, 0).getTime(), - // 2024-07-20: to fully support charts-pro and tree-view-pro on all licenses generated after this date - '2024-07': new Date(2024, 6, 20, 0, 0, 0, 0).getTime(), -}; diff --git a/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.ts b/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.ts index 55faeb3bac63..84151d4acc32 100644 --- a/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.ts +++ b/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.ts @@ -56,7 +56,6 @@ export function useLicenseVerifier( releaseInfo, licenseKey, acceptedScopes, - packageName, }); const fullPackageName = `@mui/${packageName}`; diff --git a/packages/x-license/src/utils/licenseScope.ts b/packages/x-license/src/utils/licenseScope.ts index 2d6e61c8541e..74e27de02161 100644 --- a/packages/x-license/src/utils/licenseScope.ts +++ b/packages/x-license/src/utils/licenseScope.ts @@ -1,5 +1,3 @@ export const LICENSE_SCOPES = ['pro', 'premium'] as const; -export const PRODUCT_SCOPES = ['data-grid', 'date-pickers', 'charts', 'tree-view'] as const; export type LicenseScope = (typeof LICENSE_SCOPES)[number]; -export type ProductScope = (typeof PRODUCT_SCOPES)[number]; diff --git a/packages/x-license/src/utils/licenseStatus.ts b/packages/x-license/src/utils/licenseStatus.ts index 2eef5361da6e..77a27e1d7357 100644 --- a/packages/x-license/src/utils/licenseStatus.ts +++ b/packages/x-license/src/utils/licenseStatus.ts @@ -7,7 +7,6 @@ export enum LICENSE_STATUS { ExpiredVersion = 'ExpiredVersion', Valid = 'Valid', OutOfScope = 'OutOfScope', - ProductScope = 'ProductScope', } export type LicenseStatus = keyof typeof LICENSE_STATUS; diff --git a/packages/x-license/src/verifyLicense/verifyLicense.ts b/packages/x-license/src/verifyLicense/verifyLicense.ts index bdfa65abbe5c..b280b196dd7f 100644 --- a/packages/x-license/src/verifyLicense/verifyLicense.ts +++ b/packages/x-license/src/verifyLicense/verifyLicense.ts @@ -1,8 +1,7 @@ -import { LICENSE_UPDATE_TIMESTAMPS } from '@mui/x-license/generateLicense/licenseUpdateTimestamps'; import { base64Decode, base64Encode } from '../encoding/base64'; import { md5 } from '../encoding/md5'; import { LICENSE_STATUS, LicenseStatus } from '../utils/licenseStatus'; -import { LicenseScope, LICENSE_SCOPES, ProductScope } from '../utils/licenseScope'; +import { LicenseScope, LICENSE_SCOPES } from '../utils/licenseScope'; import { LicensingModel, LICENSING_MODELS } from '../utils/licensingModel'; const getDefaultReleaseDate = () => { @@ -22,7 +21,6 @@ interface MuiLicense { licensingModel: LicensingModel | null; scope: LicenseScope | null; expiryTimestamp: number | null; - purchaseTimestamp?: number | null; } /** @@ -47,8 +45,7 @@ const decodeLicenseVersion1 = (license: string): MuiLicense => { }; /** - * Format: O=${orderNumber}[,P=${purchaseTimestamp}],E=${expiryTimestamp},S=${scope},LM=${licensingModel},KV=2`; - * purchaseTimestamp is optional. + * Format: O=${orderNumber},E=${expiryTimestamp},S=${scope},LM=${licensingModel},KV=2`; */ const decodeLicenseVersion2 = (license: string): MuiLicense => { const licenseInfo: MuiLicense = { @@ -76,13 +73,6 @@ const decodeLicenseVersion2 = (license: string): MuiLicense => { licenseInfo.expiryTimestamp = expiryTimestamp; } } - - if (key === 'P') { - const purchaseTimestamp = parseInt(value, 10); - if (purchaseTimestamp && !Number.isNaN(purchaseTimestamp)) { - licenseInfo.purchaseTimestamp = purchaseTimestamp; - } - } }); return licenseInfo; @@ -105,22 +95,14 @@ const decodeLicense = (encodedLicense: string): MuiLicense | null => { return null; }; -const extractProductScope = (packageName: string): ProductScope | null => { - const regex = /x-(.*?)(-pro|-premium)?$/; - const match = packageName.match(regex); - return match ? (match[1] as ProductScope) : null; -}; - export function verifyLicense({ releaseInfo, licenseKey, acceptedScopes, - packageName, }: { releaseInfo: string; licenseKey: string | undefined; acceptedScopes: LicenseScope[]; - packageName: string; }): { status: LicenseStatus; meta?: any } { if (!releaseInfo) { throw new Error('MUI X: The release information is missing. Not able to validate license.'); @@ -154,11 +136,6 @@ export function verifyLicense({ return { status: LICENSE_STATUS.Invalid }; } - if (license.purchaseTimestamp && license.purchaseTimestamp > license.expiryTimestamp) { - console.error('MUI X: Error checking license. Purchase timestamp cannot be later than expiry!'); - return { status: LICENSE_STATUS.Invalid }; - } - if (license.licensingModel === 'perpetual' || process.env.NODE_ENV === 'production') { const pkgTimestamp = parseInt(base64Decode(releaseInfo), 10); if (Number.isNaN(pkgTimestamp)) { @@ -196,28 +173,5 @@ export function verifyLicense({ return { status: LICENSE_STATUS.OutOfScope }; } - if (packageName) { - const productScope = extractProductScope(packageName); - - switch (productScope) { - case 'charts': - case 'tree-view': - if ( - license.purchaseTimestamp && - license.purchaseTimestamp >= LICENSE_UPDATE_TIMESTAMPS['2024-06'] - ) { - // NEW LICENSE - // therefore usage of charts-pro and tree-view-pro is allowed - break; - } - - return { status: LICENSE_STATUS.ProductScope }; - case 'data-grid': - case 'date-pickers': - default: - break; - } - } - return { status: LICENSE_STATUS.Valid }; } From 93a4e9ac4f5b09492aa8f9b3a5ddf368794deaa6 Mon Sep 17 00:00:00 2001 From: michel Date: Thu, 13 Jun 2024 11:01:12 +0200 Subject: [PATCH 03/24] second iteration on license update --- .../useLicenseVerifier/useLicenseVerifier.ts | 6 ++--- packages/x-license/src/utils/licenseScope.ts | 23 ++++++++++++++++++- .../src/verifyLicense/verifyLicense.ts | 2 +- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.ts b/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.ts index 84151d4acc32..9ab6894abf3f 100644 --- a/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.ts +++ b/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.ts @@ -10,7 +10,7 @@ import { showExpiredPackageVersionError, } from '../utils/licenseErrorMessageUtils'; import { LICENSE_STATUS, LicenseStatus } from '../utils/licenseStatus'; -import { LicenseScope } from '../utils/licenseScope'; +import { extractAcceptedScopes, LicenseScope } from '../utils/licenseScope'; import MuiLicenseInfoContext from '../Unstable_LicenseInfoProvider/MuiLicenseInfoContext'; export type MuiCommercialPackageName = @@ -47,9 +47,7 @@ export function useLicenseVerifier( return sharedLicenseStatuses[packageName]!.licenseVerifier; } - const acceptedScopes: LicenseScope[] = packageName.includes('premium') - ? ['premium'] - : ['pro', 'premium']; + const acceptedScopes: readonly LicenseScope[] = extractAcceptedScopes(packageName); const plan = packageName.includes('premium') ? 'Premium' : 'Pro'; const licenseStatus = verifyLicense({ diff --git a/packages/x-license/src/utils/licenseScope.ts b/packages/x-license/src/utils/licenseScope.ts index 74e27de02161..4db0ce787678 100644 --- a/packages/x-license/src/utils/licenseScope.ts +++ b/packages/x-license/src/utils/licenseScope.ts @@ -1,3 +1,24 @@ -export const LICENSE_SCOPES = ['pro', 'premium'] as const; +export const LICENSE_SCOPES = ['pro', 'premium', 'pro2024', 'premium2024'] as const; +export const PRODUCT_SCOPES = ['data-grid', 'date-pickers', 'charts', 'tree-view'] as const; export type LicenseScope = (typeof LICENSE_SCOPES)[number]; +export type ProductScope = (typeof PRODUCT_SCOPES)[number] | null; + +export const extractProductScope = (packageName: string): ProductScope | null => { + const regex = /x-(.*?)(-pro|-premium)?$/; + const match = packageName.match(regex); + return match ? (match[1] as ProductScope) : null; +}; + +export const extractAcceptedScopes = (packageName: string): readonly LicenseScope[] => { + const productScope = extractProductScope(packageName); + + const scopes = packageName.includes('premium') + ? LICENSE_SCOPES.filter((scope) => scope.includes('premium')) + : LICENSE_SCOPES; + + if (productScope === 'charts' || productScope === 'tree-view') { + return scopes.filter((scope) => scope.includes('2024')); + } + return scopes; +}; diff --git a/packages/x-license/src/verifyLicense/verifyLicense.ts b/packages/x-license/src/verifyLicense/verifyLicense.ts index b280b196dd7f..a953db984a71 100644 --- a/packages/x-license/src/verifyLicense/verifyLicense.ts +++ b/packages/x-license/src/verifyLicense/verifyLicense.ts @@ -102,7 +102,7 @@ export function verifyLicense({ }: { releaseInfo: string; licenseKey: string | undefined; - acceptedScopes: LicenseScope[]; + acceptedScopes: readonly LicenseScope[]; }): { status: LicenseStatus; meta?: any } { if (!releaseInfo) { throw new Error('MUI X: The release information is missing. Not able to validate license.'); From 8b4369eb64853528b1e516693ae49a3464530b8f Mon Sep 17 00:00:00 2001 From: michel Date: Thu, 13 Jun 2024 15:30:43 +0200 Subject: [PATCH 04/24] 3rd iteration --- .../generateLicense/generateLicense.test.ts | 13 ++++++++++++ .../src/generateLicense/generateLicense.ts | 14 ++++++++++--- .../useLicenseVerifier/useLicenseVerifier.ts | 9 +++++++- packages/x-license/src/utils/licenseScope.ts | 16 ++++++-------- .../src/verifyLicense/verifyLicense.test.ts | 20 ++++++++++++++++++ .../src/verifyLicense/verifyLicense.ts | 21 +++++++++++++++++-- 6 files changed, 77 insertions(+), 16 deletions(-) diff --git a/packages/x-license/src/generateLicense/generateLicense.test.ts b/packages/x-license/src/generateLicense/generateLicense.test.ts index 5a27b7880364..adbbed34cc0a 100644 --- a/packages/x-license/src/generateLicense/generateLicense.test.ts +++ b/packages/x-license/src/generateLicense/generateLicense.test.ts @@ -53,4 +53,17 @@ describe('License: generateLicense', () => { 'b16edd8e6bc83293a723779a259f520cTz1NVUktMTIzLEU9MTU5MTcyMzg3OTA2MixTPXBybyxMTT1wZXJwZXR1YWwsS1Y9Mg==', ); }); + + it('should generate a new license covering charts-pro and tree-view-pro', () => { + expect( + generateLicense({ + expiryDate: new Date(1591723879062), + orderNumber: 'MUI-123', + scope: 'pro2024', + licensingModel: 'annual', + }), + ).to.equal( + 'b3fbd10f8df5b5a6976673e6e714b8a1Tz1NVUktMTIzLEU9MTc2NzIyNTU5OTk5OSxTPXBybzIwMjQsTE09YW5udWFsLEtWPTI=', + ); + }); }); diff --git a/packages/x-license/src/generateLicense/generateLicense.ts b/packages/x-license/src/generateLicense/generateLicense.ts index 9ff33495f713..5cb150e4e2bc 100644 --- a/packages/x-license/src/generateLicense/generateLicense.ts +++ b/packages/x-license/src/generateLicense/generateLicense.ts @@ -10,6 +10,7 @@ export interface LicenseDetails { expiryDate: Date; scope: LicenseScope; licensingModel: LicensingModel; + planVersion: string; } function getClearLicenseString(details: LicenseDetails) { @@ -21,9 +22,16 @@ function getClearLicenseString(details: LicenseDetails) { throw new Error('MUI X: Invalid licensing model'); } - return `O=${details.orderNumber},E=${details.expiryDate.getTime()},S=${details.scope},LM=${ - details.licensingModel - },KV=${licenseVersion}`; + const keyParts = [ + `O=${details.orderNumber}`, + `E=${details.expiryDate.getTime()}`, + `S=${details.scope}`, + `LM=${details.licensingModel}`, + `PV=${details.planVersion}`, + `KV=${licenseVersion}`, + ]; + + return keyParts.join(','); } export function generateLicense(details: LicenseDetails) { diff --git a/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.ts b/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.ts index 9ab6894abf3f..875a8f9910af 100644 --- a/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.ts +++ b/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.ts @@ -10,7 +10,12 @@ import { showExpiredPackageVersionError, } from '../utils/licenseErrorMessageUtils'; import { LICENSE_STATUS, LicenseStatus } from '../utils/licenseStatus'; -import { extractAcceptedScopes, LicenseScope } from '../utils/licenseScope'; +import { + extractAcceptedScopes, + extractProductScope, + LicenseScope, + ProductScope, +} from '../utils/licenseScope'; import MuiLicenseInfoContext from '../Unstable_LicenseInfoProvider/MuiLicenseInfoContext'; export type MuiCommercialPackageName = @@ -48,12 +53,14 @@ export function useLicenseVerifier( } const acceptedScopes: readonly LicenseScope[] = extractAcceptedScopes(packageName); + const productScope: ProductScope | null = extractProductScope(packageName); const plan = packageName.includes('premium') ? 'Premium' : 'Pro'; const licenseStatus = verifyLicense({ releaseInfo, licenseKey, acceptedScopes, + productScope, }); const fullPackageName = `@mui/${packageName}`; diff --git a/packages/x-license/src/utils/licenseScope.ts b/packages/x-license/src/utils/licenseScope.ts index 4db0ce787678..9dfe1f811ced 100644 --- a/packages/x-license/src/utils/licenseScope.ts +++ b/packages/x-license/src/utils/licenseScope.ts @@ -1,24 +1,20 @@ -export const LICENSE_SCOPES = ['pro', 'premium', 'pro2024', 'premium2024'] as const; +export const LICENSE_SCOPES = ['pro', 'premium'] as const; export const PRODUCT_SCOPES = ['data-grid', 'date-pickers', 'charts', 'tree-view'] as const; +export const PLAN_VERSIONS = ['legacy', 'Q3-2024'] as const; export type LicenseScope = (typeof LICENSE_SCOPES)[number]; -export type ProductScope = (typeof PRODUCT_SCOPES)[number] | null; +export type ProductScope = (typeof PRODUCT_SCOPES)[number]; +export type PlanVersion = (typeof PLAN_VERSIONS)[number]; export const extractProductScope = (packageName: string): ProductScope | null => { + // extract the part between "x-" and "-pro"/"-premium" const regex = /x-(.*?)(-pro|-premium)?$/; const match = packageName.match(regex); return match ? (match[1] as ProductScope) : null; }; export const extractAcceptedScopes = (packageName: string): readonly LicenseScope[] => { - const productScope = extractProductScope(packageName); - - const scopes = packageName.includes('premium') + return packageName.includes('premium') ? LICENSE_SCOPES.filter((scope) => scope.includes('premium')) : LICENSE_SCOPES; - - if (productScope === 'charts' || productScope === 'tree-view') { - return scopes.filter((scope) => scope.includes('2024')); - } - return scopes; }; diff --git a/packages/x-license/src/verifyLicense/verifyLicense.test.ts b/packages/x-license/src/verifyLicense/verifyLicense.test.ts index 7f916614ab14..e26db47322b3 100644 --- a/packages/x-license/src/verifyLicense/verifyLicense.test.ts +++ b/packages/x-license/src/verifyLicense/verifyLicense.test.ts @@ -243,4 +243,24 @@ describe('License: verifyLicense', () => { ).to.equal(LICENSE_STATUS.Valid); }); }); + + describe('key version: 2.2', () => { + const licenseKeyPro = generateLicense({ + expiryDate: new Date(releaseDate.getTime() + oneDayInMS), + orderNumber: 'MUI-123', + scope: 'pro2024', + licensingModel: 'annual', + }); + + it('should accept licensingModel="annual"', () => { + process.env.NODE_ENV = 'production'; + expect( + verifyLicense({ + releaseInfo: RELEASE_INFO, + licenseKey: licenseKeyPro, + acceptedScopes: ['pro2024', 'premium2024'], + }).status, + ).to.equal(LICENSE_STATUS.Valid); + }); + }); }); diff --git a/packages/x-license/src/verifyLicense/verifyLicense.ts b/packages/x-license/src/verifyLicense/verifyLicense.ts index a953db984a71..f75a8b27e316 100644 --- a/packages/x-license/src/verifyLicense/verifyLicense.ts +++ b/packages/x-license/src/verifyLicense/verifyLicense.ts @@ -1,7 +1,7 @@ import { base64Decode, base64Encode } from '../encoding/base64'; import { md5 } from '../encoding/md5'; import { LICENSE_STATUS, LicenseStatus } from '../utils/licenseStatus'; -import { LicenseScope, LICENSE_SCOPES } from '../utils/licenseScope'; +import { LicenseScope, LICENSE_SCOPES, ProductScope, PlanVersion } from '../utils/licenseScope'; import { LicensingModel, LICENSING_MODELS } from '../utils/licensingModel'; const getDefaultReleaseDate = () => { @@ -21,6 +21,7 @@ interface MuiLicense { licensingModel: LicensingModel | null; scope: LicenseScope | null; expiryTimestamp: number | null; + planVersion?: PlanVersion | null; } /** @@ -45,7 +46,7 @@ const decodeLicenseVersion1 = (license: string): MuiLicense => { }; /** - * Format: O=${orderNumber},E=${expiryTimestamp},S=${scope},LM=${licensingModel},KV=2`; + * Format: O=${orderNumber},E=${expiryTimestamp},S=${scope},LM=${licensingModel},PV=${planVersion},KV=2`; */ const decodeLicenseVersion2 = (license: string): MuiLicense => { const licenseInfo: MuiLicense = { @@ -73,6 +74,10 @@ const decodeLicenseVersion2 = (license: string): MuiLicense => { licenseInfo.expiryTimestamp = expiryTimestamp; } } + + if (key === 'PV') { + licenseInfo.planVersion = value as PlanVersion; + } }); return licenseInfo; @@ -99,10 +104,12 @@ export function verifyLicense({ releaseInfo, licenseKey, acceptedScopes, + productScope, }: { releaseInfo: string; licenseKey: string | undefined; acceptedScopes: readonly LicenseScope[]; + productScope: ProductScope | null; }): { status: LicenseStatus; meta?: any } { if (!releaseInfo) { throw new Error('MUI X: The release information is missing. Not able to validate license.'); @@ -169,6 +176,16 @@ export function verifyLicense({ return { status: LICENSE_STATUS.Invalid }; } + // no planVersion or planVersion is 'legacy' + // only available for old licenses ordered between 2024-06-20 and 2024-07-20 + if (!license.planVersion || license.planVersion === 'legacy') { + // check if the productScope is 'charts' or 'tree-view' + if (productScope === 'charts' || productScope === 'tree-view') { + console.error('Error checking license. Plan version not found or invalid!'); + return { status: LICENSE_STATUS.Invalid }; + } + } + if (!acceptedScopes.includes(license.scope)) { return { status: LICENSE_STATUS.OutOfScope }; } From f815941ac72838638e45eade60b8ad549cc35466 Mon Sep 17 00:00:00 2001 From: michel Date: Thu, 13 Jun 2024 15:31:30 +0200 Subject: [PATCH 05/24] changed status return on charts/treeview check --- packages/x-license/src/verifyLicense/verifyLicense.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/x-license/src/verifyLicense/verifyLicense.ts b/packages/x-license/src/verifyLicense/verifyLicense.ts index f75a8b27e316..13848f66dbca 100644 --- a/packages/x-license/src/verifyLicense/verifyLicense.ts +++ b/packages/x-license/src/verifyLicense/verifyLicense.ts @@ -182,7 +182,7 @@ export function verifyLicense({ // check if the productScope is 'charts' or 'tree-view' if (productScope === 'charts' || productScope === 'tree-view') { console.error('Error checking license. Plan version not found or invalid!'); - return { status: LICENSE_STATUS.Invalid }; + return { status: LICENSE_STATUS.OutOfScope }; } } From 3ccba215bf71063ea685bb8bc99b504b58870212 Mon Sep 17 00:00:00 2001 From: michel Date: Thu, 13 Jun 2024 15:33:11 +0200 Subject: [PATCH 06/24] removed tests for now --- .../generateLicense/generateLicense.test.ts | 13 ------------ .../src/verifyLicense/verifyLicense.test.ts | 20 ------------------- 2 files changed, 33 deletions(-) diff --git a/packages/x-license/src/generateLicense/generateLicense.test.ts b/packages/x-license/src/generateLicense/generateLicense.test.ts index adbbed34cc0a..5a27b7880364 100644 --- a/packages/x-license/src/generateLicense/generateLicense.test.ts +++ b/packages/x-license/src/generateLicense/generateLicense.test.ts @@ -53,17 +53,4 @@ describe('License: generateLicense', () => { 'b16edd8e6bc83293a723779a259f520cTz1NVUktMTIzLEU9MTU5MTcyMzg3OTA2MixTPXBybyxMTT1wZXJwZXR1YWwsS1Y9Mg==', ); }); - - it('should generate a new license covering charts-pro and tree-view-pro', () => { - expect( - generateLicense({ - expiryDate: new Date(1591723879062), - orderNumber: 'MUI-123', - scope: 'pro2024', - licensingModel: 'annual', - }), - ).to.equal( - 'b3fbd10f8df5b5a6976673e6e714b8a1Tz1NVUktMTIzLEU9MTc2NzIyNTU5OTk5OSxTPXBybzIwMjQsTE09YW5udWFsLEtWPTI=', - ); - }); }); diff --git a/packages/x-license/src/verifyLicense/verifyLicense.test.ts b/packages/x-license/src/verifyLicense/verifyLicense.test.ts index e26db47322b3..7f916614ab14 100644 --- a/packages/x-license/src/verifyLicense/verifyLicense.test.ts +++ b/packages/x-license/src/verifyLicense/verifyLicense.test.ts @@ -243,24 +243,4 @@ describe('License: verifyLicense', () => { ).to.equal(LICENSE_STATUS.Valid); }); }); - - describe('key version: 2.2', () => { - const licenseKeyPro = generateLicense({ - expiryDate: new Date(releaseDate.getTime() + oneDayInMS), - orderNumber: 'MUI-123', - scope: 'pro2024', - licensingModel: 'annual', - }); - - it('should accept licensingModel="annual"', () => { - process.env.NODE_ENV = 'production'; - expect( - verifyLicense({ - releaseInfo: RELEASE_INFO, - licenseKey: licenseKeyPro, - acceptedScopes: ['pro2024', 'premium2024'], - }).status, - ).to.equal(LICENSE_STATUS.Valid); - }); - }); }); From 46cf0d5ceb90b96dce0eec30679cd78b86e8d48e Mon Sep 17 00:00:00 2001 From: michel Date: Thu, 13 Jun 2024 15:59:12 +0200 Subject: [PATCH 07/24] adjusted things and fixed tests --- .../src/generateLicense/generateLicense.test.ts | 12 ++++++++---- .../x-license/src/generateLicense/generateLicense.ts | 4 ++-- .../src/useLicenseVerifier/useLicenseVerifier.ts | 2 +- packages/x-license/src/utils/licenseScope.ts | 4 ++-- .../src/verifyLicense/verifyLicense.test.ts | 8 ++++++++ .../x-license/src/verifyLicense/verifyLicense.ts | 7 +++---- 6 files changed, 24 insertions(+), 13 deletions(-) diff --git a/packages/x-license/src/generateLicense/generateLicense.test.ts b/packages/x-license/src/generateLicense/generateLicense.test.ts index 5a27b7880364..f211d30b3562 100644 --- a/packages/x-license/src/generateLicense/generateLicense.test.ts +++ b/packages/x-license/src/generateLicense/generateLicense.test.ts @@ -9,9 +9,10 @@ describe('License: generateLicense', () => { orderNumber: 'MUI-123', scope: 'pro', licensingModel: 'subscription', + planVersion: 'legacy', }), ).to.equal( - 'b2b2ea9c6fd846e11770da3c795d6f63Tz1NVUktMTIzLEU9MTU5MTcyMzg3OTA2MixTPXBybyxMTT1zdWJzY3JpcHRpb24sS1Y9Mg==', + '076fc290e354b9a04720190023e1f868Tz1NVUktMTIzLEU9MTU5MTcyMzg3OTA2MixTPXBybyxMTT1zdWJzY3JpcHRpb24sUFY9bGVnYWN5LEtWPTI=', ); }); @@ -22,9 +23,10 @@ describe('License: generateLicense', () => { orderNumber: 'MUI-123', scope: 'premium', licensingModel: 'subscription', + planVersion: 'legacy', }), ).to.equal( - 'ac8d20b4ecd1f919157f3713f8ba1651Tz1NVUktMTIzLEU9MTU5MTcyMzg3OTA2MixTPXByZW1pdW0sTE09c3Vic2NyaXB0aW9uLEtWPTI=', + 'fafc52d1dbb97e825702a7ccf5986fbbTz1NVUktMTIzLEU9MTU5MTcyMzg3OTA2MixTPXByZW1pdW0sTE09c3Vic2NyaXB0aW9uLFBWPWxlZ2FjeSxLVj0y', ); }); @@ -35,9 +37,10 @@ describe('License: generateLicense', () => { orderNumber: 'MUI-123', scope: 'pro', licensingModel: 'subscription', + planVersion: 'legacy', }), ).to.equal( - 'b2b2ea9c6fd846e11770da3c795d6f63Tz1NVUktMTIzLEU9MTU5MTcyMzg3OTA2MixTPXBybyxMTT1zdWJzY3JpcHRpb24sS1Y9Mg==', + '076fc290e354b9a04720190023e1f868Tz1NVUktMTIzLEU9MTU5MTcyMzg3OTA2MixTPXBybyxMTT1zdWJzY3JpcHRpb24sUFY9bGVnYWN5LEtWPTI=', ); }); @@ -48,9 +51,10 @@ describe('License: generateLicense', () => { orderNumber: 'MUI-123', scope: 'pro', licensingModel: 'perpetual', + planVersion: 'legacy', }), ).to.equal( - 'b16edd8e6bc83293a723779a259f520cTz1NVUktMTIzLEU9MTU5MTcyMzg3OTA2MixTPXBybyxMTT1wZXJwZXR1YWwsS1Y9Mg==', + 'a32c4d6c8cce5d9ca0132527a252e999Tz1NVUktMTIzLEU9MTU5MTcyMzg3OTA2MixTPXBybyxMTT1wZXJwZXR1YWwsUFY9bGVnYWN5LEtWPTI=', ); }); }); diff --git a/packages/x-license/src/generateLicense/generateLicense.ts b/packages/x-license/src/generateLicense/generateLicense.ts index 5cb150e4e2bc..badebb90d782 100644 --- a/packages/x-license/src/generateLicense/generateLicense.ts +++ b/packages/x-license/src/generateLicense/generateLicense.ts @@ -1,6 +1,6 @@ import { md5 } from '../encoding/md5'; import { base64Encode } from '../encoding/base64'; -import { LICENSE_SCOPES, LicenseScope } from '../utils/licenseScope'; +import { LICENSE_SCOPES, LicenseScope, PlanVersion } from '../utils/licenseScope'; import { LICENSING_MODELS, LicensingModel } from '../utils/licensingModel'; const licenseVersion = '2'; @@ -10,7 +10,7 @@ export interface LicenseDetails { expiryDate: Date; scope: LicenseScope; licensingModel: LicensingModel; - planVersion: string; + planVersion: PlanVersion; } function getClearLicenseString(details: LicenseDetails) { diff --git a/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.ts b/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.ts index 875a8f9910af..210332e12fac 100644 --- a/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.ts +++ b/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.ts @@ -53,7 +53,7 @@ export function useLicenseVerifier( } const acceptedScopes: readonly LicenseScope[] = extractAcceptedScopes(packageName); - const productScope: ProductScope | null = extractProductScope(packageName); + const productScope: ProductScope = extractProductScope(packageName); const plan = packageName.includes('premium') ? 'Premium' : 'Pro'; const licenseStatus = verifyLicense({ diff --git a/packages/x-license/src/utils/licenseScope.ts b/packages/x-license/src/utils/licenseScope.ts index 9dfe1f811ced..c042eeca1aa9 100644 --- a/packages/x-license/src/utils/licenseScope.ts +++ b/packages/x-license/src/utils/licenseScope.ts @@ -1,12 +1,12 @@ export const LICENSE_SCOPES = ['pro', 'premium'] as const; -export const PRODUCT_SCOPES = ['data-grid', 'date-pickers', 'charts', 'tree-view'] as const; +export const PRODUCT_SCOPES = ['data-grid', 'date-pickers', 'charts', 'tree-view', null] as const; export const PLAN_VERSIONS = ['legacy', 'Q3-2024'] as const; export type LicenseScope = (typeof LICENSE_SCOPES)[number]; export type ProductScope = (typeof PRODUCT_SCOPES)[number]; export type PlanVersion = (typeof PLAN_VERSIONS)[number]; -export const extractProductScope = (packageName: string): ProductScope | null => { +export const extractProductScope = (packageName: string): ProductScope => { // extract the part between "x-" and "-pro"/"-premium" const regex = /x-(.*?)(-pro|-premium)?$/; const match = packageName.match(regex); diff --git a/packages/x-license/src/verifyLicense/verifyLicense.test.ts b/packages/x-license/src/verifyLicense/verifyLicense.test.ts index 7f916614ab14..fc95c0976a2a 100644 --- a/packages/x-license/src/verifyLicense/verifyLicense.test.ts +++ b/packages/x-license/src/verifyLicense/verifyLicense.test.ts @@ -53,6 +53,7 @@ describe('License: verifyLicense', () => { scope: 'pro', licensingModel: 'perpetual', orderNumber: 'MUI-123', + planVersion: 'legacy', }); expect( @@ -83,6 +84,7 @@ describe('License: verifyLicense', () => { orderNumber: 'MUI-123', scope: 'pro', licensingModel: 'subscription', + planVersion: 'legacy', }); const licenseKeyPremium = generateLicense({ @@ -90,6 +92,7 @@ describe('License: verifyLicense', () => { orderNumber: 'MUI-123', scope: 'premium', licensingModel: 'subscription', + planVersion: 'legacy', }); it('should log an error when ReleaseInfo is not valid', () => { @@ -147,6 +150,7 @@ describe('License: verifyLicense', () => { orderNumber: 'MUI-123', scope: 'pro', licensingModel: 'subscription', + planVersion: 'legacy', }); expect( @@ -164,6 +168,7 @@ describe('License: verifyLicense', () => { orderNumber: 'MUI-123', scope: 'pro', licensingModel: 'subscription', + planVersion: 'legacy', }); expect( @@ -182,6 +187,7 @@ describe('License: verifyLicense', () => { orderNumber: 'MUI-123', scope: 'pro', licensingModel: 'subscription', + planVersion: 'legacy', }); expect( @@ -199,6 +205,7 @@ describe('License: verifyLicense', () => { orderNumber: 'MUI-123', scope: 'pro', licensingModel: 'perpetual', + planVersion: 'legacy', }); expect( @@ -230,6 +237,7 @@ describe('License: verifyLicense', () => { orderNumber: 'MUI-123', scope: 'pro', licensingModel: 'annual', + planVersion: 'legacy', }); it('should accept licensingModel="annual"', () => { diff --git a/packages/x-license/src/verifyLicense/verifyLicense.ts b/packages/x-license/src/verifyLicense/verifyLicense.ts index 13848f66dbca..262e265e97c4 100644 --- a/packages/x-license/src/verifyLicense/verifyLicense.ts +++ b/packages/x-license/src/verifyLicense/verifyLicense.ts @@ -104,12 +104,12 @@ export function verifyLicense({ releaseInfo, licenseKey, acceptedScopes, - productScope, + productScope = null, }: { releaseInfo: string; licenseKey: string | undefined; acceptedScopes: readonly LicenseScope[]; - productScope: ProductScope | null; + productScope?: ProductScope; }): { status: LicenseStatus; meta?: any } { if (!releaseInfo) { throw new Error('MUI X: The release information is missing. Not able to validate license.'); @@ -176,8 +176,7 @@ export function verifyLicense({ return { status: LICENSE_STATUS.Invalid }; } - // no planVersion or planVersion is 'legacy' - // only available for old licenses ordered between 2024-06-20 and 2024-07-20 + // 'legacy' is only available for licenses ordered between 2024-06-20 and 2024-07-20 if (!license.planVersion || license.planVersion === 'legacy') { // check if the productScope is 'charts' or 'tree-view' if (productScope === 'charts' || productScope === 'tree-view') { From da18b3f856412205553155566b5f8d0f5dbfd973 Mon Sep 17 00:00:00 2001 From: michel Date: Fri, 14 Jun 2024 10:10:35 +0200 Subject: [PATCH 08/24] review remarks --- .../src/tests/license.DataGridPremium.test.tsx | 1 + .../src/generateLicense/generateLicense.test.ts | 8 ++++---- .../useLicenseVerifier.test.tsx | 2 ++ .../src/useLicenseVerifier/useLicenseVerifier.ts | 11 +++-------- packages/x-license/src/utils/licenseScope.ts | 6 +++--- .../src/verifyLicense/verifyLicense.test.ts | 16 ++++++++-------- .../x-license/src/verifyLicense/verifyLicense.ts | 11 ++++++----- test/utils/testLicense.js | 1 + 8 files changed, 28 insertions(+), 28 deletions(-) diff --git a/packages/x-data-grid-premium/src/tests/license.DataGridPremium.test.tsx b/packages/x-data-grid-premium/src/tests/license.DataGridPremium.test.tsx index 62b51732d8c0..1773384ed9a0 100644 --- a/packages/x-data-grid-premium/src/tests/license.DataGridPremium.test.tsx +++ b/packages/x-data-grid-premium/src/tests/license.DataGridPremium.test.tsx @@ -15,6 +15,7 @@ describe(' - License', () => { orderNumber: 'Test', licensingModel: 'subscription', scope: 'pro', + planVersion: 'initial', }), ); expect(() => render()).toErrorDev([ diff --git a/packages/x-license/src/generateLicense/generateLicense.test.ts b/packages/x-license/src/generateLicense/generateLicense.test.ts index f211d30b3562..4b69d6aa471b 100644 --- a/packages/x-license/src/generateLicense/generateLicense.test.ts +++ b/packages/x-license/src/generateLicense/generateLicense.test.ts @@ -9,7 +9,7 @@ describe('License: generateLicense', () => { orderNumber: 'MUI-123', scope: 'pro', licensingModel: 'subscription', - planVersion: 'legacy', + planVersion: 'initial', }), ).to.equal( '076fc290e354b9a04720190023e1f868Tz1NVUktMTIzLEU9MTU5MTcyMzg3OTA2MixTPXBybyxMTT1zdWJzY3JpcHRpb24sUFY9bGVnYWN5LEtWPTI=', @@ -23,7 +23,7 @@ describe('License: generateLicense', () => { orderNumber: 'MUI-123', scope: 'premium', licensingModel: 'subscription', - planVersion: 'legacy', + planVersion: 'initial', }), ).to.equal( 'fafc52d1dbb97e825702a7ccf5986fbbTz1NVUktMTIzLEU9MTU5MTcyMzg3OTA2MixTPXByZW1pdW0sTE09c3Vic2NyaXB0aW9uLFBWPWxlZ2FjeSxLVj0y', @@ -37,7 +37,7 @@ describe('License: generateLicense', () => { orderNumber: 'MUI-123', scope: 'pro', licensingModel: 'subscription', - planVersion: 'legacy', + planVersion: 'initial', }), ).to.equal( '076fc290e354b9a04720190023e1f868Tz1NVUktMTIzLEU9MTU5MTcyMzg3OTA2MixTPXBybyxMTT1zdWJzY3JpcHRpb24sUFY9bGVnYWN5LEtWPTI=', @@ -51,7 +51,7 @@ describe('License: generateLicense', () => { orderNumber: 'MUI-123', scope: 'pro', licensingModel: 'perpetual', - planVersion: 'legacy', + planVersion: 'initial', }), ).to.equal( 'a32c4d6c8cce5d9ca0132527a252e999Tz1NVUktMTIzLEU9MTU5MTcyMzg3OTA2MixTPXBybyxMTT1wZXJwZXR1YWwsUFY9bGVnYWN5LEtWPTI=', diff --git a/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.test.tsx b/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.test.tsx index ece7b7567f65..926e0761ecda 100644 --- a/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.test.tsx +++ b/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.test.tsx @@ -63,6 +63,7 @@ describe('useLicenseVerifier', function test() { licensingModel: 'perpetual', orderNumber: '12345', scope: 'pro', + planVersion: 'initial', }); LicenseInfo.setLicenseKey(''); @@ -88,6 +89,7 @@ describe('useLicenseVerifier', function test() { orderNumber: 'MUI-123', scope: 'pro', licensingModel: 'subscription', + planVersion: 'initial', }); LicenseInfo.setLicenseKey(expiredLicenseKey); diff --git a/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.ts b/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.ts index 210332e12fac..681ac640f5cc 100644 --- a/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.ts +++ b/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.ts @@ -10,12 +10,7 @@ import { showExpiredPackageVersionError, } from '../utils/licenseErrorMessageUtils'; import { LICENSE_STATUS, LicenseStatus } from '../utils/licenseStatus'; -import { - extractAcceptedScopes, - extractProductScope, - LicenseScope, - ProductScope, -} from '../utils/licenseScope'; +import { extractAcceptedScopes, extractProductScope } from '../utils/licenseScope'; import MuiLicenseInfoContext from '../Unstable_LicenseInfoProvider/MuiLicenseInfoContext'; export type MuiCommercialPackageName = @@ -52,8 +47,8 @@ export function useLicenseVerifier( return sharedLicenseStatuses[packageName]!.licenseVerifier; } - const acceptedScopes: readonly LicenseScope[] = extractAcceptedScopes(packageName); - const productScope: ProductScope = extractProductScope(packageName); + const acceptedScopes = extractAcceptedScopes(packageName); + const productScope = extractProductScope(packageName); const plan = packageName.includes('premium') ? 'Premium' : 'Pro'; const licenseStatus = verifyLicense({ diff --git a/packages/x-license/src/utils/licenseScope.ts b/packages/x-license/src/utils/licenseScope.ts index c042eeca1aa9..c86bd0b73779 100644 --- a/packages/x-license/src/utils/licenseScope.ts +++ b/packages/x-license/src/utils/licenseScope.ts @@ -1,9 +1,9 @@ export const LICENSE_SCOPES = ['pro', 'premium'] as const; -export const PRODUCT_SCOPES = ['data-grid', 'date-pickers', 'charts', 'tree-view', null] as const; -export const PLAN_VERSIONS = ['legacy', 'Q3-2024'] as const; +export const PRODUCT_SCOPES = ['data-grid', 'date-pickers', 'charts', 'tree-view'] as const; +export const PLAN_VERSIONS = ['initial', 'Q3-2024'] as const; export type LicenseScope = (typeof LICENSE_SCOPES)[number]; -export type ProductScope = (typeof PRODUCT_SCOPES)[number]; +export type ProductScope = (typeof PRODUCT_SCOPES)[number] | null; export type PlanVersion = (typeof PLAN_VERSIONS)[number]; export const extractProductScope = (packageName: string): ProductScope => { diff --git a/packages/x-license/src/verifyLicense/verifyLicense.test.ts b/packages/x-license/src/verifyLicense/verifyLicense.test.ts index fc95c0976a2a..f4832af2199a 100644 --- a/packages/x-license/src/verifyLicense/verifyLicense.test.ts +++ b/packages/x-license/src/verifyLicense/verifyLicense.test.ts @@ -53,7 +53,7 @@ describe('License: verifyLicense', () => { scope: 'pro', licensingModel: 'perpetual', orderNumber: 'MUI-123', - planVersion: 'legacy', + planVersion: 'initial', }); expect( @@ -84,7 +84,7 @@ describe('License: verifyLicense', () => { orderNumber: 'MUI-123', scope: 'pro', licensingModel: 'subscription', - planVersion: 'legacy', + planVersion: 'initial', }); const licenseKeyPremium = generateLicense({ @@ -92,7 +92,7 @@ describe('License: verifyLicense', () => { orderNumber: 'MUI-123', scope: 'premium', licensingModel: 'subscription', - planVersion: 'legacy', + planVersion: 'initial', }); it('should log an error when ReleaseInfo is not valid', () => { @@ -150,7 +150,7 @@ describe('License: verifyLicense', () => { orderNumber: 'MUI-123', scope: 'pro', licensingModel: 'subscription', - planVersion: 'legacy', + planVersion: 'initial', }); expect( @@ -168,7 +168,7 @@ describe('License: verifyLicense', () => { orderNumber: 'MUI-123', scope: 'pro', licensingModel: 'subscription', - planVersion: 'legacy', + planVersion: 'initial', }); expect( @@ -187,7 +187,7 @@ describe('License: verifyLicense', () => { orderNumber: 'MUI-123', scope: 'pro', licensingModel: 'subscription', - planVersion: 'legacy', + planVersion: 'initial', }); expect( @@ -205,7 +205,7 @@ describe('License: verifyLicense', () => { orderNumber: 'MUI-123', scope: 'pro', licensingModel: 'perpetual', - planVersion: 'legacy', + planVersion: 'initial', }); expect( @@ -237,7 +237,7 @@ describe('License: verifyLicense', () => { orderNumber: 'MUI-123', scope: 'pro', licensingModel: 'annual', - planVersion: 'legacy', + planVersion: 'initial', }); it('should accept licensingModel="annual"', () => { diff --git a/packages/x-license/src/verifyLicense/verifyLicense.ts b/packages/x-license/src/verifyLicense/verifyLicense.ts index 262e265e97c4..11acff4a0427 100644 --- a/packages/x-license/src/verifyLicense/verifyLicense.ts +++ b/packages/x-license/src/verifyLicense/verifyLicense.ts @@ -21,7 +21,7 @@ interface MuiLicense { licensingModel: LicensingModel | null; scope: LicenseScope | null; expiryTimestamp: number | null; - planVersion?: PlanVersion | null; + planVersion: PlanVersion; } /** @@ -42,6 +42,7 @@ const decodeLicenseVersion1 = (license: string): MuiLicense => { scope: 'pro', licensingModel: 'perpetual', expiryTimestamp, + planVersion: 'initial', }; }; @@ -53,6 +54,7 @@ const decodeLicenseVersion2 = (license: string): MuiLicense => { scope: null, licensingModel: null, expiryTimestamp: null, + planVersion: 'initial', }; license @@ -176,11 +178,10 @@ export function verifyLicense({ return { status: LICENSE_STATUS.Invalid }; } - // 'legacy' is only available for licenses ordered between 2024-06-20 and 2024-07-20 - if (!license.planVersion || license.planVersion === 'legacy') { - // check if the productScope is 'charts' or 'tree-view' + if (license.planVersion === 'initial') { + // 'charts-pro' or 'tree-view-pro' can only be used with a newer license if (productScope === 'charts' || productScope === 'tree-view') { - console.error('Error checking license. Plan version not found or invalid!'); + console.error('Error checking license. Plan version invalid!'); return { status: LICENSE_STATUS.OutOfScope }; } } diff --git a/test/utils/testLicense.js b/test/utils/testLicense.js index 8a48c66f8b06..5db1ab450933 100644 --- a/test/utils/testLicense.js +++ b/test/utils/testLicense.js @@ -10,6 +10,7 @@ export function generateTestLicenseKey() { scope: 'premium', orderNumber: 'MUI X tests', expiryDate, + planVersion: 'initial', }); } From ac5e804e11840fa1ab93ee02fcbfd24ca5cb40ee Mon Sep 17 00:00:00 2001 From: michel Date: Fri, 14 Jun 2024 14:49:18 +0200 Subject: [PATCH 09/24] removed productScope optional marking --- packages/x-license/src/utils/licenseScope.ts | 4 ++-- .../src/verifyLicense/verifyLicense.test.ts | 14 ++++++++++++++ .../x-license/src/verifyLicense/verifyLicense.ts | 4 ++-- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/packages/x-license/src/utils/licenseScope.ts b/packages/x-license/src/utils/licenseScope.ts index c86bd0b73779..d75a3b56607a 100644 --- a/packages/x-license/src/utils/licenseScope.ts +++ b/packages/x-license/src/utils/licenseScope.ts @@ -3,14 +3,14 @@ export const PRODUCT_SCOPES = ['data-grid', 'date-pickers', 'charts', 'tree-view export const PLAN_VERSIONS = ['initial', 'Q3-2024'] as const; export type LicenseScope = (typeof LICENSE_SCOPES)[number]; -export type ProductScope = (typeof PRODUCT_SCOPES)[number] | null; +export type ProductScope = (typeof PRODUCT_SCOPES)[number]; export type PlanVersion = (typeof PLAN_VERSIONS)[number]; export const extractProductScope = (packageName: string): ProductScope => { // extract the part between "x-" and "-pro"/"-premium" const regex = /x-(.*?)(-pro|-premium)?$/; const match = packageName.match(regex); - return match ? (match[1] as ProductScope) : null; + return match![1] as ProductScope; }; export const extractAcceptedScopes = (packageName: string): readonly LicenseScope[] => { diff --git a/packages/x-license/src/verifyLicense/verifyLicense.test.ts b/packages/x-license/src/verifyLicense/verifyLicense.test.ts index f4832af2199a..ea4a0f94d513 100644 --- a/packages/x-license/src/verifyLicense/verifyLicense.test.ts +++ b/packages/x-license/src/verifyLicense/verifyLicense.test.ts @@ -31,6 +31,7 @@ describe('License: verifyLicense', () => { releaseInfo: '__RELEASE_INFO__', licenseKey, acceptedScopes: ['pro', 'premium'], + productScope: 'data-grid', }).status, ).to.throw('MUI X: The release information is invalid. Not able to validate license.'); }); @@ -42,6 +43,7 @@ describe('License: verifyLicense', () => { releaseInfo: RELEASE_INFO, licenseKey, acceptedScopes: ['pro', 'premium'], + productScope: 'data-grid', }).status, ).to.equal(LICENSE_STATUS.Valid); }); @@ -61,6 +63,7 @@ describe('License: verifyLicense', () => { releaseInfo: RELEASE_INFO, licenseKey: expiredLicenseKey, acceptedScopes: ['pro', 'premium'], + productScope: 'data-grid', }).status, ).to.equal(LICENSE_STATUS.ExpiredVersion); }); @@ -73,6 +76,7 @@ describe('License: verifyLicense', () => { licenseKey: 'b43ff5f9ac93f021855ff59ff0ba5220TkFNRTpNYC1VSSBTQVMsREVWRUxPUEVSX0NPVU5UPTEwLEVYUElSWT0xNTkxNzIzMDY3MDQyLFZFUlNJT049MS4yLjM', acceptedScopes: ['pro', 'premium'], + productScope: 'data-grid', }).status, ).to.equal(LICENSE_STATUS.Invalid); }); @@ -103,6 +107,7 @@ describe('License: verifyLicense', () => { releaseInfo: '__RELEASE_INFO__', licenseKey: licenseKeyPro, acceptedScopes: ['pro', 'premium'], + productScope: 'data-grid', }).status, ).to.throw('MUI X: The release information is invalid. Not able to validate license.'); }); @@ -115,6 +120,7 @@ describe('License: verifyLicense', () => { releaseInfo: RELEASE_INFO, licenseKey: licenseKeyPro, acceptedScopes: ['pro', 'premium'], + productScope: 'data-grid', }).status, ).to.equal(LICENSE_STATUS.Valid); }); @@ -126,6 +132,7 @@ describe('License: verifyLicense', () => { releaseInfo: RELEASE_INFO, licenseKey: licenseKeyPremium, acceptedScopes: ['premium'], + productScope: 'data-grid', }).status, ).to.equal(LICENSE_STATUS.Valid); }); @@ -137,6 +144,7 @@ describe('License: verifyLicense', () => { releaseInfo: RELEASE_INFO, licenseKey: licenseKeyPro, acceptedScopes: ['premium'], + productScope: 'data-grid', }).status, ).to.equal(LICENSE_STATUS.OutOfScope); }); @@ -158,6 +166,7 @@ describe('License: verifyLicense', () => { releaseInfo: RELEASE_INFO, licenseKey: expiredLicenseKey, acceptedScopes: ['pro', 'premium'], + productScope: 'data-grid', }).status, ).to.equal(LICENSE_STATUS.Valid); }); @@ -176,6 +185,7 @@ describe('License: verifyLicense', () => { releaseInfo: RELEASE_INFO, licenseKey: expiredLicenseKey, acceptedScopes: ['pro', 'premium'], + productScope: 'data-grid', }).status, ).to.equal(LICENSE_STATUS.ExpiredAnnualGrace); }); @@ -195,6 +205,7 @@ describe('License: verifyLicense', () => { releaseInfo: RELEASE_INFO, licenseKey: expiredLicenseKey, acceptedScopes: ['pro', 'premium'], + productScope: 'data-grid', }).status, ).to.equal(LICENSE_STATUS.ExpiredAnnual); }); @@ -213,6 +224,7 @@ describe('License: verifyLicense', () => { releaseInfo: RELEASE_INFO, licenseKey: expiredLicenseKey, acceptedScopes: ['pro', 'premium'], + productScope: 'data-grid', }).status, ).to.equal(LICENSE_STATUS.Valid); }); @@ -226,6 +238,7 @@ describe('License: verifyLicense', () => { licenseKey: 'b43ff5f9ac93f021855ff59ff0ba5220TkFNRTpNYC1VSSBTQVMsREVWRUxPUEVSX0NPVU5UPTEwLEVYUElSWT0xNTkxNzIzMDY3MDQyLFZFUlNJT049MS4yLjM', acceptedScopes: ['pro', 'premium'], + productScope: 'data-grid', }).status, ).to.equal(LICENSE_STATUS.Invalid); }); @@ -247,6 +260,7 @@ describe('License: verifyLicense', () => { releaseInfo: RELEASE_INFO, licenseKey: licenseKeyPro, acceptedScopes: ['pro', 'premium'], + productScope: 'data-grid', }).status, ).to.equal(LICENSE_STATUS.Valid); }); diff --git a/packages/x-license/src/verifyLicense/verifyLicense.ts b/packages/x-license/src/verifyLicense/verifyLicense.ts index 11acff4a0427..cf1253e6887d 100644 --- a/packages/x-license/src/verifyLicense/verifyLicense.ts +++ b/packages/x-license/src/verifyLicense/verifyLicense.ts @@ -106,12 +106,12 @@ export function verifyLicense({ releaseInfo, licenseKey, acceptedScopes, - productScope = null, + productScope, }: { releaseInfo: string; licenseKey: string | undefined; acceptedScopes: readonly LicenseScope[]; - productScope?: ProductScope; + productScope: ProductScope; }): { status: LicenseStatus; meta?: any } { if (!releaseInfo) { throw new Error('MUI X: The release information is missing. Not able to validate license.'); From 300e0b54b4d1880115253a93fb51d4d426128228 Mon Sep 17 00:00:00 2001 From: Michel Engelen <32863416+michelengelen@users.noreply.github.com> Date: Fri, 14 Jun 2024 17:03:13 +0200 Subject: [PATCH 10/24] Update verifyLicense.ts Co-authored-by: Andrew Cherniavskii Signed-off-by: Michel Engelen <32863416+michelengelen@users.noreply.github.com> --- packages/x-license/src/verifyLicense/verifyLicense.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/x-license/src/verifyLicense/verifyLicense.ts b/packages/x-license/src/verifyLicense/verifyLicense.ts index cf1253e6887d..d6dd301567f3 100644 --- a/packages/x-license/src/verifyLicense/verifyLicense.ts +++ b/packages/x-license/src/verifyLicense/verifyLicense.ts @@ -181,7 +181,7 @@ export function verifyLicense({ if (license.planVersion === 'initial') { // 'charts-pro' or 'tree-view-pro' can only be used with a newer license if (productScope === 'charts' || productScope === 'tree-view') { - console.error('Error checking license. Plan version invalid!'); + console.error('MUI X: Error checking license. Plan version invalid!'); return { status: LICENSE_STATUS.OutOfScope }; } } From e62b9852d620683bca09e892b603d83713583075 Mon Sep 17 00:00:00 2001 From: michel Date: Mon, 17 Jun 2024 15:53:44 +0200 Subject: [PATCH 11/24] added license generation tests --- .../generateLicense/generateLicense.test.ts | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/packages/x-license/src/generateLicense/generateLicense.test.ts b/packages/x-license/src/generateLicense/generateLicense.test.ts index 4b69d6aa471b..c24f4fdb7305 100644 --- a/packages/x-license/src/generateLicense/generateLicense.test.ts +++ b/packages/x-license/src/generateLicense/generateLicense.test.ts @@ -12,7 +12,7 @@ describe('License: generateLicense', () => { planVersion: 'initial', }), ).to.equal( - '076fc290e354b9a04720190023e1f868Tz1NVUktMTIzLEU9MTU5MTcyMzg3OTA2MixTPXBybyxMTT1zdWJzY3JpcHRpb24sUFY9bGVnYWN5LEtWPTI=', + 'e8fad422a82720084ec67dd693f08056Tz1NVUktMTIzLEU9MTU5MTcyMzg3OTA2MixTPXBybyxMTT1zdWJzY3JpcHRpb24sUFY9aW5pdGlhbCxLVj0y', ); }); @@ -26,7 +26,7 @@ describe('License: generateLicense', () => { planVersion: 'initial', }), ).to.equal( - 'fafc52d1dbb97e825702a7ccf5986fbbTz1NVUktMTIzLEU9MTU5MTcyMzg3OTA2MixTPXByZW1pdW0sTE09c3Vic2NyaXB0aW9uLFBWPWxlZ2FjeSxLVj0y', + '8ca0384bfb92ec214d4cd72483f5110bTz1NVUktMTIzLEU9MTU5MTcyMzg3OTA2MixTPXByZW1pdW0sTE09c3Vic2NyaXB0aW9uLFBWPWluaXRpYWwsS1Y9Mg==', ); }); @@ -40,7 +40,7 @@ describe('License: generateLicense', () => { planVersion: 'initial', }), ).to.equal( - '076fc290e354b9a04720190023e1f868Tz1NVUktMTIzLEU9MTU5MTcyMzg3OTA2MixTPXBybyxMTT1zdWJzY3JpcHRpb24sUFY9bGVnYWN5LEtWPTI=', + 'e8fad422a82720084ec67dd693f08056Tz1NVUktMTIzLEU9MTU5MTcyMzg3OTA2MixTPXBybyxMTT1zdWJzY3JpcHRpb24sUFY9aW5pdGlhbCxLVj0y', ); }); @@ -54,7 +54,35 @@ describe('License: generateLicense', () => { planVersion: 'initial', }), ).to.equal( - 'a32c4d6c8cce5d9ca0132527a252e999Tz1NVUktMTIzLEU9MTU5MTcyMzg3OTA2MixTPXBybyxMTT1wZXJwZXR1YWwsUFY9bGVnYWN5LEtWPTI=', + 'aaf2e3c60b06199962fbbab985843d97Tz1NVUktMTIzLEU9MTU5MTcyMzg3OTA2MixTPXBybyxMTT1wZXJwZXR1YWwsUFY9aW5pdGlhbCxLVj0y', + ); + }); + + it('should generate perpetual pro-license when `planVersion: "Q3-2024"`', () => { + expect( + generateLicense({ + expiryDate: new Date(1591723879062), + orderNumber: 'MUI-123', + scope: 'pro', + licensingModel: 'subscription', + planVersion: 'Q3-2024', + }), + ).to.equal( + '4adf08e54d606215809064d1d31b6b39Tz1NVUktMTIzLEU9MTU5MTcyMzg3OTA2MixTPXBybyxMTT1zdWJzY3JpcHRpb24sUFY9UTMtMjAyNCxLVj0y', + ); + }); + + it('should generate perpetual premium-license when `planVersion: "Q3-2024"`', () => { + expect( + generateLicense({ + expiryDate: new Date(1591723879062), + orderNumber: 'MUI-123', + scope: 'pro', + licensingModel: 'subscription', + planVersion: 'Q3-2024', + }), + ).to.equal( + '4adf08e54d606215809064d1d31b6b39Tz1NVUktMTIzLEU9MTU5MTcyMzg3OTA2MixTPXBybyxMTT1zdWJzY3JpcHRpb24sUFY9UTMtMjAyNCxLVj0y', ); }); }); From f4b0ed5430bbc5e7ffba4a24c5fa0ae3ecf459d5 Mon Sep 17 00:00:00 2001 From: michel Date: Mon, 17 Jun 2024 15:54:49 +0200 Subject: [PATCH 12/24] fixed typo --- .../src/useLicenseVerifier/useLicenseVerifier.test.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.test.tsx b/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.test.tsx index 926e0761ecda..b2f81051631b 100644 --- a/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.test.tsx +++ b/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.test.tsx @@ -15,8 +15,8 @@ const releaseDate = new Date(3000, 0, 0, 0, 0, 0, 0); const RELEASE_INFO = generateReleaseInfo(releaseDate); function TestComponent() { - const licesenStatus = useLicenseVerifier('x-date-pickers-pro', RELEASE_INFO); - return
Status: {licesenStatus.status}
; + const licenseStatus = useLicenseVerifier('x-date-pickers-pro', RELEASE_INFO); + return
Status: {licenseStatus.status}
; } describe('useLicenseVerifier', function test() { From b5cec8b69b97b37eb2670b57fff6518153e65634 Mon Sep 17 00:00:00 2001 From: michel Date: Mon, 17 Jun 2024 16:03:18 +0200 Subject: [PATCH 13/24] Added 'Mui X:' prefix on error --- packages/x-license/src/verifyLicense/verifyLicense.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/x-license/src/verifyLicense/verifyLicense.ts b/packages/x-license/src/verifyLicense/verifyLicense.ts index d6dd301567f3..09eb48c2fee4 100644 --- a/packages/x-license/src/verifyLicense/verifyLicense.ts +++ b/packages/x-license/src/verifyLicense/verifyLicense.ts @@ -174,7 +174,7 @@ export function verifyLicense({ } if (license.scope == null || !LICENSE_SCOPES.includes(license.scope)) { - console.error('Error checking license. scope not found or invalid!'); + console.error('MUI X: Error checking license. scope not found or invalid!'); return { status: LICENSE_STATUS.Invalid }; } From 88056dd032cb47d9cebfb23e2c5e07138c732ab6 Mon Sep 17 00:00:00 2001 From: michel Date: Tue, 18 Jun 2024 14:14:51 +0200 Subject: [PATCH 14/24] Added Key Version 2.2 tests --- .../src/verifyLicense/verifyLicense.test.ts | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/packages/x-license/src/verifyLicense/verifyLicense.test.ts b/packages/x-license/src/verifyLicense/verifyLicense.test.ts index ea4a0f94d513..d9d154d3ba2e 100644 --- a/packages/x-license/src/verifyLicense/verifyLicense.test.ts +++ b/packages/x-license/src/verifyLicense/verifyLicense.test.ts @@ -265,4 +265,70 @@ describe('License: verifyLicense', () => { ).to.equal(LICENSE_STATUS.Valid); }); }); + + describe('key version: 2.2', () => { + const licenseKeyInitial = generateLicense({ + expiryDate: new Date(releaseDate.getTime() + oneDayInMS), + orderNumber: 'MUI-123', + scope: 'pro', + licensingModel: 'annual', + planVersion: 'initial', + }); + + const licenseKey2 = generateLicense({ + expiryDate: new Date(releaseDate.getTime() + oneDayInMS), + orderNumber: 'MUI-123', + scope: 'pro', + licensingModel: 'annual', + planVersion: 'Q3-2024', + }); + + it('PlanVersion "initial" should not accept charts', () => { + process.env.NODE_ENV = 'production'; + expect( + verifyLicense({ + releaseInfo: RELEASE_INFO, + licenseKey: licenseKeyInitial, + acceptedScopes: ['pro', 'premium'], + productScope: 'charts', + }).status, + ).to.equal(LICENSE_STATUS.OutOfScope); + }); + + it('PlanVersion "initial" should not accept tree-view', () => { + process.env.NODE_ENV = 'production'; + expect( + verifyLicense({ + releaseInfo: RELEASE_INFO, + licenseKey: licenseKeyInitial, + acceptedScopes: ['pro', 'premium'], + productScope: 'tree-view', + }).status, + ).to.equal(LICENSE_STATUS.OutOfScope); + }); + + it('PlanVersion "Q3-2024" should accept charts', () => { + process.env.NODE_ENV = 'production'; + expect( + verifyLicense({ + releaseInfo: RELEASE_INFO, + licenseKey: licenseKey2, + acceptedScopes: ['pro', 'premium'], + productScope: 'charts', + }).status, + ).to.equal(LICENSE_STATUS.Valid); + }); + + it('PlanVersion "Q3-2024" should accept tree-view', () => { + process.env.NODE_ENV = 'production'; + expect( + verifyLicense({ + releaseInfo: RELEASE_INFO, + licenseKey: licenseKey2, + acceptedScopes: ['pro', 'premium'], + productScope: 'tree-view', + }).status, + ).to.equal(LICENSE_STATUS.Valid); + }); + }); }); From 508c7854ee531995a8c3b7f32f737f4e54ffa7bf Mon Sep 17 00:00:00 2001 From: michel Date: Tue, 18 Jun 2024 15:59:16 +0200 Subject: [PATCH 15/24] added product scope tests --- packages/x-license/src/verifyLicense/verifyLicense.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/x-license/src/verifyLicense/verifyLicense.ts b/packages/x-license/src/verifyLicense/verifyLicense.ts index 09eb48c2fee4..633a58d0b20e 100644 --- a/packages/x-license/src/verifyLicense/verifyLicense.ts +++ b/packages/x-license/src/verifyLicense/verifyLicense.ts @@ -109,7 +109,7 @@ export function verifyLicense({ productScope, }: { releaseInfo: string; - licenseKey: string | undefined; + licenseKey?: string; acceptedScopes: readonly LicenseScope[]; productScope: ProductScope; }): { status: LicenseStatus; meta?: any } { @@ -181,7 +181,6 @@ export function verifyLicense({ if (license.planVersion === 'initial') { // 'charts-pro' or 'tree-view-pro' can only be used with a newer license if (productScope === 'charts' || productScope === 'tree-view') { - console.error('MUI X: Error checking license. Plan version invalid!'); return { status: LICENSE_STATUS.OutOfScope }; } } From c6e7a178ba334aacd5c4b2270410f7a35aefb2c0 Mon Sep 17 00:00:00 2001 From: michel Date: Tue, 18 Jun 2024 16:42:10 +0200 Subject: [PATCH 16/24] added useLicenseVerifier test and changed the licenseError for the product scope --- .../useLicenseVerifier.test.tsx | 29 ++++++++++++++++--- .../useLicenseVerifier/useLicenseVerifier.ts | 3 ++ .../src/utils/licenseErrorMessageUtils.ts | 10 +++++++ packages/x-license/src/utils/licenseStatus.ts | 1 + .../src/verifyLicense/verifyLicense.test.ts | 4 +-- .../src/verifyLicense/verifyLicense.ts | 2 +- 6 files changed, 42 insertions(+), 7 deletions(-) diff --git a/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.test.tsx b/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.test.tsx index b2f81051631b..5c92413fff73 100644 --- a/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.test.tsx +++ b/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.test.tsx @@ -6,6 +6,7 @@ import { LicenseInfo, generateLicense, Unstable_LicenseInfoProvider as LicenseInfoProvider, + MuiCommercialPackageName, } from '@mui/x-license'; import { sharedLicenseStatuses } from './useLicenseVerifier'; import { generateReleaseInfo } from '../verifyLicense'; @@ -14,8 +15,8 @@ const oneDayInMS = 1000 * 60 * 60 * 24; const releaseDate = new Date(3000, 0, 0, 0, 0, 0, 0); const RELEASE_INFO = generateReleaseInfo(releaseDate); -function TestComponent() { - const licenseStatus = useLicenseVerifier('x-date-pickers-pro', RELEASE_INFO); +function TestComponent(props: { packageName?: MuiCommercialPackageName }) { + const licenseStatus = useLicenseVerifier(props.packageName || 'x-date-pickers-pro', RELEASE_INFO); return
Status: {licenseStatus.status}
; } @@ -63,7 +64,7 @@ describe('useLicenseVerifier', function test() { licensingModel: 'perpetual', orderNumber: '12345', scope: 'pro', - planVersion: 'initial', + planVersion: 'Q3-2024', }); LicenseInfo.setLicenseKey(''); @@ -89,7 +90,7 @@ describe('useLicenseVerifier', function test() { orderNumber: 'MUI-123', scope: 'pro', licensingModel: 'subscription', - planVersion: 'initial', + planVersion: 'Q3-2024', }); LicenseInfo.setLicenseKey(expiredLicenseKey); @@ -107,5 +108,25 @@ describe('useLicenseVerifier', function test() { ]); expect(actualErrorMsg).to.match(/MUI X: Expired license key/); }); + + it('should throw if the license is not covering charts and tree-view', () => { + // Avoid Karma "Invalid left-hand side in assignment" SyntaxError + // eslint-disable-next-line no-useless-concat + process.env['NODE_' + 'ENV'] = 'development'; + + const initialLicenseKey = generateLicense({ + expiryDate: new Date(3001, 0, 0, 0, 0, 0, 0), + orderNumber: 'MUI-123', + scope: 'pro', + licensingModel: 'subscription', + planVersion: 'initial', + }); + + LicenseInfo.setLicenseKey(initialLicenseKey); + + expect(() => { + render(); + }).to.toErrorDev(['MUI X: Invalid Product coverage']); + }); }); }); diff --git a/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.ts b/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.ts index 681ac640f5cc..4be4d525d916 100644 --- a/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.ts +++ b/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.ts @@ -8,6 +8,7 @@ import { showMissingLicenseKeyError, showLicenseKeyPlanMismatchError, showExpiredPackageVersionError, + showProductScopeMismatchError, } from '../utils/licenseErrorMessageUtils'; import { LICENSE_STATUS, LicenseStatus } from '../utils/licenseStatus'; import { extractAcceptedScopes, extractProductScope } from '../utils/licenseScope'; @@ -64,6 +65,8 @@ export function useLicenseVerifier( // Skip } else if (licenseStatus.status === LICENSE_STATUS.Invalid) { showInvalidLicenseKeyError(); + } else if (licenseStatus.status === LICENSE_STATUS.OutOfProductScope) { + showProductScopeMismatchError(); } else if (licenseStatus.status === LICENSE_STATUS.OutOfScope) { showLicenseKeyPlanMismatchError(); } else if (licenseStatus.status === LICENSE_STATUS.NotFound) { diff --git a/packages/x-license/src/utils/licenseErrorMessageUtils.ts b/packages/x-license/src/utils/licenseErrorMessageUtils.ts index 9924da8404b5..63d6fecb70bd 100644 --- a/packages/x-license/src/utils/licenseErrorMessageUtils.ts +++ b/packages/x-license/src/utils/licenseErrorMessageUtils.ts @@ -32,6 +32,16 @@ export function showLicenseKeyPlanMismatchError() { ]); } +export function showProductScopeMismatchError() { + showError([ + 'MUI X: Invalid Product coverage.', + '', + 'Your use of MUI X is not compatible with the plan of your license key. The product you are trying to use is not included in the version of your license plan. This happens if you try to use `ChartsPro` or TreeViewPro` with a license plan where these products are not included.', + '', + 'To solve the issue, you can upgrade your plan version at https://mui.com/r/x-get-license.', + ]); +} + export function showMissingLicenseKeyError({ plan, packageName, diff --git a/packages/x-license/src/utils/licenseStatus.ts b/packages/x-license/src/utils/licenseStatus.ts index 77a27e1d7357..4a377a4e54f8 100644 --- a/packages/x-license/src/utils/licenseStatus.ts +++ b/packages/x-license/src/utils/licenseStatus.ts @@ -7,6 +7,7 @@ export enum LICENSE_STATUS { ExpiredVersion = 'ExpiredVersion', Valid = 'Valid', OutOfScope = 'OutOfScope', + OutOfProductScope = 'OutOfProductScope', } export type LicenseStatus = keyof typeof LICENSE_STATUS; diff --git a/packages/x-license/src/verifyLicense/verifyLicense.test.ts b/packages/x-license/src/verifyLicense/verifyLicense.test.ts index d9d154d3ba2e..6e22e2277ad6 100644 --- a/packages/x-license/src/verifyLicense/verifyLicense.test.ts +++ b/packages/x-license/src/verifyLicense/verifyLicense.test.ts @@ -292,7 +292,7 @@ describe('License: verifyLicense', () => { acceptedScopes: ['pro', 'premium'], productScope: 'charts', }).status, - ).to.equal(LICENSE_STATUS.OutOfScope); + ).to.equal(LICENSE_STATUS.OutOfProductScope); }); it('PlanVersion "initial" should not accept tree-view', () => { @@ -304,7 +304,7 @@ describe('License: verifyLicense', () => { acceptedScopes: ['pro', 'premium'], productScope: 'tree-view', }).status, - ).to.equal(LICENSE_STATUS.OutOfScope); + ).to.equal(LICENSE_STATUS.OutOfProductScope); }); it('PlanVersion "Q3-2024" should accept charts', () => { diff --git a/packages/x-license/src/verifyLicense/verifyLicense.ts b/packages/x-license/src/verifyLicense/verifyLicense.ts index 633a58d0b20e..3bfcfaa11bfa 100644 --- a/packages/x-license/src/verifyLicense/verifyLicense.ts +++ b/packages/x-license/src/verifyLicense/verifyLicense.ts @@ -181,7 +181,7 @@ export function verifyLicense({ if (license.planVersion === 'initial') { // 'charts-pro' or 'tree-view-pro' can only be used with a newer license if (productScope === 'charts' || productScope === 'tree-view') { - return { status: LICENSE_STATUS.OutOfScope }; + return { status: LICENSE_STATUS.OutOfProductScope }; } } From 1a26ce8b6b83dab504119351bacd549025c47872 Mon Sep 17 00:00:00 2001 From: Michel Engelen <32863416+michelengelen@users.noreply.github.com> Date: Wed, 19 Jun 2024 13:43:51 +0200 Subject: [PATCH 17/24] Apply suggestions from code review Co-authored-by: Andrew Cherniavskii Signed-off-by: Michel Engelen <32863416+michelengelen@users.noreply.github.com> --- .../x-license/src/generateLicense/generateLicense.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/x-license/src/generateLicense/generateLicense.test.ts b/packages/x-license/src/generateLicense/generateLicense.test.ts index c24f4fdb7305..a764e469557f 100644 --- a/packages/x-license/src/generateLicense/generateLicense.test.ts +++ b/packages/x-license/src/generateLicense/generateLicense.test.ts @@ -58,7 +58,7 @@ describe('License: generateLicense', () => { ); }); - it('should generate perpetual pro-license when `planVersion: "Q3-2024"`', () => { + it('should generate subscription Pro license when `planVersion: "Q3-2024"`', () => { expect( generateLicense({ expiryDate: new Date(1591723879062), @@ -72,12 +72,12 @@ describe('License: generateLicense', () => { ); }); - it('should generate perpetual premium-license when `planVersion: "Q3-2024"`', () => { + it('should generate subscription Premium license when `planVersion: "Q3-2024"`', () => { expect( generateLicense({ expiryDate: new Date(1591723879062), orderNumber: 'MUI-123', - scope: 'pro', + scope: 'premium', licensingModel: 'subscription', planVersion: 'Q3-2024', }), From e572ab42a118063d686a93ba240dd3248c24be1c Mon Sep 17 00:00:00 2001 From: michel Date: Wed, 19 Jun 2024 13:57:14 +0200 Subject: [PATCH 18/24] added some tests requested from @cherniavskii --- .../useLicenseVerifier.test.tsx | 64 +++++++++++++++++-- 1 file changed, 60 insertions(+), 4 deletions(-) diff --git a/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.test.tsx b/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.test.tsx index 5c92413fff73..a99d01ff638b 100644 --- a/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.test.tsx +++ b/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.test.tsx @@ -64,7 +64,7 @@ describe('useLicenseVerifier', function test() { licensingModel: 'perpetual', orderNumber: '12345', scope: 'pro', - planVersion: 'Q3-2024', + planVersion: 'initial', }); LicenseInfo.setLicenseKey(''); @@ -90,7 +90,7 @@ describe('useLicenseVerifier', function test() { orderNumber: 'MUI-123', scope: 'pro', licensingModel: 'subscription', - planVersion: 'Q3-2024', + planVersion: 'initial', }); LicenseInfo.setLicenseKey(expiredLicenseKey); @@ -114,7 +114,7 @@ describe('useLicenseVerifier', function test() { // eslint-disable-next-line no-useless-concat process.env['NODE_' + 'ENV'] = 'development'; - const initialLicenseKey = generateLicense({ + const licenseKey = generateLicense({ expiryDate: new Date(3001, 0, 0, 0, 0, 0, 0), orderNumber: 'MUI-123', scope: 'pro', @@ -122,11 +122,67 @@ describe('useLicenseVerifier', function test() { planVersion: 'initial', }); - LicenseInfo.setLicenseKey(initialLicenseKey); + LicenseInfo.setLicenseKey(licenseKey); expect(() => { render(); }).to.toErrorDev(['MUI X: Invalid Product coverage']); + + expect(() => { + render(); + }).to.toErrorDev(['MUI X: Invalid Product coverage']); + }); + + it('should not throw if the license is covering charts and tree-view', () => { + // Avoid Karma "Invalid left-hand side in assignment" SyntaxError + // eslint-disable-next-line no-useless-concat + process.env['NODE_' + 'ENV'] = 'development'; + + const licenseKey = generateLicense({ + expiryDate: new Date(3001, 0, 0, 0, 0, 0, 0), + orderNumber: 'MUI-123', + scope: 'pro', + licensingModel: 'subscription', + planVersion: 'Q3-2024', + }); + + LicenseInfo.setLicenseKey(licenseKey); + + expect(() => { + render(); + }).not.toErrorDev(); + + expect(() => { + render(); + }).not.toErrorDev(); + }); + + it('should not throw for existing pro and premium packages', () => { + // Avoid Karma "Invalid left-hand side in assignment" SyntaxError + // eslint-disable-next-line no-useless-concat + process.env['NODE_' + 'ENV'] = 'development'; + + const licenseKey = generateLicense({ + expiryDate: new Date(3001, 0, 0, 0, 0, 0, 0), + orderNumber: 'MUI-123', + scope: 'premium', + licensingModel: 'subscription', + planVersion: 'Q3-2024', + }); + + LicenseInfo.setLicenseKey(licenseKey); + + expect(() => { + render(); + }).not.toErrorDev(); + + expect(() => { + render(); + }).not.toErrorDev(); + + expect(() => { + render(); + }).not.toErrorDev(); }); }); }); From c67d0f9e5504426196db52a0dce4c228b69c25a4 Mon Sep 17 00:00:00 2001 From: Michel Engelen <32863416+michelengelen@users.noreply.github.com> Date: Wed, 19 Jun 2024 14:15:45 +0200 Subject: [PATCH 19/24] Update packages/x-license/src/utils/licenseErrorMessageUtils.ts Co-authored-by: Flavien DELANGLE Signed-off-by: Michel Engelen <32863416+michelengelen@users.noreply.github.com> --- packages/x-license/src/utils/licenseErrorMessageUtils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/x-license/src/utils/licenseErrorMessageUtils.ts b/packages/x-license/src/utils/licenseErrorMessageUtils.ts index 63d6fecb70bd..c2f1ec951f1d 100644 --- a/packages/x-license/src/utils/licenseErrorMessageUtils.ts +++ b/packages/x-license/src/utils/licenseErrorMessageUtils.ts @@ -36,7 +36,8 @@ export function showProductScopeMismatchError() { showError([ 'MUI X: Invalid Product coverage.', '', - 'Your use of MUI X is not compatible with the plan of your license key. The product you are trying to use is not included in the version of your license plan. This happens if you try to use `ChartsPro` or TreeViewPro` with a license plan where these products are not included.', + 'The component you are trying to use is not included in the Pro Plan your purchased.' + 'You are using a license that is only compatible with the `@mui/x-data-grid-pro` and `@mui/x-date-pickers-pro` commercial packages. To start using another Pro package, please consider reaching to our sales team to upgrade your license.' '', 'To solve the issue, you can upgrade your plan version at https://mui.com/r/x-get-license.', ]); From 001a41123ebef9593b66c79398e29d8428f51182 Mon Sep 17 00:00:00 2001 From: michel Date: Wed, 19 Jun 2024 14:24:31 +0200 Subject: [PATCH 20/24] changed error wording --- .../src/useLicenseVerifier/useLicenseVerifier.test.tsx | 4 ++-- .../src/useLicenseVerifier/useLicenseVerifier.ts | 6 +++--- packages/x-license/src/utils/licenseErrorMessageUtils.ts | 9 ++++----- packages/x-license/src/utils/licenseStatus.ts | 2 +- .../x-license/src/verifyLicense/verifyLicense.test.ts | 4 ++-- packages/x-license/src/verifyLicense/verifyLicense.ts | 2 +- 6 files changed, 13 insertions(+), 14 deletions(-) diff --git a/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.test.tsx b/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.test.tsx index a99d01ff638b..a9c208adb2dd 100644 --- a/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.test.tsx +++ b/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.test.tsx @@ -126,11 +126,11 @@ describe('useLicenseVerifier', function test() { expect(() => { render(); - }).to.toErrorDev(['MUI X: Invalid Product coverage']); + }).to.toErrorDev(['MUI X: Product not not covered by plan.']); expect(() => { render(); - }).to.toErrorDev(['MUI X: Invalid Product coverage']); + }).to.toErrorDev(['MUI X: Product not not covered by plan.']); }); it('should not throw if the license is covering charts and tree-view', () => { diff --git a/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.ts b/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.ts index 4be4d525d916..a79cc012b389 100644 --- a/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.ts +++ b/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.ts @@ -8,7 +8,7 @@ import { showMissingLicenseKeyError, showLicenseKeyPlanMismatchError, showExpiredPackageVersionError, - showProductScopeMismatchError, + showProductNotCoveredError, } from '../utils/licenseErrorMessageUtils'; import { LICENSE_STATUS, LicenseStatus } from '../utils/licenseStatus'; import { extractAcceptedScopes, extractProductScope } from '../utils/licenseScope'; @@ -65,8 +65,8 @@ export function useLicenseVerifier( // Skip } else if (licenseStatus.status === LICENSE_STATUS.Invalid) { showInvalidLicenseKeyError(); - } else if (licenseStatus.status === LICENSE_STATUS.OutOfProductScope) { - showProductScopeMismatchError(); + } else if (licenseStatus.status === LICENSE_STATUS.ProductNotCovered) { + showProductNotCoveredError(); } else if (licenseStatus.status === LICENSE_STATUS.OutOfScope) { showLicenseKeyPlanMismatchError(); } else if (licenseStatus.status === LICENSE_STATUS.NotFound) { diff --git a/packages/x-license/src/utils/licenseErrorMessageUtils.ts b/packages/x-license/src/utils/licenseErrorMessageUtils.ts index c2f1ec951f1d..71245767cc12 100644 --- a/packages/x-license/src/utils/licenseErrorMessageUtils.ts +++ b/packages/x-license/src/utils/licenseErrorMessageUtils.ts @@ -32,14 +32,13 @@ export function showLicenseKeyPlanMismatchError() { ]); } -export function showProductScopeMismatchError() { +export function showProductNotCoveredError() { showError([ - 'MUI X: Invalid Product coverage.', + 'MUI X: Product not not covered by plan.', '', - 'The component you are trying to use is not included in the Pro Plan your purchased.' - 'You are using a license that is only compatible with the `@mui/x-data-grid-pro` and `@mui/x-date-pickers-pro` commercial packages. To start using another Pro package, please consider reaching to our sales team to upgrade your license.' + 'The component you are trying to use is not included in the Pro Plan your purchased. You are using a license that is only compatible with the `@mui/x-data-grid-pro` and `@mui/x-date-pickers-pro` commercial packages.', '', - 'To solve the issue, you can upgrade your plan version at https://mui.com/r/x-get-license.', + 'To start using another Pro package, please consider reaching to our sales team to upgrade your license.', ]); } diff --git a/packages/x-license/src/utils/licenseStatus.ts b/packages/x-license/src/utils/licenseStatus.ts index 4a377a4e54f8..ac6828c109e2 100644 --- a/packages/x-license/src/utils/licenseStatus.ts +++ b/packages/x-license/src/utils/licenseStatus.ts @@ -7,7 +7,7 @@ export enum LICENSE_STATUS { ExpiredVersion = 'ExpiredVersion', Valid = 'Valid', OutOfScope = 'OutOfScope', - OutOfProductScope = 'OutOfProductScope', + ProductNotCovered = 'ProductNotCovered', } export type LicenseStatus = keyof typeof LICENSE_STATUS; diff --git a/packages/x-license/src/verifyLicense/verifyLicense.test.ts b/packages/x-license/src/verifyLicense/verifyLicense.test.ts index 6e22e2277ad6..1b2c2fe24eb3 100644 --- a/packages/x-license/src/verifyLicense/verifyLicense.test.ts +++ b/packages/x-license/src/verifyLicense/verifyLicense.test.ts @@ -292,7 +292,7 @@ describe('License: verifyLicense', () => { acceptedScopes: ['pro', 'premium'], productScope: 'charts', }).status, - ).to.equal(LICENSE_STATUS.OutOfProductScope); + ).to.equal(LICENSE_STATUS.ProductNotCovered); }); it('PlanVersion "initial" should not accept tree-view', () => { @@ -304,7 +304,7 @@ describe('License: verifyLicense', () => { acceptedScopes: ['pro', 'premium'], productScope: 'tree-view', }).status, - ).to.equal(LICENSE_STATUS.OutOfProductScope); + ).to.equal(LICENSE_STATUS.ProductNotCovered); }); it('PlanVersion "Q3-2024" should accept charts', () => { diff --git a/packages/x-license/src/verifyLicense/verifyLicense.ts b/packages/x-license/src/verifyLicense/verifyLicense.ts index 3bfcfaa11bfa..7d696490599d 100644 --- a/packages/x-license/src/verifyLicense/verifyLicense.ts +++ b/packages/x-license/src/verifyLicense/verifyLicense.ts @@ -181,7 +181,7 @@ export function verifyLicense({ if (license.planVersion === 'initial') { // 'charts-pro' or 'tree-view-pro' can only be used with a newer license if (productScope === 'charts' || productScope === 'tree-view') { - return { status: LICENSE_STATUS.OutOfProductScope }; + return { status: LICENSE_STATUS.ProductNotCovered }; } } From 4fffe6a6dc1de193ee0adc5a449edbf2c420cf8b Mon Sep 17 00:00:00 2001 From: michel Date: Thu, 20 Jun 2024 10:19:32 +0200 Subject: [PATCH 21/24] added link to license page back in --- packages/x-license/src/utils/licenseErrorMessageUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/x-license/src/utils/licenseErrorMessageUtils.ts b/packages/x-license/src/utils/licenseErrorMessageUtils.ts index 71245767cc12..5844163f9ec7 100644 --- a/packages/x-license/src/utils/licenseErrorMessageUtils.ts +++ b/packages/x-license/src/utils/licenseErrorMessageUtils.ts @@ -38,7 +38,7 @@ export function showProductNotCoveredError() { '', 'The component you are trying to use is not included in the Pro Plan your purchased. You are using a license that is only compatible with the `@mui/x-data-grid-pro` and `@mui/x-date-pickers-pro` commercial packages.', '', - 'To start using another Pro package, please consider reaching to our sales team to upgrade your license.', + 'To start using another Pro package, please consider reaching to our sales team to upgrade your license or visit https://mui.com/r/x-get-license to get a new license key.', ]); } From f19b30e185d5acea01a22afbfdff1490f89a3048 Mon Sep 17 00:00:00 2001 From: michel Date: Thu, 20 Jun 2024 10:31:40 +0200 Subject: [PATCH 22/24] added text for new license state to watermark --- packages/x-license/src/Watermark/Watermark.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/x-license/src/Watermark/Watermark.tsx b/packages/x-license/src/Watermark/Watermark.tsx index 92347c80bd6b..da41daf373fd 100644 --- a/packages/x-license/src/Watermark/Watermark.tsx +++ b/packages/x-license/src/Watermark/Watermark.tsx @@ -13,6 +13,8 @@ function getLicenseErrorMessage(licenseStatus: LicenseStatus) { return 'MUI X Invalid license key'; case LICENSE_STATUS.OutOfScope: return 'MUI X License key plan mismatch'; + case LICENSE_STATUS.ProductNotCovered: + return 'MUI X Product not covered by plan'; case LICENSE_STATUS.NotFound: return 'MUI X Missing license key'; default: From a0da60eabcc5c807c7657a7b50e43594b891a94f Mon Sep 17 00:00:00 2001 From: michel Date: Thu, 20 Jun 2024 11:03:49 +0200 Subject: [PATCH 23/24] fix tests --- packages/x-license/src/utils/licenseErrorMessageUtils.ts | 2 +- scripts/x-license.exports.json | 1 + test/utils/testLicense.js | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/x-license/src/utils/licenseErrorMessageUtils.ts b/packages/x-license/src/utils/licenseErrorMessageUtils.ts index 5844163f9ec7..1c29bfb5f9e4 100644 --- a/packages/x-license/src/utils/licenseErrorMessageUtils.ts +++ b/packages/x-license/src/utils/licenseErrorMessageUtils.ts @@ -34,7 +34,7 @@ export function showLicenseKeyPlanMismatchError() { export function showProductNotCoveredError() { showError([ - 'MUI X: Product not not covered by plan.', + 'MUI X: Product not covered by plan.', '', 'The component you are trying to use is not included in the Pro Plan your purchased. You are using a license that is only compatible with the `@mui/x-data-grid-pro` and `@mui/x-date-pickers-pro` commercial packages.', '', diff --git a/scripts/x-license.exports.json b/scripts/x-license.exports.json index 7de3fd6daabc..ba6c1bb8c50f 100644 --- a/scripts/x-license.exports.json +++ b/scripts/x-license.exports.json @@ -15,6 +15,7 @@ { "name": "showInvalidLicenseKeyError", "kind": "Function" }, { "name": "showLicenseKeyPlanMismatchError", "kind": "Function" }, { "name": "showMissingLicenseKeyError", "kind": "Function" }, + { "name": "showProductNotCoveredError", "kind": "Function" }, { "name": "Unstable_LicenseInfoProvider", "kind": "Function" }, { "name": "Unstable_LicenseInfoProviderProps", "kind": "Interface" }, { "name": "useLicenseVerifier", "kind": "Function" }, diff --git a/test/utils/testLicense.js b/test/utils/testLicense.js index 5db1ab450933..bf3064751157 100644 --- a/test/utils/testLicense.js +++ b/test/utils/testLicense.js @@ -10,7 +10,7 @@ export function generateTestLicenseKey() { scope: 'premium', orderNumber: 'MUI X tests', expiryDate, - planVersion: 'initial', + planVersion: 'Q3-2024', }); } From a19da9d5db43bb5b606ba2539093368ca580162c Mon Sep 17 00:00:00 2001 From: michel Date: Thu, 20 Jun 2024 14:24:19 +0200 Subject: [PATCH 24/24] fix tests --- .../x-license/src/generateLicense/generateLicense.test.ts | 2 +- .../src/useLicenseVerifier/useLicenseVerifier.test.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/x-license/src/generateLicense/generateLicense.test.ts b/packages/x-license/src/generateLicense/generateLicense.test.ts index a764e469557f..90ea9768276d 100644 --- a/packages/x-license/src/generateLicense/generateLicense.test.ts +++ b/packages/x-license/src/generateLicense/generateLicense.test.ts @@ -82,7 +82,7 @@ describe('License: generateLicense', () => { planVersion: 'Q3-2024', }), ).to.equal( - '4adf08e54d606215809064d1d31b6b39Tz1NVUktMTIzLEU9MTU5MTcyMzg3OTA2MixTPXBybyxMTT1zdWJzY3JpcHRpb24sUFY9UTMtMjAyNCxLVj0y', + 'b76c2067275b3b566fcae1d28ad23c91Tz1NVUktMTIzLEU9MTU5MTcyMzg3OTA2MixTPXByZW1pdW0sTE09c3Vic2NyaXB0aW9uLFBWPVEzLTIwMjQsS1Y9Mg==', ); }); }); diff --git a/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.test.tsx b/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.test.tsx index a9c208adb2dd..bbb04d1804a7 100644 --- a/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.test.tsx +++ b/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.test.tsx @@ -126,11 +126,11 @@ describe('useLicenseVerifier', function test() { expect(() => { render(); - }).to.toErrorDev(['MUI X: Product not not covered by plan.']); + }).to.toErrorDev(['MUI X: Product not covered by plan.']); expect(() => { render(); - }).to.toErrorDev(['MUI X: Product not not covered by plan.']); + }).to.toErrorDev(['MUI X: Product not covered by plan.']); }); it('should not throw if the license is covering charts and tree-view', () => {