Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
"sass-loader": "^10.1.1",
"style-loader": "^2.0.0",
"ts-loader": "^8.1.0",
"typescript": "^4.2.3",
"typescript": "^4.3.2",
"webpack": "^4.46.0",
"webpack-cli": "^4.6.0",
"webpack-dev-server": "^3.11.2",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"start-server-and-test": "^1.11.6",
"ts-jest": "^26.4.4",
"tsconfig-paths-webpack-plugin": "^3.2.0",
"typescript": "4.2.2"
"typescript": "4.3.2"
},
"scripts": {
"demo": "yarn workspace @stoplight/elements-demo",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import * as React from 'react';

import { useInlineRefResolver } from '../../../context/InlineRefResolver';
import { isJSONSchema } from '../../../utils/guards';
import { getOriginalObject } from '../../../utils/ref-resolving/resolvedObject';
import { MarkdownViewer } from '../../MarkdownViewer';
import { SubSectionPanel } from '../Sections';

Expand Down Expand Up @@ -46,7 +47,7 @@ export const Body = ({ body: { contents = [], description }, onChange }: BodyPro

{isJSONSchema(schema) && (
<Box>
<JsonSchemaViewer resolveRef={refResolver} schema={schema} viewMode="write" hideExamples />
<JsonSchemaViewer resolveRef={refResolver} schema={getOriginalObject(schema)} viewMode="write" hideExamples />
</Box>
)}
</SubSectionPanel>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { flatten, sortBy } from 'lodash';
import * as React from 'react';

import { MockingContext } from '../../../containers/MockingProvider';
import { useResolvedObject } from '../../../context/InlineRefResolver';
import { getServiceUriFromOperation } from '../../../utils/oas/security';
import { MarkdownViewer } from '../../MarkdownViewer';
import { TryItWithRequestSamples } from '../../TryIt';
Expand All @@ -17,7 +18,9 @@ import { Responses } from './Responses';
export type HttpOperationProps = DocsComponentProps<IHttpOperation>;

const HttpOperationComponent = React.memo<HttpOperationProps>(
({ className, data, headless, uri, hideTryIt, hideTryItPanel, allowRouting = false }) => {
({ className, data: unresolvedData, headless, uri, hideTryIt, hideTryItPanel, allowRouting = false }) => {
const data = useResolvedObject(unresolvedData) as IHttpOperation;

const mocking = React.useContext(MockingContext);
const isDeprecated = !!data.deprecated;
const isInternal = !!data.internal;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import { Dictionary, HttpParamStyles, IHttpParam } from '@stoplight/types';
import { get, isEmpty, omit, omitBy, sortBy } from 'lodash';
import * as React from 'react';

import { useInlineRefResolver } from '../../../context/InlineRefResolver';

type ParameterType = 'query' | 'header' | 'path' | 'cookie';

interface ParametersProps {
Expand All @@ -32,24 +30,12 @@ const defaultStyle = {
} as const;

export const Parameters: React.FunctionComponent<ParametersProps> = ({ parameters, parameterType }) => {
const resolveRef = useInlineRefResolver();
if (!parameters || !parameters.length) return null;

return (
<VStack spacing={2} divider={<Box borderT borderColor="light" w="full"></Box>}>
{sortBy(parameters, ['required', 'name']).map(parameter => {
const resolvedSchema =
parameter.schema?.$ref && resolveRef
? resolveRef({ pointer: parameter.schema.$ref, source: null }, null, {})
: null;

return (
<Parameter
key={parameter.name}
parameter={resolvedSchema ? { ...parameter, schema: resolvedSchema } : parameter}
parameterType={parameterType}
/>
);
return <Parameter key={parameter.name} parameter={parameter} parameterType={parameterType} />;
})}
</VStack>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { sortBy, uniqBy } from 'lodash';
import * as React from 'react';

import { useInlineRefResolver } from '../../../context/InlineRefResolver';
import { getOriginalObject } from '../../../utils/ref-resolving/resolvedObject';
import { MarkdownViewer } from '../../MarkdownViewer';
import { SectionTitle, SubSectionPanel } from '../Sections';
import { Parameters } from './Parameters';
Expand Down Expand Up @@ -97,7 +98,12 @@ const Response = ({ response: { contents = [], headers = [], description }, onMe
>
{schema && (
<Box>
<JsonSchemaViewer schema={schema} resolveRef={refResolver} viewMode="read" hideExamples />
<JsonSchemaViewer
schema={getOriginalObject(schema)}
resolveRef={refResolver}
viewMode="read"
hideExamples
/>
</Box>
)}
</SubSectionPanel>
Expand Down
9 changes: 6 additions & 3 deletions packages/elements-core/src/components/Docs/Model/Model.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,19 @@ import cn from 'classnames';
import { JSONSchema7 } from 'json-schema';
import * as React from 'react';

import { useInlineRefResolver } from '../../../context/InlineRefResolver';
import { useInlineRefResolver, useResolvedObject } from '../../../context/InlineRefResolver';
import { generateExampleFromJsonSchema } from '../../../utils/exampleGeneration';
import { getOriginalObject } from '../../../utils/ref-resolving/resolvedObject';
import { MarkdownViewer } from '../../MarkdownViewer';
import { DocsComponentProps } from '..';
import { InternalBadge } from '../HttpOperation/Badges';

export type ModelProps = DocsComponentProps<JSONSchema7>;

const ModelComponent: React.FC<ModelProps> = ({ data, className, headless, nodeTitle }) => {
const ModelComponent: React.FC<ModelProps> = ({ data: unresolvedData, className, headless, nodeTitle }) => {
const resolveRef = useInlineRefResolver();
const data = useResolvedObject(unresolvedData) as JSONSchema7;

const title = data.title ?? nodeTitle;
const isInternal = !!data['x-internal'];

Expand All @@ -39,7 +42,7 @@ const ModelComponent: React.FC<ModelProps> = ({ data, className, headless, nodeT

<Flex>
<Box flex={1}>
<JsonSchemaViewer resolveRef={resolveRef} className={className} schema={data} />
<JsonSchemaViewer resolveRef={resolveRef} className={className} schema={getOriginalObject(data)} />
</Box>
<Box ml={16} pos="relative" w="2/5" style={{ maxWidth: 500 }}>
<Panel rounded isCollapsible={false}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { useInlineRefResolver } from '../../../context/InlineRefResolver';
import { useParsedValue } from '../../../hooks/useParsedValue';
import { JSONSchema } from '../../../types';
import { isHttpOperation, isJSONSchema } from '../../../utils/guards';
import { getOriginalObject } from '../../../utils/ref-resolving/resolvedObject';
import { TryIt } from '../../TryIt';

type PartialHttpRequest = Pick<IHttpRequest, 'method' | 'url'> & Partial<IHttpRequest>;
Expand Down Expand Up @@ -46,7 +47,7 @@ const SchemaAndDescription = ({ title: titleProp, schema }: ISchemaAndDescriptio
</Flex>
)}

<JsonSchemaViewer resolveRef={resolveRef} schema={schema} />
<JsonSchemaViewer resolveRef={resolveRef} schema={getOriginalObject(schema)} />
</Box>
);
};
Expand Down
54 changes: 14 additions & 40 deletions packages/elements-core/src/context/InlineRefResolver.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,22 @@
import { resolveInlineRef } from '@stoplight/json';
import { JSONSchema7 } from 'json-schema';
import { isPlainObject } from 'lodash';
import * as React from 'react';
import { useContext } from 'react';

import { JSONSchema } from '../types';
import { defaultResolver, ReferenceResolver } from '../utils/ref-resolving/ReferenceResolver';
import { createResolvedObject } from '../utils/ref-resolving/resolvedObject';

export type SchemaTreeRefInfo = {
source: string | null;
pointer: string | null;
};

export type SchemaTreeRefDereferenceFn = (
ref: SchemaTreeRefInfo,
propertyPath: string[] | null,
schema: JSONSchema,
) => JSONSchema7;

const InlineRefResolverContext = React.createContext<SchemaTreeRefDereferenceFn | undefined>(undefined);
const InlineRefResolverContext = React.createContext<ReferenceResolver | undefined>(undefined);
InlineRefResolverContext.displayName = 'InlineRefResolverContext';

export const DocumentContext = React.createContext<unknown>(undefined);
const DocumentContext = React.createContext<unknown | undefined>(undefined);
DocumentContext.displayName = 'DocumentContext';

type InlineRefResolverProviderProps =
| {
document: unknown;
}
| {
resolver: SchemaTreeRefDereferenceFn;
resolver: ReferenceResolver;
};

/**
Expand All @@ -36,30 +25,8 @@ type InlineRefResolverProviderProps =
export const InlineRefResolverProvider: React.FC<InlineRefResolverProviderProps> = ({ children, ...props }) => {
const document = 'document' in props && isPlainObject(props.document) ? Object(props.document) : undefined;

const documentBasedRefResolver = React.useCallback<SchemaTreeRefDereferenceFn>(
({ pointer }, _, schema) => {
const activeSchema = document ?? schema;

if (pointer === null) {
return null;
}

if (pointer === '#') {
return activeSchema;
}

const resolved = resolveInlineRef(activeSchema, pointer);
if (isPlainObject(resolved)) {
return resolved;
}

throw new ReferenceError(`Could not resolve '${pointer}`);
},
[document],
);

return (
<InlineRefResolverContext.Provider value={'resolver' in props ? props.resolver : documentBasedRefResolver}>
<InlineRefResolverContext.Provider value={'resolver' in props ? props.resolver : defaultResolver(document)}>
<DocumentContext.Provider value={document}>{children}</DocumentContext.Provider>
</InlineRefResolverContext.Provider>
);
Expand All @@ -68,3 +35,10 @@ export const InlineRefResolverProvider: React.FC<InlineRefResolverProviderProps>
export const useInlineRefResolver = () => useContext(InlineRefResolverContext);

export const useDocument = () => useContext(DocumentContext);

export const useResolvedObject = (currentObject: object): object => {
const document = useDocument();
const resolver = useInlineRefResolver();

return createResolvedObject(currentObject, { contextObject: document as object, resolver });
};
4 changes: 3 additions & 1 deletion packages/elements-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export {
NodeTypePrettyName,
} from './constants';
export { MockingProvider } from './containers/MockingProvider';
export { InlineRefResolverProvider, SchemaTreeRefDereferenceFn } from './context/InlineRefResolver';
export { InlineRefResolverProvider } from './context/InlineRefResolver';
export { PersistenceContextProvider, withPersistenceBoundary } from './context/Persistence';
export { withMosaicProvider } from './hoc/withMosaicProvider';
export { withQueryClientProvider } from './hoc/withQueryClientProvider';
Expand All @@ -29,3 +29,5 @@ export { useRouter } from './hooks/useRouter';
export { useTocContents } from './hooks/useTocContents';
export { Styled, withStyles } from './styled';
export { Divider, Group, ITableOfContentsTree, Item, RoutingProps, TableOfContentItem } from './types';
export { ReferenceResolver } from './utils/ref-resolving/ReferenceResolver';
export { createResolvedObject } from './utils/ref-resolving/resolvedObject';
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { resolveInlineRef } from '@stoplight/json';
import { Dictionary } from '@stoplight/types';

export type ReferenceInfo = {
source: string | null;
pointer: string | null;
};

export type ReferenceResolver = (ref: ReferenceInfo, propertyPath: string[] | null, currentObject: object) => any;

export const defaultResolver =
(contextObject: object): ReferenceResolver =>
({ pointer }, _, currentObject) => {
const activeObject = contextObject ?? currentObject;

if (pointer === null) {
return null;
}

if (pointer === '#') {
return activeObject;
}

const resolved = resolveInlineRef(activeObject as Dictionary<string>, pointer);

if (resolved) return resolved;

throw new ReferenceError(`Could not resolve '${pointer}`);
};
Loading