Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"prebuild": "node scripts/generate-api-docs.mjs",
"start": "next start",
"lint": "next lint",
"test": "node --experimental-strip-types --test \"src/**/*.test.ts\"",
"test": "node --experimental-strip-types --test \"src/**/*.test.ts\" && node --test scripts/sdk-emitters.test.mjs",
"test:sdk-emitters": "node --test scripts/sdk-emitters.test.mjs",
"postinstall": "fumadocs-mdx && node scripts/generate-api-docs.mjs",
"content-agent:prepare": "node scripts/content-agent/prepare.mjs",
"content-agent:generate-artifacts": "node scripts/content-agent/generate-artifacts.mjs",
Expand Down
1 change: 1 addition & 0 deletions scripts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ Examples (`*.example.yaml` files under `.resources/`) are picked up automaticall
Only for **new kinds** of behavior, not new instances:

- New SDK with a new call signature → add a sample emitter to `sdk-emitters.mjs`
- PHP SDK endpoint mappings come from `php-sdk-examples.generated.json`, generated by `fleetbase/fleetbase-php`. Refresh that catalog when the SDK contract changes; ordinary CRUD examples remain intentionally concise while custom endpoints use the exact SDK method.
- New rendering pattern (different layout for some endpoint type) → add a template variant in `generate-api-docs.mjs`
- Postman invents a new YAML shape → adapt the parser in `loadRequests` / `loadExamples`

Expand Down
18 changes: 13 additions & 5 deletions scripts/api-docs.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,14 @@ const storefrontStores = {
};

/**
* Core API resources. Most are not yet wrapped by the public SDK; folders not
* in this map fall back to raw HTTP for JS/PHP samples.
* Core API resources exposed by the main Fleetbase SDK clients. JavaScript
* remains limited to its implemented stores; PHP mappings are enforced by the
* generated SDK example catalog.
*/
const coreStores = {
'Chat Channels': 'chatChannels',
Comments: 'comments',
Files: 'files',
Organizations: 'organizations',
};

Expand Down Expand Up @@ -108,14 +112,18 @@ export const apis = {
sidebarGroup: 'Core API',
type: 'api',
sdk: {
// Core resources currently fall back to raw HTTP — no first-class SDK
// wrappers for Files / Comments / Chat Channels yet. Organizations is
// mapped through the main `@fleetbase/sdk`.
// JavaScript currently maps organizations through the main SDK. PHP has
// first-class services for every Core collection listed below.
js: {
pkg: '@fleetbase/sdk',
client: 'fleetbase',
stores: coreStores,
},
php: {
pkg: 'fleetbase/fleetbase-php',
client: 'fleetbase',
stores: coreStores,
},
},
},

Expand Down
33 changes: 33 additions & 0 deletions scripts/generate-api-docs.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,23 @@ const COLLECTIONS_DIR = process.env.POSTMAN_COLLECTIONS_DIR

const OUT_BASE = path.join(ROOT, 'content/docs/api');
const NAV_OUT = path.join(ROOT, 'src/lib/api-nav.generated.ts');
const PHP_SDK_EXAMPLES_PATH = path.join(
ROOT,
'scripts/php-sdk-examples.generated.json',
);
let phpSdkExamples = {};
const usedPhpSdkExampleIds = new Set();

// ---------------------------------------------------------------------------
// Entry
// ---------------------------------------------------------------------------

async function main() {
const phpSdkCatalog = JSON.parse(
await fs.readFile(PHP_SDK_EXAMPLES_PATH, 'utf8'),
);
phpSdkExamples = phpSdkCatalog.examples ?? {};

if (!(await collectionsAvailable())) {
console.warn(
`\n⚠️ Postman collections not found at ${path.relative(ROOT, COLLECTIONS_DIR)}.\n` +
Expand Down Expand Up @@ -138,6 +149,14 @@ async function main() {

await writeNavConfig(navGroups);

const catalogIds = Object.keys(phpSdkExamples);
if (usedPhpSdkExampleIds.size !== catalogIds.length) {
const unused = catalogIds.filter((id) => !usedPhpSdkExampleIds.has(id));
throw new Error(
`PHP SDK catalog coverage is ${usedPhpSdkExampleIds.size}/${catalogIds.length}; unused IDs: ${unused.join(', ')}`,
);
}

console.log('\n✅ API docs generated.');
}

Expand Down Expand Up @@ -272,6 +291,14 @@ async function buildEndpointSection(
method: req.method,
pathSegments,
});
const sdkExampleId = slugify(`${folder}-${resourceFolder}-${req.name}`);
const sdkExample = phpSdkExamples[sdkExampleId];
if (sdkConfig?.php) {
if (!sdkExample) {
throw new Error(`No PHP SDK example is mapped for ${sdkExampleId}.`);
}
usedPhpSdkExampleIds.add(sdkExampleId);
}

const rawSamples = {
curl: emitCurl({
Expand All @@ -297,8 +324,11 @@ async function buildEndpointSection(
queryParams: req.structuredQueryParams,
endpointKind: kind,
endpointAction: action,
endpointName: req.name,
rawUrl: req.url,
resourceFolder,
sdkConfig,
sdkExample,
}),
python: emitPython({
method: req.method,
Expand Down Expand Up @@ -337,8 +367,11 @@ async function buildEndpointSection(
queryParams: req.structuredQueryParams,
endpointKind: kind,
endpointAction: action,
endpointName: req.name,
rawUrl: req.url,
resourceFolder,
sdkConfig,
sdkExample,
}),
python: emitPython({
method: req.method,
Expand Down
Loading