Skip to content

Commit 4d1feee

Browse files
author
Lewis Hemens
authored
Web API documentation (dataform-co#543)
* Add granular proto libraries so we can use them in Go * Start adding swagger docs * More documentation * Add API host in * Revert proto comments and build rules * Add keys properly, change swagger host * Fix build * Review comments * Review comments
1 parent 7b5e207 commit 4d1feee

File tree

8 files changed

+331
-25
lines changed

8 files changed

+331
-25
lines changed

docs/BUILD

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ typedoc(
5353
"core/common.ts",
5454
],
5555
data = [
56-
"//core:files",
5756
"//:tsconfig.json",
57+
"//core:files",
5858
],
5959
)
6060

@@ -67,8 +67,10 @@ DEPS = [
6767
"//:tsconfig.json",
6868
"//content",
6969
"//static:files",
70+
"@npm//axios",
7071
"@npm//typescript",
7172
"@npm//@types/react",
73+
"@npm//@types/swagger-schema-official",
7274
"@npm//@types/node",
7375
"@npm//@blueprintjs/core",
7476
"@npm//@blueprintjs/select",

docs/components/page_links.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
ul.pageLinks {
22
list-style-type: none;
3-
padding-right: 10px;
3+
padding-right: 0px;
44
}
55

66
.pageLinks li {

docs/components/swagger.css

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
.container {
2+
display: flex;
3+
flex-direction: row;
4+
justify-content: center;
5+
}
6+
7+
.sidebar {
8+
min-width: 320px;
9+
height: calc(100vh - 60px);
10+
overflow-y: hidden;
11+
position: sticky;
12+
top: 60px;
13+
padding-top: 30px;
14+
padding-right: 10px;
15+
}
16+
17+
.sidebarRight {
18+
composes: sidebar;
19+
min-width: 250px;
20+
text-align: right;
21+
}
22+
23+
.mainContent {
24+
border-left: 1px solid rgba(120, 134, 156, 0.2);
25+
padding-left: 30px;
26+
padding-top: 30px;
27+
background-color: #fff;
28+
flex-grow: 1;
29+
border-left: 1px solid #e9e9e9;
30+
min-width: 500px;
31+
padding-bottom: 800px;
32+
}
33+
34+
.definition {
35+
border-bottom: 1px solid #e3e8ee;
36+
padding-bottom: 20px;
37+
}
38+
39+
.property {
40+
border-top: 1px solid #e3e8ee;
41+
padding: 10px 0px;
42+
display: flex;
43+
}
44+
45+
.properties {
46+
}
47+
48+
.propertyName {
49+
width: 300px;
50+
flex-grow: 0;
51+
flex-shrink: 0;
52+
}
53+
54+
.propertyDescription {
55+
flex-grow: 1;
56+
}
57+
58+
.propertyComment {
59+
padding: 5px;
60+
}
61+
62+
.methodTag {
63+
margin-left: 10px;
64+
}

docs/components/swagger.tsx

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
import { Tag } from "@blueprintjs/core";
2+
import { PageLinks } from "df/docs/components/page_links";
3+
import * as styles from "df/docs/components/swagger.css";
4+
import React from "react";
5+
import {
6+
Operation as IOperation,
7+
Parameter as IParameter,
8+
Schema,
9+
Spec
10+
} from "swagger-schema-official";
11+
12+
interface IProps {
13+
apiHost: string;
14+
spec: Spec;
15+
}
16+
17+
interface IOperationWithPath extends IOperation {
18+
path: string;
19+
method: string;
20+
}
21+
22+
export class Swagger extends React.Component<IProps> {
23+
public render() {
24+
const { spec, apiHost } = this.props;
25+
const allOperations: IOperationWithPath[] = Object.keys(spec.paths)
26+
.map(path => {
27+
const schema = spec.paths[path];
28+
return [
29+
{ ...schema.post, method: "post" },
30+
{ ...schema.get, method: "get" },
31+
{ ...schema.put, method: "put" },
32+
{ ...schema.delete, method: "delete" }
33+
]
34+
.filter(operation => !!operation.operationId)
35+
.map(operation => ({ path, ...operation }));
36+
})
37+
.reduce((acc, curr) => [...acc, ...curr], []);
38+
39+
return (
40+
<div className={styles.container}>
41+
<div className={styles.sidebar} />
42+
<div className={styles.mainContent}>
43+
<div>
44+
<h1>Dataform Web API</h1>
45+
{allOperations.map(operation => (
46+
<Operation key={operation.operationId} apiHost={apiHost} operation={operation} />
47+
))}
48+
{Object.keys(spec.definitions).map(name => (
49+
<Definition key={name} name={name} schema={spec.definitions[name]} />
50+
))}
51+
</div>
52+
{this.props.children}
53+
</div>
54+
<div className={styles.sidebarRight}>
55+
<h5>Operations</h5>
56+
<PageLinks
57+
links={allOperations.map(operation => ({
58+
id: operation.operationId,
59+
text: operation.operationId
60+
}))}
61+
/>
62+
<h5>Types</h5>
63+
<PageLinks
64+
links={Object.keys(spec.definitions).map(name => ({
65+
id: `/definitions/${name}`,
66+
text: classname(name)
67+
}))}
68+
/>
69+
</div>
70+
</div>
71+
);
72+
}
73+
}
74+
75+
export class Operation extends React.Component<{ apiHost: string; operation: IOperationWithPath }> {
76+
public render() {
77+
const { operation, apiHost } = this.props;
78+
return (
79+
<div className={styles.definition}>
80+
<h2 id={operation.operationId}>
81+
{operation.operationId}
82+
<code className={styles.methodTag}>{operation.method.toUpperCase()}</code>
83+
</h2>
84+
<code>{apiHost + operation.path}</code>
85+
<p>{operation.summary}</p>
86+
<h3>Parameters</h3>
87+
{operation.parameters
88+
// These types are a nightmare :(.
89+
.map(parameter => parameter as any)
90+
.map(parameter => (
91+
<div key={parameter.name} className={styles.property}>
92+
<div className={styles.propertyName}>
93+
<code>{parameter.name}</code>
94+
</div>
95+
<div className={styles.propertyDescription}>
96+
{parameter.schema && (
97+
<a href={parameter.schema.$ref}>
98+
{classname(parameter.schema.$ref.replace("#/definitions/", ""))}
99+
</a>
100+
)}
101+
{!parameter.schema && <code>string</code>}
102+
{parameter.description && (
103+
<div className={styles.propertyComment}>{parameter.description}</div>
104+
)}
105+
</div>
106+
</div>
107+
))}
108+
<h3>Responses</h3>
109+
{Object.keys(operation.responses)
110+
.map(code => ({ code, ...operation.responses[code] }))
111+
.map(code => code as any)
112+
.map(response => (
113+
<div key={response.code} className={styles.property}>
114+
<div className={styles.propertyName}>
115+
<code>{response.code}</code>
116+
</div>
117+
<a href={response.schema.$ref}>
118+
{classname(response.schema.$ref.replace("#/definitions/", ""))}
119+
</a>
120+
</div>
121+
))}
122+
</div>
123+
);
124+
}
125+
}
126+
127+
export class Definition extends React.Component<{ name: string; schema: Schema }> {
128+
public render() {
129+
const { name, schema } = this.props;
130+
return (
131+
<div className={styles.definition}>
132+
<h2 id={`/definitions/${name}`}>{classname(name)}</h2>
133+
<div>{schema.description}</div>
134+
{schema.properties && (
135+
<>
136+
<h3>Properties</h3>
137+
<div className={styles.properties}>
138+
{Object.keys(schema.properties).map(propertyName => (
139+
<Property
140+
key={propertyName}
141+
name={propertyName}
142+
property={schema.properties[propertyName]}
143+
/>
144+
))}
145+
</div>
146+
</>
147+
)}
148+
{schema.enum &&
149+
schema.enum.map(enumValue => (
150+
<li key={String(enumValue)}>
151+
<code>{enumValue}</code>
152+
</li>
153+
))}
154+
</div>
155+
);
156+
}
157+
}
158+
159+
export class Parameter extends React.Component<{ name: string; property: Schema }> {
160+
public render() {
161+
const { name, property } = this.props;
162+
const isArray = property.type === "array";
163+
const itemSchema = isArray ? (property.items as Schema) : property;
164+
const typeTag = (
165+
<code>
166+
{itemSchema.type || classname(itemSchema.$ref.replace("#/definitions/", ""))}
167+
{isArray ? "[]" : ""}
168+
</code>
169+
);
170+
return (
171+
<div className={styles.property}>
172+
<div className={styles.propertyName}>
173+
<code>{name}</code>
174+
</div>
175+
<div className={styles.propertyDescription}>
176+
{itemSchema.$ref ? <a href={itemSchema.$ref}>{typeTag}</a> : typeTag}
177+
{property.description && (
178+
<div className={styles.propertyComment}>{property.description}</div>
179+
)}
180+
</div>
181+
</div>
182+
);
183+
}
184+
}
185+
186+
export class Property extends React.Component<{ name: string; property: Schema }> {
187+
public render() {
188+
const { name, property } = this.props;
189+
const isArray = property.type === "array";
190+
const itemSchema = isArray ? (property.items as Schema) : property;
191+
const typeTag = (
192+
<code>
193+
{itemSchema.type || classname(itemSchema.$ref.replace("#/definitions/", ""))}
194+
{isArray ? "[]" : ""}
195+
</code>
196+
);
197+
return (
198+
<div className={styles.property}>
199+
<div className={styles.propertyName}>
200+
<code>{name}</code>
201+
</div>
202+
<div className={styles.propertyDescription}>
203+
{itemSchema.$ref ? <a href={itemSchema.$ref}>{typeTag}</a> : typeTag}
204+
{property.description && (
205+
<div className={styles.propertyComment}>{property.description}</div>
206+
)}
207+
</div>
208+
</div>
209+
);
210+
}
211+
}
212+
213+
function classname(definitionName: string): string {
214+
return definitionName.match(/[A-Z].*/)[0];
215+
}

docs/global.css

Lines changed: 16 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -55,56 +55,49 @@
5555
color: black;
5656
}
5757

58-
:global body h1 {
59-
color: colorsTextMain;
58+
:global h1,
59+
:global h2,
60+
:global h3,
61+
:global h4,
62+
:global h5 {
6063
font-family: "Poppins";
61-
font-weight: 500;
6264
letter-spacing: -0.03em;
65+
color: #2f2d44;
66+
font-weight: 500;
67+
}
68+
69+
:global body h1 {
6370
line-height: 58px;
6471
font-size: 31px;
65-
margin: 0px;
66-
color: #2f2d44;
6772
}
6873

6974
:global body h2 {
70-
color: colorsTextMain;
71-
font-family: "Poppins";
72-
font-weight: 500;
73-
letter-spacing: -0.02em;
7475
line-height: 22px;
7576
font-size: 22px;
76-
color: #2f2d44;
7777
}
7878

7979
:global body h3 {
80-
color: colorsTextMain;
81-
font-family: "Poppins";
82-
font-weight: 500;
83-
letter-spacing: -0.02em;
8480
line-height: 46px;
8581
font-size: 20px;
86-
margin: 0px;
87-
color: #2f2d44;
8882
}
8983

9084
:global body h4 {
91-
color: colorsTextMain;
92-
font-family: "Poppins";
93-
font-weight: 500;
94-
letter-spacing: -0.02em;
9585
line-height: 36px;
9686
font-size: 17px;
97-
margin: 0px;
98-
color: #2f2d44;
87+
}
88+
89+
:global body h5 {
90+
line-height: 26px;
91+
font-size: 14px;
9992
}
10093

10194
:global body p,
10295
:global body code {
10396
font-style: normal;
10497
font-weight: normal;
10598
line-height: 30px;
106-
color: #373e42;
10799
}
100+
108101
:global body li {
109102
font-style: normal;
110103
font-weight: normal;

0 commit comments

Comments
 (0)