mirror of
https://github.com/Unleash/unleash.git
synced 2025-08-27 13:49:10 +02:00
Merge fixes for 5.0.1 to 5.0 branch (#3646)
This commit is contained in:
parent
9f82c08ba2
commit
77dd5d6c9f
@ -23,12 +23,12 @@ describe('project overview', () => {
|
|||||||
after(() => {
|
after(() => {
|
||||||
cy.request({
|
cy.request({
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
url: `${baseUrl}/api/admin/features/${featureToggleName}-A`,
|
url: `${baseUrl}/api/admin/projects/default/features/${featureToggleName}-A`,
|
||||||
failOnStatusCode: false,
|
failOnStatusCode: false,
|
||||||
});
|
});
|
||||||
cy.request({
|
cy.request({
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
url: `${baseUrl}/api/admin/features/${featureToggleName}-B`,
|
url: `${baseUrl}/api/admin/projects/default/features/${featureToggleName}-B`,
|
||||||
failOnStatusCode: false,
|
failOnStatusCode: false,
|
||||||
});
|
});
|
||||||
cy.request({
|
cy.request({
|
||||||
|
@ -24,7 +24,7 @@ export const createFeature_API = (
|
|||||||
export const deleteFeature_API = (name: string): Chainable<any> => {
|
export const deleteFeature_API = (name: string): Chainable<any> => {
|
||||||
cy.request({
|
cy.request({
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
url: `${baseUrl}/api/admin/features/${name}`,
|
url: `${baseUrl}/api/admin/projects/default/features/${name}`,
|
||||||
});
|
});
|
||||||
return cy.request({
|
return cy.request({
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
|
@ -95,24 +95,18 @@ export const ArchiveTable = ({
|
|||||||
|
|
||||||
const columns = useMemo(
|
const columns = useMemo(
|
||||||
() => [
|
() => [
|
||||||
...(uiConfig?.flags?.bulkOperations
|
|
||||||
? [
|
|
||||||
{
|
{
|
||||||
id: 'Select',
|
id: 'Select',
|
||||||
Header: ({ getToggleAllRowsSelectedProps }: any) => (
|
Header: ({ getToggleAllRowsSelectedProps }: any) => (
|
||||||
<Checkbox {...getToggleAllRowsSelectedProps()} />
|
<Checkbox {...getToggleAllRowsSelectedProps()} />
|
||||||
),
|
),
|
||||||
Cell: ({ row }: any) => (
|
Cell: ({ row }: any) => (
|
||||||
<RowSelectCell
|
<RowSelectCell {...row?.getToggleRowSelectedProps?.()} />
|
||||||
{...row?.getToggleRowSelectedProps?.()}
|
|
||||||
/>
|
|
||||||
),
|
),
|
||||||
maxWidth: 50,
|
maxWidth: 50,
|
||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
hideInMenu: true,
|
hideInMenu: true,
|
||||||
},
|
},
|
||||||
]
|
|
||||||
: []),
|
|
||||||
{
|
{
|
||||||
Header: 'Seen',
|
Header: 'Seen',
|
||||||
width: 85,
|
width: 85,
|
||||||
|
@ -43,12 +43,7 @@ export const ArchivedFeatureDeleteConfirm = ({
|
|||||||
if (deletedFeatures.length === 0) {
|
if (deletedFeatures.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uiConfig?.flags?.bulkOperations) {
|
|
||||||
await deleteFeatures(projectId, deletedFeatures);
|
await deleteFeatures(projectId, deletedFeatures);
|
||||||
} else {
|
|
||||||
await deleteFeature(deletedFeatures[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
await refetch();
|
await refetch();
|
||||||
setToastData({
|
setToastData({
|
||||||
|
@ -240,24 +240,18 @@ export const ProjectFeatureToggles = ({
|
|||||||
|
|
||||||
const columns = useMemo(
|
const columns = useMemo(
|
||||||
() => [
|
() => [
|
||||||
...(uiConfig?.flags?.bulkOperations
|
|
||||||
? [
|
|
||||||
{
|
{
|
||||||
id: 'Select',
|
id: 'Select',
|
||||||
Header: ({ getToggleAllRowsSelectedProps }: any) => (
|
Header: ({ getToggleAllRowsSelectedProps }: any) => (
|
||||||
<Checkbox {...getToggleAllRowsSelectedProps()} />
|
<Checkbox {...getToggleAllRowsSelectedProps()} />
|
||||||
),
|
),
|
||||||
Cell: ({ row }: any) => (
|
Cell: ({ row }: any) => (
|
||||||
<RowSelectCell
|
<RowSelectCell {...row?.getToggleRowSelectedProps?.()} />
|
||||||
{...row?.getToggleRowSelectedProps?.()}
|
|
||||||
/>
|
|
||||||
),
|
),
|
||||||
maxWidth: 50,
|
maxWidth: 50,
|
||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
hideInMenu: true,
|
hideInMenu: true,
|
||||||
},
|
},
|
||||||
]
|
|
||||||
: []),
|
|
||||||
{
|
{
|
||||||
id: 'favorite',
|
id: 'favorite',
|
||||||
Header: (
|
Header: (
|
||||||
|
@ -45,7 +45,6 @@ export interface IFlags {
|
|||||||
crOnVariants?: boolean;
|
crOnVariants?: boolean;
|
||||||
proPlanAutoCharge?: boolean;
|
proPlanAutoCharge?: boolean;
|
||||||
notifications?: boolean;
|
notifications?: boolean;
|
||||||
bulkOperations?: boolean;
|
|
||||||
personalAccessTokensKillSwitch?: boolean;
|
personalAccessTokensKillSwitch?: boolean;
|
||||||
demo?: boolean;
|
demo?: boolean;
|
||||||
strategyTitle?: boolean;
|
strategyTitle?: boolean;
|
||||||
|
@ -67,7 +67,6 @@ exports[`should create default config 1`] = `
|
|||||||
},
|
},
|
||||||
"flags": {
|
"flags": {
|
||||||
"anonymiseEventLog": false,
|
"anonymiseEventLog": false,
|
||||||
"bulkOperations": false,
|
|
||||||
"caseInsensitiveInOperators": false,
|
"caseInsensitiveInOperators": false,
|
||||||
"cleanClientApi": false,
|
"cleanClientApi": false,
|
||||||
"crOnVariants": false,
|
"crOnVariants": false,
|
||||||
@ -94,7 +93,6 @@ exports[`should create default config 1`] = `
|
|||||||
"flagResolver": FlagResolver {
|
"flagResolver": FlagResolver {
|
||||||
"experiments": {
|
"experiments": {
|
||||||
"anonymiseEventLog": false,
|
"anonymiseEventLog": false,
|
||||||
"bulkOperations": false,
|
|
||||||
"caseInsensitiveInOperators": false,
|
"caseInsensitiveInOperators": false,
|
||||||
"cleanClientApi": false,
|
"cleanClientApi": false,
|
||||||
"crOnVariants": false,
|
"crOnVariants": false,
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import owasp from 'owasp-password-strength-test';
|
||||||
import { ErrorObject } from 'ajv';
|
import { ErrorObject } from 'ajv';
|
||||||
import {
|
import {
|
||||||
ApiErrorSchema,
|
ApiErrorSchema,
|
||||||
@ -9,6 +10,7 @@ import {
|
|||||||
UnleashError,
|
UnleashError,
|
||||||
} from './api-error';
|
} from './api-error';
|
||||||
import BadDataError from './bad-data-error';
|
import BadDataError from './bad-data-error';
|
||||||
|
import OwaspValidationError from './owasp-validation-error';
|
||||||
|
|
||||||
describe('v5 deprecation: backwards compatibility', () => {
|
describe('v5 deprecation: backwards compatibility', () => {
|
||||||
it.each(UnleashApiErrorTypes)(
|
it.each(UnleashApiErrorTypes)(
|
||||||
@ -320,3 +322,14 @@ describe('OpenAPI error conversion', () => {
|
|||||||
expect(description.includes(illegalValue)).toBeTruthy();
|
expect(description.includes(illegalValue)).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Error serialization special cases', () => {
|
||||||
|
it('OwaspValidationErrors: adds `validationErrors` to `details`', () => {
|
||||||
|
const results = owasp.test('123');
|
||||||
|
const error = new OwaspValidationError(results);
|
||||||
|
const json = fromLegacyError(error).toJSON();
|
||||||
|
|
||||||
|
expect(json.details!![0].message).toBe(results.errors[0]);
|
||||||
|
expect(json.details!![0].validationErrors).toBe(results.errors);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { v4 as uuidV4 } from 'uuid';
|
import { v4 as uuidV4 } from 'uuid';
|
||||||
import { FromSchema } from 'json-schema-to-ts';
|
import { FromSchema } from 'json-schema-to-ts';
|
||||||
import { ErrorObject } from 'ajv';
|
import { ErrorObject } from 'ajv';
|
||||||
|
import OwaspValidationError from './owasp-validation-error';
|
||||||
|
|
||||||
export const UnleashApiErrorTypes = [
|
export const UnleashApiErrorTypes = [
|
||||||
'ContentTypeError',
|
'ContentTypeError',
|
||||||
@ -14,7 +15,6 @@ export const UnleashApiErrorTypes = [
|
|||||||
'NotFoundError',
|
'NotFoundError',
|
||||||
'NotImplementedError',
|
'NotImplementedError',
|
||||||
'OperationDeniedError',
|
'OperationDeniedError',
|
||||||
'OwaspValidationError',
|
|
||||||
'PasswordMismatch',
|
'PasswordMismatch',
|
||||||
'PasswordMismatchError',
|
'PasswordMismatchError',
|
||||||
'PasswordUndefinedError',
|
'PasswordUndefinedError',
|
||||||
@ -34,6 +34,7 @@ const UnleashApiErrorTypesWithExtraData = [
|
|||||||
'AuthenticationRequired',
|
'AuthenticationRequired',
|
||||||
'NoAccessError',
|
'NoAccessError',
|
||||||
'InvalidTokenError',
|
'InvalidTokenError',
|
||||||
|
'OwaspValidationError',
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
const AllUnleashApiErrorTypes = [
|
const AllUnleashApiErrorTypes = [
|
||||||
@ -136,6 +137,15 @@ type UnleashErrorData =
|
|||||||
...ValidationErrorDescription[],
|
...ValidationErrorDescription[],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
| {
|
||||||
|
name: 'OwaspValidationError';
|
||||||
|
details: [
|
||||||
|
{
|
||||||
|
validationErrors: string[];
|
||||||
|
message: string;
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export class UnleashError extends Error {
|
export class UnleashError extends Error {
|
||||||
@ -245,6 +255,15 @@ export const fromLegacyError = (e: Error): UnleashError => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (name === 'OwaspValidationError') {
|
||||||
|
return new UnleashError({
|
||||||
|
name,
|
||||||
|
message:
|
||||||
|
'Password validation failed. Refer to the `details` property.',
|
||||||
|
details: (e as OwaspValidationError).toJSON().details,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (name === 'AuthenticationRequired') {
|
if (name === 'AuthenticationRequired') {
|
||||||
return new UnleashError({
|
return new UnleashError({
|
||||||
name,
|
name,
|
||||||
|
@ -14,7 +14,6 @@ import { IAuthRequest } from '../../unleash-types';
|
|||||||
import { OpenApiService } from '../../../services/openapi-service';
|
import { OpenApiService } from '../../../services/openapi-service';
|
||||||
import { emptyResponse } from '../../../openapi/util/standard-responses';
|
import { emptyResponse } from '../../../openapi/util/standard-responses';
|
||||||
import { BatchFeaturesSchema, createRequestSchema } from '../../../openapi';
|
import { BatchFeaturesSchema, createRequestSchema } from '../../../openapi';
|
||||||
import NotFoundError from '../../../error/notfound-error';
|
|
||||||
import Controller from '../../controller';
|
import Controller from '../../controller';
|
||||||
|
|
||||||
const PATH = '/:projectId';
|
const PATH = '/:projectId';
|
||||||
@ -105,9 +104,6 @@ export default class ProjectArchiveController extends Controller {
|
|||||||
req: IAuthRequest<IProjectParam, any, BatchFeaturesSchema>,
|
req: IAuthRequest<IProjectParam, any, BatchFeaturesSchema>,
|
||||||
res: Response<void>,
|
res: Response<void>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (!this.flagResolver.isEnabled('bulkOperations')) {
|
|
||||||
throw new NotFoundError('Bulk operations are not enabled');
|
|
||||||
}
|
|
||||||
const { projectId } = req.params;
|
const { projectId } = req.params;
|
||||||
const { features } = req.body;
|
const { features } = req.body;
|
||||||
const user = extractUsername(req);
|
const user = extractUsername(req);
|
||||||
@ -119,9 +115,6 @@ export default class ProjectArchiveController extends Controller {
|
|||||||
req: IAuthRequest<IProjectParam, any, BatchFeaturesSchema>,
|
req: IAuthRequest<IProjectParam, any, BatchFeaturesSchema>,
|
||||||
res: Response<void>,
|
res: Response<void>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (!this.flagResolver.isEnabled('bulkOperations')) {
|
|
||||||
throw new NotFoundError('Bulk operations are not enabled');
|
|
||||||
}
|
|
||||||
const { projectId } = req.params;
|
const { projectId } = req.params;
|
||||||
const { features } = req.body;
|
const { features } = req.body;
|
||||||
const user = extractUsername(req);
|
const user = extractUsername(req);
|
||||||
@ -133,10 +126,6 @@ export default class ProjectArchiveController extends Controller {
|
|||||||
req: IAuthRequest<IProjectParam, void, BatchFeaturesSchema>,
|
req: IAuthRequest<IProjectParam, void, BatchFeaturesSchema>,
|
||||||
res: Response,
|
res: Response,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (!this.flagResolver.isEnabled('bulkOperations')) {
|
|
||||||
throw new NotFoundError('Bulk operations are not enabled');
|
|
||||||
}
|
|
||||||
|
|
||||||
const { features } = req.body;
|
const { features } = req.body;
|
||||||
const { projectId } = req.params;
|
const { projectId } = req.params;
|
||||||
const userName = extractUsername(req);
|
const userName = extractUsername(req);
|
||||||
|
@ -40,7 +40,6 @@ import {
|
|||||||
} from '../../../openapi';
|
} from '../../../openapi';
|
||||||
import { OpenApiService, FeatureToggleService } from '../../../services';
|
import { OpenApiService, FeatureToggleService } from '../../../services';
|
||||||
import { querySchema } from '../../../schema/feature-schema';
|
import { querySchema } from '../../../schema/feature-schema';
|
||||||
import NotFoundError from '../../../error/notfound-error';
|
|
||||||
import { BatchStaleSchema } from '../../../openapi/spec/batch-stale-schema';
|
import { BatchStaleSchema } from '../../../openapi/spec/batch-stale-schema';
|
||||||
|
|
||||||
interface FeatureStrategyParams {
|
interface FeatureStrategyParams {
|
||||||
@ -594,10 +593,6 @@ export default class ProjectFeaturesController extends Controller {
|
|||||||
req: IAuthRequest<{ projectId: string }, void, BatchStaleSchema>,
|
req: IAuthRequest<{ projectId: string }, void, BatchStaleSchema>,
|
||||||
res: Response,
|
res: Response,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (!this.flagResolver.isEnabled('bulkOperations')) {
|
|
||||||
throw new NotFoundError('Bulk operations are not enabled');
|
|
||||||
}
|
|
||||||
|
|
||||||
const { features, stale } = req.body;
|
const { features, stale } = req.body;
|
||||||
const { projectId } = req.params;
|
const { projectId } = req.params;
|
||||||
const userName = extractUsername(req);
|
const userName = extractUsername(req);
|
||||||
|
@ -24,7 +24,6 @@ import {
|
|||||||
import { emptyResponse } from '../../openapi/util/standard-responses';
|
import { emptyResponse } from '../../openapi/util/standard-responses';
|
||||||
import FeatureTagService from 'lib/services/feature-tag-service';
|
import FeatureTagService from 'lib/services/feature-tag-service';
|
||||||
import { TagsBulkAddSchema } from '../../openapi/spec/tags-bulk-add-schema';
|
import { TagsBulkAddSchema } from '../../openapi/spec/tags-bulk-add-schema';
|
||||||
import NotFoundError from '../../error/notfound-error';
|
|
||||||
import { IFlagResolver } from '../../types';
|
import { IFlagResolver } from '../../types';
|
||||||
|
|
||||||
const version = 1;
|
const version = 1;
|
||||||
@ -214,9 +213,6 @@ class TagController extends Controller {
|
|||||||
req: IAuthRequest<void, void, TagsBulkAddSchema>,
|
req: IAuthRequest<void, void, TagsBulkAddSchema>,
|
||||||
res: Response<TagSchema>,
|
res: Response<TagSchema>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (!this.flagResolver.isEnabled('bulkOperations')) {
|
|
||||||
throw new NotFoundError('Bulk operations are not enabled');
|
|
||||||
}
|
|
||||||
const { features, tags } = req.body;
|
const { features, tags } = req.body;
|
||||||
const userName = extractUsername(req);
|
const userName = extractUsername(req);
|
||||||
await this.featureTagService.updateTags(
|
await this.featureTagService.updateTags(
|
||||||
|
@ -49,10 +49,6 @@ const flags = {
|
|||||||
process.env.UNLEASH_PRO_PLAN_AUTO_CHARGE,
|
process.env.UNLEASH_PRO_PLAN_AUTO_CHARGE,
|
||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
bulkOperations: parseEnvVarBoolean(
|
|
||||||
process.env.UNLEASH_BULK_OPERATIONS,
|
|
||||||
false,
|
|
||||||
),
|
|
||||||
personalAccessTokensKillSwitch: parseEnvVarBoolean(
|
personalAccessTokensKillSwitch: parseEnvVarBoolean(
|
||||||
process.env.UNLEASH_PAT_KILL_SWITCH,
|
process.env.UNLEASH_PAT_KILL_SWITCH,
|
||||||
false,
|
false,
|
||||||
|
@ -39,7 +39,6 @@ process.nextTick(async () => {
|
|||||||
anonymiseEventLog: false,
|
anonymiseEventLog: false,
|
||||||
responseTimeWithAppNameKillSwitch: false,
|
responseTimeWithAppNameKillSwitch: false,
|
||||||
newProjectOverview: true,
|
newProjectOverview: true,
|
||||||
bulkOperations: true,
|
|
||||||
optimal304: true,
|
optimal304: true,
|
||||||
optimal304Differ: false,
|
optimal304Differ: false,
|
||||||
},
|
},
|
||||||
|
@ -15,7 +15,6 @@ beforeAll(async () => {
|
|||||||
experimental: {
|
experimental: {
|
||||||
flags: {
|
flags: {
|
||||||
strictSchemaValidation: true,
|
strictSchemaValidation: true,
|
||||||
bulkOperations: true,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -89,7 +89,6 @@ beforeAll(async () => {
|
|||||||
experimental: {
|
experimental: {
|
||||||
flags: {
|
flags: {
|
||||||
strictSchemaValidation: true,
|
strictSchemaValidation: true,
|
||||||
bulkOperations: true,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -11,7 +11,6 @@ beforeAll(async () => {
|
|||||||
experimental: {
|
experimental: {
|
||||||
flags: {
|
flags: {
|
||||||
strictSchemaValidation: true,
|
strictSchemaValidation: true,
|
||||||
bulkOperations: true,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user