29 Commits

Author SHA1 Message Date
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 865 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 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 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 associated documentation files (the "Software"), to deal in the Software without restriction, including

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

@@ -0,0 +1,49 @@
{
"$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"
]
},
"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"