-
Notifications
You must be signed in to change notification settings - Fork 2.2k
fix(openapi-sync): simplify IPC calls, fix state priorities, and improve stored spec missing UX #7489
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(openapi-sync): simplify IPC calls, fix state priorities, and improve stored spec missing UX #7489
Changes from 2 commits
034467c
e57a395
b07283b
e62a2bc
642efce
7aa39b3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,7 +21,7 @@ const SUMMARY_CARDS = [ | |
| key: 'inSync', | ||
| label: 'In Sync with Spec', | ||
| color: 'green', | ||
| tooltip: 'Endpoints that currently match the latest spec' | ||
| tooltip: 'Endpoints that currently match the latest spec from the source' | ||
| }, | ||
| { | ||
| key: 'changed', | ||
|
|
@@ -46,10 +46,8 @@ const OverviewSection = ({ collection, storedSpec, collectionDrift, specDrift, r | |
| const specMeta = useSelector(selectStoredSpecMeta(collection.uid)); | ||
| const activeError = error || reduxError; | ||
|
|
||
| const version = storedSpec?.info?.version ?? specMeta?.version; | ||
| const newVersion = specDrift?.newVersion; | ||
| const hasVersionChange = version && newVersion && version !== newVersion; | ||
| const endpointCount = countEndpoints(storedSpec) ?? specMeta?.endpointCount ?? null; | ||
| const version = specMeta?.version; | ||
| const endpointCount = specMeta?.endpointCount ?? null; | ||
| const lastSyncDate = openApiSyncConfig?.lastSyncDate; | ||
| const groupBy = openApiSyncConfig?.groupBy || 'tags'; | ||
| const autoCheckEnabled = openApiSyncConfig?.autoCheck !== false; | ||
|
|
@@ -90,7 +88,7 @@ const OverviewSection = ({ collection, storedSpec, collectionDrift, specDrift, r | |
| }; | ||
|
|
||
| const details = [ | ||
| { label: 'Spec Version', value: hasVersionChange ? `v${version} → v${newVersion}` : version ? `v${version}` : '–' }, | ||
| { label: 'Spec Version', value: version ? `v${version}` : '–' }, | ||
| { label: 'Endpoints in Spec', value: endpointCount != null ? endpointCount : '–' }, | ||
| { label: 'Last Synced At', value: lastSyncDate ? moment(lastSyncDate).fromNow() : '–', tooltip: lastSyncDate ? moment(lastSyncDate).format('MMMM D, YYYY [at] h:mm A') : undefined }, | ||
| { label: 'Folder Grouping', value: capitalize(groupBy) }, | ||
|
|
@@ -121,31 +119,31 @@ const OverviewSection = ({ collection, storedSpec, collectionDrift, specDrift, r | |
| buttons: ['review'] | ||
| }; | ||
| } | ||
| if (specDrift?.storedSpecMissing && lastSyncDate) { | ||
| return { | ||
| variant: 'warning', | ||
| title: 'Last synced spec not found', | ||
| subtitle: 'The last synced spec is missing in the storage. Restore the latest spec from the source to track future changes.', | ||
| buttons: ['restore'] | ||
| }; | ||
| } | ||
| if (!hasDriftData) return null; | ||
| if (hasSpecUpdates && hasCollectionChanges) { | ||
| return { | ||
| variant: 'warning', | ||
| title: `The API spec has new updates${versionInfo} and the collection has changes`, | ||
| title: `OpenAPI spec has new updates${versionInfo} and the collection has changes`, | ||
| subtitle: 'New or changed requests are available. Some collection changes may be overwritten.', | ||
| buttons: ['sync', 'changes'] | ||
| }; | ||
| } | ||
| if (hasSpecUpdates) { | ||
| return { | ||
| variant: 'warning', | ||
| title: `The API spec has new updates${versionInfo}`, | ||
| title: `OpenAPI spec has new updates${versionInfo}`, | ||
| subtitle: 'New or changed requests are available.', | ||
| buttons: ['sync'] | ||
| }; | ||
| } | ||
| if (specDrift?.storedSpecMissing && lastSyncDate) { | ||
| return { | ||
| variant: 'warning', | ||
| title: 'Last synced spec not found', | ||
| subtitle: 'The last synced spec is missing in the storage. Restore the latest spec from the source to track collection changes.', | ||
| buttons: ['restore'] | ||
| }; | ||
| } | ||
|
Comment on lines
+138
to
+145
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Delete-only spec changes still fall through to the “stored spec missing” banner. This new branch relies on 🤖 Prompt for AI Agents |
||
| if (!hasDriftData) return null; | ||
| if (hasCollectionChanges) { | ||
| return { | ||
| variant: 'muted', | ||
|
|
@@ -197,7 +195,7 @@ const OverviewSection = ({ collection, storedSpec, collectionDrift, specDrift, r | |
| )} | ||
| {bannerState.buttons.includes('restore') && ( | ||
| <Button size="sm" onClick={() => onTabSelect('spec-updates')}> | ||
| Restore Spec File | ||
| View Details | ||
| </Button> | ||
| )} | ||
| {bannerState.buttons.includes('spec-details') && ( | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,7 +3,8 @@ import { useSelector } from 'react-redux'; | |
| import { | ||
| IconCheck, | ||
| IconRefresh, | ||
| IconAlertTriangle | ||
| IconAlertTriangle, | ||
| IconClock | ||
| } from '@tabler/icons'; | ||
| import Button from 'ui/Button'; | ||
| import StatusBadge from 'ui/StatusBadge'; | ||
|
|
@@ -23,14 +24,20 @@ const SpecStatusSection = ({ | |
|
|
||
| const { | ||
| isSyncing, showConfirmModal, confirmGroups, | ||
| handleSyncNow, handleApplySync, cancelConfirmModal, handleConfirmModalSync | ||
| handleSyncNow, handleRestoreSpec, handleApplySync, cancelConfirmModal, handleConfirmModalSync | ||
| } = useSyncFlow({ | ||
| collection, specDrift, remoteDrift, collectionDrift, | ||
| sourceUrl, setError, checkForUpdates: onCheck | ||
| setError, checkForUpdates: onCheck | ||
| }); | ||
|
|
||
| const lastSyncedAt = openApiSyncConfig?.lastSyncDate; | ||
|
|
||
| const hasRemoteUpdates = remoteDrift && ( | ||
abhishek-bruno marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| (remoteDrift.missing?.length || 0) | ||
| + (remoteDrift.modified?.length || 0) | ||
| + (remoteDrift.localOnly?.length || 0) | ||
| ) > 0; | ||
|
Comment on lines
+35
to
+39
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don’t derive “spec updated” from
💡 Suggested change- const hasRemoteUpdates = remoteDrift && (
- (remoteDrift.missing?.length || 0)
- + (remoteDrift.modified?.length || 0)
- + (remoteDrift.localOnly?.length || 0)
- ) > 0;
+ const hasRemoteUpdates = specDrift?.hasChanges === true;Also applies to: 51-56, 120-125 🤖 Prompt for AI Agents |
||
|
|
||
| const bannerState = useMemo(() => { | ||
| if (fileNotFound) { | ||
| return { variant: 'danger', message: `Source file not found at ${sourceUrl}`, actions: ['open-settings'] }; | ||
|
|
@@ -41,13 +48,12 @@ const SpecStatusSection = ({ | |
| if (!specDrift) { | ||
| return null; | ||
| } | ||
| if (specDrift.storedSpecMissing) { | ||
| if (!lastSyncedAt) { | ||
| return { variant: 'warning', message: 'Initial sync required — your collection differs from the spec', actions: [] }; | ||
| } | ||
| return { variant: 'warning', message: 'Last synced spec not found — Restore the latest spec from the source to track future changes.', actions: [] }; | ||
| if (specDrift.storedSpecMissing && !hasRemoteUpdates) { | ||
| return null; | ||
| } | ||
| const hasEndpointUpdates = (specDrift.added?.length || 0) + (specDrift.modified?.length || 0) + (specDrift.removed?.length || 0) > 0; | ||
| const hasEndpointUpdates = specDrift.storedSpecMissing | ||
| ? hasRemoteUpdates | ||
| : (specDrift.added?.length || 0) + (specDrift.modified?.length || 0) + (specDrift.removed?.length || 0) > 0; | ||
| if (hasEndpointUpdates) { | ||
| const versionInfo = (specDrift.storedVersion && specDrift.newVersion && specDrift.storedVersion !== specDrift.newVersion) | ||
| ? ` (v${specDrift.storedVersion} → v${specDrift.newVersion})` | ||
|
|
@@ -63,7 +69,7 @@ const SpecStatusSection = ({ | |
| // lastChecked: lastCheckedAt ? moment(lastCheckedAt).fromNow() : 'just now' | ||
| // }; | ||
| return null; | ||
| }, [fileNotFound, error, sourceUrl, specDrift, lastSyncedAt, storedSpec, lastCheckedAt]); | ||
| }, [fileNotFound, error, sourceUrl, specDrift, lastSyncedAt, storedSpec, lastCheckedAt, hasRemoteUpdates]); | ||
| return ( | ||
| <> | ||
| {bannerState && ( | ||
|
|
@@ -93,7 +99,7 @@ const SpecStatusSection = ({ | |
| </div> | ||
| <div className="banner-actions"> | ||
| {bannerState.actions.includes('quick-sync') && ( | ||
| <Button size="xs" onClick={handleSyncNow}>Restore Spec File</Button> | ||
| <Button size="xs" onClick={handleRestoreSpec}>Restore Spec File</Button> | ||
| )} | ||
| {bannerState.actions.includes('open-settings') && ( | ||
| <Button variant="ghost" size="sm" onClick={onOpenSettings}> | ||
|
|
@@ -111,12 +117,12 @@ const SpecStatusSection = ({ | |
| <h4>Unable to check for updates</h4> | ||
| <p>Fix the connection issue above and check again.</p> | ||
| </div> | ||
| ) : specDrift?.storedSpecMissing && openApiSyncConfig?.lastSyncDate ? ( | ||
| ) : specDrift?.storedSpecMissing && openApiSyncConfig?.lastSyncDate && !hasRemoteUpdates ? ( | ||
| <div className="sync-review-empty-state mt-5"> | ||
| <IconRefresh size={40} className="empty-state-icon" /> | ||
| <h4>Last Synced Spec not found in storage</h4> | ||
| <p>The last synced spec is missing in the storage. Restore the latest spec from the source to track future changes.</p> | ||
| <Button className="mt-4" color="warning" onClick={handleSyncNow} loading={isSyncing}> | ||
| <IconCheck size={40} className="empty-state-icon" /> | ||
| <h4>No updates from the spec</h4> | ||
| <p>The spec endpoints have not been updated since the last sync. You can restore the spec file to track local collection changes.</p> | ||
| <Button className="mt-4" color="warning" onClick={handleRestoreSpec} loading={isSyncing}> | ||
| Restore Spec File | ||
| </Button> | ||
| </div> | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.