From 190a18e668e9d26ca524a55e88e14bc077935d15 Mon Sep 17 00:00:00 2001 From: michel <jsnerdic@gmail.com> 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 9ff33495f713b..191c151c09c1e 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 0000000000000..ec5ee68bb4561 --- /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 84151d4acc329..55faeb3bac634 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 74e27de02161a..2d6e61c8541e3 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 77a27e1d73575..2eef5361da6e3 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 b280b196dd7fd..bdfa65abbe5c4 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 <jsnerdic@gmail.com> 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 191c151c09c1e..9ff33495f713b 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 ec5ee68bb4561..0000000000000 --- 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 55faeb3bac634..84151d4acc329 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 2d6e61c8541e3..74e27de02161a 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 2eef5361da6e3..77a27e1d73575 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 bdfa65abbe5c4..b280b196dd7fd 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 <jsnerdic@gmail.com> 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 84151d4acc329..9ab6894abf3f3 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 74e27de02161a..4db0ce787678b 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 b280b196dd7fd..a953db984a716 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 <jsnerdic@gmail.com> 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 5a27b7880364b..adbbed34cc0ae 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 9ff33495f713b..5cb150e4e2bc2 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 9ab6894abf3f3..875a8f9910af2 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 4db0ce787678b..9dfe1f811ced1 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 7f916614ab142..e26db47322b32 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 a953db984a716..f75a8b27e316e 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 <jsnerdic@gmail.com> 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 f75a8b27e316e..13848f66dbca2 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 <jsnerdic@gmail.com> 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 adbbed34cc0ae..5a27b7880364b 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 e26db47322b32..7f916614ab142 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 <jsnerdic@gmail.com> 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 5a27b7880364b..f211d30b3562a 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 5cb150e4e2bc2..badebb90d782c 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 875a8f9910af2..210332e12facf 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 9dfe1f811ced1..c042eeca1aa91 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 7f916614ab142..fc95c0976a2ab 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 13848f66dbca2..262e265e97c4f 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 <jsnerdic@gmail.com> 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 62b51732d8c03..1773384ed9a05 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('<DataGridPremium /> - License', () => { orderNumber: 'Test', licensingModel: 'subscription', scope: 'pro', + planVersion: 'initial', }), ); expect(() => render(<DataGridPremium columns={[]} rows={[]} autoHeight />)).toErrorDev([ diff --git a/packages/x-license/src/generateLicense/generateLicense.test.ts b/packages/x-license/src/generateLicense/generateLicense.test.ts index f211d30b3562a..4b69d6aa471b3 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 ece7b7567f65b..926e0761ecda3 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 210332e12facf..681ac640f5ccc 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 c042eeca1aa91..c86bd0b73779a 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 fc95c0976a2ab..f4832af2199a2 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 262e265e97c4f..11acff4a0427e 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 8a48c66f8b062..5db1ab4509338 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 <jsnerdic@gmail.com> 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 c86bd0b73779a..d75a3b56607aa 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 f4832af2199a2..ea4a0f94d5134 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 11acff4a0427e..cf1253e6887da 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 <andrew.cherniavskii@gmail.com> 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 cf1253e6887da..d6dd301567f3c 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 <jsnerdic@gmail.com> 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 4b69d6aa471b3..c24f4fdb7305c 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 <jsnerdic@gmail.com> 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 926e0761ecda3..b2f81051631b0 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 <div data-testid="status">Status: {licesenStatus.status}</div>; + const licenseStatus = useLicenseVerifier('x-date-pickers-pro', RELEASE_INFO); + return <div data-testid="status">Status: {licenseStatus.status}</div>; } describe('useLicenseVerifier', function test() { From b5cec8b69b97b37eb2670b57fff6518153e65634 Mon Sep 17 00:00:00 2001 From: michel <jsnerdic@gmail.com> 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 d6dd301567f3c..09eb48c2fee47 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 <jsnerdic@gmail.com> 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 ea4a0f94d5134..d9d154d3ba2ef 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 <jsnerdic@gmail.com> 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 09eb48c2fee47..633a58d0b20e2 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 <jsnerdic@gmail.com> 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 b2f81051631b0..5c92413fff737 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 <div data-testid="status">Status: {licenseStatus.status}</div>; } @@ -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(<TestComponent packageName={'x-charts-pro'} />); + }).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 681ac640f5ccc..4be4d525d916d 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 9924da8404b51..63d6fecb70bd1 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 77a27e1d73575..4a377a4e54f8d 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 d9d154d3ba2ef..6e22e2277ad61 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 633a58d0b20e2..3bfcfaa11bfa3 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 <andrew.cherniavskii@gmail.com> 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 c24f4fdb7305c..a764e469557fd 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 <jsnerdic@gmail.com> 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 5c92413fff737..a99d01ff638bf 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(<TestComponent packageName={'x-charts-pro'} />); }).to.toErrorDev(['MUI X: Invalid Product coverage']); + + expect(() => { + render(<TestComponent packageName={'x-tree-view-pro'} />); + }).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(<TestComponent packageName={'x-charts-pro'} />); + }).not.toErrorDev(); + + expect(() => { + render(<TestComponent packageName={'x-tree-view-pro'} />); + }).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(<TestComponent packageName={'x-data-grid-pro'} />); + }).not.toErrorDev(); + + expect(() => { + render(<TestComponent packageName={'x-data-grid-premium'} />); + }).not.toErrorDev(); + + expect(() => { + render(<TestComponent packageName={'x-date-pickers-pro'} />); + }).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 <flaviendelangle@gmail.com> 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 63d6fecb70bd1..c2f1ec951f1d2 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 <jsnerdic@gmail.com> 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 a99d01ff638bf..a9c208adb2dd9 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(<TestComponent packageName={'x-charts-pro'} />); - }).to.toErrorDev(['MUI X: Invalid Product coverage']); + }).to.toErrorDev(['MUI X: Product not not covered by plan.']); expect(() => { render(<TestComponent packageName={'x-tree-view-pro'} />); - }).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 4be4d525d916d..a79cc012b3898 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 c2f1ec951f1d2..71245767cc12b 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 4a377a4e54f8d..ac6828c109e24 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 6e22e2277ad61..1b2c2fe24eb3d 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 3bfcfaa11bfa3..7d696490599dc 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 <jsnerdic@gmail.com> 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 71245767cc12b..5844163f9ec74 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 <jsnerdic@gmail.com> 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 92347c80bd6bf..da41daf373fd7 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 <jsnerdic@gmail.com> 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 5844163f9ec74..1c29bfb5f9e41 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 7de3fd6daabc8..ba6c1bb8c50fe 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 5db1ab4509338..bf3064751157b 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 <jsnerdic@gmail.com> 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 a764e469557fd..90ea9768276d2 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 a9c208adb2dd9..bbb04d1804a75 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(<TestComponent packageName={'x-charts-pro'} />); - }).to.toErrorDev(['MUI X: Product not not covered by plan.']); + }).to.toErrorDev(['MUI X: Product not covered by plan.']); expect(() => { render(<TestComponent packageName={'x-tree-view-pro'} />); - }).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', () => {