mirror of
https://github.com/strapi/strapi.git
synced 2025-12-14 08:44:16 +00:00
chore(content-releases): enforce license limits for pending releases (#19208)
This commit is contained in:
parent
6cfcadfee1
commit
2b10ca9b97
@ -1,3 +1,4 @@
|
|||||||
|
import EE from '@strapi/strapi/dist/utils/ee';
|
||||||
import type { CreateReleaseAction } from '../../../../shared/contracts/release-actions';
|
import type { CreateReleaseAction } from '../../../../shared/contracts/release-actions';
|
||||||
import createReleaseValidationService from '../validation';
|
import createReleaseValidationService from '../validation';
|
||||||
|
|
||||||
@ -10,6 +11,12 @@ const baseStrapiMock = {
|
|||||||
contentType: jest.fn(),
|
contentType: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
jest.mock('@strapi/strapi/dist/utils/ee', () => ({
|
||||||
|
features: {
|
||||||
|
get: jest.fn(),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
describe('Release Validation service', () => {
|
describe('Release Validation service', () => {
|
||||||
describe('validateEntryContentType', () => {
|
describe('validateEntryContentType', () => {
|
||||||
it('throws an error if the content type does not exist', () => {
|
it('throws an error if the content type does not exist', () => {
|
||||||
@ -107,4 +114,88 @@ describe('Release Validation service', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
describe('validatePendingReleasesLimit', () => {
|
||||||
|
it('should throw an error if the default pending release limit has been reached', () => {
|
||||||
|
// @ts-expect-error - get is a mock
|
||||||
|
EE.features.get.mockReturnValue({});
|
||||||
|
const strapiMock = {
|
||||||
|
...baseStrapiMock,
|
||||||
|
db: {
|
||||||
|
query: jest.fn().mockReturnValue({
|
||||||
|
findWithCount: jest.fn().mockReturnValue([[], 4]),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error Ignore missing properties
|
||||||
|
const releaseValidationService = createReleaseValidationService({ strapi: strapiMock });
|
||||||
|
|
||||||
|
expect(() => releaseValidationService.validatePendingReleasesLimit()).rejects.toThrow(
|
||||||
|
'You have reached the maximum number of pending releases'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should pass if the default pending release limit has NOT been reached', async () => {
|
||||||
|
// @ts-expect-error - get is a mock
|
||||||
|
EE.features.get.mockReturnValue({});
|
||||||
|
const strapiMock = {
|
||||||
|
...baseStrapiMock,
|
||||||
|
db: {
|
||||||
|
query: jest.fn().mockReturnValue({
|
||||||
|
findWithCount: jest.fn().mockReturnValue([[], 2]),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error Ignore missing properties
|
||||||
|
const releaseValidationService = createReleaseValidationService({ strapi: strapiMock });
|
||||||
|
|
||||||
|
await expect(releaseValidationService.validatePendingReleasesLimit()).resolves.not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if the license pending release limit has been reached', () => {
|
||||||
|
// @ts-expect-error - get is a mock
|
||||||
|
EE.features.get.mockReturnValue({
|
||||||
|
options: {
|
||||||
|
maximumReleases: 5,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const strapiMock = {
|
||||||
|
...baseStrapiMock,
|
||||||
|
db: {
|
||||||
|
query: jest.fn().mockReturnValue({
|
||||||
|
findWithCount: jest.fn().mockReturnValue([[], 5]),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
// @ts-expect-error Ignore missing properties
|
||||||
|
const releaseValidationService = createReleaseValidationService({ strapi: strapiMock });
|
||||||
|
|
||||||
|
expect(() => releaseValidationService.validatePendingReleasesLimit()).rejects.toThrow(
|
||||||
|
'You have reached the maximum number of pending releases'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should pass if the license pending release limit has NOT been reached', async () => {
|
||||||
|
// @ts-expect-error - get is a mock
|
||||||
|
EE.features.get.mockReturnValue({
|
||||||
|
options: {
|
||||||
|
maximumReleases: 5,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const strapiMock = {
|
||||||
|
...baseStrapiMock,
|
||||||
|
db: {
|
||||||
|
query: jest.fn().mockReturnValue({
|
||||||
|
findWithCount: jest.fn().mockReturnValue([[], 4]),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error Ignore missing properties
|
||||||
|
const releaseValidationService = createReleaseValidationService({ strapi: strapiMock });
|
||||||
|
|
||||||
|
await expect(releaseValidationService.validatePendingReleasesLimit()).resolves.not.toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { setCreatorFields, errors } from '@strapi/utils';
|
|||||||
import type { LoadedStrapi, EntityService, UID } from '@strapi/types';
|
import type { LoadedStrapi, EntityService, UID } from '@strapi/types';
|
||||||
|
|
||||||
import _ from 'lodash/fp';
|
import _ from 'lodash/fp';
|
||||||
|
|
||||||
import { RELEASE_ACTION_MODEL_UID, RELEASE_MODEL_UID } from '../constants';
|
import { RELEASE_ACTION_MODEL_UID, RELEASE_MODEL_UID } from '../constants';
|
||||||
import type {
|
import type {
|
||||||
GetReleases,
|
GetReleases,
|
||||||
@ -51,6 +52,8 @@ const createReleaseService = ({ strapi }: { strapi: LoadedStrapi }) => ({
|
|||||||
async create(releaseData: CreateRelease.Request['body'], { user }: { user: UserInfo }) {
|
async create(releaseData: CreateRelease.Request['body'], { user }: { user: UserInfo }) {
|
||||||
const releaseWithCreatorFields = await setCreatorFields({ user })(releaseData);
|
const releaseWithCreatorFields = await setCreatorFields({ user })(releaseData);
|
||||||
|
|
||||||
|
await getService('release-validation', { strapi }).validatePendingReleasesLimit();
|
||||||
|
|
||||||
return strapi.entityService.create(RELEASE_MODEL_UID, {
|
return strapi.entityService.create(RELEASE_MODEL_UID, {
|
||||||
data: releaseWithCreatorFields,
|
data: releaseWithCreatorFields,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { errors } from '@strapi/utils';
|
import { errors } from '@strapi/utils';
|
||||||
import { LoadedStrapi } from '@strapi/types';
|
import { LoadedStrapi } from '@strapi/types';
|
||||||
|
import EE from '@strapi/strapi/dist/utils/ee';
|
||||||
import type { Release } from '../../../shared/contracts/releases';
|
import type { Release } from '../../../shared/contracts/releases';
|
||||||
import type { CreateReleaseAction } from '../../../shared/contracts/release-actions';
|
import type { CreateReleaseAction } from '../../../shared/contracts/release-actions';
|
||||||
import { RELEASE_MODEL_UID } from '../constants';
|
import { RELEASE_MODEL_UID } from '../constants';
|
||||||
@ -49,6 +50,25 @@ const createReleaseValidationService = ({ strapi }: { strapi: LoadedStrapi }) =>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
async validatePendingReleasesLimit() {
|
||||||
|
// Use the maximum releases option if it exists, otherwise default to 3
|
||||||
|
const maximumPendingReleases =
|
||||||
|
// @ts-expect-error - options is not typed into features
|
||||||
|
EE.features.get('cms-content-releases')?.options?.maximumReleases || 3;
|
||||||
|
|
||||||
|
const [, pendingReleasesCount] = await strapi.db.query(RELEASE_MODEL_UID).findWithCount({
|
||||||
|
filters: {
|
||||||
|
releasedAt: {
|
||||||
|
$null: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Unlimited is a number that will never be reached like 9999
|
||||||
|
if (pendingReleasesCount >= maximumPendingReleases) {
|
||||||
|
throw new errors.ValidationError('You have reached the maximum number of pending releases');
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default createReleaseValidationService;
|
export default createReleaseValidationService;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user