From 75b3a6bea466efca7a82f0b83d8c3f485f7ceba6 Mon Sep 17 00:00:00 2001 From: Alex <33497058+bexsoft@users.noreply.github.com> Date: Tue, 11 Apr 2023 03:01:03 -0600 Subject: [PATCH] Fixed Object Version selector visibility in Add Lifecycle Rule modal (#2769) - Fixed Object Version selector visibility in Add Lifecycle Rule modal - Changed definition in swagger file to match camelCase standard - Added a playwright test case to avoid this issue in the future Signed-off-by: Benjamin Perez --- models/bucket_versioning_response.go | 24 ++++----- portal-ui/e2e/lifecycle.spec.ts | 52 +++++++++++++++++++ portal-ui/src/api/consoleApi.ts | 8 +-- .../BucketDetails/AddLifecycleModal.tsx | 16 +++--- .../BucketDetails/BucketSummaryPanel.tsx | 4 +- .../EditLifecycleConfiguration.tsx | 3 +- .../BucketDetails/EnableVersioningModal.tsx | 2 +- .../ListBuckets/BulkLifecycleModal.tsx | 3 +- .../ListObjects/DeleteMultipleObjects.tsx | 2 +- .../Objects/ListObjects/DeleteObject.tsx | 2 +- .../Console/Buckets/VersioningInfo.tsx | 8 +-- .../src/screens/Console/Buckets/types.tsx | 15 +++--- restapi/embedded_spec.go | 28 +++++----- swagger.yml | 8 +-- 14 files changed, 111 insertions(+), 64 deletions(-) create mode 100644 portal-ui/e2e/lifecycle.spec.ts diff --git a/models/bucket_versioning_response.go b/models/bucket_versioning_response.go index 25c4fcd28..5f41d26d5 100644 --- a/models/bucket_versioning_response.go +++ b/models/bucket_versioning_response.go @@ -36,17 +36,17 @@ import ( // swagger:model bucketVersioningResponse type BucketVersioningResponse struct { - // exclude folders - ExcludeFolders bool `json:"ExcludeFolders,omitempty"` - - // excluded prefixes - ExcludedPrefixes []*BucketVersioningResponseExcludedPrefixesItems0 `json:"ExcludedPrefixes"` - // m f a delete MFADelete string `json:"MFADelete,omitempty"` + // exclude folders + ExcludeFolders bool `json:"excludeFolders,omitempty"` + + // excluded prefixes + ExcludedPrefixes []*BucketVersioningResponseExcludedPrefixesItems0 `json:"excludedPrefixes"` + // status - Status string `json:"Status,omitempty"` + Status string `json:"status,omitempty"` } // Validate validates this bucket versioning response @@ -76,9 +76,9 @@ func (m *BucketVersioningResponse) validateExcludedPrefixes(formats strfmt.Regis if m.ExcludedPrefixes[i] != nil { if err := m.ExcludedPrefixes[i].Validate(formats); err != nil { if ve, ok := err.(*errors.Validation); ok { - return ve.ValidateName("ExcludedPrefixes" + "." + strconv.Itoa(i)) + return ve.ValidateName("excludedPrefixes" + "." + strconv.Itoa(i)) } else if ce, ok := err.(*errors.CompositeError); ok { - return ce.ValidateName("ExcludedPrefixes" + "." + strconv.Itoa(i)) + return ce.ValidateName("excludedPrefixes" + "." + strconv.Itoa(i)) } return err } @@ -110,9 +110,9 @@ func (m *BucketVersioningResponse) contextValidateExcludedPrefixes(ctx context.C if m.ExcludedPrefixes[i] != nil { if err := m.ExcludedPrefixes[i].ContextValidate(ctx, formats); err != nil { if ve, ok := err.(*errors.Validation); ok { - return ve.ValidateName("ExcludedPrefixes" + "." + strconv.Itoa(i)) + return ve.ValidateName("excludedPrefixes" + "." + strconv.Itoa(i)) } else if ce, ok := err.(*errors.CompositeError); ok { - return ce.ValidateName("ExcludedPrefixes" + "." + strconv.Itoa(i)) + return ce.ValidateName("excludedPrefixes" + "." + strconv.Itoa(i)) } return err } @@ -147,7 +147,7 @@ func (m *BucketVersioningResponse) UnmarshalBinary(b []byte) error { type BucketVersioningResponseExcludedPrefixesItems0 struct { // prefix - Prefix string `json:"Prefix,omitempty"` + Prefix string `json:"prefix,omitempty"` } // Validate validates this bucket versioning response excluded prefixes items0 diff --git a/portal-ui/e2e/lifecycle.spec.ts b/portal-ui/e2e/lifecycle.spec.ts new file mode 100644 index 000000000..a31368820 --- /dev/null +++ b/portal-ui/e2e/lifecycle.spec.ts @@ -0,0 +1,52 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2023 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import { expect } from "@playwright/test"; +import { test } from "./fixtures/baseFixture"; +import { minioadminFile } from "./consts"; + +test.use({ storageState: minioadminFile }); + +test.beforeEach(async ({ page }) => { + await page.goto("http://localhost:5005/buckets"); +}); + +test("Test if Object Version selector is present in Lifecycle rule modal", async ({ + page, +}) => { + await page.locator("#create-bucket").click(); + await page.getByLabel("Bucket Name*").click(); + await page.getByLabel("Bucket Name*").fill("versioned-bucket"); + await page.locator("#versioned").check(); + await page.getByRole("button", { name: "Create Bucket" }).click(); + await page.locator("#manageBucket-versioned-bucket").click(); + await page.getByRole("tab", { name: "Lifecycle" }).click(); + await page.getByRole("button", { name: "Add Lifecycle Rule" }).click(); + await expect(await page.locator("#object_version")).toBeTruthy(); +}); + +test("Test if Object Version selector is not present when bucket is not versioned", async ({ + page, +}) => { + await page.locator("#create-bucket").click(); + await page.getByLabel("Bucket Name*").click(); + await page.getByLabel("Bucket Name*").fill("non-versioned-bucket"); + await page.getByRole("button", { name: "Create Bucket" }).click(); + await page.locator("#manageBucket-non-versioned-bucket").click(); + await page.getByRole("tab", { name: "Lifecycle" }).click(); + await page.getByRole("button", { name: "Add Lifecycle Rule" }).click(); + await expect(await page.locator("#object_version").count()).toEqual(0); +}); diff --git a/portal-ui/src/api/consoleApi.ts b/portal-ui/src/api/consoleApi.ts index f42e2725e..a982f04d7 100644 --- a/portal-ui/src/api/consoleApi.ts +++ b/portal-ui/src/api/consoleApi.ts @@ -791,12 +791,12 @@ export interface ListRemoteBucketsResponse { } export interface BucketVersioningResponse { - Status?: string; + status?: string; MFADelete?: string; - ExcludedPrefixes?: { - Prefix?: string; + excludedPrefixes?: { + prefix?: string; }[]; - ExcludeFolders?: boolean; + excludeFolders?: boolean; } export interface SetBucketVersioning { diff --git a/portal-ui/src/screens/Console/Buckets/BucketDetails/AddLifecycleModal.tsx b/portal-ui/src/screens/Console/Buckets/BucketDetails/AddLifecycleModal.tsx index 45cbb7e66..ec1e6f5ae 100644 --- a/portal-ui/src/screens/Console/Buckets/BucketDetails/AddLifecycleModal.tsx +++ b/portal-ui/src/screens/Console/Buckets/BucketDetails/AddLifecycleModal.tsx @@ -49,10 +49,10 @@ import { spacingUtils, } from "../../Common/FormComponents/common/styleLibrary"; import InputUnitMenu from "../../Common/FormComponents/InputUnitMenu/InputUnitMenu"; -import { BucketVersioning } from "../types"; import FormSwitchWrapper from "../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper"; import { selDistSet, setModalErrorSnackMessage } from "../../../../systemSlice"; import { useAppDispatch } from "../../../../store"; +import { BucketVersioningInfo, ITiersDropDown } from "../types"; interface IReplicationModal { open: boolean; @@ -61,11 +61,6 @@ interface IReplicationModal { bucketName: string; } -export interface ITiersDropDown { - label: string; - value: string; -} - const styles = (theme: Theme) => createStyles({ formFieldRowFilter: { @@ -88,7 +83,8 @@ const AddLifecycleModal = ({ const [loadingTiers, setLoadingTiers] = useState(true); const [tiersList, setTiersList] = useState([]); const [addLoading, setAddLoading] = useState(false); - const [isVersioned, setIsVersioned] = useState(false); + const [versioningInfo, setVersioningInfo] = + useState(null); const [prefix, setPrefix] = useState(""); const [tags, setTags] = useState(""); const [storageClass, setStorageClass] = useState(""); @@ -146,8 +142,8 @@ const AddLifecycleModal = ({ if (loadingVersioning && distributedSetup) { api .invoke("GET", `/api/v1/buckets/${bucketName}/versioning`) - .then((res: BucketVersioning) => { - setIsVersioned(res.is_versioned); + .then((res: BucketVersioningInfo) => { + setVersioningInfo(res); setLoadingVersioning(false); }) .catch((err: ErrorResponseHandler) => { @@ -258,7 +254,7 @@ const AddLifecycleModal = ({ ]} /> - {isVersioned && ( + {versioningInfo?.status === "Enabled" && ( { } }; - let versioningStatus = versioningInfo?.Status; + let versioningStatus = versioningInfo?.status; let versioningText = "Unversioned (Default)"; if (versioningStatus === "Enabled") { versioningText = "Versioned"; @@ -604,7 +604,7 @@ const BucketSummary = ({ classes }: IBucketSummaryProps) => { isLoading={loadingVersioning} /> - {versioningInfo?.Status === "Enabled" ? ( + {versioningInfo?.status === "Enabled" ? ( ) : null} diff --git a/portal-ui/src/screens/Console/Buckets/BucketDetails/EditLifecycleConfiguration.tsx b/portal-ui/src/screens/Console/Buckets/BucketDetails/EditLifecycleConfiguration.tsx index 5c222be04..d7914aaf3 100644 --- a/portal-ui/src/screens/Console/Buckets/BucketDetails/EditLifecycleConfiguration.tsx +++ b/portal-ui/src/screens/Console/Buckets/BucketDetails/EditLifecycleConfiguration.tsx @@ -36,9 +36,8 @@ import { spacingUtils, } from "../../Common/FormComponents/common/styleLibrary"; -import { LifeCycleItem } from "../types"; +import { ITiersDropDown, LifeCycleItem } from "../types"; import { ErrorResponseHandler } from "../../../../common/types"; -import { ITiersDropDown } from "./AddLifecycleModal"; import { ITierElement, ITierResponse, diff --git a/portal-ui/src/screens/Console/Buckets/BucketDetails/EnableVersioningModal.tsx b/portal-ui/src/screens/Console/Buckets/BucketDetails/EnableVersioningModal.tsx index 872d1c4e3..5db633944 100644 --- a/portal-ui/src/screens/Console/Buckets/BucketDetails/EnableVersioningModal.tsx +++ b/portal-ui/src/screens/Console/Buckets/BucketDetails/EnableVersioningModal.tsx @@ -40,7 +40,7 @@ const EnableVersioningModal = ({ selectedBucket, versioningInfo = {}, }: IVersioningEventProps) => { - const isVersioningEnabled = versioningInfo.Status === "Enabled"; + const isVersioningEnabled = versioningInfo.status === "Enabled"; const dispatch = useAppDispatch(); const [versioningLoading, setVersioningLoading] = useState(false); diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/BulkLifecycleModal.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/BulkLifecycleModal.tsx index f62d8b353..f1fe3fe8d 100644 --- a/portal-ui/src/screens/Console/Buckets/ListBuckets/BulkLifecycleModal.tsx +++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/BulkLifecycleModal.tsx @@ -40,12 +40,11 @@ import SelectWrapper from "../../Common/FormComponents/SelectWrapper/SelectWrapp import RadioGroupSelector from "../../Common/FormComponents/RadioGroupSelector/RadioGroupSelector"; import { ErrorResponseHandler } from "../../../../common/types"; import QueryMultiSelector from "../../Common/FormComponents/QueryMultiSelector/QueryMultiSelector"; -import { ITiersDropDown } from "../BucketDetails/AddLifecycleModal"; import { ITierElement, ITierResponse, } from "../../Configurations/TiersConfiguration/types"; -import { MultiBucketResult } from "../types"; +import { ITiersDropDown, MultiBucketResult } from "../types"; import { setModalErrorSnackMessage } from "../../../../systemSlice"; import { useAppDispatch } from "../../../../store"; diff --git a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/DeleteMultipleObjects.tsx b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/DeleteMultipleObjects.tsx index 794980038..5c985802d 100644 --- a/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/DeleteMultipleObjects.tsx +++ b/portal-ui/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/DeleteMultipleObjects.tsx @@ -101,7 +101,7 @@ const DeleteObject = ({ }; const isVersionedDelete = - versioning?.Status === "Enabled" || versioning?.Status === "Suspended"; + versioning?.status === "Enabled" || versioning?.status === "Suspended"; return (
- {isVersionedMode(versioningInfo?.Status) && + {isVersionedMode(versioningInfo?.status) && selectedVersion === "" && ( - {versioningState.ExcludeFolders ? ( + {versioningState.excludeFolders ? ( ) : ( @@ -35,7 +35,7 @@ const VersioningInfo = ({ /> ) : null} - {versioningState.ExcludedPrefixes?.length ? ( + {versioningState.excludedPrefixes?.length ? ( - {versioningState.ExcludedPrefixes?.map((it) => ( + {versioningState.excludedPrefixes?.map((it) => (
{it.Prefix}
diff --git a/portal-ui/src/screens/Console/Buckets/types.tsx b/portal-ui/src/screens/Console/Buckets/types.tsx index d9bbf5cb1..628cd4b2c 100644 --- a/portal-ui/src/screens/Console/Buckets/types.tsx +++ b/portal-ui/src/screens/Console/Buckets/types.tsx @@ -52,15 +52,11 @@ export interface ArnList { arns: string[]; } -export interface BucketVersioning { - is_versioned: boolean; -} - export interface BucketVersioningInfo { - ExcludeFolders?: boolean; - ExcludedPrefixes?: Record<"Prefix", string>[]; + excludeFolders?: boolean; + excludedPrefixes?: Record<"Prefix", string>[]; MFADelete?: string; - Status?: "Enabled" | "Suspended" | ""; + status?: "Enabled" | "Suspended" | ""; } export interface BucketObjectLocking { @@ -147,3 +143,8 @@ export interface MultiBucketResult { export interface MultiBucketResult { results: MultiBucketResult[]; } + +export interface ITiersDropDown { + label: string; + value: string; +} diff --git a/restapi/embedded_spec.go b/restapi/embedded_spec.go index 1c074f546..93ac0fc64 100644 --- a/restapi/embedded_spec.go +++ b/restapi/embedded_spec.go @@ -5948,24 +5948,24 @@ func init() { "bucketVersioningResponse": { "type": "object", "properties": { - "ExcludeFolders": { + "MFADelete": { + "type": "string" + }, + "excludeFolders": { "type": "boolean" }, - "ExcludedPrefixes": { + "excludedPrefixes": { "type": "array", "items": { "type": "object", "properties": { - "Prefix": { + "prefix": { "type": "string" } } } }, - "MFADelete": { - "type": "string" - }, - "Status": { + "status": { "type": "string" } } @@ -14384,7 +14384,7 @@ func init() { "BucketVersioningResponseExcludedPrefixesItems0": { "type": "object", "properties": { - "Prefix": { + "prefix": { "type": "string" } } @@ -15084,19 +15084,19 @@ func init() { "bucketVersioningResponse": { "type": "object", "properties": { - "ExcludeFolders": { + "MFADelete": { + "type": "string" + }, + "excludeFolders": { "type": "boolean" }, - "ExcludedPrefixes": { + "excludedPrefixes": { "type": "array", "items": { "$ref": "#/definitions/BucketVersioningResponseExcludedPrefixesItems0" } }, - "MFADelete": { - "type": "string" - }, - "Status": { + "status": { "type": "string" } } diff --git a/swagger.yml b/swagger.yml index 29669e997..ec758b778 100644 --- a/swagger.yml +++ b/swagger.yml @@ -4944,18 +4944,18 @@ definitions: bucketVersioningResponse: type: object properties: - Status: + status: type: string MFADelete: type: string - ExcludedPrefixes: + excludedPrefixes: type: array items: type: object properties: - Prefix: + prefix: type: string - ExcludeFolders: + excludeFolders: type: boolean setBucketVersioning: