Skip to content
Closed
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
94 changes: 59 additions & 35 deletions mintlify-codegen/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { join } from 'node:path'

import type { Blueprint, DiscriminatedListProperty } from '@seamapi/blueprint'

import type { PathMetadata } from './load-data.js'
import { formatType, indent, sampleValue } from './property-fields.js'

/**
Expand Down Expand Up @@ -347,38 +348,35 @@ async function writeErrorPage(
routes.push(routePath)
}

// Some device sub-categories have their own docs route but no blueprint
// resource of their own — the Seam API models a lock, thermostat, or phone as a
// `device`. Their error/warning codes therefore live on the `device` resource
// under a variant group of the same key rather than on a resource of their own,
// so the per-resource loop never produces a page for them. This maps each such
// variant group to the route that should carry its dedicated page.
//
// The map is an explicit allowlist, not `'/' + groupKey`: groups like
// `access_codes`, `hardware`, and `provider_metadata` annotate the device page
// but have no standalone sub-category route. In particular /access_codes
// already documents the `access_code` resource's own, distinct errors — routing
// the device `access_codes` group there would clobber it.
const DEVICE_SUBCATEGORY_ERROR_ROUTES: Record<string, string> = {
locks: '/locks',
phones: '/phones',
thermostats: '/thermostats',
}
// Route that surfaces the `phones` device variant group. A phone is documented
// as its own resource (`/phones`), but that resource has no error/warning codes
// of its own — the one phone code (`unknown_issue_with_phone`) lives on the
// `device` resource under the `phones` group. Unlike locks/thermostats it has no
// `include_groups` filter in paths.yaml, so it surfaces only that group's codes.
const PHONES_ROUTE_PATH = '/phones'

/**
* Narrow a device `errors`/`warnings` property to a single variant group,
* flattening the kept variants to ungrouped (`variantGroupKey` cleared) so their
* codes render as top-level entries — a group heading would be redundant on a
* page already scoped to that sub-category. Returns undefined when the property
* is absent or the group has no variants, so the caller emits nothing.
* Filter a discriminated `errors`/`warnings` property to a subset of variant
* groups, flattening the kept variants into a single ungrouped, code-sorted
* list. Keeps the variants whose group is in `groups`, plus the universal
* (ungrouped) codes when `includeUngrouped` is set. This mirrors the GitBook API
* reference, which renders an `include_groups`-filtered view as one flat list
* rather than with per-group headings. Returns undefined when nothing matches,
* so the caller emits nothing.
*/
function variantGroupProp(
function filterVariantGroups(
prop: Property | undefined,
groupKey: string,
groups: readonly string[],
includeUngrouped: boolean,
): (Property & DiscriminatedListProperty) | undefined {
if (!isDiscriminatedListProperty(prop)) return undefined
const keep = new Set(groups)
const variants = prop.variants
.filter((v) => v.variantGroupKey === groupKey)
.filter((v) =>
v.variantGroupKey == null
? includeUngrouped
: keep.has(v.variantGroupKey),
)
.map((v) => ({ ...v, variantGroupKey: null }))
if (variants.length === 0) return undefined
return { ...prop, variants, variantGroups: [] }
Expand All @@ -392,38 +390,64 @@ function variantGroupProp(
export async function updateErrorPages(
blueprint: Blueprint,
docsDir: string,
pathMetadata: PathMetadata,
): Promise<string[]> {
const routes: string[] = []

for (const resource of blueprint.resources) {
if (resource.isUndocumented) continue
// Honor an `include_groups` filter from paths.yaml (e.g. /access_codes lists
// only its `locks` and `access_codes` groups, dropping a stray `thermostats`
// code): keep the universal (ungrouped) codes plus the listed groups,
// flattened — matching the GitBook API reference. Resources without a filter
// keep every group and its subheading.
const include = pathMetadata[resource.routePath]?.include_groups
const pick = (name: string): Property | undefined => {
const prop = resource.properties.find((p) => p.name === name)
return include == null ? prop : filterVariantGroups(prop, include, true)
}
await writeErrorPage(
docsDir,
resource.routePath,
resource.properties.find((p) => p.name === 'errors'),
resource.properties.find((p) => p.name === 'warnings'),
pick('errors'),
pick('warnings'),
routes,
)
}

// Pages for device sub-categories (locks, thermostats, …) that have their own
// docs route but no resource of their own: source each from the matching
// variant group on the device resource.
// Device sub-category pages. Locks and thermostats have their own docs route
// but no resource of their own (the Seam API models them as `device`s), so the
// per-resource loop never reaches them. Source their codes from the device
// resource, filtered the same way:
// - Routes that document `device` under an `include_groups` filter
// (thermostats, locks — see paths.yaml) show the universal device codes
// plus the listed groups.
// - Phones has no `include_groups` filter upstream and is a distinct concept
// from a lock/thermostat device, so it surfaces only its own group's code.
const device = blueprint.resources.find((r) => r.resourceType === 'device')
if (device != null) {
const errorsProp = device.properties.find((p) => p.name === 'errors')
const warningsProp = device.properties.find((p) => p.name === 'warnings')
for (const [groupKey, routePath] of Object.entries(
DEVICE_SUBCATEGORY_ERROR_ROUTES,
)) {

for (const [routePath, meta] of Object.entries(pathMetadata)) {
if (!meta.resources?.includes('device')) continue
if (meta.include_groups == null) continue
await writeErrorPage(
docsDir,
routePath,
variantGroupProp(errorsProp, groupKey),
variantGroupProp(warningsProp, groupKey),
filterVariantGroups(errorsProp, meta.include_groups, true),
filterVariantGroups(warningsProp, meta.include_groups, true),
routes,
)
}

await writeErrorPage(
docsDir,
PHONES_ROUTE_PATH,
filterVariantGroups(errorsProp, ['phones'], false),
filterVariantGroups(warningsProp, ['phones'], false),
routes,
)
}

return routes
Expand Down
2 changes: 1 addition & 1 deletion mintlify-codegen/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ await insertEventsPagesIntoNav(updatedEvents)
// rewritten. Nav wiring runs after events so the sidebar reads
// object -> events -> errors.
console.log('Updating error and warning documentation...')
const updatedErrors = await updateErrorPages(blueprint, outputDir)
const updatedErrors = await updateErrorPages(blueprint, outputDir, pathMetadata)
if (updatedErrors.length > 0) {
console.log(
` Generated errors pages for ${updatedErrors.length} resources: ${updatedErrors.join(', ')}`,
Expand Down
6 changes: 6 additions & 0 deletions mintlify-codegen/load-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ interface PathMetadataEntry {
description?: string
overview?: string
alpha?: string
// Resources this route documents (e.g. `device` for /thermostats and /locks,
// which have no resource of their own).
resources?: string[]
// Error/warning variant groups to surface on this route. When set, a filtered
// view shows the universal (ungrouped) codes plus only these groups.
include_groups?: string[]
}

export type PathMetadata = Record<string, PathMetadataEntry>
Expand Down
60 changes: 24 additions & 36 deletions mintlify-docs/api/access_codes/errors.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,18 @@ Indicates that the account is disconnected.

---

### `august_lock_missing_bridge`

Indicates that the lock is not connected to a bridge.

---

### `august_lock_not_authorized`

Indicates that the user is not authorized to use the August lock.

---

### `bridge_disconnected`

Indicates that the Seam API cannot communicate with [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge), for example, if the Seam Bridge executable has stopped or if the computer running the Seam Bridge executable is offline. See also [Troubleshooting Your Access Control System](https://docs.seam.co/low-level-apis/access-systems/troubleshooting-your-access-control-system#acs_system.errors.seam_bridge_disconnected).
Expand Down Expand Up @@ -132,6 +144,12 @@ Duplicate access code detected on device.

---

### `empty_backup_access_code_pool`

Indicates that the [backup access code pool](https://docs.seam.co/low-level-apis/smart-locks/access-codes/backup-access-codes) is empty.

---

### `failed_to_remove_from_device`

Failed to remove code from device.
Expand Down Expand Up @@ -186,6 +204,12 @@ This access code was overridden on the device by a newer access code programmed

---

### `salto_ks_subscription_limit_exceeded`

Indicates that the Salto site user limit has been reached.

---

### `salto_ks_user_not_subscribed`

Salto site user is not subscribed.
Expand All @@ -204,42 +228,6 @@ Indicates that the lock is not paired with a gateway.

---

### Access Codes

#### `empty_backup_access_code_pool`

Indicates that the [backup access code pool](https://docs.seam.co/low-level-apis/smart-locks/access-codes/backup-access-codes) is empty.

---

### Locks

#### `august_lock_missing_bridge`

Indicates that the lock is not connected to a bridge.

---

#### `august_lock_not_authorized`

Indicates that the user is not authorized to use the August lock.

---

#### `salto_ks_subscription_limit_exceeded`

Indicates that the Salto site user limit has been reached.

---

### Thermostats

#### `auxiliary_heat_running`

Indicates that the auxiliary heat is running.

---

## Warnings

Each warning is an object with the following shape:
Expand Down
60 changes: 24 additions & 36 deletions mintlify-docs/api/access_codes/unmanaged/errors.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,18 @@ Indicates that the account is disconnected.

---

### `august_lock_missing_bridge`

Indicates that the lock is not connected to a bridge.

---

### `august_lock_not_authorized`

Indicates that the user is not authorized to use the August lock.

---

### `bridge_disconnected`

Indicates that the Seam API cannot communicate with [Seam Bridge](https://docs.seam.co/capability-guides/seam-bridge), for example, if the Seam Bridge executable has stopped or if the computer running the Seam Bridge executable is offline. See also [Troubleshooting Your Access Control System](https://docs.seam.co/low-level-apis/access-systems/troubleshooting-your-access-control-system#acs_system.errors.seam_bridge_disconnected).
Expand Down Expand Up @@ -132,6 +144,12 @@ Duplicate access code detected on device.

---

### `empty_backup_access_code_pool`

Indicates that the [backup access code pool](https://docs.seam.co/low-level-apis/smart-locks/access-codes/backup-access-codes) is empty.

---

### `failed_to_remove_from_device`

Failed to remove code from device.
Expand Down Expand Up @@ -186,6 +204,12 @@ This access code was overridden on the device by a newer access code programmed

---

### `salto_ks_subscription_limit_exceeded`

Indicates that the Salto site user limit has been reached.

---

### `salto_ks_user_not_subscribed`

Salto site user is not subscribed.
Expand All @@ -204,42 +228,6 @@ Indicates that the lock is not paired with a gateway.

---

### Access Codes

#### `empty_backup_access_code_pool`

Indicates that the [backup access code pool](https://docs.seam.co/low-level-apis/smart-locks/access-codes/backup-access-codes) is empty.

---

### Locks

#### `august_lock_missing_bridge`

Indicates that the lock is not connected to a bridge.

---

#### `august_lock_not_authorized`

Indicates that the user is not authorized to use the August lock.

---

#### `salto_ks_subscription_limit_exceeded`

Indicates that the Salto site user limit has been reached.

---

### Thermostats

#### `auxiliary_heat_running`

Indicates that the auxiliary heat is running.

---

## Warnings

Each warning is an object with the following shape:
Expand Down
Loading
Loading