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

chore: add convert functionality #100

Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3,045 changes: 2,397 additions & 648 deletions package-lock.json

Large diffs are not rendered by default.

23 changes: 12 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
@@ -10,13 +10,6 @@
"@headlessui/react": "1.4.1",
"@hookstate/core": "^3.0.12",
"@monaco-editor/react": "^4.2.2",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^11.2.7",
"@testing-library/user-event": "^12.8.3",
"@types/jest": "^26.0.24",
"@types/node": "^12.20.26",
"@types/react": "^17.0.24",
"@types/react-dom": "^17.0.9",
"js-base64": "^3.7.2",
"js-file-download": "^0.4.12",
"js-yaml": "^4.1.0",
@@ -26,16 +19,15 @@
"react-dom": "^17.0.2",
"react-hot-toast": "2.1.1",
"react-icons": "^4.2.0",
"react-scripts": "4.0.3",
"react-split-pane": "^0.1.92",
"tailwindcss": "^2.2.16",
"typescript": "^4.4.3",
"web-vitals": "^1.1.2"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --passWithNoTests",
"test": "npm run test:unit",
"test:unit": "react-scripts test --detectOpenHandles",
"eject": "react-scripts eject",
"lint": "echo \"No linter configured\"",
"generate:assets": "npm run build"
@@ -59,6 +51,15 @@
]
},
"devDependencies": {
"@types/js-yaml": "^4.0.3"
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^11.2.7",
"@testing-library/user-event": "^12.8.3",
"@types/jest": "^26.0.24",
"@types/js-yaml": "^4.0.3",
"@types/node": "^12.20.26",
"@types/react": "^17.0.24",
"@types/react-dom": "^17.0.9",
"react-scripts": "4.0.3",
"typescript": "^4.4.3"
}
}
10 changes: 10 additions & 0 deletions src/components/Editor/EditorDropdown.tsx
Original file line number Diff line number Diff line change
@@ -3,6 +3,8 @@ import toast from 'react-hot-toast';
import { FaEllipsisH } from 'react-icons/fa';

import {
ConverterModal,
ImportBase64Modal,
ImportURLModal,
} from '../Modals';
import { Dropdown } from '../common';
@@ -53,6 +55,14 @@ export const EditorDropdown: React.FunctionComponent<EditorDropdownProps> = () =
Import File
</label>
</li>
<li className="hover:bg-gray-900">
<ImportBase64Modal />
</li>
</div>
<div>
<li className="hover:bg-gray-900">
<ConverterModal />
</li>
</div>
</ul>
</Dropdown>
85 changes: 85 additions & 0 deletions src/components/Modals/ConverterModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import React, { useState } from 'react';
import toast from 'react-hot-toast';
import { ConfirmModal } from './index';

import { EditorService, SpecificationService } from '../../services';
import state from '../../state';

export const ConverterModal: React.FunctionComponent = () => {
const [version, setVersion] = useState('');
const parserState = state.useParserState();

const actualVersion = parserState.parsedSpec.get()?.version();
const latestVersion = SpecificationService.getLastVersion();
let allowedVersions = Object.keys(SpecificationService.getSpecs());
actualVersion && (allowedVersions.splice(0, allowedVersions.indexOf(actualVersion) + 1));

const onSubmit = () => {
toast.promise(EditorService.convertSpec(version), {
loading: 'Converting...',
success: (
<div>
<span className="block text-bold">
Document succesfully converted!
</span>
</div>
),
error: (
<div>
<span className="block text-bold text-red-400">
Failed to convert document.
</span>
</div>
),
});
};

return (
<ConfirmModal
title="Convert AsyncAPI document"
confirmText="Convert"
confirmDisabled={!version || allowedVersions.length === 0}
opener={
<button
type="button"
className="px-4 py-1 w-full text-left text-sm rounded-md focus:outline-none transition ease-in-out duration-150"
title="Convert AsyncAPI document"
>
Convert document
</button>
}
onSubmit={onSubmit}
>
<div className="flex content-center justify-center">
{allowedVersions.length > 0 ? (
<>
<label
htmlFor="asyncapi-version"
className="flex justify-right items-center w-1/2 content-center text-sm font-medium text-gray-700"
>
To version:
</label>
<select
name="asyncapi-version"
className="mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-pink-500 focus:border-pink-500 sm:text-sm rounded-md"
onChange={e => setVersion(e.target.value)}
value={version}
>
<option value="">Please Select</option>
{allowedVersions.length > 1 && (
<option value={latestVersion}>Latest ({latestVersion})</option>
)}
{allowedVersions.map(v => (
<option key={v} value={v}>
{v}
</option>
))}
</select>
</>
) : (
<div>Uses the latest version.</div>
)}
</div>
</ConfirmModal>
);
};
63 changes: 63 additions & 0 deletions src/components/Modals/ImportBase64Modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React, { useState } from 'react';
import toast from 'react-hot-toast';
import { ConfirmModal } from './index';

import { EditorService } from '../../services';

export const ImportBase64Modal: React.FunctionComponent = () => {
const [base64, setBase64] = useState('');

const onSubmit = () => {
toast.promise(EditorService.importBase64(base64), {
loading: 'Importing...',
success: (
<div>
<span className="block text-bold">
Document succesfully imported!
</span>
</div>
),
error: (
<div>
<span className="block text-bold text-red-400">
Failed to import document.
</span>
</div>
),
});
};

return (
<ConfirmModal
title="Import AsyncAPI document from Base64"
confirmText="Import"
confirmDisabled={!base64}
opener={
<button
type="button"
className="px-4 py-1 w-full text-left text-sm rounded-md focus:outline-none transition ease-in-out duration-150"
title="Import from Base64"
>
Import from Base64
</button>
}
onSubmit={onSubmit}
>
<div className="flex content-center justify-center">
<label
htmlFor="base64-source"
className="flex justify-right items-center w-1/2 content-center text-sm font-medium text-gray-700 hidden"
>
Base64 content
</label>
<textarea
name="base64-source"
placeholder="Paste Base64 content here"
className="shadow-sm focus:ring-pink-500 focus:border-pink-500 w-1/2 block w-full sm:text-sm border-gray-300 rounded-md"
onChange={e => setBase64(e.target.value)}
rows={10}
/>
</div>
</ConfirmModal>
);
};
2 changes: 2 additions & 0 deletions src/components/Modals/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export * from './ConfirmModal';
export * from './ConverterModal';
export * from './ImportBase64Modal';
export * from './ImportURLModal';

export interface IModalProps {
2 changes: 1 addition & 1 deletion src/components/Terminal/ProblemsTab.tsx
Original file line number Diff line number Diff line change
@@ -55,7 +55,7 @@ export const ProblemsTabContent: React.FunctionComponent<ProblemsTabProps> = ()
</div>
) : (
<div className="py-2 px-4 w-full text-sm">
No problems have been detected in the specification so far.
No problems have been detected in the AsyncAPI document so far.
</div>
)}
</div>
19 changes: 19 additions & 0 deletions src/services/editor.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as monacoAPI from 'monaco-editor/esm/vs/editor/editor.api';

import { FormatService } from "./format.service";
import { SpecificationService } from "./specification.service";

import state from '../state';

@@ -51,6 +52,14 @@ export class EditorService {
}
}

static async convertSpec(version?: string) {
const converted = await SpecificationService.convertSpec(
this.getValue(),
version || SpecificationService.getLastVersion(),
);
this.updateState(converted, true);
}

static async importFromURL(url: string): Promise<void> {
if (url) {
return fetch(url)
@@ -82,4 +91,14 @@ export class EditorService {
};
fileReader.readAsText(file, 'UTF-8');
}

static async importBase64(content: string) {
try {
const decoded = FormatService.decodeBase64(content);
this.updateState(decoded, true);
} catch (err) {
console.error(err);
throw err;
}
}
}
32 changes: 32 additions & 0 deletions src/services/format.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,36 @@
import { encode, decode } from 'js-base64';
import YAML from 'js-yaml';

export class FormatService {
static convertToYaml(spec: string) {
try {
// Editor content -> JS object -> YAML string
const jsonContent = YAML.load(spec);
return YAML.dump(jsonContent);
} catch (err) {
console.error(err);
}
}

static convertToJSON(spec: string) {
try {
// JSON or YAML String -> JS object
const jsonContent = YAML.load(spec);
// JS Object -> pretty JSON string
return JSON.stringify(jsonContent, null, 2);
} catch (err) {
console.error(err);
}
}

static encodeBase64(content: string) {
return encode(content);
}

static decodeBase64(content: string) {
return decode(content);
}

static retrieveLangauge(content: string) {
if (content.trim()[0] === '{') {
return 'json';
2 changes: 1 addition & 1 deletion src/services/monaco.service.ts
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ import { SpecificationService } from './specification.service';
import state from '../state';

export class MonacoService {
private static actualVersion: string = SpecificationService.getLastVersion();
private static actualVersion: string = 'X.X.X';
private static Monaco: any = null;
private static Editor: any = null;

Loading