Skip to content

Commit

Permalink
Update 5.0 branch to tag (#3645)
Browse files Browse the repository at this point in the history
  • Loading branch information
sjaanus committed Apr 28, 2023
1 parent 7a47f18 commit 9f82c08
Show file tree
Hide file tree
Showing 17 changed files with 204 additions and 100 deletions.
6 changes: 6 additions & 0 deletions .mergify.yml
@@ -0,0 +1,6 @@
pull_request_rules:
- name: Automatic update of all PRs
conditions:
- base=main # Targeting the main branch
actions:
update: {} # Update PR with base branch
Expand Up @@ -9,7 +9,6 @@ import {
StyledInput,
StyledTextField,
} from './ProjectForm.styles';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { StickinessSelect } from 'component/feature/StrategyTypes/FlexibleStrategy/StickinessSelect/StickinessSelect';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import Select from 'component/common/select';
Expand Down Expand Up @@ -60,9 +59,6 @@ const ProjectForm: React.FC<IProjectForm> = ({
validateProjectId,
clearErrors,
}) => {
const { uiConfig } = useUiConfig();
const { projectScopedStickiness } = uiConfig.flags;

return (
<StyledForm onSubmit={handleSubmit}>
<StyledContainer>
Expand Down Expand Up @@ -109,10 +105,7 @@ const ProjectForm: React.FC<IProjectForm> = ({
/>

<ConditionallyRender
condition={
Boolean(projectScopedStickiness) &&
setProjectStickiness != null
}
condition={setProjectStickiness != null}
show={
<>
<StyledDescription>
Expand Down
7 changes: 1 addition & 6 deletions frontend/src/hooks/useDefaultProjectSettings.ts
@@ -1,15 +1,10 @@
import useUiConfig from './api/getters/useUiConfig/useUiConfig';
import useProject from './api/getters/useProject/useProject';

const DEFAULT_STICKINESS = 'default';
export const useDefaultProjectSettings = (projectId: string) => {
const { uiConfig } = useUiConfig();

const { projectScopedStickiness } = uiConfig.flags;

const { project, loading, error } = useProject(projectId);
return {
defaultStickiness: Boolean(projectScopedStickiness)
defaultStickiness: Boolean(project.defaultStickiness)
? project.defaultStickiness
: DEFAULT_STICKINESS,
mode: project.mode,
Expand Down
1 change: 0 additions & 1 deletion frontend/src/interfaces/uiConfig.ts
Expand Up @@ -46,7 +46,6 @@ export interface IFlags {
proPlanAutoCharge?: boolean;
notifications?: boolean;
bulkOperations?: boolean;
projectScopedStickiness?: boolean;
personalAccessTokensKillSwitch?: boolean;
demo?: boolean;
strategyTitle?: boolean;
Expand Down
2 changes: 1 addition & 1 deletion package.json
@@ -1,7 +1,7 @@
{
"name": "unleash-server",
"description": "Unleash is an enterprise ready feature toggles service. It provides different strategies for handling feature toggles.",
"version": "5.0.0-beta.0",
"version": "5.0.0",
"keywords": [
"unleash",
"feature toggle",
Expand Down
2 changes: 0 additions & 2 deletions src/lib/__snapshots__/create-config.test.ts.snap
Expand Up @@ -85,7 +85,6 @@ exports[`should create default config 1`] = `
"optimal304Differ": false,
"personalAccessTokensKillSwitch": false,
"proPlanAutoCharge": false,
"projectScopedStickiness": false,
"responseTimeWithAppNameKillSwitch": false,
"strategyDisable": false,
"strategyTitle": false,
Expand Down Expand Up @@ -113,7 +112,6 @@ exports[`should create default config 1`] = `
"optimal304Differ": false,
"personalAccessTokensKillSwitch": false,
"proPlanAutoCharge": false,
"projectScopedStickiness": false,
"responseTimeWithAppNameKillSwitch": false,
"strategyDisable": false,
"strategyTitle": false,
Expand Down
139 changes: 121 additions & 18 deletions src/lib/error/api-error.test.ts
@@ -1,9 +1,47 @@
import { ErrorObject } from 'ajv';
import {
ApiErrorSchema,
fromLegacyError,
fromOpenApiValidationError,
fromOpenApiValidationErrors,
UnleashApiErrorNameWithoutExtraData,
UnleashApiErrorTypes,
UnleashError,
} from './api-error';
import BadDataError from './bad-data-error';

describe('v5 deprecation: backwards compatibility', () => {
it.each(UnleashApiErrorTypes)(
'Adds details to error type: "%s"',
(name: UnleashApiErrorNameWithoutExtraData) => {
const message = `Error type: ${name}`;
const error = new UnleashError({ name, message }).toJSON();

expect(error.message).toBe(message);
expect(error.details).toStrictEqual([
{
message,
description: message,
},
]);
},
);
});

describe('Standard/legacy error conversion', () => {
it('Moves message to the details list for baddataerror', () => {
const message = `: message!`;
const result = fromLegacyError(new BadDataError(message)).toJSON();

expect(result.message.includes('`details`'));
expect(result.details).toStrictEqual([
{
message,
description: message,
},
]);
});
});

describe('OpenAPI error conversion', () => {
it('Gives useful error messages for missing properties', () => {
Expand All @@ -21,9 +59,9 @@ describe('OpenAPI error conversion', () => {
const { description } = fromOpenApiValidationError({})(error);

// it tells the user that the property is required
expect(description.includes('required'));
expect(description.includes('required')).toBeTruthy();
// it tells the user the name of the missing property
expect(description.includes(error.params.missingProperty));
expect(description.includes(error.params.missingProperty)).toBeTruthy();
});

it('Gives useful error messages for type errors', () => {
Expand All @@ -45,9 +83,11 @@ describe('OpenAPI error conversion', () => {
})(error);

// it provides the message
expect(description.includes(error.message));
expect(description.includes(error.message)).toBeTruthy();
// it tells the user what they provided
expect(description.includes(JSON.stringify(parameterValue)));
expect(
description.includes(JSON.stringify(parameterValue)),
).toBeTruthy();
});

it('Gives useful pattern error messages', () => {
Expand All @@ -69,11 +109,11 @@ describe('OpenAPI error conversion', () => {
})(error);

// it tells the user what the pattern it should match is
expect(description.includes(error.params.pattern));
expect(description.includes(error.params.pattern)).toBeTruthy();
// it tells the user which property it pertains to
expect(description.includes('description'));
expect(description.includes('description')).toBeTruthy();
// it tells the user what they provided
expect(description.includes(requestDescription));
expect(description.includes(requestDescription)).toBeTruthy();
});

it('Gives useful min/maxlength error messages', () => {
Expand All @@ -95,11 +135,13 @@ describe('OpenAPI error conversion', () => {
})(error);

// it tells the user what the pattern it should match is
expect(description.includes(error.params.limit.toString()));
expect(
description.includes(error.params.limit.toString()),
).toBeTruthy();
// it tells the user which property it pertains to
expect(description.includes('description'));
expect(description.includes('description')).toBeTruthy();
// it tells the user what they provided
expect(description.includes(requestDescription));
expect(description.includes(requestDescription)).toBeTruthy();
});

it('Handles numerical min/max errors', () => {
Expand All @@ -123,13 +165,15 @@ describe('OpenAPI error conversion', () => {
})(error);

// it tells the user what the limit is
expect(description.includes(error.params.limit.toString()));
expect(
description.includes(error.params.limit.toString()),
).toBeTruthy();
// it tells the user what kind of comparison it performed
expect(description.includes(error.params.comparison));
expect(description.includes(error.params.comparison)).toBeTruthy();
// it tells the user which property it pertains to
expect(description.includes('newprop'));
expect(description.includes('newprop')).toBeTruthy();
// it tells the user what they provided
expect(description.includes(propertyValue.toString()));
expect(description.includes(propertyValue.toString())).toBeTruthy();
});

it('Handles multiple errors', () => {
Expand Down Expand Up @@ -175,6 +219,65 @@ describe('OpenAPI error conversion', () => {
);
});

describe('Disallowed additional properties', () => {
it('gives useful messages for base-level properties', () => {
const openApiError = {
keyword: 'additionalProperties',
instancePath: '',
dataPath: '.body',
schemaPath:
'#/components/schemas/addonCreateUpdateSchema/additionalProperties',
params: { additionalProperty: 'bogus' },
message: 'should NOT have additional properties',
};

const error = fromOpenApiValidationError({ bogus: 5 })(
openApiError,
);

expect(
error.description.includes(
openApiError.params.additionalProperty,
),
).toBeTruthy();
expect(error.description).toMatch(/\broot\b/i);
expect(error.description).toMatch(/\badditional properties\b/i);
});

it('gives useful messages for nested properties', () => {
const request2 = {
nestedObject: {
nested2: { extraPropertyName: 'illegal property' },
},
};
const openApiError = {
keyword: 'additionalProperties',
instancePath: '',
dataPath: '.body.nestedObject.nested2',
schemaPath:
'#/components/schemas/addonCreateUpdateSchema/properties/nestedObject/properties/nested2/additionalProperties',
params: { additionalProperty: 'extraPropertyName' },
message: 'should NOT have additional properties',
};

const error = fromOpenApiValidationError(request2)(openApiError);

expect(
error.description.includes('nestedObject.nested2'),
).toBeTruthy();
expect(
error.description.includes(
openApiError.params.additionalProperty,
),
).toBeTruthy();
expect(
error.description
.toLowerCase()
.includes('additional properties'),
).toBeTruthy();
});
});

it('Handles deeply nested properties gracefully', () => {
const error = {
keyword: 'type',
Expand All @@ -191,9 +294,9 @@ describe('OpenAPI error conversion', () => {
})(error);

// it should hold the full path to the error
expect(description.includes('nestedObject.a.b'));
expect(description).toMatch(/\bnestedObject.a.b\b/);
// it should include the value that the user sent
expect(description.includes('[]'));
expect(description.includes('[]')).toBeTruthy();
});

it('Handles deeply nested properties on referenced schemas', () => {
Expand All @@ -212,8 +315,8 @@ describe('OpenAPI error conversion', () => {
})(error);

// it should hold the full path to the error
expect(description.includes('nestedObject.a.b'));
expect(description).toMatch(/\bnestedObject.a.b\b/);
// it should include the value that the user sent
expect(description.includes(illegalValue));
expect(description.includes(illegalValue)).toBeTruthy();
});
});

0 comments on commit 9f82c08

Please sign in to comment.