Skip to content

Commit f1dbd5a

Browse files
authored
Merge pull request #391 from open-rpc/fix/fallback-to-schema-examples
fix: fallback example pairings to schema examples
2 parents f8a233b + 8ba92b4 commit f1dbd5a

File tree

4 files changed

+189
-39
lines changed

4 files changed

+189
-39
lines changed

src/ExamplePairing/ExamplePairing.test.tsx

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,42 +3,39 @@ import ReactDOM from "react-dom";
33
import ExamplePairing from "./ExamplePairing";
44
import examples from "@open-rpc/examples";
55
import refParser from "json-schema-ref-parser";
6-
import { OpenrpcDocument } from "@open-rpc/meta-schema";
6+
import { MethodObject, OpenrpcDocument, ExamplePairingObject } from "@open-rpc/meta-schema";
77

88
it("renders handles no method", async () => {
99
const div = document.createElement("div");
10-
ReactDOM.render(<ExamplePairing method={undefined} examplePosition={0}/>, div);
10+
ReactDOM.render(<ExamplePairing methodName={undefined} examplePairing={{} as any} />, div);
1111
expect(div.innerHTML).toBe("");
1212
ReactDOM.unmountComponentAtNode(div);
1313
});
1414

1515
it("renders handles no method examples", async () => {
1616
const div = document.createElement("div");
17-
ReactDOM.render(<ExamplePairing method={{} as any} examplePosition={0} />, div);
18-
expect(div.innerHTML).toBe("");
19-
ReactDOM.unmountComponentAtNode(div);
20-
});
21-
22-
it("renders handles no examplePosition", async () => {
23-
const div = document.createElement("div");
24-
const simpleMath = await refParser.dereference(examples.simpleMath) as OpenrpcDocument;
25-
ReactDOM.render(<ExamplePairing method={simpleMath.methods[0]} />, div);
17+
ReactDOM.render(<ExamplePairing methodName={"foo"} />, div);
2618
expect(div.innerHTML).toBe("");
2719
ReactDOM.unmountComponentAtNode(div);
2820
});
2921

3022
it("renders examples", async () => {
3123
const div = document.createElement("div");
3224
const simpleMath = await refParser.dereference(examples.simpleMath) as OpenrpcDocument;
33-
ReactDOM.render(<ExamplePairing method={simpleMath.methods[0]} examplePosition={0} />, div);
25+
ReactDOM.render(
26+
<ExamplePairing
27+
methodName={simpleMath.methods[0].name}
28+
examplePairing={simpleMath.methods[0].examples && simpleMath.methods[0].examples[0] as any}
29+
/>
30+
, div);
3431
expect(div.innerHTML.includes("2")).toBe(true);
3532
expect(div.innerHTML.includes("4")).toBe(true);
3633
ReactDOM.unmountComponentAtNode(div);
3734
});
3835

3936
it("renders examples with params by-name", async () => {
4037
const div = document.createElement("div");
41-
ReactDOM.render(<ExamplePairing method={{
38+
const method: MethodObject = {
4239
examples: [
4340
{
4441
name: "fooExample",
@@ -68,7 +65,13 @@ it("renders examples with params by-name", async () => {
6865
type: "string",
6966
},
7067
},
71-
}} examplePosition={0} />, div);
68+
};
69+
ReactDOM.render(
70+
<ExamplePairing
71+
methodName={method.name}
72+
examplePairing={method.examples && method.examples[0] as ExamplePairingObject}
73+
paramStructure={method.paramStructure || "by-position"} />
74+
, div);
7275
expect(div.innerHTML.includes("foo")).toBe(true);
7376
expect(div.innerHTML.includes("bar")).toBe(true);
7477
ReactDOM.unmountComponentAtNode(div);

src/ExamplePairing/ExamplePairing.tsx

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@ import Grid from "@material-ui/core/Grid";
33
import { Card, CardContent, Theme, withStyles, WithStyles } from "@material-ui/core";
44
import ReactJson from "react-json-view";
55
import ReactMarkdown from "react-markdown";
6-
import { MethodObject, ExampleObject, ExamplePairingObject } from "@open-rpc/meta-schema";
6+
import { ExampleObject, ExamplePairingObject } from "@open-rpc/meta-schema";
77
import _ from "lodash";
88

99
export type TParamStructure = "either" | "by-name" | "by-position";
1010

1111
interface IProps extends WithStyles<typeof styles> {
12-
examplePosition?: number;
13-
method?: MethodObject;
12+
examplePairing?: ExamplePairingObject;
13+
paramStructure?: TParamStructure;
14+
methodName?: string;
1415
reactJsonOptions?: any;
1516
}
1617

@@ -22,37 +23,32 @@ const styles = (theme: Theme) => ({
2223

2324
class ExamplePairing extends Component<IProps, {}> {
2425
public render() {
25-
const { examplePosition, method, classes } = this.props;
26-
if (_.isUndefined(examplePosition)) {
26+
const { examplePairing, paramStructure, classes, methodName } = this.props;
27+
if (_.isUndefined(examplePairing)) {
2728
return null;
2829
}
29-
let example;
30-
if (method && method.examples && method.examples[examplePosition]) {
31-
example = method.examples[examplePosition] as ExamplePairingObject;
32-
}
33-
if (!example || _.isEmpty(example)) {
30+
if (_.isUndefined(methodName)) {
3431
return null;
3532
}
36-
const paramStructure: TParamStructure = method?.paramStructure || "either";
3733
const params = paramStructure === "by-name"
38-
? (example.params as ExampleObject[]).reduce(((memo, p) => {
34+
? (examplePairing.params as ExampleObject[]).reduce(((memo, p) => {
3935
memo[p.name] = p.value;
4036
return memo;
4137
}), {} as any)
42-
: (example.params as ExampleObject[]).map(((p) => p.value));
38+
: (examplePairing.params as ExampleObject[]).map(((p) => p.value));
4339

4440
return (
4541
<Grid container spacing={10}>
4642
<Grid item xs={12}>
47-
<ReactMarkdown source={example.description} className={classes.description} />
43+
<ReactMarkdown source={examplePairing.description} className={classes.description} />
4844
</Grid>
4945
<Grid item xs={6}>
5046
<Card>
5147
<CardContent>
52-
{example.params && <ReactJson src={{
48+
{examplePairing.params && <ReactJson src={{
5349
id: 1,
5450
jsonrpc: "2.0",
55-
method: method && method.name,
51+
method: methodName,
5652
params,
5753
}} {...this.props.reactJsonOptions} />}
5854
</CardContent>
@@ -61,10 +57,10 @@ class ExamplePairing extends Component<IProps, {}> {
6157
<Grid item xs={6}>
6258
<Card>
6359
<CardContent>
64-
{example.result && <ReactJson src={{
60+
{examplePairing.result && <ReactJson src={{
6561
id: 1,
6662
jsonrpc: "2.0",
67-
result: (example.result as ExampleObject).value,
63+
result: (examplePairing.result as ExampleObject).value,
6864
}} {...this.props.reactJsonOptions} />}
6965
</CardContent>
7066
</Card>

src/ExamplePairings/ExamplePairings.test.tsx

Lines changed: 110 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,118 @@ it("renders examples", async () => {
3232
<ExamplePairings
3333
method={simpleMath.methods[0]}
3434
examples={simpleMath.methods[0].examples as ExamplePairingObject[]
35-
} />
36-
, div);
35+
} />
36+
, div);
3737
expect(div.innerHTML.includes("simpleMathAdditionTwo")).toBe(true);
3838
expect(div.innerHTML.includes("2")).toBe(true);
3939
expect(div.innerHTML.includes("4")).toBe(true);
4040
ReactDOM.unmountComponentAtNode(div);
4141
});
42+
43+
it("renders examples with only schema examples", async () => {
44+
const div = document.createElement("div");
45+
const testDoc: OpenrpcDocument = {
46+
info: {
47+
title: "test",
48+
version: "0.0.0",
49+
},
50+
methods: [
51+
{
52+
name: "test-method",
53+
params: [{
54+
name: "testparam1",
55+
schema: {
56+
examples: ["bob"],
57+
type: "string",
58+
},
59+
}],
60+
result: {
61+
name: "test-method-result",
62+
schema: {
63+
examples: ["potato"],
64+
type: "string",
65+
},
66+
},
67+
},
68+
],
69+
openrpc: "1.0.0",
70+
};
71+
ReactDOM.render(
72+
<ExamplePairings
73+
method={testDoc.methods[0]}
74+
examples={testDoc.methods[0].examples as ExamplePairingObject[]
75+
} />
76+
, div);
77+
expect(div.innerHTML.includes("potato")).toBe(true);
78+
expect(div.innerHTML.includes("bob")).toBe(true);
79+
ReactDOM.unmountComponentAtNode(div);
80+
});
81+
82+
it("renders examples with only schema examples with no params", async () => {
83+
const div = document.createElement("div");
84+
const testDoc: OpenrpcDocument = {
85+
info: {
86+
title: "test",
87+
version: "0.0.0",
88+
},
89+
methods: [
90+
{
91+
name: "test-method",
92+
params: [],
93+
result: {
94+
name: "test-method-result",
95+
schema: {
96+
examples: ["potato"],
97+
type: "string",
98+
},
99+
},
100+
},
101+
],
102+
openrpc: "1.0.0",
103+
};
104+
ReactDOM.render(
105+
<ExamplePairings
106+
method={testDoc.methods[0]}
107+
examples={testDoc.methods[0].examples as ExamplePairingObject[]
108+
} />
109+
, div);
110+
expect(div.innerHTML.includes("potato")).toBe(true);
111+
expect(div.innerHTML.includes("bob")).toBe(true);
112+
ReactDOM.unmountComponentAtNode(div);
113+
});
114+
115+
it("renders examples with only schema examples and no method", async () => {
116+
const div = document.createElement("div");
117+
const testDoc: OpenrpcDocument = {
118+
info: {
119+
title: "test",
120+
version: "0.0.0",
121+
},
122+
methods: [
123+
{
124+
name: "test-method",
125+
params: [{
126+
name: "testparam1",
127+
schema: {
128+
examples: ["bob"],
129+
type: "string",
130+
},
131+
}],
132+
result: {
133+
name: "test-method-result",
134+
schema: {
135+
examples: ["potato"],
136+
type: "string",
137+
},
138+
},
139+
},
140+
],
141+
openrpc: "1.0.0",
142+
};
143+
ReactDOM.render(
144+
<ExamplePairings
145+
examples={testDoc.methods[0].examples as ExamplePairingObject[]
146+
} />
147+
, div);
148+
ReactDOM.unmountComponentAtNode(div);
149+
});

src/ExamplePairings/ExamplePairings.tsx

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React, { Component } from "react";
22
import ExamplePairing from "../ExamplePairing/ExamplePairing";
33
import { Typography, List, ListItem, ListItemText, Grid, MenuItem, Menu, withStyles } from "@material-ui/core";
4-
import { MethodObject, ExamplePairingObject } from "@open-rpc/meta-schema";
4+
import { MethodObject, ExamplePairingObject, ContentDescriptorObject, ReferenceObject } from "@open-rpc/meta-schema";
55

66
interface IProps {
77
method?: MethodObject;
@@ -15,6 +15,47 @@ interface IState {
1515
currentExample?: ExamplePairingObject;
1616
}
1717

18+
const newExample: ExamplePairingObject = {
19+
name: "generated-example",
20+
params: [
21+
],
22+
result: {
23+
name: "example-result",
24+
value: null,
25+
},
26+
};
27+
const getExamplesFromMethod = (method?: MethodObject): ExamplePairingObject[] => {
28+
if (!method) { return []; }
29+
const examples: ExamplePairingObject[] = [];
30+
31+
(method.params as ContentDescriptorObject[]).forEach((param, index: number) => {
32+
if (param.schema.examples && param.schema.examples.length > 0) {
33+
param.schema.examples.forEach((ex: any, i: number) => {
34+
if (!examples[i]) {
35+
examples.push({ ...newExample });
36+
}
37+
examples[i].params.push({
38+
name: param.name,
39+
value: ex,
40+
});
41+
});
42+
}
43+
});
44+
const methodResult = method.result as ContentDescriptorObject;
45+
if (methodResult && methodResult.schema && methodResult.schema.examples && methodResult.schema.examples.length > 0) {
46+
methodResult.schema.examples.forEach((ex: any, i: number) => {
47+
if (!examples[i]) {
48+
examples.push({ ...newExample });
49+
}
50+
examples[i].result = {
51+
name: methodResult.name,
52+
value: ex,
53+
};
54+
});
55+
}
56+
return examples;
57+
};
58+
1859
class ExamplePairings extends Component<IProps, IState> {
1960
constructor(props: IProps) {
2061
super(props);
@@ -43,8 +84,10 @@ class ExamplePairings extends Component<IProps, IState> {
4384
this.setState({ anchorEl: null });
4485
}
4586
public render() {
46-
const { examples, method } = this.props;
87+
let { examples } = this.props;
88+
const { method } = this.props;
4789
const { anchorEl } = this.state;
90+
examples = examples || getExamplesFromMethod(method);
4891
if (!examples || examples.length === 0) {
4992
return null;
5093
}
@@ -84,10 +127,10 @@ class ExamplePairings extends Component<IProps, IState> {
84127
</List>
85128
</Grid>
86129
<Grid item xs={12}>
87-
{this.props.examples &&
130+
{examples &&
88131
<ExamplePairing
89-
method={method}
90-
examplePosition={this.state.selectedIndex}
132+
examplePairing={examples[this.state.selectedIndex]}
133+
methodName={this.props.method && this.props.method.name}
91134
reactJsonOptions={this.props.reactJsonOptions} />}
92135
</Grid>
93136
</Grid>

0 commit comments

Comments
 (0)