mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-23 12:45:38 +00:00
Feat/openapi sync beta tag (#7461)
* feat: introduce OpenAPI Sync beta feedback feature - Added a feedback section in the OpenAPISyncTab and ConnectSpecForm to encourage user input during the beta phase. - Styled the feedback message and button for better visibility. - Updated the beta features list to include OpenAPI Sync and adjusted related components to reflect its beta status with appropriate badges. - Enhanced the StatusBadge component to support a new 'xs' size for better integration in various UI elements. * feat: integrate OpenAPI Sync beta feature toggle - Updated the ImportCollectionLocation component to conditionally enable the "Check for Spec Updates" option based on the OpenAPI Sync beta feature status. - Modified default preferences to disable OpenAPI Sync by default, ensuring users are not prompted for updates unless explicitly enabled. * feat: enhance beta features integration in Preferences - Updated the BETA_FEATURES array to use constants from utils/beta-features for better maintainability. - Improved the handling of beta preferences by merging new preferences with existing ones, ensuring a smoother user experience when toggling features. * feat: enhance OpenAPI Sync polling with beta feature toggle - Integrated a beta feature toggle for OpenAPI Sync polling, allowing conditional activation based on user settings. - Updated the pollingEnabled logic to incorporate the new beta feature status, ensuring better control over sync behavior.
This commit is contained in:
committed by
lohit-bruno
parent
4d17809562
commit
663ece708e
@@ -124,6 +124,17 @@ const ConnectSpecForm = ({ sourceUrl, setSourceUrl, isLoading, error, setError,
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<p className="beta-feedback-inline">
|
||||
OpenAPI Sync is in Beta — we'd love to hear your feedback and suggestions.{' '}
|
||||
<button
|
||||
type="button"
|
||||
className="beta-feedback-link"
|
||||
onClick={() => window?.ipcRenderer?.openExternal('https://github.com/usebruno/bruno/discussions/7401')}
|
||||
>
|
||||
Share feedback
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -2278,6 +2278,26 @@ const StyledWrapper = styled.div`
|
||||
gap: 0.5rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.beta-feedback-inline {
|
||||
margin-top: 2rem;
|
||||
font-size: ${(props) => props.theme.font.size.xs};
|
||||
color: ${(props) => props.theme.colors.text.muted};
|
||||
|
||||
.beta-feedback-link {
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
color: ${(props) => props.theme.status.info.text};
|
||||
cursor: pointer;
|
||||
font-size: inherit;
|
||||
text-decoration: underline;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default StyledWrapper;
|
||||
|
||||
@@ -134,6 +134,16 @@ const OpenAPISyncTab = ({ collection }) => {
|
||||
fileNotFound={fileNotFound}
|
||||
onOpenSettings={() => setShowSettingsModal(true)}
|
||||
/>
|
||||
<p className="beta-feedback-inline">
|
||||
OpenAPI Sync is in Beta — we'd love to hear your feedback and suggestions.{' '}
|
||||
<button
|
||||
type="button"
|
||||
className="beta-feedback-link"
|
||||
onClick={() => window?.ipcRenderer?.openExternal('https://github.com/usebruno/bruno/discussions/7401')}
|
||||
>
|
||||
Share feedback
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -195,6 +205,7 @@ const OpenAPISyncTab = ({ collection }) => {
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
</div>
|
||||
|
||||
{showSettingsModal && (
|
||||
|
||||
@@ -8,17 +8,19 @@ import debounce from 'lodash/debounce';
|
||||
import toast from 'react-hot-toast';
|
||||
import { IconFlask } from '@tabler/icons';
|
||||
import get from 'lodash/get';
|
||||
import { BETA_FEATURES as BETA_FEATURE_IDS } from 'utils/beta-features';
|
||||
|
||||
/**
|
||||
* Add beta features here.
|
||||
* Example:
|
||||
* {
|
||||
* id: 'nodevm',
|
||||
* label: 'Node VM Runtime',
|
||||
* description: 'Enable Node VM runtime for JavaScript execution in Developer Mode'
|
||||
* }
|
||||
* UI metadata for beta features rendered in Preferences.
|
||||
* IDs must match keys from utils/beta-features.js BETA_FEATURES.
|
||||
*/
|
||||
const BETA_FEATURES = [];
|
||||
const BETA_FEATURES = [
|
||||
{
|
||||
id: BETA_FEATURE_IDS.OPENAPI_SYNC,
|
||||
label: 'OpenAPI Sync',
|
||||
description: 'Synchronize your Bruno collection with an OpenAPI specification. Detect drift, review changes, and sync with a single click.'
|
||||
}
|
||||
];
|
||||
|
||||
const Beta = ({ close }) => {
|
||||
const preferences = useSelector((state) => state.app.preferences);
|
||||
@@ -45,6 +47,7 @@ const Beta = ({ close }) => {
|
||||
const betaSchema = generateValidationSchema();
|
||||
|
||||
const formik = useFormik({
|
||||
enableReinitialize: true,
|
||||
initialValues: generateInitialValues(),
|
||||
validationSchema: betaSchema,
|
||||
onSubmit: async (values) => {
|
||||
@@ -61,7 +64,10 @@ const Beta = ({ close }) => {
|
||||
dispatch(
|
||||
savePreferences({
|
||||
...preferences,
|
||||
beta: newBetaPreferences
|
||||
beta: {
|
||||
...preferences.beta,
|
||||
...newBetaPreferences
|
||||
}
|
||||
})
|
||||
)
|
||||
.catch((err) => console.log(err) && toast.error('Failed to update beta preferences'));
|
||||
|
||||
@@ -32,6 +32,8 @@ import { getRevealInFolderLabel } from 'utils/common/platform';
|
||||
import classNames from 'classnames';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import { useTheme } from 'providers/Theme';
|
||||
import { useBetaFeature, BETA_FEATURES } from 'utils/beta-features';
|
||||
import StatusBadge from 'ui/StatusBadge/index';
|
||||
|
||||
const CollectionHeader = ({ collection, isScratchCollection }) => {
|
||||
const dispatch = useDispatch();
|
||||
@@ -42,6 +44,8 @@ const CollectionHeader = ({ collection, isScratchCollection }) => {
|
||||
|
||||
// Get the current active workspace
|
||||
const currentWorkspace = workspaces.find((w) => w.uid === activeWorkspaceUid);
|
||||
const gitRootPath = collection?.git?.gitRootPath;
|
||||
const isOpenAPISyncEnabled = useBetaFeature(BETA_FEATURES.OPENAPI_SYNC);
|
||||
|
||||
// Workspace rename state
|
||||
const [isRenamingWorkspace, setIsRenamingWorkspace] = useState(false);
|
||||
@@ -201,8 +205,8 @@ const CollectionHeader = ({ collection, isScratchCollection }) => {
|
||||
// Build overflow menu items for the "..." dropdown
|
||||
const overflowMenuItems = [
|
||||
{ id: 'variables', label: 'Variables', leftSection: IconEye, onClick: viewVariables },
|
||||
...(!hasOpenApiSyncConfigured
|
||||
? [{ id: 'openapi-sync', label: 'OpenAPI', leftSection: OpenAPISyncIcon, onClick: viewOpenApiSync }]
|
||||
...(isOpenAPISyncEnabled && !hasOpenApiSyncConfigured
|
||||
? [{ id: 'openapi-sync', label: 'OpenAPI', leftSection: OpenAPISyncIcon, rightSection: <StatusBadge status="info" size="xs">Beta</StatusBadge>, onClick: viewOpenApiSync }]
|
||||
: []),
|
||||
{ id: 'collection-settings', label: 'Collection Settings', leftSection: IconSettings, onClick: viewCollectionSettings }
|
||||
];
|
||||
@@ -461,8 +465,8 @@ const CollectionHeader = ({ collection, isScratchCollection }) => {
|
||||
{/* Right side: Actions (only for regular collections) */}
|
||||
{!isScratchCollection && (
|
||||
<div className="flex flex-grow gap-1.5 items-center justify-end">
|
||||
{/* OpenAPI Sync - standalone only when configured */}
|
||||
{hasOpenApiSyncConfigured && (
|
||||
{/* OpenAPI Sync - standalone only when configured and beta enabled */}
|
||||
{isOpenAPISyncEnabled && hasOpenApiSyncConfigured && (
|
||||
<ToolHint
|
||||
text={hasOpenApiError ? 'OpenAPI Error' : hasOpenApiUpdates ? 'OpenAPI Updates Available' : 'OpenAPI'}
|
||||
toolhintId="OpenApiSyncToolhintId"
|
||||
|
||||
@@ -2,6 +2,7 @@ import React from 'react';
|
||||
import GradientCloseButton from './GradientCloseButton';
|
||||
import { IconVariable, IconSettings, IconRun, IconFolder, IconDatabase, IconWorld, IconHome, IconFileCode } from '@tabler/icons';
|
||||
import OpenAPISyncIcon from 'components/Icons/OpenAPISync';
|
||||
import StatusBadge from 'ui/StatusBadge/index';
|
||||
|
||||
const SpecialTab = ({ handleCloseClick, type, tabName, handleDoubleClick, hasDraft }) => {
|
||||
const getTabInfo = (type, tabName) => {
|
||||
@@ -90,7 +91,8 @@ const SpecialTab = ({ handleCloseClick, type, tabName, handleDoubleClick, hasDra
|
||||
return (
|
||||
<>
|
||||
<OpenAPISyncIcon size={14} className="special-tab-icon flex-shrink-0" />
|
||||
<span className="ml-1 tab-name">OpenAPI</span>
|
||||
<span className="ml-1 tab-name mr-1">OpenAPI</span>
|
||||
<StatusBadge status="info" size="xs">Beta</StatusBadge>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -48,6 +48,8 @@ import { getRevealInFolderLabel } from 'utils/common/platform';
|
||||
import { openDevtoolsAndSwitchToTerminal } from 'utils/terminal';
|
||||
import ActionIcon from 'ui/ActionIcon';
|
||||
import MenuDropdown from 'ui/MenuDropdown';
|
||||
import StatusBadge from 'ui/StatusBadge';
|
||||
import { useBetaFeature, BETA_FEATURES } from 'utils/beta-features';
|
||||
import { useSidebarAccordion } from 'components/Sidebar/SidebarAccordionContext';
|
||||
import { createEmptyStateMenuItems } from 'utils/collections/emptyStateRequest';
|
||||
|
||||
@@ -56,6 +58,7 @@ import { createEmptyStateMenuItems } from 'utils/collections/emptyStateRequest';
|
||||
const EMPTY_STATE_DELAY_MS = 300;
|
||||
|
||||
const Collection = ({ collection, searchText }) => {
|
||||
const isOpenAPISyncEnabled = useBetaFeature(BETA_FEATURES.OPENAPI_SYNC);
|
||||
const { dropdownContainerRef } = useSidebarAccordion();
|
||||
const [showNewFolderModal, setShowNewFolderModal] = useState(false);
|
||||
const [showNewRequestModal, setShowNewRequestModal] = useState(false);
|
||||
@@ -382,12 +385,13 @@ const Collection = ({ collection, searchText }) => {
|
||||
setShowCloneCollectionModalOpen(true);
|
||||
}
|
||||
},
|
||||
{
|
||||
...(isOpenAPISyncEnabled ? [{
|
||||
id: 'sync-openapi',
|
||||
leftSection: OpenAPISyncIcon,
|
||||
label: 'OpenAPI',
|
||||
rightSection: <StatusBadge status="info" size="xs">Beta</StatusBadge>,
|
||||
onClick: openOpenAPISyncTab
|
||||
},
|
||||
}] : []),
|
||||
...(hasCopiedItems
|
||||
? [
|
||||
{
|
||||
|
||||
@@ -13,6 +13,7 @@ import { processBrunoCollection } from 'utils/importers/bruno-collection';
|
||||
import { processOpenCollection } from 'utils/importers/opencollection';
|
||||
import { wsdlToBruno } from '@usebruno/converters';
|
||||
import { toastError } from 'utils/common/error';
|
||||
import { useBetaFeature, BETA_FEATURES } from 'utils/beta-features';
|
||||
import Modal from 'components/Modal';
|
||||
import Help from 'components/Help';
|
||||
import Dropdown from 'components/Dropdown';
|
||||
@@ -101,13 +102,14 @@ const ImportCollectionLocation = ({ onClose, handleSubmit, rawData, format, sour
|
||||
const dispatch = useDispatch();
|
||||
const [groupingType, setGroupingType] = useState('tags');
|
||||
const [collectionFormat, setCollectionFormat] = useState(DEFAULT_COLLECTION_FORMAT);
|
||||
const [enableCheckForSpecUpdates, setEnableCheckForSpecUpdates] = useState(true);
|
||||
const isOpenAPISyncEnabled = useBetaFeature(BETA_FEATURES.OPENAPI_SYNC);
|
||||
const [enableCheckForSpecUpdates, setEnableCheckForSpecUpdates] = useState(isOpenAPISyncEnabled);
|
||||
const dropdownTippyRef = useRef();
|
||||
const isOpenApi = format === 'openapi';
|
||||
const isZipImport = format === 'bruno-zip';
|
||||
const isOpenApiFromUrl = isOpenApi && !!sourceUrl && !filePath;
|
||||
const isOpenApiFromFile = isOpenApi && !!filePath && !sourceUrl;
|
||||
const showCheckForSpecUpdatesOption = isOpenApiFromUrl || isOpenApiFromFile;
|
||||
const showCheckForSpecUpdatesOption = isOpenAPISyncEnabled && (isOpenApiFromUrl || isOpenApiFromFile);
|
||||
|
||||
const { workspaces, activeWorkspaceUid } = useSelector((state) => state.workspaces);
|
||||
const preferences = useSelector((state) => state.app.preferences);
|
||||
|
||||
@@ -2,14 +2,16 @@ import { useEffect, useMemo, useRef } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { checkActiveWorkspaceCollectionsForUpdates } from 'providers/ReduxStore/slices/openapi-sync';
|
||||
import { normalizePath } from 'utils/common/path';
|
||||
import { useBetaFeature, BETA_FEATURES } from 'utils/beta-features';
|
||||
|
||||
const POLL_INTERVAL = 5 * 60 * 1000; // 5 minutes
|
||||
|
||||
const useOpenAPISyncPolling = () => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
// Global toggle for pausing all OpenAPI sync polling (defaults to true, not yet wired to any UI)
|
||||
const pollingEnabled = useSelector((state) => state.openapiSync?.pollingEnabled ?? true);
|
||||
const isOpenAPISyncEnabled = useBetaFeature(BETA_FEATURES.OPENAPI_SYNC);
|
||||
// Global toggle for pausing all OpenAPI sync polling
|
||||
const pollingEnabled = useSelector((state) => state.openapiSync?.pollingEnabled ?? true) && isOpenAPISyncEnabled;
|
||||
const collections = useSelector((state) => state.collections?.collections || []);
|
||||
const { workspaces, activeWorkspaceUid } = useSelector((state) => state.workspaces);
|
||||
const activeWorkspace = workspaces.find((w) => w.uid === activeWorkspaceUid);
|
||||
|
||||
@@ -5,7 +5,8 @@ import { useSelector } from 'react-redux';
|
||||
* Contains all available beta feature keys
|
||||
*/
|
||||
export const BETA_FEATURES = Object.freeze({
|
||||
NODE_VM: 'nodevm'
|
||||
NODE_VM: 'nodevm',
|
||||
OPENAPI_SYNC: 'openapi-sync'
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@@ -45,7 +45,9 @@ const defaultPreferences = {
|
||||
layout: {
|
||||
responsePaneOrientation: 'horizontal'
|
||||
},
|
||||
beta: {},
|
||||
beta: {
|
||||
'openapi-sync': false
|
||||
},
|
||||
onboarding: {
|
||||
hasLaunchedBefore: false,
|
||||
hasSeenWelcomeModal: true
|
||||
@@ -154,6 +156,7 @@ const preferencesSchema = Yup.object().shape({
|
||||
responsePaneOrientation: Yup.string().oneOf(['horizontal', 'vertical'])
|
||||
}),
|
||||
beta: Yup.object({
|
||||
'openapi-sync': Yup.boolean()
|
||||
}),
|
||||
onboarding: Yup.object({
|
||||
hasLaunchedBefore: Yup.boolean(),
|
||||
|
||||
Reference in New Issue
Block a user