Skip to content

Commit

Permalink
Basic Forms: Hide parameters based on the deployment event (install o…
Browse files Browse the repository at this point in the history
…r upgrade) (#1808)

* Hide parameters based on the deployment event (install or upgrade)

- Feature implemented
- Tests updated
- Doc updated

* Revision

- Support only "event" or "path/value" at same time, not both.
- Change isUpgrade check to use the deploymentEvent
  • Loading branch information
batiati committed Jun 22, 2020
1 parent 99225a5 commit d083020
Show file tree
Hide file tree
Showing 11 changed files with 179 additions and 22 deletions.
1 change: 1 addition & 0 deletions dashboard/src/components/DeploymentForm/DeploymentForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ class DeploymentForm extends React.Component<IDeploymentFormProps, IDeploymentFo
/>
</div>
<DeploymentFormBody
deploymentEvent="install"
chartNamespace={this.props.chartNamespace}
chartID={this.props.chartID}
chartVersion={this.props.chartVersion}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ exports[`renders the full DeploymentForm 1`] = `
chartID="foo"
chartNamespace="other-namespace"
chartVersion="1.0.0"
deploymentEvent="install"
getChartVersion={[Function]}
namespace="default"
push={[Function]}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import * as React from "react";

import { mount, shallow } from "enzyme";
import { IBasicFormParam, IBasicFormSliderParam } from "shared/types";
import { DeploymentEvent, IBasicFormParam, IBasicFormSliderParam } from "shared/types";
import BasicDeploymentForm from "./BasicDeploymentForm";
import Subsection from "./Subsection";

const defaultProps = {
deploymentEvent: "install" as DeploymentEvent,
params: [],
handleBasicFormParamChange: jest.fn(() => jest.fn()),
appValues: "",
Expand Down Expand Up @@ -284,3 +285,88 @@ it("should hide an element if it depends on multiple params (NOR) (object)", ()
const hiddenParam = wrapper.find("div").filterWhere(p => p.prop("hidden") === true);
expect(hiddenParam).toExist();
});

it("should hide an element if it depends on the deploymentEvent (install | upgrade) (object)", () => {
const params = [
{
path: "foo",
type: "string",
hidden: {
event: "upgrade",
},
},
] as IBasicFormParam[];
const appValues = "foo: 1\nbar: disabled\nbaz: enabled";
const wrapper = shallow(
<BasicDeploymentForm
{...defaultProps}
deploymentEvent="upgrade"
params={params}
appValues={appValues}
/>,
);

const hiddenParam = wrapper.find("div").filterWhere(p => p.prop("hidden") === true);
expect(hiddenParam).toExist();
});

it("should NOT hide an element if it depends on the deploymentEvent (install | upgrade) (object)", () => {
const params = [
{
path: "foo",
type: "string",
hidden: {
event: "upgrade",
},
},
] as IBasicFormParam[];
const appValues = "foo: 1\nbar: disabled\nbaz: enabled";
const wrapper = shallow(
<BasicDeploymentForm
{...defaultProps}
deploymentEvent="install"
params={params}
appValues={appValues}
/>,
);

const hiddenParam = wrapper.find("div").filterWhere(p => p.prop("hidden") === true);
expect(hiddenParam).not.toExist();
});

it("should hide an element if it depends on deploymentEvent (install | upgrade) combined with multiple params (object)", () => {
const params = [
{
path: "foo",
type: "string",
hidden: {
conditions: [
{
event: "upgrade",
},
{
value: "enabled",
path: "bar",
},
],
operator: "or",
},
},
{
path: "bar",
type: "string",
},
] as IBasicFormParam[];
const appValues = "foo: 1\nbar: disabled";
const wrapper = shallow(
<BasicDeploymentForm
{...defaultProps}
deploymentEvent="upgrade"
params={params}
appValues={appValues}
/>,
);

const hiddenParam = wrapper.find("div").filterWhere(p => p.prop("hidden") === true);
expect(hiddenParam).toExist();
});
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { isArray } from "lodash";
import * as React from "react";
import { IBasicFormParam, IBasicFormSliderParam } from "shared/types";
import { DeploymentEvent, IBasicFormParam, IBasicFormSliderParam } from "shared/types";
import TextParam from "./TextParam";

import { getValue } from "../../../shared/schema";
Expand All @@ -10,6 +10,7 @@ import SliderParam from "./SliderParam";
import Subsection from "./Subsection";

export interface IBasicDeploymentFormProps {
deploymentEvent: DeploymentEvent;
params: IBasicFormParam[];
handleBasicFormParamChange: (
p: IBasicFormParam,
Expand All @@ -35,12 +36,24 @@ class BasicDeploymentForm extends React.Component<IBasicDeploymentFormProps> {
);
}

private evalCondition(
path: string,
expectedValue?: any,
deploymentEvent?: DeploymentEvent,
): boolean {
if (deploymentEvent == null) {
return getValue(this.props.appValues, path) === (expectedValue ?? true);
} else {
return deploymentEvent === this.props.deploymentEvent;
}
}

private isHidden = (param: IBasicFormParam) => {
const hidden = param.hidden;
switch (typeof hidden) {
case "string":
// If hidden is a string, it points to the value that should be true
return getValue(this.props.appValues, hidden) === true;
return this.evalCondition(hidden);
case "object":
// Two type of supported objects
// A single condition: {value: string, path: any}
Expand All @@ -52,29 +65,21 @@ class BasicDeploymentForm extends React.Component<IBasicDeploymentFormProps> {
case "and":
// Every value matches the referenced
// value (via jsonpath) in all the conditions
return hidden.conditions.every(
c => getValue(this.props.appValues, c.path) === c.value,
);
return hidden.conditions.every(c => this.evalCondition(c.path, c.value, c.event));
case "or":
// It is enough if the value matches the referenced
// value (via jsonpath) in any of the conditions
return hidden.conditions.some(
c => getValue(this.props.appValues, c.path) === c.value,
);
return hidden.conditions.some(c => this.evalCondition(c.path, c.value, c.event));
case "nor":
// Every value mismatches the referenced
// value (via jsonpath) in any of the conditions
return hidden.conditions.every(
c => getValue(this.props.appValues, c.path) !== c.value,
);
return hidden.conditions.every(c => !this.evalCondition(c.path, c.value, c.event));
default:
// we consider 'and' as the default operator
return hidden.conditions.every(
c => getValue(this.props.appValues, c.path) === c.value,
);
return hidden.conditions.every(c => this.evalCondition(c.path, c.value, c.event));
}
} else {
return getValue(this.props.appValues, hidden.path) === hidden.value;
return this.evalCondition(hidden.path, hidden.value, hidden.event);
}
case "undefined":
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
exports[`renders a basic deployment with a disk size 1`] = `
<BasicDeploymentForm
appValues=""
deploymentEvent="install"
handleBasicFormParamChange={[Function]}
handleValuesChange={[Function]}
params={
Expand Down Expand Up @@ -346,6 +347,7 @@ exports[`renders a basic deployment with a disk size 1`] = `
exports[`renders a basic deployment with a email 1`] = `
<BasicDeploymentForm
appValues=""
deploymentEvent="install"
handleBasicFormParamChange={[Function]}
handleValuesChange={[Function]}
params={
Expand Down Expand Up @@ -419,6 +421,7 @@ exports[`renders a basic deployment with a email 1`] = `
exports[`renders a basic deployment with a generic boolean 1`] = `
<BasicDeploymentForm
appValues=""
deploymentEvent="install"
handleBasicFormParamChange={[Function]}
handleValuesChange={[Function]}
params={
Expand Down Expand Up @@ -689,6 +692,7 @@ exports[`renders a basic deployment with a generic boolean 1`] = `
exports[`renders a basic deployment with a generic number 1`] = `
<BasicDeploymentForm
appValues=""
deploymentEvent="install"
handleBasicFormParamChange={[Function]}
handleValuesChange={[Function]}
params={
Expand Down Expand Up @@ -764,6 +768,7 @@ exports[`renders a basic deployment with a generic number 1`] = `
exports[`renders a basic deployment with a generic string 1`] = `
<BasicDeploymentForm
appValues=""
deploymentEvent="install"
handleBasicFormParamChange={[Function]}
handleValuesChange={[Function]}
params={
Expand Down Expand Up @@ -839,6 +844,7 @@ exports[`renders a basic deployment with a generic string 1`] = `
exports[`renders a basic deployment with a integer disk size 1`] = `
<BasicDeploymentForm
appValues=""
deploymentEvent="install"
handleBasicFormParamChange={[Function]}
handleValuesChange={[Function]}
params={
Expand Down Expand Up @@ -1182,6 +1188,7 @@ exports[`renders a basic deployment with a integer disk size 1`] = `
exports[`renders a basic deployment with a number disk size 1`] = `
<BasicDeploymentForm
appValues=""
deploymentEvent="install"
handleBasicFormParamChange={[Function]}
handleValuesChange={[Function]}
params={
Expand Down Expand Up @@ -1525,6 +1532,7 @@ exports[`renders a basic deployment with a number disk size 1`] = `
exports[`renders a basic deployment with a password 1`] = `
<BasicDeploymentForm
appValues=""
deploymentEvent="install"
handleBasicFormParamChange={[Function]}
handleValuesChange={[Function]}
params={
Expand Down Expand Up @@ -1598,6 +1606,7 @@ exports[`renders a basic deployment with a password 1`] = `
exports[`renders a basic deployment with a username 1`] = `
<BasicDeploymentForm
appValues=""
deploymentEvent="install"
handleBasicFormParamChange={[Function]}
handleValuesChange={[Function]}
params={
Expand Down Expand Up @@ -1671,6 +1680,7 @@ exports[`renders a basic deployment with a username 1`] = `
exports[`renders a basic deployment with custom configuration 1`] = `
<BasicDeploymentForm
appValues=""
deploymentEvent="install"
handleBasicFormParamChange={[Function]}
handleValuesChange={[Function]}
params={
Expand Down Expand Up @@ -1749,6 +1759,7 @@ exports[`renders a basic deployment with custom configuration 1`] = `
exports[`renders a basic deployment with slider parameters 1`] = `
<BasicDeploymentForm
appValues=""
deploymentEvent="install"
handleBasicFormParamChange={[Function]}
handleValuesChange={[Function]}
params={
Expand Down Expand Up @@ -2102,6 +2113,7 @@ exports[`renders a basic deployment with slider parameters 1`] = `
exports[`renders a basic deployment with username, password, email and a generic string 1`] = `
<BasicDeploymentForm
appValues=""
deploymentEvent="install"
handleBasicFormParamChange={[Function]}
handleValuesChange={[Function]}
params={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import Differential from "./Differential";


const defaultProps = {
deploymentEvent: "install",
chartNamespace: "chart-namespace",
chartID: "foo",
chartVersion: "1.0.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as React from "react";
import { Tab, TabList, TabPanel, Tabs } from "react-tabs";

import { retrieveBasicFormParams, setValue } from "../../shared/schema";
import { IBasicFormParam, IChartState } from "../../shared/types";
import { DeploymentEvent, IBasicFormParam, IChartState } from "../../shared/types";
import * as url from "../../shared/url";
import { getValueFromEvent } from "../../shared/utils";
import ConfirmDialog from "../ConfirmDialog";
Expand All @@ -19,6 +19,7 @@ import Differential from "./Differential";
import "./Tabs.css";

export interface IDeploymentFormBodyProps {
deploymentEvent: DeploymentEvent;
chartNamespace: string;
chartID: string;
chartVersion: string;
Expand Down Expand Up @@ -146,10 +147,9 @@ class DeploymentFormBody extends React.Component<
// TODO(andres): This requires refactoring. Currently, the deploy and upgrade
// forms behave differently. In the deployment form, a change in the version
// changes the route but in the case of the upgrade it only changes the state
const isUpgradeForm = !!this.props.releaseVersion;
const { namespace, selected } = this.props;
const { namespace, selected, deploymentEvent } = this.props;

if (isUpgradeForm) {
if (deploymentEvent === "upgrade") {
const { chartID, chartNamespace, getChartVersion } = this.props;
getChartVersion(chartNamespace, chartID, e.currentTarget.value);
} else {
Expand Down Expand Up @@ -197,6 +197,7 @@ class DeploymentFormBody extends React.Component<
{this.shouldRenderBasicForm() && (
<TabPanel>
<BasicDeploymentForm
deploymentEvent={this.props.deploymentEvent}
params={this.state.basicFormParameters}
handleBasicFormParamChange={this.handleBasicFormParamChange}
appValues={this.props.appValues}
Expand Down
1 change: 1 addition & 0 deletions dashboard/src/components/UpgradeForm/UpgradeForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ class UpgradeForm extends React.Component<IUpgradeFormProps, IUpgradeFormState>
</div>
<div className="col-8">
<DeploymentFormBody
deploymentEvent="upgrade"
chartNamespace={this.props.repoNamespace}
chartID={chartID}
chartVersion={this.props.appCurrentVersion}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ exports[`renders the full UpgradeForm 1`] = `
chartID="my-repo/my-chart"
chartNamespace="kubeapps"
chartVersion="1.0.0"
deploymentEvent="upgrade"
getChartVersion={[Function]}
goBack={[Function]}
namespace="default"
Expand Down
8 changes: 6 additions & 2 deletions dashboard/src/shared/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ export class UnprocessableEntity extends CustomError {}

export class InternalServerError extends CustomError {}

export type DeploymentEvent = "install" | "upgrade";

export interface IRepo {
namespace: string;
name: string;
Expand Down Expand Up @@ -543,10 +545,12 @@ export interface IBasicFormParam {
enum?: string[];
hidden?:
| {
path: any;
event: DeploymentEvent;
path: string;
value: string;
conditions: Array<{
path: any;
event: DeploymentEvent;
path: string;
value: string;
}>;
operator: string;
Expand Down

0 comments on commit d083020

Please sign in to comment.