Skip to content
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

feat: add spec 3.0 validation support #747

Merged
merged 12 commits into from
Aug 9, 2023
5 changes: 5 additions & 0 deletions .changeset/selfish-ducks-hear.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@asyncapi/studio": minor
---

Support spec V3.0.0 in studio
4 changes: 2 additions & 2 deletions apps/studio/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@
"@asyncapi/avro-schema-parser": "^3.0.2",
"@asyncapi/converter": "^1.3.1",
"@asyncapi/openapi-schema-parser": "^3.0.4",
"@asyncapi/parser": "^2.0.3",
"@asyncapi/parser": "^2.1.0-next-major-spec.3",
"@asyncapi/react-component": "^1.0.0-next.48",
"@asyncapi/specs": "^4.2.1",
"@asyncapi/specs": "^6.0.0-next-major-spec.6",
"@ebay/nice-modal-react": "^1.2.10",
"@headlessui/react": "^1.7.4",
"@hookstate/core": "^4.0.0-rc21",
Expand Down
8 changes: 5 additions & 3 deletions apps/studio/src/components/Content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,18 @@ import { Template } from './Template';
import { VisualiserTemplate } from './Visualiser';

import { debounce } from '../helpers';
import { usePanelsState } from '../state';
import { usePanelsState, useDocumentsState, useSettingsState } from '../state';

import type { FunctionComponent } from 'react';

interface ContentProps {}

export const Content: FunctionComponent<ContentProps> = () => { // eslint-disable-line sonarjs/cognitive-complexity
const { show, secondaryPanelType } = usePanelsState();

const navigationEnabled = show.primarySidebar;
const document = useDocumentsState(state => state.documents['asyncapi']?.document) || null;
const v3Enabled = useSettingsState(state => state.editor.v3support) || false;
const isV3 = document?.version() === '3.0.0' && v3Enabled;
const navigationEnabled = isV3 ? false : show.primarySidebar;
const editorEnabled = show.primaryPanel;
const viewEnabled = show.secondaryPanel;
const viewType = secondaryPanelType;
Expand Down
20 changes: 18 additions & 2 deletions apps/studio/src/components/Modals/Settings/SettingsModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,14 @@ export const SettingsModal = create<SettingsModalProps>(({ activeTab = 'editor'
const [governanceHints, setGovernanceHints] = useState(settings.governance.show.hints);
const [autoRendering, setAutoRendering] = useState(settings.templates.autoRendering);
const [confirmDisabled, setConfirmDisabled] = useState(true);
const [v3support, setV3support] = useState(settings.editor.v3support);

const createNewState = (): SettingsState => {
return {
editor: {
autoSaving,
savingDelay,
v3support
},
governance: {
show: {
Expand All @@ -86,7 +88,7 @@ export const SettingsModal = create<SettingsModalProps>(({ activeTab = 'editor'
const newState = createNewState();
const isThisSameObjects = settingsSvc.isEqual(newState);
setConfirmDisabled(isThisSameObjects);
}, [autoSaving, savingDelay, autoRendering, governanceWarnings, governanceInformations, governanceHints]);
}, [autoSaving, savingDelay, autoRendering, governanceWarnings, governanceInformations, governanceHints, v3support]);

const onCancel = useCallback(() => {
modal.hide();
Expand Down Expand Up @@ -129,7 +131,21 @@ export const SettingsModal = create<SettingsModalProps>(({ activeTab = 'editor'
Save automatically after each change in the document or manually.
</div>
</div>
<div className={`flex flex-col mt-4 text-sm pl-8 ${autoSaving ? 'opacity-1' : 'opacity-25'}`}>
<div className="flex flex-col mt-4 text-sm">
<div className="flex flex-row content-center justify-between">
<label
htmlFor="settings-auto-saving"
className="flex justify-right items-center w-1/2 content-center font-medium text-gray-700"
>
Enable v3 support
</label>
<Switch
toggle={v3support}
onChange={(v) => setV3support(v)}
/>
</div>
</div>
<div className="flex flex-col mt-4 text-sm">
<div className="flex flex-row content-center justify-between">
<label
htmlFor="settings-template-delay"
Expand Down
16 changes: 14 additions & 2 deletions apps/studio/src/components/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { show as showModal } from '@ebay/nice-modal-react';
import { Tooltip } from './common';
import { SettingsModal, NewFileModal } from './Modals';

import { usePanelsState, panelsState } from '../state';
import { usePanelsState, panelsState, useDocumentsState, useSettingsState } from '../state';

import type { FunctionComponent, ReactNode } from 'react';
import type { PanelsState } from '../state/panels.state';
Expand Down Expand Up @@ -45,17 +45,22 @@ interface NavItem {
onClick: () => void;
icon: ReactNode;
tooltip: ReactNode;
enabled: boolean;
}

interface SidebarProps {}

export const Sidebar: FunctionComponent<SidebarProps> = () => {
const { show, secondaryPanelType } = usePanelsState();
const document = useDocumentsState(state => state.documents['asyncapi']?.document) || null;
const v3Enabled = useSettingsState(state => state.editor.v3support) || false;
const isV3 = document?.version() === '3.0.0' && v3Enabled;

if (show.activityBar === false) {
return null;
}

const navigation: NavItem[] = [
let navigation: NavItem[] = [
// navigation
{
name: 'primarySidebar',
Expand All @@ -64,6 +69,7 @@ export const Sidebar: FunctionComponent<SidebarProps> = () => {
onClick: () => updateState('primarySidebar'),
icon: <VscListSelection className="w-5 h-5" />,
tooltip: 'Navigation',
enabled: !isV3
},
// editor
{
Expand All @@ -73,6 +79,7 @@ export const Sidebar: FunctionComponent<SidebarProps> = () => {
onClick: () => updateState('primaryPanel'),
icon: <VscCode className="w-5 h-5" />,
tooltip: 'Editor',
enabled: true
},
// template
{
Expand All @@ -82,6 +89,7 @@ export const Sidebar: FunctionComponent<SidebarProps> = () => {
onClick: () => updateState('secondaryPanel', 'template'),
icon: <VscOpenPreview className="w-5 h-5" />,
tooltip: 'HTML preview',
enabled: !isV3
},
// visuliser
{
Expand All @@ -91,6 +99,7 @@ export const Sidebar: FunctionComponent<SidebarProps> = () => {
onClick: () => updateState('secondaryPanel', 'visualiser'),
icon: <VscGraph className="w-5 h-5" />,
tooltip: 'Blocks visualiser',
enabled: !isV3
},
// newFile
{
Expand All @@ -100,9 +109,12 @@ export const Sidebar: FunctionComponent<SidebarProps> = () => {
onClick: () => showModal(NewFileModal),
icon: <VscNewFile className="w-5 h-5" />,
tooltip: 'New file',
enabled: true
},
];

navigation = navigation.filter(item => item.enabled);

return (
<div className="flex flex-col bg-gray-800 shadow-lg border-r border-gray-700 justify-between">
<div className="flex flex-col">
Expand Down
8 changes: 8 additions & 0 deletions apps/studio/src/components/Template/HTMLWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ export const HTMLWrapper: React.FunctionComponent<HTMLWrapperProps> = () => {
);
}

if (document?.version() === '3.0.0') {
return (
<div className="flex flex-1 overflow-hidden h-full justify-center items-center text-2xl mx-auto px-6 text-center">
<p>Documentation preview is not supported yet for v3.0.0 specifications.</p>
</div>
);
}

return (
parsedSpec && (
<div className="flex flex-1 flex-col h-full overflow-hidden">
Expand Down
7 changes: 5 additions & 2 deletions apps/studio/src/components/Terminal/TerminalTabs.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useState } from 'react';

import { TerminalInfo } from './TerminalInfo';
import { otherState } from '../../state';
import { otherState, useSettingsState, useDocumentsState } from '../../state';

export interface TerminalTab {
name: string;
Expand All @@ -19,6 +19,9 @@ export const TerminalTabs: React.FunctionComponent<TerminalTabsProps> = ({
active = 0,
}) => {
const [activeTab, setActiveTab] = useState(active);
const document = useDocumentsState(state => state.documents['asyncapi']?.document) || null;
const v3Enabled = useSettingsState(state => state.editor.v3support) || false;
const isV3 = document?.version() === '3.0.0' && v3Enabled;
if (tabs.length === 0) {
return null;
}
Expand Down Expand Up @@ -55,7 +58,7 @@ export const TerminalTabs: React.FunctionComponent<TerminalTabsProps> = ({
}}
>
<ul className="flex flex-row">
{tabs.map(tab => (
{!isV3 && tabs.map(tab => (
<li
key={tab.name}
className="px-2 cursor-pointer"
Expand Down
3 changes: 2 additions & 1 deletion apps/studio/src/services/parser.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ export class ParserService extends AbstractService {
try {
const { document, diagnostics: _diagnostics, extras } = await this.parser.parse(spec, options);
diagnostics = _diagnostics;

if (document) {
// This is needed as we are still using the old Parser API
// @todo: migrate to Parser API v2
const oldDocument = convertToOldAPI(document);
this.updateDocument(uri, {
uri,
Expand Down
24 changes: 18 additions & 6 deletions apps/studio/src/services/specification.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,30 @@ import { show } from '@ebay/nice-modal-react';

import { ConvertToLatestModal } from '../components/Modals';

import { documentsState } from '../state';
import { documentsState, settingsState } from '../state';

import type { SpecVersions } from '../types';

export class SpecificationService extends AbstractService {
private keySessionStorage = 'informed-about-latest';
override onInit() {
this.subcribeToDocuments();
this.subscribeToSettings();
}

get specs() {
return specs;
return specs.schemas;
}

get latestVersion(): SpecVersions {
return Object.keys(specs).pop() as SpecVersions;
const { editor: { v3support } } = settingsState.getState();
return v3support ?
Object.keys(this.specs).pop() as SpecVersions :
Object.keys(this.specs).at(-2) as SpecVersions;
}

getSpec(version: SpecVersions) {
return specs[String(version) as SpecVersions];
return this.specs[String(version) as SpecVersions];
}

private subcribeToDocuments() {
Expand All @@ -42,14 +47,21 @@ export class SpecificationService extends AbstractService {
});
}

private subscribeToSettings() {
settingsState.subscribe((state, prevState) => {
if (state.editor.v3support === prevState.editor.v3support) return;
sessionStorage.removeItem(this.keySessionStorage);
});
}

private tryInformAboutLatestVersion(
version: string,
): boolean {
const oneDay = 24 * 60 * 60 * 1000; /* ms */

const nowDate = new Date();
let dateOfLastQuestion = nowDate;
const localStorageItem = sessionStorage.getItem('informed-about-latest');
const localStorageItem = sessionStorage.getItem(this.keySessionStorage);
if (localStorageItem) {
dateOfLastQuestion = new Date(localStorageItem);
}
Expand All @@ -58,7 +70,7 @@ export class SpecificationService extends AbstractService {
nowDate === dateOfLastQuestion ||
nowDate.getTime() - dateOfLastQuestion.getTime() > oneDay;
if (isOvertime && version !== this.latestVersion) {
sessionStorage.setItem('informed-about-latest', nowDate.toString());
sessionStorage.setItem(this.keySessionStorage, nowDate.toString());
return true;
}

Expand Down
2 changes: 2 additions & 0 deletions apps/studio/src/state/settings.state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export type SettingsState = {
editor: {
autoSaving: boolean;
savingDelay: number;
v3support: boolean;
};
governance: {
show: {
Expand All @@ -24,6 +25,7 @@ export const settingsState = create(
editor: {
autoSaving: true,
savingDelay: 625,
v3support: false,
},
governance: {
show: {
Expand Down
2 changes: 1 addition & 1 deletion apps/studio/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import type specs from '@asyncapi/specs';

export type SpecVersions = keyof typeof specs;
export type SpecVersions = keyof typeof specs.schemas;
Loading