30 Commits

Author SHA1 Message Date
e6f9610a51 Add 'NETWORK_DISCONNECTED' to error code enum
All checks were successful
Generate and Release Protos / release (push) Successful in 42s
2026-05-15 20:25:19 +01:00
e7e2093054 Add 'm' key for 'mode' to be set only when the service is a Ship or Bus.
All checks were successful
Generate and Release Protos / release (push) Successful in 39s
2026-05-10 19:29:14 +01:00
12db4b79fd Fix formatting
All checks were successful
Generate and Release Protos / release (push) Successful in 41s
2026-05-08 19:06:41 +01:00
eb6d90bbb8 Add service data for 'False Destination' and 'Via text'.
Add envelope support for PUBLIC/STAFF enum for display decisions
2026-05-08 19:06:14 +01:00
5918cc54a8 Extend board to include passing times
All checks were successful
Generate and Release Protos / release (push) Successful in 39s
2026-05-07 14:11:53 +01:00
3b1a9a2be1 Reorganise the board JSON response
All checks were successful
Generate and Release Protos / release (push) Successful in 38s
2026-05-07 13:02:51 +01:00
b8c773c3c9 Add type for departure board data
All checks were successful
Generate and Release Protos / release (push) Successful in 40s
2026-05-06 19:47:33 +01:00
a7fe008fee Ensure required fields exist...
All checks were successful
Generate and Release Protos / release (push) Successful in 37s
2026-05-03 10:20:32 +01:00
6cfc12b538 Remove unused fields from TrainDetails response 2026-05-03 10:20:09 +01:00
53ce528d56 Change type of Cancel Reason & Delay Reason to string, the string will be sent.
All checks were successful
Generate and Release Protos / release (push) Successful in 36s
2026-05-03 10:05:56 +01:00
25c1793df3 Add 'cancelled throughout' field, to highlight in the search results whether a service is completely cancelled
All checks were successful
Generate and Release Protos / release (push) Successful in 38s
2026-05-03 09:34:31 +01:00
48f1a31378 Remove reference to other schema, it seems that I can either configure it for TS or Go generation, but not both.
All checks were successful
Generate and Release Protos / release (push) Successful in 38s
2026-05-03 00:38:48 +01:00
376370c729 Fix incorrect path
Some checks failed
Generate and Release Protos / release (push) Failing after 32s
2026-05-03 00:32:10 +01:00
180886fd68 Adjust script & paths so that referencing other schemas work
Some checks failed
Generate and Release Protos / release (push) Failing after 28s
2026-05-03 00:30:46 +01:00
513196c43d Try and adjust path
Some checks failed
Generate and Release Protos / release (push) Failing after 27s
2026-05-03 00:25:17 +01:00
5d4271c193 Add PIS item to the 'TrainDetails' response
Some checks failed
Generate and Release Protos / release (push) Failing after 34s
2026-05-03 00:21:12 +01:00
c02ff3ebab Adjust how time types are specified
All checks were successful
Generate and Release Protos / release (push) Successful in 40s
2026-05-02 09:14:16 +01:00
519acebcba Add train details type
All checks were successful
Generate and Release Protos / release (push) Successful in 44s
2026-05-02 01:11:10 +01:00
645faf1003 Add TrainByHeadcode response type
All checks were successful
Generate and Release Protos / release (push) Successful in 38s
2026-04-27 00:12:16 +01:00
a2308198e9 Add nearest stations schema
All checks were successful
Generate and Release Protos / release (push) Successful in 32s
2026-03-30 20:50:03 +01:00
8c2ed1ad8f Ensure TIPLOC is an optional entry. 'stations' will not include a TIPLOC as there is no 1-to-1 mapping.
All checks were successful
Generate and Release Protos / release (push) Successful in 25s
2026-03-25 09:36:47 +00:00
0848fe3b27 Create location filter schema
All checks were successful
Generate and Release Protos / release (push) Successful in 29s
2026-03-24 00:38:08 +00:00
8ac0215247 Feat: Add 'skip' object to the PISObject schema to allow for partial matches to be described.
All checks were successful
Generate and Release Protos / release (push) Successful in 28s
2026-02-20 22:31:01 +00:00
07f224ff18 Relax type requirements on 'd' (data payload) field to improve type generation
All checks were successful
Generate and Release Protos / release (push) Successful in 29s
2026-02-19 21:05:33 +00:00
91e9432a07 Update to handle /v3 version
All checks were successful
Generate and Release Protos / release (push) Successful in 29s
2026-02-19 17:13:47 +00:00
50e2907f47 Update package.json values for output 2026-02-17 20:50:28 +00:00
e3bab25418 Rename envelope to improve output-type naming
All checks were successful
Generate and Release Protos / release (push) Successful in 28s
2026-02-17 20:31:19 +00:00
6298763f2f Rename directories to fit with API schema
All checks were successful
Generate and Release Protos / release (push) Successful in 37s
2026-02-17 20:15:20 +00:00
7259dfaa37 Initial schema push - covers PIS only 2026-02-17 19:53:33 +00:00
64df9edb95 Update copyright notice in license file 2026-02-17 19:01:15 +00:00
10 changed files with 866 additions and 1 deletions

View File

@@ -0,0 +1,121 @@
name: Generate and Release Protos
on:
push:
tags:
- 'v*'
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
persist-credentials: false
- name: Get Version
id: get_version
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
- uses: actions/setup-go@v5
with:
go-version: '1.24'
- uses: actions/setup-node@v6
with:
node-version: '18.18.x'
registry-url: 'https://git.fjla.uk/api/packages/owlboard/npm'
scope: '@owlboard'
- name: Install Generators
run: |
npm install -g json-schema-to-typescript typescript
go install github.com/atombender/go-jsonschema@latest
echo "$(go env GOPATH)/bin" >> $GITHUB_PATH
- run: bash scripts/build.sh
- name: Build and Publish TS
working-directory: gen/ts
run: |
npm init -y
# Build index.ts
echo "// Auto-generated" > index.ts
find . -maxdepth 1 -name "*.ts" -not -name "index.ts" | sed 's|^\./||; s|\.ts$||' | awk '{
# Use gsub to turn hyphens into underscores so we can split easily
clean = $0;
gsub(/-/, "_", clean);
n = split(clean, parts, "_");
name = "";
for (i=1; i<=n; i++) {
if (length(parts[i]) > 0) {
name = name toupper(substr(parts[i],1,1)) substr(parts[i],2);
}
}
# name will now be 'DataIngressPisData' (valid TS)
printf "export * as %s from \"./%s.js\";\n", name, $0
}' >> index.ts
VERSION="${{ steps.get_version.outputs.VERSION }}"
REPO_URL="${{ github.server_url }}/${{ github.repository }}.git"
jq --arg ver "$VERSION" \
--arg name "@owlboard/api-schema-types" \
--arg repo "$REPO_URL" \
'.name = $name |
.description = "TypeScript type definitions for OwlBoard API schemas" |
.author = "Frederick Boniface" |
.version = $ver |
.type = "module" |
.main = "./dist/index.js" |
.license = "MIT" |
.repository = { "type": "git", "url": $repo } |
.files = ["dist"] |
.sideEffects = false |
.dependencies = {} |
.devDependencies = {} |
.exports = {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
} |
.types = "./dist/index.d.ts"' \
package.json > package.json.new && mv package.json.new package.json
# Compile
npx tsc index.ts --declaration --module nodenext --target es2022 --moduleResolution nodenext --outDir dist/ --skipLibCheck true
# Publish
npm config set //git.fjla.uk/api/packages/owlboard/npm/:_authToken ${{ secrets.PACKAGE_PUSH }}
npm publish
- name: Publish Go
run: |
VERSION="v${{ steps.get_version.outputs.VERSION }}"
MOD_NAME="git.fjla.uk/owlboard/api-schema-types/v3"
ZIP_ROOT="/tmp/go_upload"
FULL_PATH="$ZIP_ROOT/$MOD_NAME@$VERSION"
# 1. Prepare
cd gen/go
# 2. Initialize the module
go mod init "$MOD_NAME"
# 3. Create the structure
mkdir -p "$FULL_PATH"
# 4. Copy the CONTENTS of models to the root of the module
# This flattens the structure so the .go files are next to go.mod
cp -r models/* "$FULL_PATH/"
cp go.mod "$FULL_PATH/"
# 5. Zip and Upload
cd "$ZIP_ROOT"
zip -r -D "$GITHUB_WORKSPACE/module.zip" .
curl -f --user "owlbot:${{ secrets.PACKAGE_PUSH }}" \
--upload-file "$GITHUB_WORKSPACE/module.zip" \
"${{ github.server_url }}/api/packages/owlboard/go/upload?version=$VERSION"

View File

@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2026 OwlBoard
Copyright (c) 2026 Frederick Boniface
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction, including

50
schemas/api/envelope.json Normal file
View File

@@ -0,0 +1,50 @@
{
"$id": "https://schema.owlboard.info/api/api-envelope.schema.json",
"$schema": "https://json-schema.org/draft-07/schema#",
"title": "Envelope",
"description": "OwlBoard API Envelope",
"type": "object",
"properties": {
"t": {
"type": "integer",
"minimum": 0,
"description": "Unix timestamp showing when the data was generated, or the time the error was encountered"
},
"p": {
"type": "string",
"title": "Privilege Type",
"description": "Whether the data is public or staff, omitted where no differences",
"enum": ["public", "staff"]
},
"d": {
"description": "Payload data. Type depends on request endpoint, typically an array of the response type"
},
"e": {
"type": "object",
"properties": {
"code": {
"type": "string",
"description": "Type of error encountered",
"enum": [
"VALIDATION",
"AUTH",
"NOT_FOUND",
"RATE_LIMIT",
"SERVER",
"NETWORK_DISCONNECTED"
]
},
"msg": {
"type": "string",
"description": "Human-readable descriptive error message."
}
}
}
},
"required": ["t"],
"oneOf": [
{"required": ["e"]},
{"required": ["d"]}
],
"additionalProperties": false
}

View File

@@ -0,0 +1,31 @@
{
"$id": "https://schema.owlboard.info/api/location-filter.schema.json",
"$schema": "https://json-schema.org/draft-07/schema#",
"title": "LocationFilterObject",
"description": "Location filter API Response. Provides a location's data for filtering on the frontend",
"type": "object",
"required": ["n", "s"],
"additionalProperties": false,
"properties": {
"n": {
"type": "string",
"name": "Name",
"description": "Name of the location"
},
"t": {
"type": "string",
"name": "TIPLOC",
"description": "TIPLOC of the location"
},
"c": {
"type": "string",
"name": "CRS",
"description": "CRS of the location"
},
"s": {
"type": "string",
"name": "searchString",
"description": "Generated string for efficient filtering"
}
}
}

View File

@@ -0,0 +1,56 @@
{
"$id": "https://schema.owlboard.info/api/pis-object.schema.json",
"$schema": "https://json-schema.org/draft-07/schema#",
"title": "PisObjects",
"description": "PIS API Resonse, contains the code and optionally, TOC and/or a list of CRS, TIPLOC",
"type": "object",
"properties": {
"code": {
"type": "string",
"description": "PIS Code - Code that is entered in to the PIS system"
},
"toc": {
"type": "string",
"minLength": 2,
"maxLength": 2,
"pattern": "^[a-zA-Z]+$",
"description": "Two letter TOC Code"
},
"crsStops": {
"type": "array",
"items": {
"type": "string",
"minLength": 3,
"maxLength": 3,
"pattern": "^[a-zA-Z]+$"
},
"description": "List of 3ALPHA/CRS Codes"
},
"tiplocStops": {
"type": "array",
"items": {
"type": "string",
"minLength": 4,
"maxLength": 7,
"pattern": "^[a-zA-Z0-9]+$"
},
"description": "List of TIPLOC Codes"
},
"skip": {
"type": "object",
"properties": {
"skip": {
"type": "integer",
"description": "Number of stops to skip"
},
"position": {
"type": "string",
"enum": ["head", "tail"],
"description": "Position of stops to be skipped, either 'head' or 'tail'"
}
}
}
},
"required": ["code"],
"additionalProperties": false
}

View File

@@ -0,0 +1,309 @@
{
"$id": "https://schema.owlboard.info/api/stations/board.schema.json",
"$schema": "https://json-schema.org/draft-07/schema#",
"title": "StationsBoard",
"description": "Arr/Dep/Pass Board",
"type": "object",
"properties": {
"d": {
"title": "Metadata",
"type": "object",
"$ref": "#/definitions/metadata"
},
"s": {
"title": "Services",
"type": "array",
"items": {
"$ref": "#/definitions/boardService"
}
},
"m": {
"type": "array",
"title": "messages",
"items": {
"$ref": "#/definitions/boardMsgs"
}
}
},
"required": [
"n"
],
"definitions": {
"metadata": {
"type": "object",
"required": [
"n",
"o"
],
"properties": {
"n": {
"type": "string",
"title": "Location Name",
"description": "The name of the location"
},
"o": {
"type": "string",
"title": "Station Operator",
"description": "The operator of the station (not present for location)"
}
}
},
"boardService": {
"type": "object",
"required": [
"r",
"o",
"ip",
"og",
"dt"
],
"properties": {
"r": {
"type": "string",
"title": "RID",
"description": "Services RID"
},
"m": {
"type": "string",
"title": "mode",
"description": "Transport mode, default value: Train",
"enum": ["SHIP", "BUS"]
},
"o": {
"type": "string",
"title": "TOC",
"description": "The services operator code"
},
"ip": {
"type": "boolean",
"title": "isPassenger",
"description": "Whether this is a passenger service"
},
"og": {
"type": "object",
"title": "Origin",
"description": "The services origin",
"required": [
"t",
"n"
],
"properties": {
"t": {
"type": "string",
"title": "TIPLOC",
"description": "The Origin TIPLOC"
},
"n": {
"type": "string",
"title": "Name",
"description": "The Origin Name"
}
}
},
"dt": {
"type": "object",
"title": "Destination",
"description": "The services destination",
"required": [
"t",
"n"
],
"properties": {
"t": {
"type": "string",
"title": "TIPLOC",
"description": "The Destination TIPLOC"
},
"n": {
"type": "string",
"title": "Name",
"description": "The Destination Name"
}
}
},
"h": {
"type": "string",
"title": "Headcode",
"description": "The headcode of the service"
},
"fd": {
"type": "object",
"title": "False Destination",
"description": "False destination should be preferred on public boards",
"required": [
"t",
"n"
],
"properties": {
"t": {
"type": "string",
"title": "TIPLOC",
"description": "The Destination TIPLOC"
},
"n": {
"type": "string",
"title": "Name",
"description": "The Destination Name"
}
}
},
"v": {
"type": "string",
"title": "via Text",
"description": "via text that should be displayed if present"
},
"sta": {
"type": "string",
"format": "date-time",
"title": "Scheduled Arrival",
"description": "The scheduled arrival time of the service (public or working)"
},
"ata": {
"type": "string",
"format": "date-time",
"title": "Actual Arrival",
"description": "The actual arrival time of the service"
},
"eta": {
"type": "string",
"format": "date-time",
"title": "Estimated Arrival",
"description": "The estimated arrival time of the service"
},
"std": {
"type": "string",
"format": "date-time",
"title": "Scheduled Departure",
"description": "The scheduled departure time of the service (public or working)"
},
"atd": {
"type": "string",
"format": "date-time",
"title": "Actual Departure",
"description": "The actual departure time of the service"
},
"etd": {
"type": "string",
"format": "date-time",
"title": "Estimated Departure",
"description": "The estimated departure time of the service"
},
"wtp": {
"type": "string",
"format": "date-time",
"title": "Scheduled Pass",
"description": "The scheduled pass time of the service"
},
"atp": {
"type": "string",
"format": "date-time",
"title": "Actual Pass",
"description": "The actual pass time of the service"
},
"etp": {
"type": "string",
"format": "date-time",
"title": "Estimated Pass",
"description": "The estimated pass time of the service"
},
"p": {
"type": "string",
"title": "Platform",
"description": "The platform for this service"
},
"pc": {
"type": "boolean",
"title": "Platform Changed",
"description": "Whether the platform has changed (alteration)"
},
"ps": {
"type": "boolean",
"title": "Platform suppressed",
"description": "Whether the platform number is suppressed"
},
"c": {
"type": "boolean",
"title": "Cancelled",
"description": "Whether the service is cancelled at this location"
},
"da": {
"type": "boolean",
"title": "Delayed Arrival",
"description": "Whether to show the service as delayed arrival"
},
"dd": {
"type": "boolean",
"title": "Delayed Departure",
"description": "Whether to show the service as delayed departure"
},
"cr": {
"$ref": "#/definitions/reason"
},
"dr": {
"$ref": "#/definitions/reason"
},
"act": {
"type": "string",
"title": "Activities",
"description": "Activities at this location"
},
"f": {
"$ref": "#/definitions/formation"
}
}
},
"reason": {
"type": "object",
"properties": {
"r": {
"type": "string",
"title": "Reason",
"description": "The textual reason"
},
"l": {
"type": "string",
"title": "Location",
"description": "The location the reason occurred"
},
"n": {
"type": "boolean",
"title": "Near",
"description": "Whether the reason occured NEAR LOCATION (else AT LOCATION)"
}
}
},
"formation": {
"type": "object",
"properties": {
"fid": {
"type": "string",
"title": "FID",
"description": "The serviced FID at this location"
}
}
},
"boardMsgs": {
"type": "object",
"required": [
"t"
],
"properties": {
"t": {
"type": "string",
"title": "Text",
"description": "The message text"
},
"l": {
"type": "string",
"title": "Link",
"description": "The NRE Link to the incident page"
},
"lt": {
"type": "string",
"title": "Link Text",
"description": "The text to display as the link"
}
}
}
}
}

View File

@@ -0,0 +1,19 @@
{
"$id": "https://schema.owlboard.info/api/stations/nearestStations.schema.json",
"$schema": "https://json-schema.org/draft-07/schema#",
"title": "StationsNearestStations",
"description": "Nearest Stations API Resonse. Returned as an Array of the object. Response array will be sorted - nearest first",
"type": "object",
"properties": {
"c": {
"type": "string",
"name": "CRS"
},
"n": {
"type": "string",
"name": "Station Name"
}
},
"required": ["c", "n"],
"additionalProperties": false
}

View File

@@ -0,0 +1,47 @@
{
"$schema": "https://json-schema.org/draft-07/schema#",
"title": "TrainByHeadcodeResponse",
"type": "object",
"required": ["r", "ot", "od", "dt", "o"],
"additionalProperties": false,
"properties": {
"r": {
"type": "string",
"name": "rid",
"description": "The RID of the service described"
},
"ot": {
"type": "string",
"name": "Origin TIPLOC",
"description": "The TIPLOC at which the service originates",
"minLength": 4,
"maxLength": 7
},
"od": {
"type": "string",
"name": "Origin Departure",
"format": "date-time",
"description": "The time that the service departs the originating location"
},
"dt": {
"type": "string",
"name": "Destination TIPLOC",
"description": "The TIPLOC at which the service terminates",
"minimum": 4,
"maximum": 7
},
"o": {
"type": "string",
"name": "Operator (TOC)",
"description": "The TOC operating the service",
"minLength": 2,
"maxLength": 2
},
"ct": {
"type": "boolean",
"title": "Cancelled Throughout",
"name": "Cancelled Throughout",
"description": "Whether the train is cancelled throughout"
}
}
}

View File

@@ -0,0 +1,207 @@
{
"$schema": "https://json-schema.org/draft-07/schema#",
"title": "TrainDetailsResponse",
"$defs": {
"ServiceLocation": {
"type": "object",
"description": "A specific location along the services journey",
"additionalProperties": false,
"properties": {
"t": {
"type": "string",
"title": "TIPLOC",
"description": "The TIPLOC of the location"
},
"r": {
"type": "string",
"enum": ["ORIG", "CALL", "PASS", "DEST"],
"title": "Location Role",
"description": "The role of this location in the journey"
},
"s": {
"type": "integer",
"title": "Sequence Number",
"description": "The sequence at which this location sits in the schedule"
},
"act": {
"title": "activities",
"description": "Activities carried out at this location",
"type": "string"
},
"p": {
"title": "Platform",
"type": "string",
"description": "The platform that this event takes place"
},
"pc": {
"title": "Platform Changed",
"type": "boolean"
},
"pcn": {
"title": "Platform Confirmed",
"type": "boolean"
},
"ps": {
"title": "Platform Surpressed",
"description": "Whether platform data is surpressed",
"type": "boolean"
},
"can": {
"title": "Cancelled",
"description": "Whether this location is cancelled in the journey",
"type": "boolean"
},
"al": {
"type": "integer",
"title": "Average Loading",
"description": "The average loading at this location"
},
"fid": {
"type": "string",
"title": "Formation ID",
"description": "The formation ID at this location"
},
"pta": {
"type": "string",
"format": "date-time",
"title": "Public time of arrival"
},
"wta": {
"type": "string",
"format": "date-time",
"title": "Working time of arrival"
},
"wtp": {
"type": "string",
"format": "date-time",
"title": "Working time of pass"
},
"ptd": {
"type": "string",
"format": "date-time",
"title": "Public time of departure"
},
"wtd": {
"type": "string",
"format": "date-time",
"title": "Working time of departure"
},
"eta": {
"type": "string",
"format": "date-time",
"title": "Estimated time of arrival"
},
"etd": {
"type": "string",
"format": "date-time",
"title": "Estimated time of departure"
},
"etp": {
"type": "string",
"format": "date-time",
"title": "Estimated time of pass"
},
"ata": {
"type": "string",
"format": "date-time",
"title": "Actual time of arrival"
},
"atd": {
"type": "string",
"format": "date-time",
"title": "Actual time of departure"
},
"atp": {
"type": "string",
"format": "date-time",
"title": "Actual time of pass"
}
},
"required": ["t", "r", "s", "act"]
}
},
"type": "object",
"required": ["header", "locations"],
"additionalProperties": false,
"properties": {
"header": {
"type": "object",
"additionalProperties": false,
"properties": {
"h": {
"title": "train_id",
"description": "Headcode",
"maxLength": 4,
"minLength": 4,
"type": "string"
},
"t": {
"title": "TOC",
"description": "The TOC operating the service",
"type": "string",
"maxLength": 3,
"minLength": 2
},
"st": {
"title": "Status",
"description": "Train status",
"type": "string"
},
"cr": {
"title": "Cancel Reason",
"type": "string"
},
"cl": {
"title": "Cancel Location",
"type": "string",
"maxLength": 7
},
"cn": {
"title": "Cancel Near",
"description": "Whether the train was cancelled NEAR the 'cancel_loation', else at the 'cancel_location'",
"type": "boolean"
},
"dr": {
"title": "Delay Reason",
"type": "string"
},
"dl": {
"title": "Delay Location",
"type": "string",
"maxLength": 7
},
"dn": {
"title": "Delay Near",
"description": "Whether the train was delayed NEAR the 'delay_loation', else at the 'delay_location'",
"type": "boolean"
},
"ip": {
"title": "is_passenger",
"description": "Whether this is a passenger service",
"type": "boolean"
},
"ic": {
"title": "is_charter",
"description": "Whether this is a charter service",
"type": "boolean"
},
"rs": {
"title": "RSID",
"description": "Retail Service ID",
"type": "string"
}
},
"required": ["h", "t", "ip", "ic"]
},
"locations": {
"type": "array",
"additionalItems": false,
"items": {
"$ref": "#/$defs/ServiceLocation"
}
},
"pis": {
"description": "PIS data for the service (if available)"
}
}
}

25
scripts/build.sh Normal file
View File

@@ -0,0 +1,25 @@
#!/bin/bash
set -e
# Create clean output directories
rm -rf gen && mkdir -p gen/ts gen/go/models
# Find all .json files
FILES=$(find schemas -name "*.json")
# Initialize the TypeScript Barrel File
echo "// Auto-generated barrel file" > gen/ts/index.ts
for file in $FILES; do
# Get a clean name (e.g., data-ingress_pis-mapping)
clean_name=$(echo "${file#schemas/}" | sed 's/\//_/g' | sed 's/\.json//g')
# OGenerate TS
npx --yes json-schema-to-typescript "$file" > "./gen/ts/${clean_name}.ts"
# Generate Go
go-jsonschema -p contracts "$file" > "./gen/go/models/${clean_name}.go"
done
echo "✅ Generated single TS package in gen/ts"
echo "✅ Generated single Go package in gen/go/models"