From dff945d6586b436c8a66dbc83ca8ba440e5adaa8 Mon Sep 17 00:00:00 2001 From: Marcell Toth Date: Sun, 13 Sep 2020 05:24:21 +0200 Subject: [PATCH 01/12] chore: extract duplicated inline ref resolver --- packages/elements/src/containers/Docs.tsx | 14 +++----------- packages/elements/src/containers/Provider.tsx | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/packages/elements/src/containers/Docs.tsx b/packages/elements/src/containers/Docs.tsx index ead82fa0d..89dddc976 100644 --- a/packages/elements/src/containers/Docs.tsx +++ b/packages/elements/src/containers/Docs.tsx @@ -1,15 +1,12 @@ -import { pointerToPath } from '@stoplight/json'; -import { SchemaTreeRefDereferenceFn } from '@stoplight/json-schema-viewer'; import { NodeType } from '@stoplight/types'; import { FAIcon, NonIdealState } from '@stoplight/ui-kit'; -import { get, isObject } from 'lodash'; import * as React from 'react'; import { useQuery } from 'urql'; import { DocsSkeleton, ParsedDocs } from '../components/Docs'; import { bundledBranchNode } from '../graphql/BranchNodeBySlug'; import { useParsedData } from '../hooks/useParsedData'; -import { ActiveInfoContext, InlineRefResolverContext, IProvider, Provider } from './Provider'; +import { ActiveInfoContext, InlineRefResolverProvider, IProvider, Provider } from './Provider'; export interface IDocsProps { className?: string; @@ -23,15 +20,10 @@ interface IDocsProvider extends IProvider { const DocsPopup = React.memo<{ nodeType: NodeType; nodeData: unknown; className?: string }>( ({ nodeType, nodeData, className }) => { const document = useParsedData(nodeType, nodeData); - const inlineRefResolver = React.useCallback( - ({ pointer }, _, schema) => - pointer === null ? null : get(isObject(document) ? document : schema, pointerToPath(pointer)), - [document], - ); return ( - + - + ); }, ); diff --git a/packages/elements/src/containers/Provider.tsx b/packages/elements/src/containers/Provider.tsx index a0fb44931..73f7d8968 100644 --- a/packages/elements/src/containers/Provider.tsx +++ b/packages/elements/src/containers/Provider.tsx @@ -1,5 +1,7 @@ +import { pointerToPath } from '@stoplight/json'; import { SchemaTreeRefDereferenceFn } from '@stoplight/json-schema-viewer'; import { IComponentMapping } from '@stoplight/markdown-viewer'; +import { get, isObject } from 'lodash'; import * as React from 'react'; import { Client, Provider as UrqlProvider } from 'urql'; @@ -27,6 +29,22 @@ export const ComponentsContext = createNamedContext(void 0); +interface InlineRefResolverProviderTypes { + document: unknown; +} + +/** + * Populates `InlineRefResolverContext` with a standard inline ref resolver based on `document`. + */ +export const InlineRefResolverProvider: React.FC = ({ document, children }) => { + const inlineRefResolver = React.useCallback( + ({ pointer }, _, schema) => + pointer === null ? null : get(isObject(document) ? document : schema, pointerToPath(pointer)), + [document], + ); + return {children}; +}; + const defaultIcons: NodeIconMapping = {}; export const IconsContext = createNamedContext('IconsContext', defaultIcons); From 6ff78b06b5f3ecd8be6e0ee6c78ba15e77d8101e Mon Sep 17 00:00:00 2001 From: Marcell Toth Date: Sun, 13 Sep 2020 05:41:24 +0200 Subject: [PATCH 02/12] feat: resolve inline refs in API components --- .../src/__stories__/components/Api.tsx | 3 +-- packages/elements/src/containers/API.tsx | 22 ++++++++++--------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/packages/elements/src/__stories__/components/Api.tsx b/packages/elements/src/__stories__/components/Api.tsx index a45ef77bf..0b40ed2e8 100644 --- a/packages/elements/src/__stories__/components/Api.tsx +++ b/packages/elements/src/__stories__/components/Api.tsx @@ -1,5 +1,4 @@ -import { text, withKnobs } from '@storybook/addon-knobs'; -import { boolean } from '@storybook/addon-knobs/react'; +import { boolean, text, withKnobs } from '@storybook/addon-knobs'; import { storiesOf } from '@storybook/react'; import cn from 'classnames'; import * as React from 'react'; diff --git a/packages/elements/src/containers/API.tsx b/packages/elements/src/containers/API.tsx index 114cfde53..d8fa0e799 100644 --- a/packages/elements/src/containers/API.tsx +++ b/packages/elements/src/containers/API.tsx @@ -5,8 +5,7 @@ import * as React from 'react'; import { useLocation } from 'react-router-dom'; import useSwr from 'swr'; -import { Docs } from '../components/Docs'; -import { DocsSkeleton } from '../components/Docs/Skeleton'; +import { Docs, DocsSkeleton } from '../components/Docs'; import { Row } from '../components/TableOfContents/Row'; import { TryIt } from '../components/TryIt'; import { TryItHeader } from '../components/TryIt/header'; @@ -18,6 +17,7 @@ import { IAPI } from '../types'; import { computeTocTree, isOas2, isOas3, isOperation, IUriMap, MODEL_REGEXP, OPERATION_REGEXP } from '../utils/oas'; import { computeOas2UriMap } from '../utils/oas/oas2'; import { computeOas3UriMap } from '../utils/oas/oas3'; +import { InlineRefResolverProvider } from './Provider'; const fetcher = (url: string) => axios.get(url).then(res => res.data); @@ -88,15 +88,17 @@ const APIImpl = withRouter(({ apiDescriptionUrl, linkComponent: LinkCompon />
- - {showTryIt && ( -
-
- - + + + {showTryIt && ( +
+
+ + +
-
- )} + )} +
From 9a2d421eca91c8c0659ee3d21d22edc8535a8c50 Mon Sep 17 00:00:00 2001 From: mmiask Date: Tue, 15 Sep 2020 17:19:44 +0200 Subject: [PATCH 03/12] feat: attempt to add external ref resolver --- packages/elements/src/containers/API.tsx | 4 +-- packages/elements/src/hooks/useBundledData.ts | 34 +++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 packages/elements/src/hooks/useBundledData.ts diff --git a/packages/elements/src/containers/API.tsx b/packages/elements/src/containers/API.tsx index d8fa0e799..fe0243f2c 100644 --- a/packages/elements/src/containers/API.tsx +++ b/packages/elements/src/containers/API.tsx @@ -10,7 +10,7 @@ import { Row } from '../components/TableOfContents/Row'; import { TryIt } from '../components/TryIt'; import { TryItHeader } from '../components/TryIt/header'; import { withRouter } from '../hoc/withRouter'; -import { useParsedValue } from '../hooks/useParsedValue'; +import { useBundledData } from '../hooks/useBundledData'; import { useTocContents } from '../hooks/useTocContents'; import { withStyles } from '../styled'; import { IAPI } from '../types'; @@ -32,7 +32,7 @@ const APIImpl = withRouter(({ apiDescriptionUrl, linkComponent: LinkCompon } }, [error]); - const document = useParsedValue(data); + const document = useBundledData(NodeType.Model, data); const showTryIt = isOperation(pathname); const uriMap = React.useMemo(() => { diff --git a/packages/elements/src/hooks/useBundledData.ts b/packages/elements/src/hooks/useBundledData.ts new file mode 100644 index 000000000..2193d3578 --- /dev/null +++ b/packages/elements/src/hooks/useBundledData.ts @@ -0,0 +1,34 @@ +import $RefParser from '@stoplight/json-schema-ref-parser'; +import { NodeType } from '@stoplight/types'; +import { isObject } from 'lodash'; +import * as React from 'react'; + +import { useParsedData } from './useParsedData'; + +/** + * @param type branch node snapshot type + * @param data branch node snapshot data + */ +export function useBundledData(type: NodeType, data: string) { + const parsedData = useParsedData(type, data); + + const [bundledData, setBundledData] = React.useState(parsedData); + + React.useEffect(() => { + if (!isObject(parsedData) || type !== NodeType.Model) { + setBundledData(parsedData); + return; + } + + $RefParser + .dereference(parsedData, { continueOnError: true }) + .then(res => setBundledData(res)) + .catch(reason => { + console.error(`Could not dereference model: ${reason.message}`); + console.error(reason); + setBundledData(parsedData); + }); + }, [parsedData, type]); + + return bundledData; +} From c78130a6eb8347e98f95085445b33f5e1d3ae8c8 Mon Sep 17 00:00:00 2001 From: Marcell Toth Date: Tue, 15 Sep 2020 17:32:18 +0200 Subject: [PATCH 04/12] fix: external document dereferencing --- packages/elements/src/containers/API.tsx | 10 ++++++---- packages/elements/src/hooks/useDereferencedData.ts | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/elements/src/containers/API.tsx b/packages/elements/src/containers/API.tsx index fe0243f2c..fb82457d1 100644 --- a/packages/elements/src/containers/API.tsx +++ b/packages/elements/src/containers/API.tsx @@ -10,7 +10,8 @@ import { Row } from '../components/TableOfContents/Row'; import { TryIt } from '../components/TryIt'; import { TryItHeader } from '../components/TryIt/header'; import { withRouter } from '../hoc/withRouter'; -import { useBundledData } from '../hooks/useBundledData'; +import { useDereferencedData } from '../hooks/useDereferencedData'; +import { useParsedValue } from '../hooks/useParsedValue'; import { useTocContents } from '../hooks/useTocContents'; import { withStyles } from '../styled'; import { IAPI } from '../types'; @@ -32,7 +33,7 @@ const APIImpl = withRouter(({ apiDescriptionUrl, linkComponent: LinkCompon } }, [error]); - const document = useBundledData(NodeType.Model, data); + const document = useParsedValue(data); const showTryIt = isOperation(pathname); const uriMap = React.useMemo(() => { @@ -62,6 +63,7 @@ const APIImpl = withRouter(({ apiDescriptionUrl, linkComponent: LinkCompon ? NodeType.HttpOperation : NodeType.HttpService; const nodeData = uriMap[pathname] || uriMap['/']; + const dereferencedNodeData = useDereferencedData(nodeType, nodeData); if (error) { return ( @@ -89,12 +91,12 @@ const APIImpl = withRouter(({ apiDescriptionUrl, linkComponent: LinkCompon
- + {showTryIt && (
- +
)} diff --git a/packages/elements/src/hooks/useDereferencedData.ts b/packages/elements/src/hooks/useDereferencedData.ts index 2d9c84bf4..f7de74c8d 100644 --- a/packages/elements/src/hooks/useDereferencedData.ts +++ b/packages/elements/src/hooks/useDereferencedData.ts @@ -10,7 +10,7 @@ import { useParsedData } from './useParsedData'; * @param type branch node snapshot type * @param data branch node snapshot data */ -export function useDereferencedData(type: NodeType, data: string) { +export function useDereferencedData(type: NodeType, data: unknown) { const parsedData = useParsedData(type, data); const [dereferencedData, setDereferencedData] = React.useState(parsedData); From eab78e9914710d5c08e9748984e3ef58571b26e0 Mon Sep 17 00:00:00 2001 From: mmiask Date: Wed, 16 Sep 2020 23:12:51 +0200 Subject: [PATCH 05/12] feat: attemp to write fileResolver --- packages/elements/src/containers/API.tsx | 2 +- .../elements/src/hooks/useDereferencedData.ts | 34 ++++++++++++++++--- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/packages/elements/src/containers/API.tsx b/packages/elements/src/containers/API.tsx index fb82457d1..1f33f2ad9 100644 --- a/packages/elements/src/containers/API.tsx +++ b/packages/elements/src/containers/API.tsx @@ -63,7 +63,7 @@ const APIImpl = withRouter(({ apiDescriptionUrl, linkComponent: LinkCompon ? NodeType.HttpOperation : NodeType.HttpService; const nodeData = uriMap[pathname] || uriMap['/']; - const dereferencedNodeData = useDereferencedData(nodeType, nodeData); + const dereferencedNodeData = useDereferencedData(nodeType, nodeData, { baseUrl: apiDescriptionUrl }); if (error) { return ( diff --git a/packages/elements/src/hooks/useDereferencedData.ts b/packages/elements/src/hooks/useDereferencedData.ts index f7de74c8d..7b497b95b 100644 --- a/packages/elements/src/hooks/useDereferencedData.ts +++ b/packages/elements/src/hooks/useDereferencedData.ts @@ -1,5 +1,6 @@ -import $RefParser from '@stoplight/json-schema-ref-parser'; +import $RefParser, { ResolverOptions } from '@stoplight/json-schema-ref-parser'; import { NodeType } from '@stoplight/types'; +import axios from 'axios'; import { isObject } from 'lodash'; import * as React from 'react'; @@ -10,7 +11,12 @@ import { useParsedData } from './useParsedData'; * @param type branch node snapshot type * @param data branch node snapshot data */ -export function useDereferencedData(type: NodeType, data: unknown) { + +interface Options { + baseUrl?: string; +} + +export function useDereferencedData(type: NodeType, data: unknown, options?: Options) { const parsedData = useParsedData(type, data); const [dereferencedData, setDereferencedData] = React.useState(parsedData); @@ -21,16 +27,36 @@ export function useDereferencedData(type: NodeType, data: unknown) { setDereferencedData(parsedData); return; } + let fileResolver: ResolverOptions | undefined; + if (options?.baseUrl) { + fileResolver = { + order: 202, + canRead: true, + + async read(file) { + const fullPath = new URL(file.url, options.baseUrl); + console.log(fullPath.toString()); + const response = await axios.get(fullPath.toString(), { responseType: 'arraybuffer' }); + console.log(response); + return response.data; + }, + }; + } else { + fileResolver = undefined; + } $RefParser - .dereference(parsedData, { continueOnError: true }) + .dereference(parsedData, { + continueOnError: true, + resolve: fileResolver ? { file: fileResolver } : {}, + }) .then(res => setDereferencedData(res)) .catch(reason => { console.error(`Could not dereference operation: ${reason.message}`); console.error(reason); setDereferencedData(parsedData); }); - }, [parsedData, type]); + }, [parsedData, type, options?.baseUrl]); return dereferencedData; } From eade97236e31398afb7b1d158f5b165e5d042ec9 Mon Sep 17 00:00:00 2001 From: Marcell Toth Date: Thu, 17 Sep 2020 00:36:06 +0200 Subject: [PATCH 06/12] feat: fix relative path resolving --- .../elements/src/hooks/useDereferencedData.ts | 35 ++++++------------- 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/packages/elements/src/hooks/useDereferencedData.ts b/packages/elements/src/hooks/useDereferencedData.ts index 7b497b95b..36adbbc28 100644 --- a/packages/elements/src/hooks/useDereferencedData.ts +++ b/packages/elements/src/hooks/useDereferencedData.ts @@ -1,6 +1,5 @@ -import $RefParser, { ResolverOptions } from '@stoplight/json-schema-ref-parser'; +import $RefParser from '@stoplight/json-schema-ref-parser'; import { NodeType } from '@stoplight/types'; -import axios from 'axios'; import { isObject } from 'lodash'; import * as React from 'react'; @@ -27,29 +26,8 @@ export function useDereferencedData(type: NodeType, data: unknown, options?: Opt setDereferencedData(parsedData); return; } - let fileResolver: ResolverOptions | undefined; - if (options?.baseUrl) { - fileResolver = { - order: 202, - canRead: true, - - async read(file) { - const fullPath = new URL(file.url, options.baseUrl); - console.log(fullPath.toString()); - const response = await axios.get(fullPath.toString(), { responseType: 'arraybuffer' }); - console.log(response); - return response.data; - }, - }; - } else { - fileResolver = undefined; - } - $RefParser - .dereference(parsedData, { - continueOnError: true, - resolve: fileResolver ? { file: fileResolver } : {}, - }) + doDereference(parsedData, options?.baseUrl) .then(res => setDereferencedData(res)) .catch(reason => { console.error(`Could not dereference operation: ${reason.message}`); @@ -60,3 +38,12 @@ export function useDereferencedData(type: NodeType, data: unknown, options?: Opt return dereferencedData; } + +const commonDereferenceOptions = { continueOnError: true }; +const doDereference = (data: object, baseUrl?: string) => { + if (!baseUrl) { + return $RefParser.dereference(data, commonDereferenceOptions); + } else { + return $RefParser.dereference(baseUrl, data, commonDereferenceOptions); + } +}; From 8723f004e2d60b3e973507d7f1464fca0f6044ee Mon Sep 17 00:00:00 2001 From: mmiask Date: Thu, 17 Sep 2020 14:50:53 +0200 Subject: [PATCH 07/12] fix: deleting redundant useBundledData hook --- packages/elements/src/hooks/useBundledData.ts | 34 ------------------- 1 file changed, 34 deletions(-) delete mode 100644 packages/elements/src/hooks/useBundledData.ts diff --git a/packages/elements/src/hooks/useBundledData.ts b/packages/elements/src/hooks/useBundledData.ts deleted file mode 100644 index 2193d3578..000000000 --- a/packages/elements/src/hooks/useBundledData.ts +++ /dev/null @@ -1,34 +0,0 @@ -import $RefParser from '@stoplight/json-schema-ref-parser'; -import { NodeType } from '@stoplight/types'; -import { isObject } from 'lodash'; -import * as React from 'react'; - -import { useParsedData } from './useParsedData'; - -/** - * @param type branch node snapshot type - * @param data branch node snapshot data - */ -export function useBundledData(type: NodeType, data: string) { - const parsedData = useParsedData(type, data); - - const [bundledData, setBundledData] = React.useState(parsedData); - - React.useEffect(() => { - if (!isObject(parsedData) || type !== NodeType.Model) { - setBundledData(parsedData); - return; - } - - $RefParser - .dereference(parsedData, { continueOnError: true }) - .then(res => setBundledData(res)) - .catch(reason => { - console.error(`Could not dereference model: ${reason.message}`); - console.error(reason); - setBundledData(parsedData); - }); - }, [parsedData, type]); - - return bundledData; -} From 5e9b2b4efb1c01cc2746ce51ace92fa1ce5a104e Mon Sep 17 00:00:00 2001 From: mmiask Date: Fri, 18 Sep 2020 16:03:29 +0200 Subject: [PATCH 08/12] fix: addressed review comments using bundled data instead of dereferenced in API container --- packages/elements/src/containers/API.tsx | 8 ++-- packages/elements/src/hooks/useBundledData.ts | 47 +++++++++++++++++++ .../elements/src/hooks/useDereferencedData.ts | 21 ++------- 3 files changed, 55 insertions(+), 21 deletions(-) create mode 100644 packages/elements/src/hooks/useBundledData.ts diff --git a/packages/elements/src/containers/API.tsx b/packages/elements/src/containers/API.tsx index 1f33f2ad9..44e0c0ef5 100644 --- a/packages/elements/src/containers/API.tsx +++ b/packages/elements/src/containers/API.tsx @@ -10,7 +10,7 @@ import { Row } from '../components/TableOfContents/Row'; import { TryIt } from '../components/TryIt'; import { TryItHeader } from '../components/TryIt/header'; import { withRouter } from '../hoc/withRouter'; -import { useDereferencedData } from '../hooks/useDereferencedData'; +import { useBundledData } from '../hooks/useBundledData'; import { useParsedValue } from '../hooks/useParsedValue'; import { useTocContents } from '../hooks/useTocContents'; import { withStyles } from '../styled'; @@ -63,7 +63,7 @@ const APIImpl = withRouter(({ apiDescriptionUrl, linkComponent: LinkCompon ? NodeType.HttpOperation : NodeType.HttpService; const nodeData = uriMap[pathname] || uriMap['/']; - const dereferencedNodeData = useDereferencedData(nodeType, nodeData, { baseUrl: apiDescriptionUrl }); + const bundledNodeData = useBundledData(nodeType, nodeData, { baseUrl: apiDescriptionUrl }); if (error) { return ( @@ -91,12 +91,12 @@ const APIImpl = withRouter(({ apiDescriptionUrl, linkComponent: LinkCompon
- + {showTryIt && (
- +
)} diff --git a/packages/elements/src/hooks/useBundledData.ts b/packages/elements/src/hooks/useBundledData.ts new file mode 100644 index 000000000..dcc4fa6c5 --- /dev/null +++ b/packages/elements/src/hooks/useBundledData.ts @@ -0,0 +1,47 @@ +import $RefParser from '@stoplight/json-schema-ref-parser'; +import { NodeType } from '@stoplight/types'; +import { isObject } from 'lodash'; +import * as React from 'react'; + +import { useParsedData } from './useParsedData'; + +/** + * @param type branch node snapshot type + * @param data branch node snapshot data + */ + +interface Options { + baseUrl?: string; +} + +export function useBundledData(type: NodeType, data: unknown, options?: Options) { + const parsedData = useParsedData(type, data); + + const [bundledData, setBundledData] = React.useState(parsedData); + + React.useEffect(() => { + if (!isObject(parsedData) || type !== NodeType.HttpOperation) { + setBundledData(parsedData); + return; + } + + doBundle(parsedData, options?.baseUrl) + .then(res => setBundledData(res)) + .catch(reason => { + console.error(`Could not bundle: ${reason.message}`); + console.error(reason); + setBundledData(parsedData); + }); + }, [parsedData, type, options?.baseUrl]); + + return bundledData; +} + +const commonBundleOptions = { continueOnError: true }; +const doBundle = (data: object, baseUrl?: string) => { + if (!baseUrl) { + return $RefParser.bundle(data, commonBundleOptions); + } else { + return $RefParser.bundle(baseUrl, data, commonBundleOptions); + } +}; diff --git a/packages/elements/src/hooks/useDereferencedData.ts b/packages/elements/src/hooks/useDereferencedData.ts index 36adbbc28..2d9c84bf4 100644 --- a/packages/elements/src/hooks/useDereferencedData.ts +++ b/packages/elements/src/hooks/useDereferencedData.ts @@ -10,12 +10,7 @@ import { useParsedData } from './useParsedData'; * @param type branch node snapshot type * @param data branch node snapshot data */ - -interface Options { - baseUrl?: string; -} - -export function useDereferencedData(type: NodeType, data: unknown, options?: Options) { +export function useDereferencedData(type: NodeType, data: string) { const parsedData = useParsedData(type, data); const [dereferencedData, setDereferencedData] = React.useState(parsedData); @@ -27,23 +22,15 @@ export function useDereferencedData(type: NodeType, data: unknown, options?: Opt return; } - doDereference(parsedData, options?.baseUrl) + $RefParser + .dereference(parsedData, { continueOnError: true }) .then(res => setDereferencedData(res)) .catch(reason => { console.error(`Could not dereference operation: ${reason.message}`); console.error(reason); setDereferencedData(parsedData); }); - }, [parsedData, type, options?.baseUrl]); + }, [parsedData, type]); return dereferencedData; } - -const commonDereferenceOptions = { continueOnError: true }; -const doDereference = (data: object, baseUrl?: string) => { - if (!baseUrl) { - return $RefParser.dereference(data, commonDereferenceOptions); - } else { - return $RefParser.dereference(baseUrl, data, commonDereferenceOptions); - } -}; From e6b0d07551ce0ede4085f59a4c231f927b0603cd Mon Sep 17 00:00:00 2001 From: mmiask Date: Mon, 28 Sep 2020 14:34:03 +0200 Subject: [PATCH 09/12] fix: fixed imports --- packages/elements/src/components/API/SidebarLayout.tsx | 4 ++-- packages/elements/src/components/API/StackedLayout.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/elements/src/components/API/SidebarLayout.tsx b/packages/elements/src/components/API/SidebarLayout.tsx index b0c8caefe..34374b7d2 100644 --- a/packages/elements/src/components/API/SidebarLayout.tsx +++ b/packages/elements/src/components/API/SidebarLayout.tsx @@ -1,8 +1,8 @@ import { TableOfContents } from '@stoplight/ui-kit'; -import { InlineRefResolverProvider } from 'elements/src/containers/Provider'; -import { useBundledData } from 'elements/src/hooks/useBundledData'; import * as React from 'react'; +import { InlineRefResolverProvider } from '../../containers/Provider'; +import { useBundledData } from '../../hooks/useBundledData'; import { useTocContents } from '../../hooks/useTocContents'; import { ILinkComponentProps, ITableOfContentsTree } from '../../types'; import { getNodeType, isOperation, IUriMap } from '../../utils/oas'; diff --git a/packages/elements/src/components/API/StackedLayout.tsx b/packages/elements/src/components/API/StackedLayout.tsx index b64f8db04..66911e483 100644 --- a/packages/elements/src/components/API/StackedLayout.tsx +++ b/packages/elements/src/components/API/StackedLayout.tsx @@ -2,12 +2,12 @@ import { Group as GroupItem, isGroup, isItem, ITableOfContents, Item } from '@st import { IHttpOperation, NodeType } from '@stoplight/types'; import { Collapse, Icon, Tab, Tabs } from '@stoplight/ui-kit'; import cn from 'classnames'; -import { InlineRefResolverProvider } from 'elements/src/containers/Provider'; -import { useBundledData } from 'elements/src/hooks/useBundledData'; import * as React from 'react'; import { useLocation } from 'react-router-dom'; import { HttpMethodColors } from '../../constants'; +import { InlineRefResolverProvider } from '../../containers/Provider'; +import { useBundledData } from '../../hooks/useBundledData'; import { getNodeType, IUriMap } from '../../utils/oas'; import { Docs } from '../Docs'; import { TryIt } from '../TryIt'; From a438206085c76a0f4c42b9d89a38a230b91f8430 Mon Sep 17 00:00:00 2001 From: mmiask Date: Mon, 28 Sep 2020 18:06:06 +0200 Subject: [PATCH 10/12] fix: address review comments --- .../src/components/API/SidebarLayout.tsx | 31 +++++------- .../src/components/API/StackedLayout.tsx | 49 +++---------------- packages/elements/src/containers/API.tsx | 34 +++++++------ 3 files changed, 38 insertions(+), 76 deletions(-) diff --git a/packages/elements/src/components/API/SidebarLayout.tsx b/packages/elements/src/components/API/SidebarLayout.tsx index 34374b7d2..bebefe7ac 100644 --- a/packages/elements/src/components/API/SidebarLayout.tsx +++ b/packages/elements/src/components/API/SidebarLayout.tsx @@ -1,11 +1,9 @@ import { TableOfContents } from '@stoplight/ui-kit'; import * as React from 'react'; -import { InlineRefResolverProvider } from '../../containers/Provider'; -import { useBundledData } from '../../hooks/useBundledData'; import { useTocContents } from '../../hooks/useTocContents'; import { ILinkComponentProps, ITableOfContentsTree } from '../../types'; -import { getNodeType, isOperation, IUriMap } from '../../utils/oas'; +import { getNodeType, isOperation } from '../../utils/oas'; import { Docs } from '../Docs'; import { Row } from '../TableOfContents/Row'; import { TryIt } from '../TryIt'; @@ -13,20 +11,17 @@ import { TryItHeader } from '../TryIt/header'; type SidebarLayoutProps = { pathname: string; - uriMap: IUriMap; tree: ITableOfContentsTree; + bundledNodeData: unknown; linkComponent?: React.ComponentType; apiDescriptionUrl?: string; - document?: unknown; }; export const SidebarLayout: React.FC = ({ pathname, tree, - uriMap, + bundledNodeData, linkComponent: LinkComponent, - apiDescriptionUrl, - document, }) => { const contents = useTocContents(tree).map(item => ({ ...item, @@ -35,9 +30,7 @@ export const SidebarLayout: React.FC = ({ })); const nodeType = getNodeType(pathname); - const nodeData = uriMap[pathname] || uriMap['/']; const showTryIt = isOperation(pathname); - const bundledNodeData = useBundledData(nodeType, nodeData, { baseUrl: apiDescriptionUrl }); return ( <> @@ -48,17 +41,15 @@ export const SidebarLayout: React.FC = ({ />
- - - {showTryIt && ( -
-
- - -
+ + {showTryIt && ( +
+
+ +
- )} - +
+ )}
diff --git a/packages/elements/src/components/API/StackedLayout.tsx b/packages/elements/src/components/API/StackedLayout.tsx index 66911e483..170989a11 100644 --- a/packages/elements/src/components/API/StackedLayout.tsx +++ b/packages/elements/src/components/API/StackedLayout.tsx @@ -6,8 +6,6 @@ import * as React from 'react'; import { useLocation } from 'react-router-dom'; import { HttpMethodColors } from '../../constants'; -import { InlineRefResolverProvider } from '../../containers/Provider'; -import { useBundledData } from '../../hooks/useBundledData'; import { getNodeType, IUriMap } from '../../utils/oas'; import { Docs } from '../Docs'; import { TryIt } from '../TryIt'; @@ -15,8 +13,7 @@ import { TryIt } from '../TryIt'; type StackedLayoutProps = { uriMap: IUriMap; tree: ITableOfContents; - apiDescriptionUrl?: string; - document?: unknown; + bundledNodeData: unknown; }; type ItemRowProps = { @@ -24,15 +21,13 @@ type ItemRowProps = { nodeType: NodeType; type: string; title: string; - apiDescriptionUrl?: string; - document?: unknown; }; const itemMatchesHash = (hash: string, item: Pick) => { return hash.substr(1) === `${item.title}-${item.type}`; }; -export const StackedLayout: React.FC = ({ uriMap, tree, apiDescriptionUrl, document }) => { +export const StackedLayout: React.FC = ({ uriMap, tree, bundledNodeData }) => { const groups = tree.items.filter(isGroup); return ( @@ -41,24 +36,13 @@ export const StackedLayout: React.FC = ({ uriMap, tree, apiD
{groups.map(group => ( - + ))}
); }; -const Group: React.FC<{ group: GroupItem; uriMap: IUriMap; apiDescriptionUrl?: string; document?: unknown }> = ({ - group, - uriMap, - apiDescriptionUrl, - document, -}) => { +const Group: React.FC<{ group: GroupItem; uriMap: IUriMap }> = ({ group, uriMap }) => { const [isExpanded, setIsExpanded] = React.useState(false); const { hash } = useLocation(); const scrollRef = React.useRef(null); @@ -120,17 +104,7 @@ const Group: React.FC<{ group: GroupItem; uriMap: IUriMap; apiDescriptionUrl?: s .filter(isItem) .map(mapItems) .map(({ nodeData, nodeType, type, title, uri }) => { - return ( - - ); + return ; })}
@@ -139,14 +113,13 @@ const Group: React.FC<{ group: GroupItem; uriMap: IUriMap; apiDescriptionUrl?: s type PanelTabId = 'docs' | 'tryit'; -const ItemRow: React.FC = ({ data, nodeType, type, title, apiDescriptionUrl, document }) => { +const ItemRow: React.FC = ({ data, nodeType, type, title }) => { const [isExpanded, setIsExpanded] = React.useState(false); const { hash } = useLocation(); const scrollRef = React.useRef(null); const [tabId, setTabId] = React.useState('docs'); const color = HttpMethodColors[type] || 'gray'; const showTabs = nodeType === NodeType.HttpOperation; - const bundledNodeData = useBundledData(nodeType, data, { baseUrl: apiDescriptionUrl }); const onClick = React.useCallback(() => setIsExpanded(!isExpanded), [isExpanded]); @@ -195,11 +168,7 @@ const ItemRow: React.FC = ({ data, nodeType, type, title, apiDescr id="docs" title="Docs" className="p-4" - panel={ - - - - } + panel={} /> = ({ data, nodeType, type, title, apiDescr /> ) : ( - - - + )}
diff --git a/packages/elements/src/containers/API.tsx b/packages/elements/src/containers/API.tsx index 42bc79460..64315401f 100644 --- a/packages/elements/src/containers/API.tsx +++ b/packages/elements/src/containers/API.tsx @@ -9,12 +9,14 @@ import { SidebarLayout } from '../components/API/SidebarLayout'; import { StackedLayout } from '../components/API/StackedLayout'; import { DocsSkeleton } from '../components/Docs/Skeleton'; import { withRouter } from '../hoc/withRouter'; +import { useBundledData } from '../hooks/useBundledData'; import { useParsedValue } from '../hooks/useParsedValue'; import { withStyles } from '../styled'; import { LinkComponentType, RoutingProps } from '../types'; -import { computeNodeData, isOas2, isOas3, IUriMap } from '../utils/oas'; +import { computeNodeData, getNodeType, isOas2, isOas3, IUriMap } from '../utils/oas'; import { computeOas2UriMap } from '../utils/oas/oas2'; import { computeOas3UriMap } from '../utils/oas/oas3'; +import { InlineRefResolverProvider } from './Provider'; const fetcher = (url: string) => axios.get(url).then(res => res.data); @@ -54,6 +56,8 @@ const APIImpl = withRouter(function API({ apiDescriptionUrl, linkCompo const nodes = computeNodeData(uriMap); const tree = generateToC(nodes); const nodeData = uriMap[pathname] || uriMap['/']; + const nodeType = getNodeType(pathname); + const bundledNodeData = useBundledData(nodeType, nodeData, { baseUrl: apiDescriptionUrl }); if (error) { return ( @@ -72,20 +76,20 @@ const APIImpl = withRouter(function API({ apiDescriptionUrl, linkCompo } return ( -
- {layout === 'stacked' ? ( - - ) : ( - - )} -
+ +
+ {layout === 'stacked' ? ( + + ) : ( + + )} +
+
); }); From 0a7e67647f7b04565341a8a3f40a42a0d691d63b Mon Sep 17 00:00:00 2001 From: Marcell Toth Date: Tue, 29 Sep 2020 11:49:02 +0200 Subject: [PATCH 11/12] chore: clean up bundling logic --- .../src/__stories__/components/Api.tsx | 5 +-- .../src/components/API/SidebarLayout.tsx | 12 +++---- .../src/components/API/StackedLayout.tsx | 19 +++-------- packages/elements/src/containers/API.tsx | 28 ++++++--------- ...edData.ts => useBundleRefsIntoDocument.ts} | 34 +++++++++++-------- 5 files changed, 42 insertions(+), 56 deletions(-) rename packages/elements/src/hooks/{useBundledData.ts => useBundleRefsIntoDocument.ts} (50%) diff --git a/packages/elements/src/__stories__/components/Api.tsx b/packages/elements/src/__stories__/components/Api.tsx index 1837bec8f..a7e5e900e 100644 --- a/packages/elements/src/__stories__/components/Api.tsx +++ b/packages/elements/src/__stories__/components/Api.tsx @@ -7,10 +7,7 @@ import { API } from '../../containers/API'; const darkMode = () => boolean('dark mode', false); const apiDescriptionUrl = () => - text( - 'apiDescriptionUrl', - 'https://raw.githubusercontent.com/stoplightio/Public-APIs/master/reference/zoom/zoom.yaml', - ); + text('apiDescriptionUrl', 'https://raw.githubusercontent.com/mmiask/anothertesting/master/reference/test.v1.yaml'); storiesOf('Public/API', module) .addDecorator(withKnobs()) diff --git a/packages/elements/src/components/API/SidebarLayout.tsx b/packages/elements/src/components/API/SidebarLayout.tsx index bebefe7ac..1f495413b 100644 --- a/packages/elements/src/components/API/SidebarLayout.tsx +++ b/packages/elements/src/components/API/SidebarLayout.tsx @@ -3,7 +3,7 @@ import * as React from 'react'; import { useTocContents } from '../../hooks/useTocContents'; import { ILinkComponentProps, ITableOfContentsTree } from '../../types'; -import { getNodeType, isOperation } from '../../utils/oas'; +import { getNodeType, isOperation, IUriMap } from '../../utils/oas'; import { Docs } from '../Docs'; import { Row } from '../TableOfContents/Row'; import { TryIt } from '../TryIt'; @@ -11,16 +11,15 @@ import { TryItHeader } from '../TryIt/header'; type SidebarLayoutProps = { pathname: string; + uriMap: IUriMap; tree: ITableOfContentsTree; - bundledNodeData: unknown; linkComponent?: React.ComponentType; - apiDescriptionUrl?: string; }; export const SidebarLayout: React.FC = ({ pathname, tree, - bundledNodeData, + uriMap, linkComponent: LinkComponent, }) => { const contents = useTocContents(tree).map(item => ({ @@ -30,6 +29,7 @@ export const SidebarLayout: React.FC = ({ })); const nodeType = getNodeType(pathname); + const nodeData = uriMap[pathname] || uriMap['/']; const showTryIt = isOperation(pathname); return ( @@ -41,12 +41,12 @@ export const SidebarLayout: React.FC = ({ />
- + {showTryIt && (
- +
)} diff --git a/packages/elements/src/components/API/StackedLayout.tsx b/packages/elements/src/components/API/StackedLayout.tsx index 170989a11..e7731555a 100644 --- a/packages/elements/src/components/API/StackedLayout.tsx +++ b/packages/elements/src/components/API/StackedLayout.tsx @@ -13,7 +13,6 @@ import { TryIt } from '../TryIt'; type StackedLayoutProps = { uriMap: IUriMap; tree: ITableOfContents; - bundledNodeData: unknown; }; type ItemRowProps = { @@ -27,7 +26,7 @@ const itemMatchesHash = (hash: string, item: Pick = ({ uriMap, tree, bundledNodeData }) => { +export const StackedLayout: React.FC = ({ uriMap, tree }) => { const groups = tree.items.filter(isGroup); return ( @@ -164,21 +163,11 @@ const ItemRow: React.FC = ({ data, nodeType, type, title }) => { onChange={(tabId: PanelTabId) => setTabId(tabId)} renderActiveTabPanelOnly > - } - /> - } - /> + } /> + } /> ) : ( - + )}
diff --git a/packages/elements/src/containers/API.tsx b/packages/elements/src/containers/API.tsx index 64315401f..82911c313 100644 --- a/packages/elements/src/containers/API.tsx +++ b/packages/elements/src/containers/API.tsx @@ -9,11 +9,11 @@ import { SidebarLayout } from '../components/API/SidebarLayout'; import { StackedLayout } from '../components/API/StackedLayout'; import { DocsSkeleton } from '../components/Docs/Skeleton'; import { withRouter } from '../hoc/withRouter'; -import { useBundledData } from '../hooks/useBundledData'; +import { useBundleRefsIntoDocument } from '../hooks/useBundleRefsIntoDocument'; import { useParsedValue } from '../hooks/useParsedValue'; import { withStyles } from '../styled'; import { LinkComponentType, RoutingProps } from '../types'; -import { computeNodeData, getNodeType, isOas2, isOas3, IUriMap } from '../utils/oas'; +import { computeNodeData, isOas2, isOas3, IUriMap } from '../utils/oas'; import { computeOas2UriMap } from '../utils/oas/oas2'; import { computeOas3UriMap } from '../utils/oas/oas3'; import { InlineRefResolverProvider } from './Provider'; @@ -38,26 +38,25 @@ const APIImpl = withRouter(function API({ apiDescriptionUrl, linkCompo }, [error]); const document = useParsedValue(data); + const bundledDocument = useBundleRefsIntoDocument(document, { baseUrl: apiDescriptionUrl }); const uriMap = React.useMemo(() => { let map: IUriMap = {}; - if (document) { - if (isOas3(document)) { - map = computeOas3UriMap(document); - } else if (isOas2(document)) { - map = computeOas2UriMap(document); + if (bundledDocument) { + if (isOas3(bundledDocument)) { + map = computeOas3UriMap(bundledDocument); + } else if (isOas2(bundledDocument)) { + map = computeOas2UriMap(bundledDocument); } else { console.warn('Document type is unknown'); } } return map; - }, [document]); + }, [bundledDocument]); const nodes = computeNodeData(uriMap); const tree = generateToC(nodes); const nodeData = uriMap[pathname] || uriMap['/']; - const nodeType = getNodeType(pathname); - const bundledNodeData = useBundledData(nodeType, nodeData, { baseUrl: apiDescriptionUrl }); if (error) { return ( @@ -79,14 +78,9 @@ const APIImpl = withRouter(function API({ apiDescriptionUrl, linkCompo
{layout === 'stacked' ? ( - + ) : ( - + )}
diff --git a/packages/elements/src/hooks/useBundledData.ts b/packages/elements/src/hooks/useBundleRefsIntoDocument.ts similarity index 50% rename from packages/elements/src/hooks/useBundledData.ts rename to packages/elements/src/hooks/useBundleRefsIntoDocument.ts index dcc4fa6c5..2cd156071 100644 --- a/packages/elements/src/hooks/useBundledData.ts +++ b/packages/elements/src/hooks/useBundleRefsIntoDocument.ts @@ -1,10 +1,7 @@ import $RefParser from '@stoplight/json-schema-ref-parser'; -import { NodeType } from '@stoplight/types'; import { isObject } from 'lodash'; import * as React from 'react'; -import { useParsedData } from './useParsedData'; - /** * @param type branch node snapshot type * @param data branch node snapshot data @@ -14,25 +11,34 @@ interface Options { baseUrl?: string; } -export function useBundledData(type: NodeType, data: unknown, options?: Options) { - const parsedData = useParsedData(type, data); - - const [bundledData, setBundledData] = React.useState(parsedData); +/** + * Fetches and bundles external $refs into an OAS document. Internal $refs are not resolved. + */ +export function useBundleRefsIntoDocument(document: unknown, options?: Options) { + const [bundledData, setBundledData] = React.useState(document); React.useEffect(() => { - if (!isObject(parsedData) || type !== NodeType.HttpOperation) { - setBundledData(parsedData); + if (!isObject(document)) { + setBundledData(document); return; } - doBundle(parsedData, options?.baseUrl) - .then(res => setBundledData(res)) + let isActive = true; + doBundle(document, options?.baseUrl) + .then(res => { + if (isActive) { + setBundledData({ ...res }); // this hmm....library mutates document so a shallow copy is required to force a rerender in all cases + } + }) .catch(reason => { console.error(`Could not bundle: ${reason.message}`); - console.error(reason); - setBundledData(parsedData); + setBundledData(document); }); - }, [parsedData, type, options?.baseUrl]); + + return () => { + isActive = false; + }; + }, [document, options?.baseUrl]); return bundledData; } From 5792351bfc1281e93c02e1bdb6d3d9dd0da2ac99 Mon Sep 17 00:00:00 2001 From: Marcell Toth Date: Tue, 29 Sep 2020 11:51:07 +0200 Subject: [PATCH 12/12] chore: undo accidental story change --- packages/elements/src/__stories__/components/Api.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/elements/src/__stories__/components/Api.tsx b/packages/elements/src/__stories__/components/Api.tsx index a7e5e900e..1837bec8f 100644 --- a/packages/elements/src/__stories__/components/Api.tsx +++ b/packages/elements/src/__stories__/components/Api.tsx @@ -7,7 +7,10 @@ import { API } from '../../containers/API'; const darkMode = () => boolean('dark mode', false); const apiDescriptionUrl = () => - text('apiDescriptionUrl', 'https://raw.githubusercontent.com/mmiask/anothertesting/master/reference/test.v1.yaml'); + text( + 'apiDescriptionUrl', + 'https://raw.githubusercontent.com/stoplightio/Public-APIs/master/reference/zoom/zoom.yaml', + ); storiesOf('Public/API', module) .addDecorator(withKnobs())