mirror of
https://github.com/screentinker/screentinker.git
synced 2026-08-13 13:53:12 -06:00
Merge docs/api-device-network-fields: document device network fields, pin the spec version
This commit is contained in:
commit
234bff795d
|
|
@ -1,7 +1,7 @@
|
|||
openapi: 3.1.0
|
||||
info:
|
||||
title: ScreenTinker Public API
|
||||
version: 1.9.0
|
||||
version: 1.9.25
|
||||
description: |
|
||||
Public, token-scoped REST API for ScreenTinker digital signage.
|
||||
|
||||
|
|
@ -109,6 +109,60 @@ components:
|
|||
created_at:
|
||||
type: integer
|
||||
|
||||
# --- Network addresses -------------------------------------------------------------
|
||||
# Two different addresses, easy to confuse, so both are spelled out. They answer
|
||||
# different questions and either can be null.
|
||||
ip_address:
|
||||
type: [string, "null"]
|
||||
description: |
|
||||
The device's **public (WAN)** address as observed by the server when the player
|
||||
connected — i.e. what the internet sees. Behind a reverse proxy this is taken from
|
||||
the first `X-Forwarded-For` entry, otherwise the socket peer address. Every device
|
||||
on one site normally shares this. Null until the device has connected at least once.
|
||||
local_ip:
|
||||
type: [string, "null"]
|
||||
description: |
|
||||
The device's **own address on its local network** (e.g. `192.168.1.42`), as
|
||||
reported by the player itself. This is the one to use to reach a panel directly on
|
||||
site. Null on players that do not report it, or where the platform withholds it.
|
||||
|
||||
# --- Latest telemetry --------------------------------------------------------------
|
||||
# Flattened from the most recent telemetry report. All null for a device that has
|
||||
# never reported, and for platforms that cannot supply a given metric — treat every
|
||||
# field here as optional rather than assuming a web player reports what Android does.
|
||||
wifi_ssid:
|
||||
type: [string, "null"]
|
||||
description: |
|
||||
Wi-Fi network name, or null on a wired/unknown connection.
|
||||
|
||||
Special value `"permission"` means the device is on Wi-Fi but the operating system
|
||||
withheld the name — on Android 10+ reading the SSID requires a location permission
|
||||
that ScreenTinker only asks for if an operator opts in. Treat `"permission"` as
|
||||
"connected, name unavailable", not as a network literally called that.
|
||||
wifi_rssi:
|
||||
type: [integer, "null"]
|
||||
description: Signal strength in dBm (negative; closer to zero is stronger).
|
||||
battery_level:
|
||||
type: [integer, "null"]
|
||||
description: Battery percentage 0-100, or null on mains-powered hardware.
|
||||
battery_charging:
|
||||
type: [integer, "null"]
|
||||
description: 1 charging, 0 not charging, null unknown.
|
||||
storage_free_mb:
|
||||
type: [integer, "null"]
|
||||
storage_total_mb:
|
||||
type: [integer, "null"]
|
||||
ram_free_mb:
|
||||
type: [integer, "null"]
|
||||
ram_total_mb:
|
||||
type: [integer, "null"]
|
||||
cpu_usage:
|
||||
type: [number, "null"]
|
||||
description: Recent CPU utilisation as a percentage, where the platform exposes it.
|
||||
uptime_seconds:
|
||||
type: [integer, "null"]
|
||||
description: Seconds since the device booted.
|
||||
|
||||
Playlist:
|
||||
type: object
|
||||
properties:
|
||||
|
|
|
|||
|
|
@ -80,8 +80,17 @@ sed -i -E "s/(versionCode.*\?:[[:space:]]*)\"[0-9]+\"/\1\"$((CODE + 1))\"/" andr
|
|||
NUMERIC="${NEW%%-*}"
|
||||
sed -i -E "/^<\?xml/! s/([[:space:]]version=\")[0-9][^\"]*(\")/\1${NUMERIC}\2/" tizen/config.xml
|
||||
|
||||
# 5) commit + annotated tag (no push)
|
||||
git add VERSION server/package.json server/package-lock.json android/app/build.gradle.kts tizen/config.xml
|
||||
# 5) public API spec version. This is the number Redoc prints at the top of the published
|
||||
# API reference (frontend/api-docs.html renders docs/openapi.yaml directly), so leaving it
|
||||
# behind means customers read a version that has not existed for months — it had drifted to
|
||||
# 1.9.0 while shipping 1.9.25 precisely because this step did not exist. Anchored to the
|
||||
# two-space ` version:` under `info:`; operation-level and schema-level keys are indented
|
||||
# deeper and are not touched. As with Tizen, use the numeric form: the spec version is a
|
||||
# published API identity, not a build label.
|
||||
sed -i -E "0,/^ version:/s/^( version:[[:space:]]*).*/\1${NUMERIC}/" docs/openapi.yaml
|
||||
|
||||
# 6) commit + annotated tag (no push)
|
||||
git add VERSION server/package.json server/package-lock.json android/app/build.gradle.kts tizen/config.xml docs/openapi.yaml
|
||||
git commit -q -m "chore(release): v$NEW"
|
||||
git tag -a "v$NEW" -m "ScreenTinker v$NEW"
|
||||
|
||||
|
|
|
|||
|
|
@ -53,3 +53,48 @@ test('openapi: every documented path is a token-reachable (public) router, never
|
|||
}
|
||||
assert.deepEqual(offenders, [], 'spec documents non-public paths:\n' + offenders.join('\n'));
|
||||
});
|
||||
|
||||
// The published spec version is what Redoc prints at the top of the API reference, so a stale
|
||||
// value tells integrators they are reading docs for a release that no longer exists. It HAD gone
|
||||
// stale — the spec said 1.9.0 while 1.9.25 was shipping — because bump-version.sh updated every
|
||||
// other version source and not this one. That step now exists; this test is what keeps it honest,
|
||||
// since the failure mode is silent and nobody reads a version number they already trust.
|
||||
test('openapi: the spec version tracks the shipped release', () => {
|
||||
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8'));
|
||||
// Pre-release labels (1.9.26-beta.1) live on the build, not on the published API identity,
|
||||
// so compare the numeric core the way bump-version.sh writes it.
|
||||
const numeric = (v) => String(v).split('-')[0];
|
||||
assert.equal(
|
||||
numeric(spec.info.version),
|
||||
numeric(pkg.version),
|
||||
'docs/openapi.yaml info.version drifted from server/package.json — bump-version.sh should ' +
|
||||
'have moved both; if you edited a version by hand, move this one too',
|
||||
);
|
||||
});
|
||||
|
||||
// Two addresses that are easy to mix up: ip_address is the public/WAN address the SERVER observed
|
||||
// on connect, local_ip is the LAN address the PLAYER reported about itself. An integrator reaching
|
||||
// a panel on site needs local_ip; one correlating sites needs ip_address. Both are returned by
|
||||
// GET /devices, so both must be documented and must not be described interchangeably.
|
||||
test('openapi: a device documents its WAN and LAN addresses distinctly', () => {
|
||||
const props = spec.components.schemas.Device.properties;
|
||||
for (const field of ['ip_address', 'local_ip']) {
|
||||
assert.ok(props[field], `Device.${field} is returned by GET /devices but is not documented`);
|
||||
assert.ok(
|
||||
props[field].type.includes('null'),
|
||||
`Device.${field} must be nullable — it is absent until a device reports/connects`,
|
||||
);
|
||||
assert.ok(props[field].description, `Device.${field} needs a description to be told apart`);
|
||||
}
|
||||
assert.match(props.ip_address.description, /WAN|public/i);
|
||||
assert.match(props.local_ip.description, /local network|LAN/i);
|
||||
});
|
||||
|
||||
// "permission" is a sentinel, not a network name: Android 10+ withholds the SSID without a
|
||||
// location permission ScreenTinker only requests if an operator opts in. An integrator who does
|
||||
// not know that will render it as the Wi-Fi name to an end user.
|
||||
test('openapi: the wifi_ssid permission sentinel is documented', () => {
|
||||
const ssid = spec.components.schemas.Device.properties.wifi_ssid;
|
||||
assert.ok(ssid, 'wifi_ssid is returned by GET /devices but is not documented');
|
||||
assert.match(ssid.description, /permission/, 'the sentinel value must be explained');
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue