Skip to content

Commit a338a81

Browse files
Merge branch 'benface/token-api' of https://github.com/graphprotocol/docs into benface/token-api
2 parents ff20a4f + a8ddf9e commit a338a81

19 files changed

Lines changed: 517 additions & 386 deletions

nginx.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ http {
158158
rewrite ^/docs/([a-zA-Z][a-zA-Z])/supported-networks/near/$ $scheme://$http_host/docs/$1/subgraphs/cookbook/near/ permanent;
159159
rewrite ^/docs/([a-zA-Z][a-zA-Z])/tap/$ $scheme://$http_host/docs/$1/indexing/tap/ permanent;
160160
rewrite ^/docs/([a-zA-Z][a-zA-Z])/tokenomics/$ $scheme://$http_host/docs/$1/resources/tokenomics/ permanent;
161+
rewrite ^/docs/([a-zA-Z][a-zA-Z])/token-api/$ $scheme://$http_host/docs/$1/token-api/quick-start/ permanent;
161162

162163
# Temporary redirects (302)
163164
rewrite ^/docs/en/querying/graph-client/$ $scheme://$http_host/docs/en/subgraphs/querying/graph-client/README/ redirect;

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "graph-docs",
33
"private": true,
44
"version": "1.0.0",
5-
"packageManager": "pnpm@10.6.5",
5+
"packageManager": "pnpm@10.7.0",
66
"scripts": {
77
"dev": "turbo run dev",
88
"build": "NODE_OPTIONS='--max_old_space_size=8192' turbo run build",

packages/og-image/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"tsx": "^4.19.3",
2323
"typescript": "^5.8.2",
2424
"vitest": "^2.1.9",
25-
"wrangler": "^3.114.2"
25+
"wrangler": "^3.114.3"
2626
},
2727
"sideEffects": false
2828
}

pnpm-lock.yaml

Lines changed: 245 additions & 209 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

website/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
"dependencies": {
1919
"@docsearch/react": "^3.9.0",
2020
"@edgeandnode/common": "^7.0.3",
21-
"@edgeandnode/gds": "^6.5.9",
22-
"@edgeandnode/go": "^9.4.9",
21+
"@edgeandnode/gds": "6.5.10-for-token-api-1743022982923-f081f270f7c4552988f2f05e9461aff3077c1594",
22+
"@edgeandnode/go": "9.4.10-for-token-api-1743022982923-f081f270f7c4552988f2f05e9461aff3077c1594",
2323
"@emotion/react": "^11.14.0",
2424
"@graphprotocol/contracts": "6.2.1",
2525
"@pinax/graph-networks-registry": "^0.6.7",
@@ -29,7 +29,7 @@
2929
"fetch-har": "^11.1.1",
3030
"lodash": "^4.17.21",
3131
"mixpanel-browser": "^2.61.2",
32-
"motion": "^12.6.0",
32+
"motion": "^12.6.2",
3333
"next": "^14.2.26",
3434
"next-seo": "^6.6.0",
3535
"next-sitemap": "^4.2.3",

website/scripts/fetch-api-reference.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,14 @@ async function fetchApiReference() {
2525
} catch {
2626
// File doesn't exist, create it
2727
const content = `---
28-
title: ${operation.summary || operation.description || operation.operationId}
28+
title: ${operation.summary || operation.operationId}
2929
template:
3030
type: openApi
3131
apiId: ${apiId}
3232
operationId: ${operation.operationId}
3333
---
34+
35+
${operation.description}
3436
`
3537
await fs.writeFile(filePath, content, 'utf-8')
3638
console.log(`Created ${path.relative(process.cwd(), filePath)}`)

website/src/layout/templates/openApi/aside.tsx

Lines changed: 93 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
1+
import { usePrevious } from '@react-hookz/web'
12
import { HTTPSnippet } from '@readme/httpsnippet'
23
import fetchHar from 'fetch-har'
34
import { type ComponentPropsWithoutRef, useContext, useState } from 'react'
45

5-
import { ExperimentalButton, ExperimentalCodeBlock, ExperimentalInput, objectKeys } from '@edgeandnode/gds'
6+
import {
7+
ExperimentalButton,
8+
ExperimentalCodeBlock,
9+
ExperimentalInput,
10+
ExperimentalStatus,
11+
objectEntries,
12+
objectFromEntries,
13+
objectKeys,
14+
} from '@edgeandnode/gds'
615
import { Play } from '@edgeandnode/gds/icons'
716

817
import { useI18n } from '@/i18n'
@@ -24,6 +33,7 @@ export default function TemplateOpenApiAside(props: ComponentPropsWithoutRef<'di
2433
return <TemplateOpenApiAsideContent {...props} />
2534
}
2635

36+
// TODO: Allow showing the aside on mobile?
2737
function TemplateOpenApiAsideContent(props: ComponentPropsWithoutRef<'div'>) {
2838
const openApiContext = useContext(OpenApiContext)!
2939
const { operation, values } = openApiContext
@@ -114,70 +124,97 @@ function TemplateOpenApiAsideContent(props: ComponentPropsWithoutRef<'div'>) {
114124
javascript: 'JavaScript',
115125
python: 'Python',
116126
go: 'Go',
117-
// TODO: Add `php` and `java` after GDS update
118-
}
127+
php: 'PHP',
128+
java: 'Java',
129+
} as const
130+
131+
const snippets = objectFromEntries(
132+
objectKeys(snippetLanguages).map((language) => {
133+
let code = (snippet.convert(language) || [])[0] ?? ''
134+
// Unencode placeholders potentially present in the URL
135+
for (const placeholder of [
136+
...Object.values(PLACEHOLDERS),
137+
...operation.pathParameters.map((parameter) => `{${parameter.name}}`),
138+
]) {
139+
const encodedPlaceholder = encodeURIComponent(placeholder)
140+
code = code.replaceAll(encodedPlaceholder, placeholder)
141+
}
142+
return [language, code] as const
143+
}),
144+
)
145+
146+
const previousShellSnippet = usePrevious(snippets.shell)
147+
const snippetsChanged = previousShellSnippet !== undefined && previousShellSnippet !== snippets.shell
119148

120149
const [isSendingRequest, setIsSendingRequest] = useState(false)
121-
const [responseStatus, setResponseStatus] = useState<number | null>(null)
122-
const [responseText, setResponseText] = useState<string | null>(null)
150+
const [response, setResponse] = useState<{ status: number; text: string } | null>(null)
123151

124152
const sendRequest = async () => {
125153
setIsSendingRequest(true)
126154
try {
127155
// fetch-har's types are bad
128156
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
129157
const response = await fetchHar({ log: { entries: snippet.entries } } as any)
130-
setResponseStatus(response.status)
131-
let text = await response.text()
158+
let responseText = await response.text()
132159
try {
133-
const data = JSON.parse(text)
134-
text = JSON.stringify(data, null, 2)
160+
responseText = JSON.parse(responseText)
135161
} catch {}
136-
setResponseText(text)
162+
responseText = JSON.stringify(responseText, null, 2)
163+
setResponse({ status: response.status, text: responseText })
137164
} catch (error) {
138165
console.error({ error })
139-
setResponseStatus(null)
140-
setResponseText(null)
166+
setResponse(null)
141167
}
168+
setSelectedResponseTabIndex(0)
142169
setIsSendingRequest(false)
143170
}
144171

145-
const actualResponseLabel =
146-
responseStatus && responseText ? `${t('global.openApi.responses.actualResponse')} (${responseStatus})` : undefined
147172
const responseTabs = [
148-
...(actualResponseLabel
173+
...(response
149174
? [
150175
{
151-
label: actualResponseLabel,
176+
label: {
177+
label: `${response.status} (${t('global.openApi.responses.liveResponse')})`,
178+
iconBefore: <ExperimentalStatus variant={response.status === 200 ? 'success' : 'error'} />,
179+
},
152180
content: (
153181
<ExperimentalCodeBlock
154-
key="actual-response"
155-
label={operation.potentialResponses.length === 0 ? actualResponseLabel : undefined}
182+
key="live-response"
183+
label={
184+
operation.potentialResponses.length === 0
185+
? `${response.status} (${t('global.openApi.responses.liveResponse')})`
186+
: undefined
187+
}
156188
language="json"
189+
lineNumbers={response.text.split('\n').length > 1}
157190
>
158-
{responseText!}
191+
{response.text}
159192
</ExperimentalCodeBlock>
160193
),
161194
},
162195
]
163196
: []),
164197
...operation.potentialResponses.map((potentialResponse) => {
165-
const label = `${t('global.openApi.responses.exampleResponse')} (${potentialResponse.status})`
198+
const label = `${potentialResponse.status} ${t('global.openApi.responses.example')}`
199+
const exampleResponseText = JSON.stringify(potentialResponse.example, null, 2)
166200
return {
167-
label: operation.potentialResponses.length === 1 && actualResponseLabel ? label : potentialResponse.status,
201+
label,
168202
content: (
169203
<ExperimentalCodeBlock
170204
key={potentialResponse.status}
171-
label={operation.potentialResponses.length === 1 && !actualResponseLabel ? label : undefined}
205+
label={operation.potentialResponses.length === 1 && !response ? label : undefined}
172206
language="json"
207+
lineNumbers={exampleResponseText.split('\n').length > 1}
173208
>
174-
{JSON.stringify(potentialResponse.example, null, 2)}
209+
{exampleResponseText}
175210
</ExperimentalCodeBlock>
176211
),
177212
}
178213
}),
179214
]
180215

216+
const [selectedResponseTabIndex, setSelectedResponseTabIndex] = useState(0)
217+
181218
return (
182219
<div key={operation.path} {...props}>
183220
<ExperimentalInput
@@ -202,35 +239,42 @@ function TemplateOpenApiAsideContent(props: ComponentPropsWithoutRef<'div'>) {
202239
}
203240
className="mb-6"
204241
/>
205-
<ExperimentalCodeBlock.Tabs tabs={Object.values(snippetLanguages)}>
206-
{objectKeys(snippetLanguages).map((language) => {
207-
let code = (snippet.convert(language) || [])[0] ?? ''
208-
// Unencode placeholders potentially present in the URL
209-
for (const placeholder of [
210-
...Object.values(PLACEHOLDERS),
211-
...operation.pathParameters.map((parameter) => `{${parameter.name}}`),
212-
]) {
213-
const encodedPlaceholder = encodeURIComponent(placeholder)
214-
code = code.replaceAll(encodedPlaceholder, placeholder)
215-
}
216-
return (
217-
<ExperimentalCodeBlock
218-
key={language}
219-
language={language}
220-
className="[tab-size:4]" // TODO: Remove after GDS update
221-
>
242+
<div>
243+
<ExperimentalCodeBlock.Tabs tabs={Object.values(snippetLanguages)}>
244+
{objectEntries(snippets).map(([language, code]) => (
245+
<ExperimentalCodeBlock key={language} language={language}>
222246
{code}
223247
</ExperimentalCodeBlock>
224-
)
225-
})}
226-
</ExperimentalCodeBlock.Tabs>
227-
{responseTabs.length === 1 ? (
228-
<div className="mt-6">{responseTabs[0]!.content}</div>
229-
) : (
230-
<ExperimentalCodeBlock.Tabs tabs={responseTabs.map((responseTab) => responseTab.label)} className="mt-6">
231-
{responseTabs.map((responseTab) => responseTab.content)}
248+
))}
232249
</ExperimentalCodeBlock.Tabs>
233-
)}
250+
{snippetsChanged ? (
251+
<div
252+
key={snippets.shell}
253+
className="animate-opacity-to-0 absolute inset-0 rounded-8 border border-solar-600 pointer-events-none animate-fill-forwards"
254+
/>
255+
) : null}
256+
</div>
257+
{responseTabs.length > 0 ? (
258+
<div className="mt-6">
259+
{responseTabs.length === 1 ? (
260+
responseTabs[0]!.content
261+
) : (
262+
<ExperimentalCodeBlock.Tabs
263+
tabs={responseTabs.map((responseTab) => responseTab.label)}
264+
selectedIndex={selectedResponseTabIndex}
265+
onChange={setSelectedResponseTabIndex}
266+
>
267+
{responseTabs.map((responseTab) => responseTab.content)}
268+
</ExperimentalCodeBlock.Tabs>
269+
)}
270+
{response ? (
271+
<div
272+
key={response.text}
273+
className="animate-opacity-to-0 absolute inset-0 rounded-8 border border-solar-600 pointer-events-none animate-fill-forwards"
274+
/>
275+
) : null}
276+
</div>
277+
) : null}
234278
</div>
235279
)
236280
}

website/src/layout/templates/openApi/content.tsx

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,7 @@ export default function TemplateOpenApiContent({ children, ...props }: Component
6666
return (
6767
<tr key={parameter.name}>
6868
<td className="whitespace-nowrap">
69-
{/* TODO: Remove `+` variant after GDS update */}
70-
<ExperimentalCodeInline className="+:whitespace-nowrap">
71-
{parameter.name}
72-
</ExperimentalCodeInline>
69+
<ExperimentalCodeInline className="whitespace-nowrap">{parameter.name}</ExperimentalCodeInline>
7370
<div className="mt-1 text-12 text-space-500">{parameter.schema.type}</div>
7471
</td>
7572
<td className="text-body-xsmall">
@@ -122,7 +119,6 @@ export default function TemplateOpenApiContent({ children, ...props }: Component
122119
<ExperimentalCodeInline>{parameter.serializationFormat}</ExperimentalCodeInline>
123120
</li>
124121
) : null}
125-
{/* TODO: If `schema.type` is `object` or `array`, show the schema */}
126122
</ul>
127123
</td>
128124
<td className="py-3.5">
@@ -145,12 +141,11 @@ export default function TemplateOpenApiContent({ children, ...props }: Component
145141
/>
146142
) : enumValues.length > 0 ? (
147143
<div
148-
// TODO: Replace `focus-visible:border-purple-500` by `focus-visible:border-focus` after GDS update
149144
className={`
150145
flex h-8 w-full items-center border border-space-1500 px-2 text-14 transition
151146
hover:border-space-1300
152147
active:transition-none
153-
has-focus-visible:border-purple-500
148+
has-focus-visible:border-focus
154149
`}
155150
>
156151
<span className="me-1 w-0 flex-1 truncate">{parameterValue}</span>
@@ -170,16 +165,16 @@ export default function TemplateOpenApiContent({ children, ...props }: Component
170165
</select>
171166
</div>
172167
) : (
168+
// Known limitation: we don't support `object` or `array` parameter types
173169
<input
174170
type="text"
175171
aria-label={parameter.name}
176172
value={parameterValue}
177173
onChange={(event) => setParameterValue(event.target.value)}
178-
// TODO: Replace `focus-visible:border-purple-500` by `focus-visible:border-focus` after GDS update
179174
className={`
180175
h-8 w-full border border-space-1500 px-2 text-14 transition outline-none
181176
hover:border-space-1300
182-
focus-visible:border-purple-500
177+
focus-visible:border-focus
183178
active:transition-none
184179
`}
185180
/>

website/src/layout/templates/openApi/main.tsx

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { type ComponentPropsWithoutRef, useContext } from 'react'
44
import { ExperimentalCodeInline } from '@edgeandnode/gds'
55

66
import { Callout } from '@/components'
7+
import { useI18n } from '@/i18n'
78
import { getApi, isApiId } from '@/openApi'
89

910
import { LayoutContext } from '../../shared'
@@ -13,15 +14,19 @@ import { OpenApiProvider } from './OpenApiContext'
1314

1415
export default function TemplateOpenApiMain({ children, ...props }: ComponentPropsWithoutRef<'div'>) {
1516
const { template } = useContext(LayoutContext)!
17+
const { t } = useI18n()
1618
const securitySchemes = useMap<string, string>() // Keeping this state here instead of in `OpenApiProvider` to persist it across OpenAPI pages
1719

1820
if (template.type !== 'openApi' || !isApiId(template.apiId)) {
1921
return (
2022
<TemplateDefaultMain {...props}>
2123
<Callout variant="important">
2224
<p>
23-
Could not retrieve API{' '}
24-
<ExperimentalCodeInline>{'apiId' in template ? template.apiId : '<undefined>'}</ExperimentalCodeInline>.
25+
{t('global.openApi.errors.invalidApi', [
26+
<ExperimentalCodeInline key="0">
27+
{'apiId' in template ? template.apiId : '<undefined>'}
28+
</ExperimentalCodeInline>,
29+
])}
2530
</p>
2631
</Callout>
2732
</TemplateDefaultMain>
@@ -36,8 +41,10 @@ export default function TemplateOpenApiMain({ children, ...props }: ComponentPro
3641
<TemplateDefaultMain {...props}>
3742
<Callout variant="important">
3843
<p>
39-
Could not retrieve operation <ExperimentalCodeInline>{template.operationId}</ExperimentalCodeInline> in API{' '}
40-
<ExperimentalCodeInline>{template.apiId}</ExperimentalCodeInline>.
44+
{t('global.openApi.errors.invalidOperation', [
45+
<ExperimentalCodeInline key="0">{template.operationId}</ExperimentalCodeInline>,
46+
<ExperimentalCodeInline key="1">{template.apiId}</ExperimentalCodeInline>,
47+
])}
4148
</p>
4249
</Callout>
4350
</TemplateDefaultMain>

0 commit comments

Comments
 (0)