mirror of
https://github.com/LukeHagar/OpenAPI.gg.git
synced 2025-12-06 12:37:48 +00:00
Saving progress on paths and methods
This commit is contained in:
@@ -10,7 +10,7 @@
|
|||||||
'This operation clears all the current values, unsaved data will be lost, are you sure?'
|
'This operation clears all the current values, unsaved data will be lost, are you sure?'
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
loadSpec(structuredClone(newSpec));
|
loadSpec(newSpec());
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
on:click={async () => {
|
on:click={async () => {
|
||||||
if (confirm(`Are you sure you want to delete all saved specs?`)) {
|
if (confirm(`Are you sure you want to delete all saved specs?`)) {
|
||||||
db.apiSpecs.clear();
|
db.apiSpecs.clear();
|
||||||
loadSpec(structuredClone(newSpec));
|
loadSpec(newSpec());
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
if (specs.length > 0){
|
if (specs.length > 0){
|
||||||
loadSpec(specs[0]);
|
loadSpec(specs[0]);
|
||||||
} else {
|
} else {
|
||||||
loadSpec(structuredClone(newSpec));
|
loadSpec(newSpec());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -3,6 +3,9 @@
|
|||||||
import filenamify from 'filenamify';
|
import filenamify from 'filenamify';
|
||||||
import { stringify } from 'yaml';
|
import { stringify } from 'yaml';
|
||||||
|
|
||||||
|
export let width: CssClasses = "w-full"
|
||||||
|
export let padding: CssClasses = "px-2 py-0.5"
|
||||||
|
|
||||||
$: fileName = filenamify($selectedSpec.spec?.info?.title) || 'openapi';
|
$: fileName = filenamify($selectedSpec.spec?.info?.title) || 'openapi';
|
||||||
|
|
||||||
const saveYAML = () => {
|
const saveYAML = () => {
|
||||||
@@ -36,12 +39,10 @@
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 text-xs gap-2">
|
<button
|
||||||
<span class="text-center font-bold text-sm">Download</span>
|
|
||||||
<button
|
|
||||||
type="button"
|
type="button"
|
||||||
on:click={saveYAML}
|
on:click={saveYAML}
|
||||||
class="btn btn-sm rounded-lg w-full variant-ghost-tertiary gap-1"
|
class="btn btn-sm {width} {padding} variant-ghost-tertiary gap-1"
|
||||||
aria-label="Download a YAML representation of the OpenAPI document."
|
aria-label="Download a YAML representation of the OpenAPI document."
|
||||||
>
|
>
|
||||||
YAML
|
YAML
|
||||||
@@ -63,7 +64,7 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
on:click={saveJSON}
|
on:click={saveJSON}
|
||||||
class="btn btn-sm rounded-lg w-full variant-ghost-tertiary gap-1"
|
class="btn btn-sm {width} {padding} variant-ghost-tertiary gap-1"
|
||||||
aria-label="Download a JSON representation of the OpenAPI document."
|
aria-label="Download a JSON representation of the OpenAPI document."
|
||||||
>
|
>
|
||||||
JSON
|
JSON
|
||||||
@@ -82,4 +83,3 @@
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
import type { CssClasses } from '@skeletonlabs/skeleton';
|
import type { CssClasses } from '@skeletonlabs/skeleton';
|
||||||
|
|
||||||
export let width: CssClasses = "w-full"
|
export let width: CssClasses = "w-full"
|
||||||
|
export let padding: CssClasses = "px-3 py-1"
|
||||||
|
|
||||||
async function onSave(e: Event): Promise<void> {
|
async function onSave(e: Event): Promise<void> {
|
||||||
console.log('Save button clicked');
|
console.log('Save button clicked');
|
||||||
@@ -12,4 +13,4 @@
|
|||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<button class="btn variant-ghost-success {width}" on:click={onSave}> Save </button>
|
<button class="btn variant-ghost-success {width} {padding}" on:click={onSave}> Save </button>
|
||||||
|
|||||||
@@ -1,36 +1,75 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { selectedSpec } from '$lib/db';
|
import { selectedSpec } from '$lib/db';
|
||||||
import spdxLicenseList from 'spdx-license-list';
|
import spdxLicenseList from 'spdx-license-list';
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
|
const modifiedLicenseList = Object.entries(spdxLicenseList).map((entry) => {
|
||||||
|
return {
|
||||||
|
identifier: entry[0],
|
||||||
|
name: entry[1].name,
|
||||||
|
url: entry[1].url
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
const popularLicenses = ['MIT', 'Apache-2.0', 'GPL-3.0', 'Unlicense'];
|
const popularLicenses = ['MIT', 'Apache-2.0', 'GPL-3.0', 'Unlicense'];
|
||||||
|
|
||||||
|
let selectedLicense: {
|
||||||
|
name: string;
|
||||||
|
url: string;
|
||||||
|
identifier: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
selectedSpec.subscribe((spec) => {
|
||||||
|
if (!spec.spec.info.license) return;
|
||||||
|
|
||||||
|
let licenseCandidate;
|
||||||
|
|
||||||
|
if (spec.spec.info.license.identifier) {
|
||||||
|
licenseCandidate = modifiedLicenseList.find(
|
||||||
|
(entry) => entry.identifier === spec.spec.info.license!.identifier
|
||||||
|
);
|
||||||
|
} else if (spec.spec.info.license.url) {
|
||||||
|
licenseCandidate = modifiedLicenseList.find(
|
||||||
|
(entry) => entry.url === spec.spec.info.license!.url
|
||||||
|
);
|
||||||
|
} else if (spec.spec.info.license.name) {
|
||||||
|
licenseCandidate = modifiedLicenseList.find(
|
||||||
|
(entry) => entry.name === spec.spec.info.license!.name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (licenseCandidate) {
|
||||||
|
selectedLicense = licenseCandidate;
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="border-token rounded-container-token space-y-1 p-4">
|
<div class="border-token grow rounded-container-token bg-surface-backdrop-token space-y-1 p-4">
|
||||||
<div class="flex flex-row justify-between">
|
<div class="flex flex-row flex-wrap justify-between">
|
||||||
<h4 class="h4">License</h4>
|
<h4 class="h4">License</h4>
|
||||||
{#if $selectedSpec.spec.info.license}
|
{#if $selectedSpec.spec.info.license}
|
||||||
<label class="text-sm space-x-2">
|
<label class="text-sm ">
|
||||||
<span>Pick a license</span>
|
<span>Pick a license</span>
|
||||||
<select
|
<select
|
||||||
class="select w-56 text-sm"
|
class="select md:w-56 text-sm"
|
||||||
bind:value={$selectedSpec.spec.info.license.identifier}
|
bind:value={selectedLicense}
|
||||||
on:change={() => {
|
on:change={() => {
|
||||||
// @ts-expect-error - This is literally inside a null check
|
$selectedSpec.spec.info.license = {
|
||||||
$selectedSpec.spec.info.license.url = null
|
name: selectedLicense.name,
|
||||||
// @ts-expect-error - This is only running on identifier change
|
url: selectedLicense.url
|
||||||
$selectedSpec.spec.info.license.name =
|
};
|
||||||
// @ts-expect-error - This is only running on identifier change
|
|
||||||
spdxLicenseList[$selectedSpec.spec.info.license.identifier].name;
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<optgroup label="Popular Licenses">
|
<optgroup label="Popular Licenses">
|
||||||
{#each Object.keys(spdxLicenseList).filter( (entry) => popularLicenses.includes(entry) ) as license}
|
{#each modifiedLicenseList.filter( (entry) => popularLicenses.includes(entry.identifier) ) as license}
|
||||||
<option value={license}>{spdxLicenseList[license].name}</option>
|
<option value={license}>{license.name}</option>
|
||||||
{/each}
|
{/each}
|
||||||
</optgroup>
|
</optgroup>
|
||||||
<optgroup label="All Licenses">
|
<optgroup label="All Licenses">
|
||||||
{#each Object.keys(spdxLicenseList).sort() as license}
|
{#each modifiedLicenseList
|
||||||
<option value={license}>{spdxLicenseList[license].name}</option>
|
.filter((entry) => !popularLicenses.includes(entry.identifier))
|
||||||
|
.sort() as license}
|
||||||
|
<option value={license}>{license.name}</option>
|
||||||
{/each}
|
{/each}
|
||||||
</optgroup>
|
</optgroup>
|
||||||
</select>
|
</select>
|
||||||
@@ -75,9 +114,8 @@
|
|||||||
class="btn variant-filled-primary"
|
class="btn variant-filled-primary"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
$selectedSpec.spec.info.license = {
|
$selectedSpec.spec.info.license = {
|
||||||
name: '',
|
name: spdxLicenseList['MIT'].name,
|
||||||
identifier: '',
|
url: spdxLicenseList['MIT'].url
|
||||||
url: ''
|
|
||||||
};
|
};
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {
|
import { addPath, deletePath, renamePath } from '$lib';
|
||||||
addPath,
|
|
||||||
deletePath,
|
|
||||||
renamePath
|
|
||||||
} from '$lib';
|
|
||||||
import { getModalStore } from '@skeletonlabs/skeleton';
|
import { getModalStore } from '@skeletonlabs/skeleton';
|
||||||
const modalStore = getModalStore();
|
const modalStore = getModalStore();
|
||||||
|
|
||||||
@@ -13,31 +9,33 @@
|
|||||||
export let id: number;
|
export let id: number;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex justify-between">
|
<div class="flex flex-wrap justify-between gap-1">
|
||||||
<h3 class="">{pathName}</h3>
|
<h3 class="truncate font-mono"><span class="md:hidden">Path: </span>{pathName}</h3>
|
||||||
<div class="flex gap-2">
|
<div class="flex md:gap-2 flex-wrap gap-1">
|
||||||
<a href="/paths/{id}" class="btn btn-sm variant-filled-primary">Edit</a>
|
<a href="/paths/{pathName}" class="btn btn-sm variant-filled-primary grow md:w-fit w-full">Edit</a>
|
||||||
|
<div class="grow flex flex-row gap-1">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-sm variant-filled-warning grow"
|
||||||
|
on:click={() => {
|
||||||
|
renamePath(modalStore, pathName);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Change path
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-sm variant-filled-secondary grow"
|
||||||
|
on:click={() => {
|
||||||
|
addPath(modalStore, pathName);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Add Sub-Route
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-sm variant-filled-warning"
|
class="btn btn-sm variant-ringed-error hover:variant-filled-error grow"
|
||||||
on:click={() => {
|
|
||||||
renamePath(modalStore, pathName);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Change path
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="btn btn-sm variant-filled-secondary"
|
|
||||||
on:click={() => {
|
|
||||||
addPath(modalStore, pathName);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Add Sub-Route
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="btn btn-sm variant-ringed-error hover:variant-filled-error"
|
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
deletePath(modalStore, pathName);
|
deletePath(modalStore, pathName);
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -68,15 +68,15 @@
|
|||||||
<hr />
|
<hr />
|
||||||
{/each}
|
{/each}
|
||||||
|
|
||||||
<span class="flex justify-center items-center gap-2 max-w-sm mx-auto">
|
<span class="flex flex-wrap justify-center items-center gap-2 max-w-sm mx-auto">
|
||||||
<select name="security-schema" bind:value={selectedSchema} class="input w-fit text-sm">
|
<select name="security-schema" bind:value={selectedSchema} class="input w-fit text-sm grow">
|
||||||
<option value="basicAuth" selected>Basic Auth</option>
|
<option value="basicAuth" selected>Basic Auth</option>
|
||||||
<option value="bearerAuth">Bearer Auth</option>
|
<option value="bearerAuth">Bearer Auth</option>
|
||||||
<option value="ApiKeyAuth">API Key Auth</option>
|
<option value="ApiKeyAuth">API Key Auth</option>
|
||||||
<option value="openId">OpenID</option>
|
<option value="openId">OpenID</option>
|
||||||
<option value="oAuthSample">OAuth2</option>
|
<option value="oAuthSample">OAuth2</option>
|
||||||
</select>
|
</select>
|
||||||
<button type="button" class="btn text-sm variant-filled-primary" on:click={addSecuritySchema}>
|
<button type="button" class="btn text-sm variant-filled-primary grow" on:click={addSecuritySchema}>
|
||||||
Add Security Schema
|
Add Security Schema
|
||||||
</button>
|
</button>
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -48,7 +48,10 @@
|
|||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="border-token rounded-container-token bg-surface-backdrop-token space-y-1 p-4">
|
<div class="flex flex-row flex-wrap w-full gap-2">
|
||||||
|
|
||||||
|
|
||||||
|
<div class="border-token grow rounded-container-token bg-surface-backdrop-token space-y-1 p-4">
|
||||||
<h4 class="h4">Contact Information</h4>
|
<h4 class="h4">Contact Information</h4>
|
||||||
{#if $selectedSpec.spec.info.contact}
|
{#if $selectedSpec.spec.info.contact}
|
||||||
<label class="space-y-1">
|
<label class="space-y-1">
|
||||||
@@ -99,4 +102,5 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<LicenseAtom />
|
<LicenseAtom />
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -18,12 +18,12 @@
|
|||||||
|
|
||||||
<div class="w-full flex flex-row justify-center">
|
<div class="w-full flex flex-row justify-center">
|
||||||
<div
|
<div
|
||||||
class="fixed bottom-10 px-5 w-full max-w-xs shadow-xl flex flex-row gap-2 justify-between bg-surface-100-800-token rounded-full p-4"
|
class="fixed bottom-1 md:bottom-5 md:w-full md:max-w-[270px] w-fit shadow-xl flex flex-row gap-2 justify-between bg-surface-100-800-token rounded-full md:p-3 p-1"
|
||||||
>
|
>
|
||||||
<span class="flex justify-center">
|
<span class="flex justify-center">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn gap-2 variant-filled-primary"
|
class="btn btn-sm grow gap-2 variant-filled-primary"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
addPath(modalStore);
|
addPath(modalStore);
|
||||||
}}
|
}}
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
<span class="flex justify-center">
|
<span class="flex justify-center">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-sm gap-2 variant-filled-secondary"
|
class="btn btn-sm grow gap-2 variant-filled-secondary"
|
||||||
on:click={sortPathsAlphabetically}
|
on:click={sortPathsAlphabetically}
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
@@ -71,7 +71,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mx-auto border-token rounded-container-token bg-surface-backdrop-token px-6 py-4 space-y-4"
|
class="mx-auto border-token rounded-container-token bg-surface-backdrop-token md:px-6 px-2 md:py-4 py-2 space-y-4 mb-10"
|
||||||
>
|
>
|
||||||
{#if Object.keys(paths).length === 0}
|
{#if Object.keys(paths).length === 0}
|
||||||
<p class="text-center">Wow such empty.</p>
|
<p class="text-center">Wow such empty.</p>
|
||||||
|
|||||||
@@ -41,23 +41,26 @@ export const blankSpec: OpenAPIV3_1.Document = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const newSpec: APISpec = {
|
export const newSpec: () => APISpec = () => structuredClone({
|
||||||
name: 'OpenAPI',
|
name: 'OpenAPI',
|
||||||
spec: blankSpec
|
spec: blankSpec
|
||||||
} as const
|
})
|
||||||
|
|
||||||
export const selectedSpecId: Writable<string | undefined> = persisted("selectedSpecId",undefined)
|
export const selectedSpecId: Writable<string | undefined> = persisted("selectedSpecId",undefined)
|
||||||
export const selectedSpec: Writable<APISpec> = writable(newSpec)
|
export const selectedSpec: Writable<APISpec> = writable(newSpec())
|
||||||
|
|
||||||
selectedSpec.subscribe((spec) => {
|
selectedSpec.subscribe((spec) => {
|
||||||
if(!spec){
|
if(!spec){
|
||||||
spec = structuredClone(newSpec)
|
spec = newSpec()
|
||||||
}
|
}
|
||||||
if(spec.id){
|
if(spec.id){
|
||||||
selectedSpecId.set(spec.id)
|
selectedSpecId.set(spec.id)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const specLoaded: Writable<boolean> = writable(false);
|
||||||
|
export const pageLoaded: Writable<boolean> = writable(false);
|
||||||
|
|
||||||
export interface APISpec {
|
export interface APISpec {
|
||||||
id?: string;
|
id?: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
|||||||
@@ -1,27 +1,97 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import FancyAppRail from './FancyAppRail.svelte';
|
import FancyAppRail from './FancyAppRail.svelte';
|
||||||
|
|
||||||
import { AppShell, type ModalComponent } from '@skeletonlabs/skeleton';
|
import { AppBar, AppShell, type ModalComponent } from '@skeletonlabs/skeleton';
|
||||||
import '../app.postcss';
|
import '../app.postcss';
|
||||||
// Floating UI for Popups
|
// Floating UI for Popups
|
||||||
import { arrow, autoUpdate, computePosition, flip, offset, shift } from '@floating-ui/dom';
|
import { arrow, autoUpdate, computePosition, flip, offset, shift } from '@floating-ui/dom';
|
||||||
import { storePopup } from '@skeletonlabs/skeleton';
|
import { storePopup } from '@skeletonlabs/skeleton';
|
||||||
// Modal
|
// Modal
|
||||||
import { initializeStores, Modal } from '@skeletonlabs/skeleton';
|
import { initializeStores, Modal } from '@skeletonlabs/skeleton';
|
||||||
import UploadModal from '$lib/components/FileManagement/UploadModal.svelte';
|
import UploadModal from '$lib/components/FileManagement/UploadModal.svelte';
|
||||||
|
import {
|
||||||
|
db,
|
||||||
|
loadSpec,
|
||||||
|
pageLoaded,
|
||||||
|
saveSpec,
|
||||||
|
selectedSpec,
|
||||||
|
selectedSpecId,
|
||||||
|
specLoaded
|
||||||
|
} from '$lib/db';
|
||||||
|
import { pathCount } from '$lib';
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
import { liveQuery } from 'dexie';
|
||||||
|
import SaveButton from '$lib/components/FileManagement/SaveButton.svelte';
|
||||||
|
import DownloadButtons from '$lib/components/FileManagement/DownloadButtons.svelte';
|
||||||
initializeStores();
|
initializeStores();
|
||||||
|
|
||||||
const components: Record<string, ModalComponent> = {
|
const components: Record<string, ModalComponent> = {
|
||||||
// Set a unique modal ID, then pass the component reference
|
// Set a unique modal ID, then pass the component reference
|
||||||
uploadModal: { ref: UploadModal },
|
uploadModal: { ref: UploadModal }
|
||||||
// ...
|
// ...
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const apiSpecs = liveQuery(() => db.apiSpecs.toArray());
|
||||||
|
|
||||||
|
apiSpecs.subscribe((specs) => {
|
||||||
|
if ($specLoaded) return;
|
||||||
|
|
||||||
|
if ($selectedSpecId !== $selectedSpec.id) {
|
||||||
|
const found = specs.find((spec) => spec.id === $selectedSpecId);
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
loadSpec(found);
|
||||||
|
specLoaded.set(true);
|
||||||
|
}
|
||||||
|
} else if (specs.length > 0) {
|
||||||
|
loadSpec(specs[0]);
|
||||||
|
specLoaded.set(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
console.log('onMount', $selectedSpecId, $selectedSpec);
|
||||||
|
if ($selectedSpec) {
|
||||||
|
pageLoaded.set(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// $: console.log(
|
||||||
|
// 'newSpec',
|
||||||
|
// $selectedSpec,
|
||||||
|
// $selectedSpecId,
|
||||||
|
// $apiSpecs,
|
||||||
|
// $apiSpecs?.length,
|
||||||
|
// specLoaded
|
||||||
|
// );
|
||||||
|
|
||||||
|
//window keydown event listener for ctrl+s for saving
|
||||||
|
const saveListener = (e: KeyboardEvent) => {
|
||||||
|
if (e.ctrlKey && e.key === 's') {
|
||||||
|
e.preventDefault();
|
||||||
|
console.log('ctrl+s');
|
||||||
|
saveSpec($selectedSpec);
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Modal {components} />
|
<Modal {components} />
|
||||||
|
|
||||||
<AppShell slotPageContent="px-6 py-4">
|
<svelte:window on:keydown={saveListener} />
|
||||||
|
|
||||||
|
<AppShell slotPageContent="md:p-4 p-2">
|
||||||
|
<svelte:fragment slot="header">
|
||||||
|
<AppBar padding="px-2 py-1 ">
|
||||||
|
<svelte:fragment slot="lead"> OpenAPI.gg</svelte:fragment>
|
||||||
|
<svelte:fragment slot="trail">
|
||||||
|
<div class="p-1 flex flex-row md:gap-3 gap-1">
|
||||||
|
<DownloadButtons />
|
||||||
|
<SaveButton />
|
||||||
|
</div>
|
||||||
|
</svelte:fragment>
|
||||||
|
<!-- <svelte:fragment slot="headline">(headline)</svelte:fragment> -->
|
||||||
|
</AppBar>
|
||||||
|
</svelte:fragment>
|
||||||
<svelte:fragment slot="sidebarLeft">
|
<svelte:fragment slot="sidebarLeft">
|
||||||
<FancyAppRail />
|
<FancyAppRail />
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { pathCount, operationCount } from '$lib';
|
import { operationCount, pathCount } from '$lib';
|
||||||
import CreateNewButton from '$lib/components/FileManagement/CreateNewButton.svelte';
|
import CreateNewButton from '$lib/components/FileManagement/CreateNewButton.svelte';
|
||||||
import DeleteAllButton from '$lib/components/FileManagement/DeleteAllButton.svelte';
|
import DeleteAllButton from '$lib/components/FileManagement/DeleteAllButton.svelte';
|
||||||
import DeleteButton from '$lib/components/FileManagement/DeleteButton.svelte';
|
import DeleteButton from '$lib/components/FileManagement/DeleteButton.svelte';
|
||||||
@@ -7,30 +7,11 @@
|
|||||||
import SaveButton from '$lib/components/FileManagement/SaveButton.svelte';
|
import SaveButton from '$lib/components/FileManagement/SaveButton.svelte';
|
||||||
import SaveNewButton from '$lib/components/FileManagement/SaveNewButton.svelte';
|
import SaveNewButton from '$lib/components/FileManagement/SaveNewButton.svelte';
|
||||||
import UploadButton from '$lib/components/FileManagement/UploadButton.svelte';
|
import UploadButton from '$lib/components/FileManagement/UploadButton.svelte';
|
||||||
import { db, loadSpec, newSpec, selectedSpec, selectedSpecId } from '$lib/db';
|
import { db, pageLoaded, selectedSpec, specLoaded } from '$lib/db';
|
||||||
import { liveQuery } from 'dexie';
|
|
||||||
import { ProgressRadial } from '@skeletonlabs/skeleton';
|
import { ProgressRadial } from '@skeletonlabs/skeleton';
|
||||||
import { onMount } from 'svelte';
|
import { liveQuery } from 'dexie';
|
||||||
|
|
||||||
let apiSpecs = liveQuery(() => db.apiSpecs.toArray());
|
const apiSpecs = liveQuery(() => db.apiSpecs.toArray());
|
||||||
let specLoaded = false;
|
|
||||||
let pageLoaded = false;
|
|
||||||
|
|
||||||
apiSpecs.subscribe((specs) => {
|
|
||||||
if (specLoaded) return;
|
|
||||||
|
|
||||||
if ($selectedSpecId !== $selectedSpec.id) {
|
|
||||||
const found = specs.find((spec) => spec.id === $selectedSpecId);
|
|
||||||
|
|
||||||
if (found) {
|
|
||||||
loadSpec(found);
|
|
||||||
specLoaded = true;
|
|
||||||
}
|
|
||||||
} else if (specs.length > 0) {
|
|
||||||
loadSpec(specs[0]);
|
|
||||||
specLoaded = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$: stats = [
|
$: stats = [
|
||||||
{
|
{
|
||||||
@@ -42,15 +23,6 @@
|
|||||||
value: operationCount($selectedSpec.spec)
|
value: operationCount($selectedSpec.spec)
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
console.log('onMount', $selectedSpecId, $selectedSpec);
|
|
||||||
if ($selectedSpec) {
|
|
||||||
pageLoaded = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$: console.log('newSpec', newSpec, $selectedSpec, $selectedSpecId, $apiSpecs, $apiSpecs?.length, specLoaded);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="grid place-content-center h-full gap-2 px-1">
|
<div class="grid place-content-center h-full gap-2 px-1">
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<AppRail
|
<AppRail
|
||||||
width="w-28"
|
width="md:w-28 w-20"
|
||||||
aspectRatio="aspect-[20/14]"
|
aspectRatio="aspect-[20/14]"
|
||||||
background="variant-ghost-surface"
|
background="variant-ghost-surface"
|
||||||
border="ring-0"
|
border="ring-0"
|
||||||
@@ -13,15 +13,21 @@
|
|||||||
<svelte:fragment slot="lead">
|
<svelte:fragment slot="lead">
|
||||||
<div>
|
<div>
|
||||||
<AppRailAnchor href="/">
|
<AppRailAnchor href="/">
|
||||||
<div class="flex flex-col gap-2 py-4">
|
<svg
|
||||||
<p class="font-bold">OpenAPI</p>
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
<p>Generator</p>
|
fill="none"
|
||||||
<code
|
viewBox="0 0 24 24"
|
||||||
class="mx-auto w-min text-xs variant-filled-success p-1 px-2 rounded-container-token"
|
stroke-width="1.5"
|
||||||
>
|
stroke="currentColor"
|
||||||
3.1.0
|
class="size-6 mx-auto"
|
||||||
</code>
|
>
|
||||||
</div>
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
d="M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
File
|
||||||
</AppRailAnchor>
|
</AppRailAnchor>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr />
|
||||||
@@ -129,11 +135,7 @@
|
|||||||
Components
|
Components
|
||||||
</AppRailAnchor>
|
</AppRailAnchor>
|
||||||
|
|
||||||
|
|
||||||
<svelte:fragment slot="trail">
|
<svelte:fragment slot="trail">
|
||||||
<div class="p-2">
|
|
||||||
<DownloadButtons />
|
|
||||||
</div>
|
|
||||||
<div class="flex justify-center my-4">
|
<div class="flex justify-center my-4">
|
||||||
<LightSwitch />
|
<LightSwitch />
|
||||||
</div>
|
</div>
|
||||||
@@ -152,7 +154,7 @@
|
|||||||
d="M13.5 6H5.25A2.25 2.25 0 0 0 3 8.25v10.5A2.25 2.25 0 0 0 5.25 21h10.5A2.25 2.25 0 0 0 18 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25"
|
d="M13.5 6H5.25A2.25 2.25 0 0 0 3 8.25v10.5A2.25 2.25 0 0 0 5.25 21h10.5A2.25 2.25 0 0 0 18 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
API Reference
|
<span class=" whitespace-break-spaces px-1">API Reference</span>
|
||||||
</AppRailAnchor>
|
</AppRailAnchor>
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</AppRail>
|
</AppRail>
|
||||||
|
|||||||
206
src/routes/paths/[...pathName]/+page.svelte
Normal file
206
src/routes/paths/[...pathName]/+page.svelte
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { HttpMethods } from '$lib';
|
||||||
|
import ParameterInput from '$lib/components/atoms/ParameterInput.svelte';
|
||||||
|
import { getPathVariables } from '$lib/pathHandling';
|
||||||
|
import type { OpenAPIV3_1 } from '$lib/openAPITypes';
|
||||||
|
import { Accordion, AccordionItem, filter } from '@skeletonlabs/skeleton';
|
||||||
|
import { selectedSpec } from '$lib/db';
|
||||||
|
import { page } from '$app/stores';
|
||||||
|
|
||||||
|
$: console.log($page.params.pathName);
|
||||||
|
|
||||||
|
const filterParams = (
|
||||||
|
param: OpenAPIV3_1.ParameterObject | OpenAPIV3_1.ReferenceObject
|
||||||
|
): param is OpenAPIV3_1.ParameterObject => {
|
||||||
|
return !('$ref' in param);
|
||||||
|
};
|
||||||
|
|
||||||
|
let newParam: 'query' | 'header' | 'cookie' = 'query';
|
||||||
|
let tempPath: OpenAPIV3_1.PathItemObject = {
|
||||||
|
parameters: []
|
||||||
|
};
|
||||||
|
|
||||||
|
const pathVariables = getPathVariables($page.params.pathName ?? '');
|
||||||
|
|
||||||
|
pathVariables.forEach((variable) => {
|
||||||
|
// push path variables to the parameters array
|
||||||
|
// @ts-expect-error - working with a array thats loosely defined
|
||||||
|
tempPath.parameters.push({
|
||||||
|
name: variable,
|
||||||
|
in: 'path',
|
||||||
|
required: true
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const newMethod = () =>
|
||||||
|
structuredClone({
|
||||||
|
tags: [],
|
||||||
|
summary: '',
|
||||||
|
description: '',
|
||||||
|
externalDocs: {
|
||||||
|
description: '',
|
||||||
|
url: ''
|
||||||
|
},
|
||||||
|
operationId: '',
|
||||||
|
parameters: [],
|
||||||
|
requestBody: {
|
||||||
|
content: {},
|
||||||
|
description: '',
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
responses: {},
|
||||||
|
callbacks: {},
|
||||||
|
deprecated: false,
|
||||||
|
security: [],
|
||||||
|
servers: []
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="border-token border-surface-500 space-y-4 px-6 py-4 rounded-container-token variant-glass-surface"
|
||||||
|
>
|
||||||
|
<h3 class="h3 font-mono">
|
||||||
|
Path: {$page.params.pathName}
|
||||||
|
</h3>
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
{#if $selectedSpec.spec.paths != undefined && $page.params.pathName != undefined && $selectedSpec.spec.paths[$page.params.pathName] != undefined}
|
||||||
|
<Accordion>
|
||||||
|
<AccordionItem open>
|
||||||
|
<svelte:fragment slot="summary">
|
||||||
|
<h4 class="h4">General</h4>
|
||||||
|
</svelte:fragment>
|
||||||
|
<svelte:fragment slot="content">
|
||||||
|
<label class="space-y-2">
|
||||||
|
<p>Summary</p>
|
||||||
|
{#if $selectedSpec.spec.paths[$page.params.pathName]?.summary != undefined}
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="input"
|
||||||
|
bind:value={$selectedSpec.spec.paths[$page.params.pathName].summary}
|
||||||
|
placeholder="Summary of the path"
|
||||||
|
/>
|
||||||
|
{:else}
|
||||||
|
<button
|
||||||
|
class="btn variant-ghost-tertiary"
|
||||||
|
on:click={() => {
|
||||||
|
// @ts-expect-error - already inside a nullcheck
|
||||||
|
$selectedSpec.spec.paths[$page.params.pathName].summary = '';
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Add Summary
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
</label>
|
||||||
|
<label class="space-y-2">
|
||||||
|
<p>Description</p>
|
||||||
|
{#if $selectedSpec.spec.paths[$page.params.pathName]?.description != undefined}
|
||||||
|
<textarea
|
||||||
|
class="textarea"
|
||||||
|
bind:value={$selectedSpec.spec.paths[$page.params.pathName].description}
|
||||||
|
placeholder="Description of the path. Supports markdown."
|
||||||
|
/>
|
||||||
|
{:else}
|
||||||
|
<button
|
||||||
|
class="btn variant-ghost-tertiary"
|
||||||
|
on:click={() => {
|
||||||
|
// @ts-expect-error - already inside a nullcheck
|
||||||
|
$selectedSpec.spec.paths[$page.params.pathName].description = '';
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Add Description
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
</label>
|
||||||
|
</svelte:fragment>
|
||||||
|
</AccordionItem>
|
||||||
|
<AccordionItem open>
|
||||||
|
<svelte:fragment slot="summary">
|
||||||
|
<h4 class="h4">Custom Servers</h4>
|
||||||
|
</svelte:fragment>
|
||||||
|
<svelte:fragment slot="content">
|
||||||
|
<p>Here you can add custom servers for this specific call.</p>
|
||||||
|
{#if tempPath.servers && tempPath.servers.length > 0}
|
||||||
|
{#each tempPath.servers as server, index}
|
||||||
|
<label class="space-y-2">
|
||||||
|
<p>Server {index + 1}</p>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="input"
|
||||||
|
bind:value={server.url}
|
||||||
|
placeholder="URL of the server"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<button type="button" class="btn variant-filled-primary"> Add Server </button>
|
||||||
|
</svelte:fragment>
|
||||||
|
</AccordionItem>
|
||||||
|
<AccordionItem>
|
||||||
|
<svelte:fragment slot="summary">
|
||||||
|
<h4 class="h4">Parameters</h4>
|
||||||
|
</svelte:fragment>
|
||||||
|
<svelte:fragment slot="content">
|
||||||
|
{#if tempPath.parameters}
|
||||||
|
{#each tempPath.parameters.filter(filterParams) as param}
|
||||||
|
<ParameterInput variableName={param.name} bind:value={param} location="path" />
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<span class="flex items-center gap-2">
|
||||||
|
<select name="newParameter" bind:value={newParam} class="select w-min">
|
||||||
|
<option value="query">Query</option>
|
||||||
|
<option value="header">Header</option>
|
||||||
|
<option value="cookie">Cookie</option>
|
||||||
|
</select>
|
||||||
|
<button type="button" class="btn variant-filled-primary">
|
||||||
|
Add {newParam} Parameter
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</svelte:fragment>
|
||||||
|
</AccordionItem>
|
||||||
|
</Accordion>
|
||||||
|
<h4 class="h4">Operations</h4>
|
||||||
|
|
||||||
|
<p>Here you can add operations for this path. Select only the operations you want to support</p>
|
||||||
|
|
||||||
|
{#if $selectedSpec.spec.paths[$page.params.pathName]}
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
{#each Object.values(HttpMethods) as method}
|
||||||
|
<div class="flex flex-row gap-2 justify-between max-w-xs">
|
||||||
|
<label class="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
class="checkbox"
|
||||||
|
checked={$selectedSpec.spec.paths[$page.params.pathName]?.hasOwnProperty(method)}
|
||||||
|
on:change={(event) => {
|
||||||
|
// @ts-expect-error - this is a valid access
|
||||||
|
switch (event.target.checked) {
|
||||||
|
case true:
|
||||||
|
// @ts-expect-error - already inside a nullcheck
|
||||||
|
$selectedSpec.spec.paths[$page.params.pathName][method] = newMethod();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case false:
|
||||||
|
if (confirm('Are you sure you want to remove this operation?')) {
|
||||||
|
// @ts-expect-error - already inside a nullcheck
|
||||||
|
$selectedSpec.spec.paths[$page.params.pathName][method] = undefined;
|
||||||
|
} else {
|
||||||
|
event.target.checked = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{method.toUpperCase()}
|
||||||
|
</label>
|
||||||
|
<a href={`/paths/${$page.params.pathName}/z/${method}`} class="btn btn-sm variant-ghost-success">
|
||||||
|
Edit
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
14
src/routes/paths/[...pathName]/z/[method]/+page.svelte
Normal file
14
src/routes/paths/[...pathName]/z/[method]/+page.svelte
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { page } from '$app/stores';
|
||||||
|
|
||||||
|
$: console.log($page.params.method);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="border-token border-surface-500 space-y-4 px-6 py-4 rounded-container-token variant-glass-surface"
|
||||||
|
>
|
||||||
|
<h3 class="h3 font-mono">
|
||||||
|
Path: {$page.params.pathName}#{$page.params.method}
|
||||||
|
</h3>
|
||||||
|
<hr />
|
||||||
|
</div>
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { page } from '$app/stores';
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="w-full h-full flex justify-center items-center">
|
|
||||||
<div class="card max-w-lg">
|
|
||||||
<header class="card-header">
|
|
||||||
<enhanced:img src="./error.jpg" alt="stormy, grayscale seaside" />
|
|
||||||
</header>
|
|
||||||
<section class="p-4">
|
|
||||||
<h3 class="h3">An unfortunate error occured.</h3>
|
|
||||||
</section>
|
|
||||||
<footer class="card-footer">
|
|
||||||
{$page.status} - {$page.error?.message}
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -1,171 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { HttpMethods } from '$lib';
|
|
||||||
import ParameterInput from '$lib/components/atoms/ParameterInput.svelte';
|
|
||||||
import { getPathVariables } from '$lib/pathHandling';
|
|
||||||
import type { OpenAPIV3_1 } from '$lib/openAPITypes';
|
|
||||||
import type { PageData } from './$types';
|
|
||||||
import { Accordion, AccordionItem, filter } from '@skeletonlabs/skeleton';
|
|
||||||
import { selectedSpec } from '$lib/db';
|
|
||||||
|
|
||||||
export let data: PageData;
|
|
||||||
|
|
||||||
const filterParams = (param: OpenAPIV3_1.ParameterObject | OpenAPIV3_1.ReferenceObject): param is OpenAPIV3_1.ParameterObject => {
|
|
||||||
return !("$ref" in param);
|
|
||||||
};
|
|
||||||
|
|
||||||
let newParam: 'query' | 'header' | 'cookie' = 'query';
|
|
||||||
let tempPath: OpenAPIV3_1.PathItemObject = {
|
|
||||||
parameters: []
|
|
||||||
};
|
|
||||||
selectedSpec.subscribe((store) => {
|
|
||||||
if (!data.pathName) return;
|
|
||||||
if (store.spec.paths == undefined) tempPath = {};
|
|
||||||
if (!store.spec.paths!.hasOwnProperty(data.pathName)) tempPath = {};
|
|
||||||
// @ts-expect-error - working with a known not empty object
|
|
||||||
tempPath = store.paths[data.pathName] ?? {};
|
|
||||||
|
|
||||||
if (!tempPath.hasOwnProperty('parameters')) tempPath.parameters = [];
|
|
||||||
});
|
|
||||||
|
|
||||||
const pathVariables = getPathVariables(data.pathName ?? '');
|
|
||||||
|
|
||||||
pathVariables.forEach((variable) => {
|
|
||||||
// push path variables to the parameters array
|
|
||||||
// @ts-expect-error - working with a array thats loosely defined
|
|
||||||
tempPath.parameters.push({
|
|
||||||
name: variable,
|
|
||||||
in: 'path',
|
|
||||||
required: true
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="border-token border-surface-500 space-y-4 px-6 py-4 rounded-container-token variant-glass-surface"
|
|
||||||
>
|
|
||||||
<h3 class="h3">
|
|
||||||
{data.pathName}
|
|
||||||
</h3>
|
|
||||||
<hr />
|
|
||||||
|
|
||||||
<Accordion>
|
|
||||||
<AccordionItem>
|
|
||||||
<svelte:fragment slot="summary">
|
|
||||||
<h4 class="h4">General</h4>
|
|
||||||
</svelte:fragment>
|
|
||||||
<svelte:fragment slot="content">
|
|
||||||
<label class="space-y-2">
|
|
||||||
<p>Summary</p>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="input"
|
|
||||||
bind:value={tempPath.summary}
|
|
||||||
placeholder="Summary of the path"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label class="space-y-2">
|
|
||||||
<p>Description</p>
|
|
||||||
<textarea
|
|
||||||
class="textarea"
|
|
||||||
bind:value={tempPath.description}
|
|
||||||
placeholder="Description of the path. Supports markdown."
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</svelte:fragment>
|
|
||||||
</AccordionItem>
|
|
||||||
<AccordionItem>
|
|
||||||
<svelte:fragment slot="summary">
|
|
||||||
<h4 class="h4">Custom Servers</h4>
|
|
||||||
</svelte:fragment>
|
|
||||||
<svelte:fragment slot="content">
|
|
||||||
<p>Here you can add custom servers for this specific call.</p>
|
|
||||||
{#if tempPath.servers && tempPath.servers.length > 0}
|
|
||||||
{#each tempPath.servers as server, index}
|
|
||||||
<label class="space-y-2">
|
|
||||||
<p>Server {index + 1}</p>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="input"
|
|
||||||
bind:value={server.url}
|
|
||||||
placeholder="URL of the server"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
{/each}
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<button type="button" class="btn variant-filled-primary"> Add Server </button>
|
|
||||||
</svelte:fragment>
|
|
||||||
</AccordionItem>
|
|
||||||
<AccordionItem>
|
|
||||||
<svelte:fragment slot="summary">
|
|
||||||
<h4 class="h4">Parameters</h4>
|
|
||||||
</svelte:fragment>
|
|
||||||
<svelte:fragment slot="content">
|
|
||||||
{#if tempPath.parameters}
|
|
||||||
{#each tempPath.parameters.filter(filterParams) as param}
|
|
||||||
<ParameterInput variableName={param.name} bind:value={param} location="path" />
|
|
||||||
{/each}
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<span class="flex items-center gap-2">
|
|
||||||
<select name="newParameter" bind:value={newParam} class="select w-min">
|
|
||||||
<option value="query">Query</option>
|
|
||||||
<option value="header">Header</option>
|
|
||||||
<option value="cookie">Cookie</option>
|
|
||||||
</select>
|
|
||||||
<button type="button" class="btn variant-filled-primary">
|
|
||||||
Add {newParam} Parameter
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
</svelte:fragment>
|
|
||||||
</AccordionItem>
|
|
||||||
<AccordionItem>
|
|
||||||
<svelte:fragment slot="summary">
|
|
||||||
<h4 class="h4">Operations</h4>
|
|
||||||
</svelte:fragment>
|
|
||||||
<svelte:fragment slot="content">
|
|
||||||
<p>
|
|
||||||
Here you can add operations for this path. Select only the operations you want to support
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div class="flex gap-4">
|
|
||||||
{#each Object.values(HttpMethods) as method}
|
|
||||||
<label class="flex items-center gap-2">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
class="checkbox"
|
|
||||||
on:input={(event) => {
|
|
||||||
//@ts-expect-error - working with a known object
|
|
||||||
if (event.target?.checked) {
|
|
||||||
tempPath[method] = {
|
|
||||||
tags: [],
|
|
||||||
summary: '',
|
|
||||||
description: '',
|
|
||||||
externalDocs: {
|
|
||||||
description: '',
|
|
||||||
url: ''
|
|
||||||
},
|
|
||||||
operationId: '',
|
|
||||||
parameters: [],
|
|
||||||
requestBody: {
|
|
||||||
content: {},
|
|
||||||
description: '',
|
|
||||||
required: false
|
|
||||||
},
|
|
||||||
responses: {},
|
|
||||||
callbacks: {},
|
|
||||||
deprecated: false,
|
|
||||||
security: [],
|
|
||||||
servers: []
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{method.toUpperCase()}
|
|
||||||
</label>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
</svelte:fragment>
|
|
||||||
</AccordionItem>
|
|
||||||
</Accordion>
|
|
||||||
</div>
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
import type { PageLoad } from './$types';
|
|
||||||
import { error } from '@sveltejs/kit';
|
|
||||||
import type { OpenAPIV3_1 } from '$lib/openAPITypes';
|
|
||||||
import { selectedSpec } from '$lib/db';
|
|
||||||
|
|
||||||
export const load = (async (event) => {
|
|
||||||
// check if path param is an integer
|
|
||||||
if (!/^\d+$/.test(event.params.index)) error(404, 'Invalid path index');
|
|
||||||
const index = parseInt(event.params.index);
|
|
||||||
let apiObject: OpenAPIV3_1.Document;
|
|
||||||
selectedSpec.subscribe((value) => (apiObject = value.spec));
|
|
||||||
let pathName: string | undefined;
|
|
||||||
// @ts-expect-error - svelte stores populate the value
|
|
||||||
if (apiObject && !apiObject.paths) error(404, 'No paths found in the OpenAPI document');
|
|
||||||
|
|
||||||
const path = (() => {
|
|
||||||
// @ts-expect-error - svelte stores populate the value
|
|
||||||
const paths = Object.keys(apiObject.paths) || [];
|
|
||||||
if (paths.length === 0) return;
|
|
||||||
if (paths.length <= index) return;
|
|
||||||
pathName = paths[index];
|
|
||||||
// @ts-expect-error - svelte stores populate the value
|
|
||||||
if (!apiObject || !apiObject.paths) return;
|
|
||||||
return apiObject.paths[paths[index]];
|
|
||||||
})();
|
|
||||||
return {
|
|
||||||
path,
|
|
||||||
pathName
|
|
||||||
};
|
|
||||||
}) satisfies PageLoad;
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.2 MiB |
Reference in New Issue
Block a user