Skip to content

Commit 951b5c5

Browse files
authored
fix: readOnly/writeOnly not respected for >=2 levels (#120)
1 parent 8b80e97 commit 951b5c5

File tree

3 files changed

+68
-35
lines changed

3 files changed

+68
-35
lines changed

src/components/JsonSchemaViewer.tsx

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,22 @@ export interface IJsonSchemaViewer {
3333
export const ViewModeContext = React.createContext<ViewMode>('standalone');
3434
ViewModeContext.displayName = 'ViewModeContext';
3535

36+
export const SchemaTreeStoreContext = React.createContext<TreeStore<SchemaTree>>(
37+
new TreeStore(
38+
new SchemaTree({}, new TreeState(), {
39+
mergeAllOf: true,
40+
expandedDepth: -1,
41+
resolveRef: void 0,
42+
shouldResolveEagerly: false,
43+
onPopulate: void 0,
44+
}),
45+
new TreeState(),
46+
),
47+
);
48+
SchemaTreeStoreContext.displayName = 'SchemaTreeStoreContext';
49+
3650
export class JsonSchemaViewerComponent extends React.PureComponent<IJsonSchemaViewer & ErrorBoundaryForwardedProps> {
37-
protected readonly treeStore: TreeStore;
51+
protected readonly treeStore: TreeStore<SchemaTree>;
3852
protected readonly tree: SchemaTree;
3953
protected readonly treeState: TreeState;
4054

@@ -124,7 +138,15 @@ export class JsonSchemaViewerComponent extends React.PureComponent<IJsonSchemaVi
124138
return (
125139
<div className={cn(className, 'JsonSchemaViewer flex flex-col relative')}>
126140
<ViewModeContext.Provider value={this.props.viewMode ?? 'standalone'}>
127-
<SchemaTreeComponent expanded={expanded} name={name} schema={schema} treeStore={this.treeStore} {...props} />
141+
<SchemaTreeStoreContext.Provider value={this.treeStore}>
142+
<SchemaTreeComponent
143+
expanded={expanded}
144+
name={name}
145+
schema={schema}
146+
treeStore={this.treeStore}
147+
{...props}
148+
/>
149+
</SchemaTreeStoreContext.Provider>
128150
</ViewModeContext.Provider>
129151
</div>
130152
);

src/components/shared/Property.tsx

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,19 @@
11
import { isLocalRef } from '@stoplight/json';
22
import { Optional } from '@stoplight/types';
3-
import { JSONSchema4 } from 'json-schema';
4-
import { isObject as _isObject, size as _size } from 'lodash';
53
import * as React from 'react';
64
import { getSchemaNodeMetadata } from '../../tree/metadata';
7-
import { GoToRefHandler, IArrayNode, IObjectNode, SchemaKind, SchemaNode, SchemaTreeListNode } from '../../types';
5+
import { GoToRefHandler, IArrayNode, SchemaKind, SchemaNode, SchemaTreeListNode } from '../../types';
86
import { getPrimaryType } from '../../utils/getPrimaryType';
97
import { hasRefItems, isArrayNodeWithItems, isCombinerNode, isRefNode } from '../../utils/guards';
108
import { inferType } from '../../utils/inferType';
9+
import { SchemaTreeStoreContext } from '../JsonSchemaViewer';
1110
import { Types } from './Types';
1211

1312
export interface IProperty {
1413
node: SchemaTreeListNode;
1514
onGoToRef?: GoToRefHandler;
1615
}
1716

18-
function count(obj: Optional<JSONSchema4 | null>): number | null {
19-
if (_isObject(obj)) {
20-
return _size(obj);
21-
}
22-
23-
return null;
24-
}
25-
2617
function shouldShowPropertyName(treeNode: SchemaTreeListNode) {
2718
if (treeNode.parent === null) return false;
2819
try {
@@ -77,22 +68,9 @@ export const Property: React.FunctionComponent<IProperty> = ({ node: treeNode, o
7768
const type = isRefNode(node) ? '$ref' : isCombinerNode(node) ? node.combiner : node.type;
7869
const subtype = isArrayNodeWithItems(node) ? (hasRefItems(node) ? '$ref' : inferType(node.items)) : void 0;
7970
const title = getTitle(node);
71+
const treeStore = React.useContext(SchemaTreeStoreContext);
8072

81-
const childrenCount = React.useMemo<number | null>(() => {
82-
if (type === SchemaKind.Object || (Array.isArray(type) && type.includes(SchemaKind.Object))) {
83-
return count((node as IObjectNode).properties);
84-
}
85-
86-
if (subtype === SchemaKind.Object) {
87-
return count(((node as IArrayNode).items as IObjectNode).properties);
88-
}
89-
90-
if (subtype === SchemaKind.Array) {
91-
return count((node as IArrayNode).items as IArrayNode);
92-
}
93-
94-
return null;
95-
}, [node]);
73+
const childrenCount = React.useMemo<number | null>(() => treeStore.tree.getChildrenCount(node), [node, treeStore]);
9674

9775
const handleGoToRef = React.useCallback<React.MouseEventHandler>(() => {
9876
if (onGoToRef && isRefNode(node) && node.$ref !== null) {

src/tree/tree.ts

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ import { JsonPath, Optional } from '@stoplight/types';
44
import { JSONSchema4 } from 'json-schema';
55
import { get as _get, isEqual as _isEqual, isObject as _isObject } from 'lodash';
66
import { ResolvingError } from '../errors';
7-
import { ViewMode } from '../types';
8-
import { hasRefItems, isRefNode } from '../utils/guards';
7+
import { IArrayNode, IObjectNode, SchemaKind, SchemaNode, ViewMode } from '../types';
8+
import { hasRefItems, isArrayNodeWithItems, isCombinerNode, isRefNode } from '../utils/guards';
9+
import { inferType } from '../utils/inferType';
910
import { getSchemaNodeMetadata } from './metadata';
1011
import { canStepIn } from './utils/canStepIn';
1112
import { createErrorTreeNode } from './utils/createErrorTreeNode';
@@ -50,16 +51,45 @@ export class SchemaTree extends Tree {
5051

5152
protected readonly visited = new WeakSet();
5253

54+
protected isViewModeRespected = (fragment: JSONSchema4) => {
55+
return !(
56+
!!fragment.writeOnly !== !!fragment.readOnly &&
57+
((this.treeOptions.viewMode === 'read' && fragment.writeOnly) ||
58+
(this.treeOptions.viewMode === 'write' && fragment.readOnly))
59+
);
60+
};
61+
62+
public getChildrenCount(node: SchemaNode) {
63+
const type = isRefNode(node) ? '$ref' : isCombinerNode(node) ? node.combiner : node.type;
64+
const subtype = isArrayNodeWithItems(node) ? (hasRefItems(node) ? '$ref' : inferType(node.items)) : void 0;
65+
66+
let children;
67+
68+
if (type === SchemaKind.Object || (Array.isArray(type) && type.includes(SchemaKind.Object))) {
69+
children = (node as IObjectNode).properties;
70+
}
71+
72+
if (subtype === SchemaKind.Object) {
73+
children = ((node as IArrayNode).items as IObjectNode).properties;
74+
}
75+
76+
if (subtype === SchemaKind.Array) {
77+
children = (node as IArrayNode).items as IArrayNode;
78+
}
79+
80+
if (typeof children === 'object' && children !== null) {
81+
return Object.values(children).filter(this.isViewModeRespected).length;
82+
}
83+
84+
return null;
85+
}
86+
5387
public populate() {
5488
const expanded = {};
5589
populateTree(this.schema, this.root, 0, [], {
5690
mergeAllOf: this.treeOptions.mergeAllOf,
5791
onNode: (fragment, node, parentTreeNode, level): boolean => {
58-
if (
59-
!!fragment.writeOnly !== !!fragment.readOnly &&
60-
((this.treeOptions.viewMode === 'read' && fragment.writeOnly) ||
61-
(this.treeOptions.viewMode === 'write' && fragment.readOnly))
62-
) {
92+
if (!this.isViewModeRespected(fragment)) {
6393
return false;
6494
}
6595
if (
@@ -88,6 +118,9 @@ export class SchemaTree extends Tree {
88118
populateTree(schema, artificialRoot, initialLevel, path, {
89119
mergeAllOf: this.treeOptions.mergeAllOf,
90120
onNode: (fragment, node, parentTreeNode, level) => {
121+
if (!this.isViewModeRespected(fragment)) {
122+
return false;
123+
}
91124
if (level <= this.treeOptions.expandedDepth || level <= initialLevel) return true;
92125
return stepIn && level <= initialLevel + 1 && canStepIn(getSchemaNodeMetadata(parentTreeNode).schema);
93126
},

0 commit comments

Comments
 (0)