Skip to content

Commit

Permalink
Fleshing out the paths explorer more
Browse files Browse the repository at this point in the history
  • Loading branch information
riccardo-forina committed Nov 26, 2024
1 parent f4f52ca commit 7f6f333
Show file tree
Hide file tree
Showing 12 changed files with 608 additions and 174 deletions.
1 change: 1 addition & 0 deletions packages/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"@apicurio/data-models": "^1.1.28",
"lodash": "^4.17.21",
"react-markdown": "^9.0.1",
"rehype-raw": "^7.0.0",
"remark-gfm": "^4.0.0",
"xstate": "^5.18.2",
"yaml": "^2.6.0"
Expand Down
26 changes: 11 additions & 15 deletions packages/ui/src/OpenApiEditorModels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,11 @@ export type RequestBody = {
mediaTypes: string;
};

export type Response =
| {
statusCode: number;
description?: string;
mimeType?: string;
}
| {
ref: string; // TODO
};
export type Response = {
statusCode: number;
description?: string;
mimeType?: string;
};

export const Operations = [
"get" as const,
Expand All @@ -41,9 +37,9 @@ export type Operation = {
id: string;
tags: string[];
servers: Server[];
queryParameters: "TODO";
headerParameters: "TODO";
cookieParameters: "TODO";
pathParameters: DataTypeProperty[];
headerParameters: DataTypeProperty[];
cookieParameters: DataTypeProperty[];
requestBody?: RequestBody;
responses: Response[];
securityRequirements: SecurityRequirement[];
Expand All @@ -54,9 +50,9 @@ export type DocumentPath = {
summary: string;
description: string;
servers: Server[];
queryParameters: "TODO";
headerParameters: "TODO";
cookieParameters: "TODO";
pathParameters: DataTypeProperty[];
headerParameters: DataTypeProperty[];
cookieParameters: DataTypeProperty[];
operations: {
get?: Operation;
put?: Operation;
Expand Down
156 changes: 96 additions & 60 deletions packages/ui/src/OpenApiEditorWorker.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as DM from "@apicurio/data-models";
import { Oas20Operation } from "@apicurio/data-models";
import { FindPathItemsVisitor } from "../../visitors/src/path-items.visitor.ts";
import { FindResponseDefinitionsVisitor } from "../../visitors/src/response-definitions.visitor.ts";
import { FindSchemaDefinitionsVisitor } from "../../visitors/src/schema-definitions.visitor.ts";
Expand Down Expand Up @@ -69,6 +68,57 @@ function findSelectedNode(problem: DM.ValidationProblem): SelectedNode {
);
}

function simplifiedTypeToString(st: DM.SimplifiedType) {
if (!st) {
return "No Type";
}
if (st.isRef()) {
return st.type.substr(st.type.lastIndexOf("/") + 1);
} else if (st.isArray()) {
if (st.of && st.of.as) {
return "Array of: " + st.of.type + " as " + st.of.as;
}
if (st.of && st.of.isSimpleType()) {
return "Array of: " + st.of.type;
}
if (st.of && st.of.isRef()) {
return "Array of: " + st.of.type.substr(st.of.type.lastIndexOf("/") + 1);
}
return "Array";
} else if (st.isEnum()) {
return `Enum (${st.enum_.length} items)`;
} else if (st.isSimpleType()) {
if (st.as) {
return st.type + " as " + st.as;
} else {
return st.type;
}
} else {
return "No Type";
}
}

function parameterToTypeToString(p: DM.OasParameter) {
try {
const st = DM.SimplifiedPropertyType.fromParameter(p as DM.Oas20Parameter);
console.log({ p, st });
return simplifiedTypeToString(st);
} catch (e) {
console.error("propertySchemaToTypeToString", e);
}
return "Unknown type";
}

function propertySchemaToTypeToString(p: DM.IPropertySchema) {
try {
const st = DM.SimplifiedPropertyType.fromPropertySchema(p);
return simplifiedTypeToString(st);
} catch (e) {
console.error("propertySchemaToTypeToString", e);
}
return "Unknown type";
}

function getOasPaths(_filter = ""): DM.OasPathItem[] {
const filter = _filter.toLowerCase();
const viz = new FindPathItemsVisitor(filter);
Expand Down Expand Up @@ -207,20 +257,36 @@ export async function parseOpenApi(schema: string) {
}

function oasOperationToOperation(
operation?: DM.Operation
parent: DM.OasPathItem,
operation?: DM.Oas20Operation | DM.Oas30Operation
): Operation | undefined {
if (operation) {
return {
summary: operation.summary,
description: operation.description,
id: operation.operationId,
tags: (operation as Oas20Operation).tags,
tags: operation.tags,
servers: [],
queryParameters: "TODO",
headerParameters: "TODO",
cookieParameters: "TODO",
pathParameters: parent
.getParametersIn("path")
.map<DataTypeProperty>((p) => {
return {
required: p.required,
type: parameterToTypeToString(
parent.getParameter("path", p.getName())
),
name: p.getName(),
description: p.description,
};
}),
headerParameters: [],
cookieParameters: [],
requestBody: undefined,
responses: [],
responses: operation.responses.getResponses().map((r) => ({
statusCode: parseInt(r.getStatusCode(), 10),
description: r.description,
mimeType: "TODO",
})),
securityRequirements: [],
};
}
Expand All @@ -233,14 +299,14 @@ function oasNodeToPath(_path: DM.Node): DocumentPath {
const description = path.description;
const servers: Server[] = [];
const operations = {
get: oasOperationToOperation(path.get),
put: oasOperationToOperation(path.put),
post: oasOperationToOperation(path.post),
delete: oasOperationToOperation(path.delete),
options: oasOperationToOperation(path.options),
head: oasOperationToOperation(path.head),
patch: oasOperationToOperation(path.patch),
trace: oasOperationToOperation(path["trace"]),
get: oasOperationToOperation(path, path.get as DM.Oas30Operation),
put: oasOperationToOperation(path, path.put as DM.Oas30Operation),
post: oasOperationToOperation(path, path.post as DM.Oas30Operation),
delete: oasOperationToOperation(path, path.delete as DM.Oas30Operation),
options: oasOperationToOperation(path, path.options as DM.Oas30Operation),
head: oasOperationToOperation(path, path.head as DM.Oas30Operation),
patch: oasOperationToOperation(path, path.patch as DM.Oas30Operation),
trace: oasOperationToOperation(path, path["trace"] as DM.Oas30Operation),
};
return {
node: {
Expand All @@ -252,20 +318,20 @@ function oasNodeToPath(_path: DM.Node): DocumentPath {
description,
servers,
operations,
queryParameters: "TODO",
headerParameters: "TODO",
cookieParameters: "TODO",
pathParameters: [],
headerParameters: [],
cookieParameters: [],
};
} else {
const path = _path as DM.Oas20PathItem;
const operations = {
get: oasOperationToOperation(path.get),
put: oasOperationToOperation(path.put),
post: oasOperationToOperation(path.post),
delete: oasOperationToOperation(path.delete),
options: oasOperationToOperation(path.options),
head: oasOperationToOperation(path.head),
patch: oasOperationToOperation(path.patch),
get: oasOperationToOperation(path, path.get as DM.Oas20Operation),
put: oasOperationToOperation(path, path.put as DM.Oas20Operation),
post: oasOperationToOperation(path, path.post as DM.Oas20Operation),
delete: oasOperationToOperation(path, path.delete as DM.Oas20Operation),
options: oasOperationToOperation(path, path.options as DM.Oas20Operation),
head: oasOperationToOperation(path, path.head as DM.Oas20Operation),
patch: oasOperationToOperation(path, path.patch as DM.Oas20Operation),
trace: undefined,
};
return {
Expand All @@ -278,9 +344,9 @@ function oasNodeToPath(_path: DM.Node): DocumentPath {
description: "",
servers: [],
operations,
queryParameters: "TODO",
headerParameters: "TODO",
cookieParameters: "TODO",
pathParameters: [],
headerParameters: [],
cookieParameters: [],
};
}
}
Expand All @@ -305,42 +371,12 @@ export async function getDataTypeSnapshot(
}
return false;
}
function typeToString() {
const st = DM.SimplifiedPropertyType.fromPropertySchema(
p
) as DM.SimplifiedType;
if (st.isRef()) {
return st.type.substr(st.type.lastIndexOf("/") + 1);
} else if (st.isArray()) {
if (st.of && st.of.as) {
return "Array of: " + st.of.type + " as " + st.of.as;
}
if (st.of && st.of.isSimpleType()) {
return "Array of: " + st.of.type;
}
if (st.of && st.of.isRef()) {
return (
"Array of: " + st.of.type.substr(st.of.type.lastIndexOf("/") + 1)
);
}
return "Array";
} else if (st.isEnum()) {
return `Enum (${st.enum_.length} items)`;
} else if (st.isSimpleType()) {
if (st.as) {
return st.type + " as " + st.as;
} else {
return st.type;
}
} else {
return "No Type";
}
}

return {
name: p.getPropertyName(),
description: p.description,
required: isRequired(),
type: typeToString(),
type: propertySchemaToTypeToString(p),
};
});

Expand Down
4 changes: 3 additions & 1 deletion packages/ui/src/components/AccordionSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,16 @@ export function AccordionSection({
id,
count,
isFixed = false,
startExpanded = true,
}: {
children: ReactNode;
title: ReactNode;
id: string;
count?: number;
isFixed?: boolean;
startExpanded?: boolean;
}) {
const [isExpanded, setIsExpanded] = useState(true);
const [isExpanded, setIsExpanded] = useState(startExpanded);
const onToggle = () => setIsExpanded((v) => !v);
return (
<AccordionItem isExpanded={isExpanded}>
Expand Down
14 changes: 13 additions & 1 deletion packages/ui/src/components/Markdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { useState } from "react";
import ReactMarkdown, { ExtraProps } from "react-markdown";
import { JSX } from "react/jsx-runtime";
import remarkGfm from "remark-gfm";
import rehypeRaw from "rehype-raw";
import {
CodeEditor,
CodeEditorControl,
Expand All @@ -33,14 +34,19 @@ export function Markdown({
children,
label,
editing = false,
searchTerm,
onChange,
}: {
children: string;
label?: string;
onChange?: (value: string) => void;
searchTerm?: string;
editing?: boolean;
}) {
const darkMode = useDarkMode();
const highlightedMarkdown = searchTerm
? highlightText(children, searchTerm)
: children;
return !editing ? (
<ReactMarkdown
components={{
Expand All @@ -57,9 +63,10 @@ export function Markdown({
li: ListItemMd,
}}
remarkPlugins={[remarkGfm]}
rehypePlugins={[rehypeRaw]}
className={"pf-v6-c-content"}
>
{children}
{highlightedMarkdown}
</ReactMarkdown>
) : (
<Form onSubmit={noop}>
Expand Down Expand Up @@ -94,6 +101,11 @@ export function Markdown({
);
}

const highlightText = (markdown: string, searchTerm: string) => {
const regex = new RegExp(`(${searchTerm})`, "gi");
return markdown.replace(regex, "<mark>$1</mark>");
};

function TextMd({ children, ...props }: IntrinsicElements["p"] & ExtraProps) {
return (
<Content component={ContentVariants.p} {...props}>
Expand Down
Loading

0 comments on commit 7f6f333

Please sign in to comment.