Skip to content

Commit cb280ef

Browse files
ejntaylorclaudemariojgt
authored
Threat Intelligence Beta: OpenAPI spec, interactive reference, Postman (#22)
* Slim Beta tier page to narrative; endpoint detail lives in OpenAPI reference Removes the per-endpoint tables, request bodies, field reference, and response shapes from beta.md now that they're generated from the OpenAPI spec. Keeps the narrative that belongs on a prose page: auth, platforms, pagination strategy, error codes, testing recipes, and v2 migration notes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Flesh out OpenAPI info, tag, and operation descriptions Tag landing pages (e.g. /operations/tags/vulnerabilities/) and the overview were rendering only the one-line blurb for each tag. Expand the info, tags[].description, and the thinner operation descriptions so each generated page has usable content: auth, platforms, pagination strategies, errors, related pages, and per-tag endpoint guides with deep links. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Wire starlight-openapi plugin and register API reference sidebar group Pins starlight-openapi ^0.22.1 (latest version compatible with Starlight 0.34 / Astro 5.9; 0.23+ requires Starlight ≥0.38). Renders the Threat Intelligence Beta YAML under /api-reference/threat-intelligence-beta/ and adds an "API reference" sidebar group populated by openAPISidebarGroups. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Publish Threat Intelligence Beta spec + Postman collection - Move schemas/threat-intel-beta.yaml into public/ so it's served at https://docs.patchstack.com/schemas/threat-intel-beta.yaml. - Add a small Astro integration that runs openapi-to-postmanv2 at config:setup, writing public/schemas/threat-intel-beta.postman_collection.json on every build. The generated collection is gitignored — the OpenAPI YAML stays the single source of truth. - Expand beta.md with a "Use with Postman / Insomnia / Bruno / Hoppscotch" section including a Run in Postman button, a Claude Code / LLM assistants section, and openapi-generator snippets for SDK generation. Only the Threat Intelligence Beta API is wired up for now. Additional APIs can drop a YAML into public/schemas/ and add an entry to the postmanCollections array and starlightOpenAPI config. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Consolidate API sidebar and pin Beta to top - Merge the standalone "API reference" top-level sidebar group into "API solutions" → "Threat Intelligence API", immediately below the Beta narrative. Users now have a single entry point to find anything API-related; the narrative and interactive reference sit side-by-side under the same parent. - Convert "API solutions" from autogenerate to a manual items list so the generated OpenAPI pages (openAPISidebarGroups) can be interleaved with the .md files. - Pin Beta to the top of the Threat Intelligence group via sidebar.order: 0 and add a "New" tip badge so it's discoverable. - Rename the OpenAPI reference label from "Threat Intelligence API (Beta)" to "Interactive reference (Beta)" to avoid redundancy with the parent group name. Trade-off: new pages dropped into API solutions subfolders no longer auto-appear in the sidebar — astro.config.mjs needs a manual entry. Acceptable for the current 6 pages across 2 APIs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Restore API solutions Introduction page in sidebar The manual items list dropped the index.mdx entry that autogenerate was picking up. Add { slug: 'api-solutions' } at the top so the Introduction page appears before App API and Threat Intelligence API, preserving the original order. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Group Beta narrative + reference under one sidebar item Move the Beta entries to the bottom of the Threat Intelligence API group and nest them under a single expandable "Beta tier API" group (with the "New" badge on the group itself). The narrative is now labelled "Guide" and the OpenAPI-generated pages are labelled "Reference" — both together inside the Beta parent, so everything Beta is one click and one glance. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Make Postman collection deterministic and commit it Strip `id` and `_postman_id` UUIDs from the generated collection (both optional in Collection v2.1 — Postman assigns new ones on import) and add example values to schemas where openapi-to-postmanv2 was otherwise synthesising random data (OffsetPagination, CursorPagination, ProductType/Name/Version path params). Two consecutive builds now produce byte-identical output. Commit the collection so it's reachable via GitHub raw URL the moment this PR merges, instead of waiting on a docs.patchstack.com deploy. Build-time regeneration stays in place so the JSON can never drift from the YAML. Update the Run in Postman button to use the GitHub raw URL on main and add a direct-download fallback link for testing locally and on deploy previews — Postman's cloud cannot fetch a localhost: URL, which was producing the "blank Run in Postman modal" symptom. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Remove broken Run in Postman button from Beta guide The "Run in Postman" button relied on the deprecated `app.getpostman.com/run-collection/?collection=<url>` endpoint, which no longer accepts raw collection URLs — it only works with collections published to Postman's Public API Network and referenced by UID. The button silently did nothing when clicked. Replacing it with a plain download link plus a `File → Import → Link` instruction is honest about the flow and actually works today. Can be re-added later if/when the collection is published to Postman. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Revert "Remove broken Run in Postman button from Beta guide" This reverts commit fe4d16c. * Remove broken Run in Postman button from Beta guide Per Mario's review on PR #22: the app.getpostman.com/run-collection/?collection=<url> endpoint was deprecated. Postman now only accepts published collections referenced by UID through the Public API Network — raw JSON URLs fail silently, which is what produced the blank-modal symptom. Replace the button with the honest flow: download + File → Import → Link. Can be re-added later without a breaking change if someone publishes the collection to Postman. Co-Authored-By: Mario Tarosso <mariojgt2@gmail.com> Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Rename Beta tier → Beta API and add partner-only guidance to Overview Rename the sidebar group and page title from "Beta tier API" to "Beta API" — Beta isn't an access tier alongside Standard / Extended, it's a new generation of the API. The "tier" naming invited the wrong mental model. Add a "Which API should I use?" comparison table to Overview and clearly flag Beta as available to selected partners working directly with Patchstack only. Update the Beta guide intro and Overview's Beta blurb to match. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Un-bold the Beta API sidebar group label Starlight applies .large (heavier weight + larger size) to sidebar group labels. In a sub-group where Beta API sits next to leaf items (Standard tier API, Extended tier API, API properties) the bold treatment made Beta look visually promoted, even though its "New" badge already carries the emphasis. Scope the override via :has() so it only fires on group labels that carry a badge — today that's Beta API, and any future badged group will get the same treatment automatically without affecting plain groups like App API or Threat Intelligence API. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Mario Tarosso <mariojgt2@gmail.com>
1 parent 2053632 commit cb280ef

8 files changed

Lines changed: 3748 additions & 303 deletions

File tree

astro.config.mjs

Lines changed: 94 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,69 @@
1+
import { readFile, writeFile } from 'node:fs/promises';
2+
import { fileURLToPath } from 'node:url';
13
import { defineConfig, passthroughImageService } from 'astro/config';
24
import starlight from '@astrojs/starlight';
35
import starlightLlmsTxt from 'starlight-llms-txt'
6+
import starlightOpenAPI, { openAPISidebarGroups } from 'starlight-openapi'
7+
import Converter from 'openapi-to-postmanv2'
48

59

610
const site_url = process.env.URL;
711

812
const site = site_url || 'http://localhost:4321';
13+
14+
const postmanCollections = [
15+
{
16+
openapi: './public/schemas/threat-intel-beta.yaml',
17+
output: './public/schemas/threat-intel-beta.postman_collection.json',
18+
},
19+
];
20+
21+
// Recursively drop `id` and `_postman_id` keys so the output is deterministic.
22+
// openapi-to-postmanv2 inserts fresh UUIDs on every run, which would cause
23+
// churn in git. Both fields are optional in Collection v2.1 — Postman assigns
24+
// new ids on import.
25+
function stripIds(value) {
26+
if (Array.isArray(value)) return value.map(stripIds);
27+
if (value && typeof value === 'object') {
28+
const out = {};
29+
for (const [key, val] of Object.entries(value)) {
30+
if (key === 'id' || key === '_postman_id') continue;
31+
out[key] = stripIds(val);
32+
}
33+
return out;
34+
}
35+
return value;
36+
}
37+
38+
function postmanFromOpenAPI() {
39+
return {
40+
name: 'postman-from-openapi',
41+
hooks: {
42+
'astro:config:setup': async ({ logger }) => {
43+
for (const { openapi, output } of postmanCollections) {
44+
const spec = await readFile(fileURLToPath(new URL(openapi, import.meta.url)), 'utf8');
45+
await new Promise((resolve, reject) => {
46+
Converter.convert(
47+
{ type: 'string', data: spec },
48+
{ requestParametersResolution: 'Example' },
49+
async (err, result) => {
50+
if (err || !result.result) {
51+
return reject(err ?? new Error(result.reason ?? 'Postman conversion failed'));
52+
}
53+
await writeFile(
54+
fileURLToPath(new URL(output, import.meta.url)),
55+
JSON.stringify(stripIds(result.output[0].data), null, 2),
56+
);
57+
logger.info(`generated ${output}`);
58+
resolve();
59+
},
60+
);
61+
});
62+
}
63+
},
64+
},
65+
};
66+
}
967
// https://astro.build/config
1068
export default defineConfig({
1169
site: site,
@@ -18,17 +76,17 @@ export default defineConfig({
1876

1977
},
2078
integrations: [
79+
postmanFromOpenAPI(),
2180
starlight({
2281
plugins: [
23-
starlightLlmsTxt()
24-
// Generate the OpenAPI documentation pages.
25-
// starlightOpenAPI([
26-
// {
27-
// base: 'developer-api',
28-
// label: 'My API',
29-
// schema: './schemas/test.yaml',
30-
// },
31-
// ])
82+
starlightLlmsTxt(),
83+
starlightOpenAPI([
84+
{
85+
base: 'api-reference/threat-intelligence-beta',
86+
label: 'Reference',
87+
schema: './public/schemas/threat-intel-beta.yaml',
88+
},
89+
]),
3290
],
3391
title: 'Patchstack Docs',
3492
favicon: '/images/psfavicon.svg',
@@ -73,7 +131,33 @@ export default defineConfig({
73131
{
74132
label: 'API solutions',
75133
collapsed: true,
76-
autogenerate: { directory: 'API solutions', collapsed: true },
134+
items: [
135+
{ slug: 'api-solutions' },
136+
{
137+
label: 'App API',
138+
collapsed: true,
139+
autogenerate: { directory: 'API solutions/App API', collapsed: true },
140+
},
141+
{
142+
label: 'Threat Intelligence API',
143+
collapsed: true,
144+
items: [
145+
{ slug: 'api-solutions/threat-intelligence-api/overview' },
146+
{ slug: 'api-solutions/threat-intelligence-api/standard' },
147+
{ slug: 'api-solutions/threat-intelligence-api/extended' },
148+
{ slug: 'api-solutions/threat-intelligence-api/api-properties' },
149+
{
150+
label: 'Beta API',
151+
badge: { text: 'New', variant: 'tip' },
152+
collapsed: true,
153+
items: [
154+
{ slug: 'api-solutions/threat-intelligence-api/beta' },
155+
...openAPISidebarGroups,
156+
],
157+
},
158+
],
159+
},
160+
],
77161
},
78162
{
79163
label: 'Vulnerability Disclosure Program',

0 commit comments

Comments
 (0)