jentic-openapi-validator-spectral 1.0.0a14__tar.gz → 1.0.0a16__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,14 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jentic-openapi-validator-spectral
3
- Version: 1.0.0a14
3
+ Version: 1.0.0a16
4
4
  Summary: Jentic OpenAPI Spectral Validator Backend
5
5
  Author: Jentic
6
6
  Author-email: Jentic <hello@jentic.com>
7
7
  License-Expression: Apache-2.0
8
8
  License-File: LICENSE
9
9
  License-File: NOTICE
10
- Requires-Dist: jentic-openapi-common~=1.0.0a14
11
- Requires-Dist: jentic-openapi-validator~=1.0.0a14
10
+ Requires-Dist: jentic-openapi-common~=1.0.0a16
11
+ Requires-Dist: jentic-openapi-validator~=1.0.0a16
12
12
  Requires-Dist: lsprotocol~=2025.0.0
13
13
  Requires-Python: >=3.11
14
14
  Project-URL: Homepage, https://github.com/jentic/jentic-openapi-tools
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "jentic-openapi-validator-spectral"
3
- version = "1.0.0-alpha.14"
3
+ version = "1.0.0-alpha.16"
4
4
  description = "Jentic OpenAPI Spectral Validator Backend"
5
5
  readme = "README.md"
6
6
  authors = [{ name = "Jentic", email = "hello@jentic.com" }]
@@ -8,8 +8,8 @@ license = "Apache-2.0"
8
8
  license-files = ["LICENSE", "NOTICE"]
9
9
  requires-python = ">=3.11"
10
10
  dependencies = [
11
- "jentic-openapi-common~=1.0.0-alpha.14",
12
- "jentic-openapi-validator~=1.0.0-alpha.14",
11
+ "jentic-openapi-common~=1.0.0-alpha.16",
12
+ "jentic-openapi-validator~=1.0.0-alpha.16",
13
13
  "lsprotocol~=2025.0.0"
14
14
  ]
15
15
 
@@ -36,4 +36,4 @@ build-backend = "uv_build"
36
36
  spectral = "jentic.apitools.openapi.validator.backends.spectral:SpectralValidatorBackend"
37
37
 
38
38
  [tool.setuptools.package-data]
39
- "jentic.apitools.openapi.validator.backends.spectral" = ["rulesets/*.yaml", "rulesets/*.yml"]
39
+ "jentic.apitools.openapi.validator.backends.spectral" = ["rulesets/*.yaml", "rulesets/*.yml", "rulesets/*.mjs"]
@@ -23,7 +23,7 @@ __all__ = ["SpectralValidatorBackend"]
23
23
 
24
24
 
25
25
  rulesets_files_dir = files("jentic.apitools.openapi.validator.backends.spectral.rulesets")
26
- ruleset_file = rulesets_files_dir.joinpath("spectral.yaml")
26
+ ruleset_file = rulesets_files_dir.joinpath("spectral.mjs")
27
27
 
28
28
 
29
29
  class SpectralValidatorBackend(BaseValidatorBackend):
@@ -124,11 +124,12 @@ class SpectralValidatorBackend(BaseValidatorBackend):
124
124
  )
125
125
 
126
126
  # Validate ruleset path if it's a filesystem path (skip non-path URIs)
127
+ # Spectral supports YAML, JSON, and JavaScript module formats
127
128
  validated_ruleset_path = (
128
129
  validate_path(
129
130
  self.ruleset_path,
130
131
  allowed_base=self.allowed_base_dir,
131
- allowed_extensions=(".yaml", ".yml"),
132
+ allowed_extensions=(".yaml", ".yml", ".js", ".mjs", ".cjs"),
132
133
  )
133
134
  if self.ruleset_path is not None and is_path(self.ruleset_path)
134
135
  else self.ruleset_path
@@ -0,0 +1,814 @@
1
+ {
2
+ "openapi": "3.1.0",
3
+ "info": {
4
+ "title": "Irish Rail Realtime API",
5
+ "description": "API for accessing Irish Rail real-time information \n",
6
+ "version": "1.0.1"
7
+ },
8
+ "paths": {
9
+ "/stations": {
10
+ "get": {
11
+ "summary": "Get All Stations optionally filtered by `StationType`",
12
+ "description": "When provided with no parameters, it returns a list of all stations in XML format. When provided with a `StationType`, it returns a list of all stations of that type in XML format. ",
13
+ "operationId": "get_all_stations_by_type_getStations_get",
14
+ "parameters": [
15
+ {
16
+ "name": "StationType",
17
+ "in": "query",
18
+ "description": "Station type filter (A=All, D=Dart, M=Mainline, S=Suburban, Else=All)",
19
+ "required": false,
20
+ "schema": {
21
+ "type": "string",
22
+ "description": "Station type filter (A=All, D=Dart, M=Mainline, S=Suburban, Else=All)",
23
+ "enum": [
24
+ "A",
25
+ "D",
26
+ "M",
27
+ "S"
28
+ ],
29
+ "title": "StationType"
30
+ }
31
+ }
32
+ ],
33
+ "responses": {
34
+ "200": {
35
+ "description": "List of Stations (XML)",
36
+ "content": {
37
+ "application/xml": {
38
+ "schema": {
39
+ "type": "array",
40
+ "items": {
41
+ "$ref": "#/components/schemas/objStation"
42
+ },
43
+ "xml": {
44
+ "name": "ArrayOfObjStation",
45
+ "namespace": "http://api.irishrail.ie/realtime/",
46
+ "wrapped": true
47
+ }
48
+ },
49
+ "examples": {
50
+ "stations": {
51
+ "summary": "List of stations",
52
+ "value": "<arrayOfObjStation xmlns=\"http://api.irishrail.ie/realtime/\">\n <objStation>\n <StationDesc>Belfast</StationDesc>\n <StationAlias/>\n <StationLatitude>54.6123</StationLatitude>\n <StationLongitude>-5.91744</StationLongitude>\n <StationCode>BFSTC</StationCode>\n <StationId>228</StationId>\n </objStation>\n <objStation>\n <StationDesc>Lisburn</StationDesc>\n <StationAlias/>\n <StationLatitude>54.514</StationLatitude>\n <StationLongitude>-6.04327</StationLongitude>\n <StationCode>LBURN</StationCode>\n <StationId>238</StationId>\n </objStation>\n <objStation>\n <StationDesc>Lurgan</StationDesc>\n <StationAlias/>\n <StationLatitude>54.4672</StationLatitude>\n <StationLongitude>-6.33547</StationLongitude>\n <StationCode>LURGN</StationCode>\n <StationId>241</StationId>\n </objStation>\n</arrayOfObjStation>"
53
+ }
54
+ }
55
+ }
56
+ }
57
+ },
58
+ "422": {
59
+ "$ref": "#/components/responses/HTTP422Response"
60
+ },
61
+ "500": {
62
+ "$ref": "#/components/responses/HTTP500Response"
63
+ }
64
+ }
65
+ }
66
+ },
67
+ "/station-timetables": {
68
+ "get": {
69
+ "summary": "Get Station timetable info based on `StationCode` or `StationName`.",
70
+ "description": "Returns a list of station data based on the provided `StationCode` or `StationName`. The station data provides a timetable (arrivals and departures at the station) in XML format. \nWhen a `NumMins` parameter is provided, the returned data is limited to the next NumMins of minutes in XML format. \n**Note:** A `StationCode` or `StationName` MUST be specified.",
71
+ "operationId": "get_station_by_code_with_mins_getStationTimetable_code_get",
72
+ "parameters": [
73
+ {
74
+ "name": "StationCode",
75
+ "description": "Station Code",
76
+ "in": "query",
77
+ "required": false,
78
+ "schema": {
79
+ "type": "string",
80
+ "enum": ["BFSTC", "LBURN", "LURGN", "PDOWN", "SLIGO", "NEWRY", "COLNY", "BALNA", "BMOTE", "DDALK", "FXFRD", "BOYLE", "CKOSH", "DRMOD", "CLBAR", "MNLAJ", "WPORT", "BYHNS", "CSREA", "LFORD", "CLMRS", "DGHDA", "ETOWN", "LTOWN", "GSTON", "RSCMN", "BBRGN", "SKRES", "MLGAR", "RLUSK", "DBATE", "MHIDE", "M3WAY", "ATLNE", "DBYNE", "PMNCK", "ENFLD", "KCOCK", "GRGRD", "SUTTN", "BYSDE", "HWTHJ", "HOWTH", "KBRCK", "HAFLD", "CLSLA", "CNOCK", "RAHNY", "HTOWN", "MYNTH", "PHNPK", "CMINE", "ASHTN", "PELTN", "LXCON", "KLSTR", "BBRDG", "LXLSA", "DCDRA", "CTARF", "CNLLY", "DCKLS", "TARA", "HSTON", "PERSE", "WLAWN", "GCDK", "CLARA", "BSLOE", "ADAMS", "ADAMF", "ADMTN", "LDWNE", "KISHO", "KISHF", "KISHS", "PWESF", "PWESS", "CHORC", "CLDKN", "CLONS", "CLONF", "SMONT", "HZLCH", "HAZEF", "HAZES", "ATMON", "SIDNY", "BTSTN", "BROCK", "ATHRY", "SEAPT", "SHILL", "DLERY", "SCOVE", "GLGRY", "DLKEY", "ORNMR", "GALWY", "TMORE", "KILNY", "SALNS", "SKILL", "CRGHW", "WBROK", "BRAY", "NBRGE", "CURAH", "KDARE", "ARHAN", "PTRTN", "MONVN", "GSTNS", "KCOOL", "GORT", "PTLSE", "ATHY", "WLOW", "RCREA", "CJRDN", "RDRUM", "BBRHY", "NNAGH", "CRLOW", "ENNIS", "ARKLW", "TPMOR", "BHILL", "SXMBR", "CCONL", "MNEBG", "THRLS", "GOREY", "LMRCK", "KKNNY", "THTWN", "ECRTY", "LMRKJ", "TIPRY", "CAHIR", "CLMEL", "CKOSR", "CVILL", "WXFRD", "CPILE", "BCLAN", "RLSTD", "TRLEE", "WBDGE", "WFORD", "RLEPT", "BRGTN", "FFORE", "MLLOW", "BTEER", "RMORE", "MLSRT", "KLRNY", "MDLTN", "CGTWL", "GHANE", "LSLND", "CORK", "FOTA", "CGLOE", "RBROK", "COBH", "CITYJ", "CENTJ", "DUNMR", "MOIRA"],
81
+ "description": "Station Code",
82
+ "title": "StationCode"
83
+ }
84
+ },
85
+ {
86
+ "name": "StationName",
87
+ "description": "The Station name",
88
+ "in": "query",
89
+ "schema": {
90
+ "type": "string"
91
+ }
92
+ },
93
+ {
94
+ "name": "NumMins",
95
+ "in": "query",
96
+ "required": false,
97
+ "schema": {
98
+ "type": "string",
99
+ "description": "Number of Minutes to filter the data for",
100
+ "title": "NumMins"
101
+ },
102
+ "description": "Number of Minutes"
103
+ }
104
+ ],
105
+ "responses": {
106
+ "200": {
107
+ "description": "List of Station Train Info (XML)",
108
+ "content": {
109
+ "application/xml": {
110
+ "schema": {
111
+ "type": "array",
112
+ "items": {
113
+ "$ref": "#/components/schemas/objTrainPositions"
114
+ },
115
+ "xml": {
116
+ "name": "ArrayOfObjStationData",
117
+ "namespace": "http://api.irishrail.ie/realtime/",
118
+ "wrapped": true
119
+ }
120
+ },
121
+ "examples": {
122
+ "stationsData": {
123
+ "summary": "List of station train info",
124
+ "value": "<ArrayOfObjStationData xmlns=\"http://api.irishrail.ie/realtime/\">\n <objStationData>\n <Servertime>2025-11-03T15:11:02.47</Servertime>\n <Traincode>P411 </Traincode>\n <Stationfullname>Dublin Connolly</Stationfullname>\n <Stationcode>CNLLY</Stationcode>\n <Querytime>15:11:02</Querytime>\n <Traindate>03 Nov 2025</Traindate>\n <Origin>Hazelhatch</Origin>\n <Destination>Grand Canal Dock</Destination>\n <Origintime>14:35</Origintime>\n <Destinationtime>15:23</Destinationtime>\n <Status>En Route</Status>\n <Lastlocation>Departed North Strand Junction</Lastlocation>\n <Duein>3</Duein>\n <Late>0</Late>\n <Exparrival>15:13</Exparrival>\n <Expdepart>15:14</Expdepart>\n <Scharrival>15:13</Scharrival>\n <Schdepart>15:14</Schdepart>\n <Direction>Northbound</Direction>\n <Traintype>Train</Traintype>\n <Locationtype>S</Locationtype>\n </objStationData>\n <objStationData>\n <Servertime>2025-11-03T15:11:02.47</Servertime>\n <Traincode>E826 </Traincode>\n <Stationfullname>Dublin Connolly</Stationfullname>\n <Stationcode>CNLLY</Stationcode>\n <Querytime>15:11:02</Querytime>\n <Traindate>03 Nov 2025</Traindate>\n <Origin>Bray</Origin>\n <Destination>Malahide</Destination>\n <Origintime>14:25</Origintime>\n <Destinationtime>15:40</Destinationtime>\n <Status>En Route</Status>\n <Lastlocation>Arrived Tara Street</Lastlocation>\n <Duein>4</Duein>\n <Late>2</Late>\n <Exparrival>15:14</Exparrival>\n <Expdepart>15:15</Expdepart>\n <Scharrival>15:12</Scharrival>\n <Schdepart>15:13</Schdepart>\n <Direction>Northbound</Direction>\n <Traintype>DART</Traintype>\n <Locationtype>S</Locationtype>\n </objStationData>\n</ArrayOfObjStationData> "
125
+ }
126
+ }
127
+ },
128
+ "application/json": {
129
+ "schema": {
130
+ "type": "array",
131
+ "items": {
132
+ "$ref": "#/components/schemas/objTrainPositions"
133
+ }
134
+ },
135
+ "examples": {
136
+ "stationsData": {
137
+ "summary": "List of station train info",
138
+ "value": {"test": 3}
139
+ }
140
+ }
141
+ }
142
+ }
143
+ },
144
+ "422": {
145
+ "$ref": "#/components/responses/HTTP422Response"
146
+ },
147
+ "500": {
148
+ "$ref": "#/components/responses/HTTP500Response"
149
+ }
150
+ }
151
+ }
152
+ },
153
+ "/station-summaries": {
154
+ "get": {
155
+ "summary": "Get Station Names, Codes, and Descriptions by text filter",
156
+ "description": "When provided with a StationText, it returns a list of stations with matching text in their names in XML format. Specifically, it returns an array of objStationFilter objects.",
157
+ "operationId": "get_station_by_filter_getStations_search_get",
158
+ "parameters": [
159
+ {
160
+ "name": "StationText",
161
+ "in": "query",
162
+ "required": false,
163
+ "schema": {
164
+ "type": "string",
165
+ "description": "Station Text",
166
+ "title": "Stationtext"
167
+ },
168
+ "description": "Station Text"
169
+ }
170
+ ],
171
+ "responses": {
172
+ "200": {
173
+ "description": "List of StationFiltered Objects (XML)",
174
+ "content": {
175
+ "application/xml": {
176
+ "schema": {
177
+ "type": "array",
178
+ "items": {
179
+ "$ref": "#/components/schemas/objStationFilter"
180
+ },
181
+ "xml": {
182
+ "name": "ArrayOfObjStationFilter",
183
+ "namespace": "http://api.irishrail.ie/realtime/",
184
+ "wrapped": true
185
+ }
186
+ },
187
+ "examples": {
188
+ "stationSummaries": {
189
+ "summary": "List of station train info",
190
+ "value": "<ArrayOfObjStationFilter xmlns=\"http://api.irishrail.ie/realtime/\">\n <objStationFilter>\n <StationDesc_sp>Dublin&nbsp;City&nbsp;Centre</StationDesc_sp>\n <StationDesc>Dublin City Centre</StationDesc>\n <StationCode>DUBCE</StationCode>\n </objStationFilter>\n <objStationFilter>\n <StationDesc_sp>Dublin&nbsp;Connolly</StationDesc_sp>\n <StationDesc>Dublin Connolly</StationDesc>\n <StationCode>CNLLY</StationCode>\n </objStationFilter>\n <objStationFilter>\n <StationDesc_sp>Dublin&nbsp;Heuston</StationDesc_sp>\n <StationDesc>Dublin Heuston</StationDesc>\n <StationCode>HSTON</StationCode>\n </objStationFilter>\n <objStationFilter>\n <StationDesc_sp>Dublin&nbsp;Pearse</StationDesc_sp>\n <StationDesc>Dublin Pearse</StationDesc>\n <StationCode>PERSE</StationCode>\n </objStationFilter>\n</ArrayOfObjStationFilter> "
191
+ }
192
+ }
193
+ }
194
+ }
195
+ },
196
+ "422": {
197
+ "$ref": "#/components/responses/HTTP422Response"
198
+ },
199
+ "500": {
200
+ "$ref": "#/components/responses/HTTP500Response"
201
+ }
202
+ }
203
+ }
204
+ },
205
+ "/trains": {
206
+ "get": {
207
+ "summary": "Get Current Trains",
208
+ "description": "When provided with no parameters, it returns a list of all trains in XML format. When provided with a `TrainType`, it returns a list of all trains of that type in XML format. Specifically, it returns an array of objTrainPositions objects.",
209
+ "operationId": "get_trains_by_type_getTrains_get",
210
+ "parameters": [
211
+ {
212
+ "name": "TrainType",
213
+ "in": "query",
214
+ "required": false,
215
+ "schema": {
216
+ "type": "string",
217
+ "enum": [
218
+ "A",
219
+ "D",
220
+ "M",
221
+ "S"
222
+ ],
223
+ "description": "Train type filter (A=All, M=Mainline, S=Suburban, D=DART)",
224
+ "title": "Traintype"
225
+ },
226
+ "description": "Train type filter (A=All, M=Mainline, S=Suburban, D=DART)"
227
+ }
228
+ ],
229
+ "responses": {
230
+ "200": {
231
+ "description": "List of Train Objects (XML)",
232
+ "content": {
233
+ "application/xml": {
234
+ "schema": {
235
+ "type": "array",
236
+ "items": {
237
+ "$ref": "#/components/schemas/objTrainPositions"
238
+ },
239
+ "xml": {
240
+ "name": "ArrayOfObjTrainPositions",
241
+ "namespace": "http://api.irishrail.ie/realtime/",
242
+ "wrapped": true
243
+ }
244
+ },
245
+ "examples": {
246
+ "trainPositions": {
247
+ "summary": "List of station train info",
248
+ "value": "<ArrayOfObjTrainPositions xmlns=\"http://api.irishrail.ie/realtime/\">\n <objTrainPositions>\n <TrainStatus>N</TrainStatus>\n <TrainLatitude>51.8491</TrainLatitude>\n <TrainLongitude>-8.29956</TrainLongitude>\n <TrainCode>P508</TrainCode>\n <TrainDate>04 Nov 2025</TrainDate>\n <PublicMessage>P508 Cobh to Cork Expected Departure 08:00</PublicMessage>\n <Direction>To Cork</Direction>\n </objTrainPositions>\n <objTrainPositions>\n <TrainStatus>N</TrainStatus>\n <TrainLatitude>51.9018</TrainLatitude>\n <TrainLongitude>-8.4582</TrainLongitude>\n <TrainCode>D507</TrainCode>\n <TrainDate>04 Nov 2025</TrainDate>\n <PublicMessage>D507 Cork to Cobh Expected Departure 08:00</PublicMessage>\n <Direction>To Cobh</Direction>\n </objTrainPositions>\n</ArrayOfObjTrainPositions> "
249
+ }
250
+ }
251
+ }
252
+ }
253
+ },
254
+ "422": {
255
+ "$ref": "#/components/responses/HTTP422Response"
256
+ },
257
+ "500": {
258
+ "$ref": "#/components/responses/HTTP500Response"
259
+ }
260
+ }
261
+ }
262
+ },
263
+ "/trains/{TrainId}/movements": {
264
+ "get": {
265
+ "summary": "Get the movements of a specific train",
266
+ "description": "Get a list of movements for a given `TrainId` When provided with a date in the format `DD Mon YYYY`, it will return a list of train movements for that date in XML format. Specifically, it will return an array of objTrainMovements objects.",
267
+ "operationId": "get_train_movements_getTrainMovements_get",
268
+ "parameters": [
269
+ {
270
+ "name": "TrainId",
271
+ "in": "path",
272
+ "required": true,
273
+ "schema": {
274
+ "type": "string",
275
+ "description": "The ID of the train.",
276
+ "title": "Trainid"
277
+ },
278
+ "description": "The ID of the train."
279
+ },
280
+ {
281
+ "name": "TrainDate",
282
+ "in": "query",
283
+ "required": false,
284
+ "schema": {
285
+ "type": "string",
286
+ "description": "The date to be checked (can be past date or today)",
287
+ "format": "date",
288
+ "pattern": "^(0?[1-9]|[12][0-9]|3[01])\\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\\s(19|20)\\d{2}$",
289
+ "title": "Traindate"
290
+ },
291
+ "description": "The date to be checked (can be past date or today)"
292
+ }
293
+ ],
294
+ "responses": {
295
+ "200": {
296
+ "description": "List of Train Movement Objects (XML)",
297
+ "content": {
298
+ "application/xml": {
299
+ "schema": {
300
+ "type": "array",
301
+ "items": {
302
+ "$ref": "#/components/schemas/objTrainMovements"
303
+ },
304
+ "xml": {
305
+ "name": "ArrayOfObjTrainMovements",
306
+ "namespace": "http://api.irishrail.ie/realtime/",
307
+ "wrapped": true
308
+ }
309
+ },
310
+ "examples": {
311
+ "trainMovmements": {
312
+ "summary": "List of train movements",
313
+ "value": "<ArrayOfObjTrainMovements xmlns=\"http://api.irishrail.ie/realtime/\">\n <objTrainMovements>\n <TrainCode>E109 </TrainCode>\n <TrainDate>11 Dec 2011</TrainDate>\n <LocationCode>MHIDE</LocationCode>\n <LocationFullName>Malahide</LocationFullName>\n <LocationOrder>1</LocationOrder>\n <LocationType>O</LocationType>\n <TrainOrigin>Malahide</TrainOrigin>\n <TrainDestination>Greystones</TrainDestination>\n <ScheduledArrival>00:00:00</ScheduledArrival>\n <ScheduledDeparture>18:05:00</ScheduledDeparture>\n <ExpectedArrival>00:00:00</ExpectedArrival>\n <ExpectedDeparture>18:05:00</ExpectedDeparture>\n <Arrival>17:52:36</Arrival>\n <Departure>18:05:54</Departure>\n <AutoArrival>1</AutoArrival>\n <AutoDepart>1</AutoDepart>\n <StopType>-</StopType>\n </objTrainMovements>\n <objTrainMovements>\n <TrainCode>E109 </TrainCode>\n <TrainDate>11 Dec 2011</TrainDate>\n <LocationCode>PMNCK</LocationCode>\n <LocationFullName>Portmarnock</LocationFullName>\n <LocationOrder>2</LocationOrder>\n <LocationType>S</LocationType>\n <TrainOrigin>Malahide</TrainOrigin>\n <TrainDestination>Greystones</TrainDestination>\n <ScheduledArrival>18:08:30</ScheduledArrival>\n <ScheduledDeparture>18:09:00</ScheduledDeparture>\n <ExpectedArrival>18:09:24</ExpectedArrival>\n <ExpectedDeparture>18:09:30</ExpectedDeparture>\n <Arrival>18:09:00</Arrival>\n <Departure>18:10:36</Departure>\n <AutoArrival>1</AutoArrival>\n <AutoDepart>1</AutoDepart>\n <StopType>-</StopType>\n </objTrainMovements>\n</ArrayOfObjTrainMovements> "
314
+ }
315
+ }
316
+ }
317
+ }
318
+ },
319
+ "422": {
320
+ "$ref": "#/components/responses/HTTP422Response"
321
+ },
322
+ "500": {
323
+ "$ref": "#/components/responses/HTTP500Response"
324
+ }
325
+ }
326
+ }
327
+ }
328
+ },
329
+ "components": {
330
+ "schemas": {
331
+ "HTTPValidationError": {
332
+ "properties": {
333
+ "detail": {
334
+ "items": {
335
+ "$ref": "#/components/schemas/ValidationError"
336
+ },
337
+ "type": "array",
338
+ "title": "Detail"
339
+ }
340
+ },
341
+ "type": "object",
342
+ "title": "HTTPValidationError"
343
+ },
344
+ "ValidationError": {
345
+ "properties": {
346
+ "loc": {
347
+ "items": {
348
+ "anyOf": [
349
+ {
350
+ "type": "string"
351
+ },
352
+ {
353
+ "type": "integer"
354
+ }
355
+ ]
356
+ },
357
+ "type": "array",
358
+ "title": "Location"
359
+ },
360
+ "msg": {
361
+ "type": "string",
362
+ "title": "Message"
363
+ },
364
+ "type": {
365
+ "type": "string",
366
+ "title": "Error Type"
367
+ }
368
+ },
369
+ "type": "object",
370
+ "required": [
371
+ "loc",
372
+ "msg",
373
+ "type"
374
+ ],
375
+ "title": "ValidationError"
376
+ },
377
+ "objStation": {
378
+ "xml": {
379
+ "name": "objStation"
380
+ },
381
+ "properties": {
382
+ "StationDesc": {
383
+ "type": "string",
384
+ "title": "Stationdesc",
385
+ "description": "The name of the station"
386
+ },
387
+ "StationAlias": {
388
+ "anyOf": [
389
+ {
390
+ "type": "string"
391
+ },
392
+ {
393
+ "type": "null"
394
+ }
395
+ ],
396
+ "title": "Stationalias"
397
+ },
398
+ "StationLatitude": {
399
+ "type": "number",
400
+ "title": "Stationlatitude",
401
+ "description": "The latitude of the station"
402
+ },
403
+ "StationLongitude": {
404
+ "type": "number",
405
+ "title": "Stationlongitude",
406
+ "description": "The longitude of the station"
407
+ },
408
+ "StationCode": {
409
+ "type": "string",
410
+ "title": "Stationcode",
411
+ "description": "The station code"
412
+ },
413
+ "StationId": {
414
+ "type": "integer",
415
+ "title": "Stationid",
416
+ "description": "The station ID"
417
+ }
418
+ },
419
+ "type": "object",
420
+ "required": [
421
+ "StationDesc",
422
+ "StationLatitude",
423
+ "StationLongitude",
424
+ "StationCode",
425
+ "StationId"
426
+ ],
427
+ "title": "objStation"
428
+ },
429
+ "objStationData": {
430
+ "xml": {
431
+ "name": "objStationData"
432
+ },
433
+ "properties": {
434
+ "ServerTime": {
435
+ "type": "string",
436
+ "title": "Servertime",
437
+ "description": "Time on the server"
438
+ },
439
+ "TrainCode": {
440
+ "type": "string",
441
+ "title": "Traincode",
442
+ "description": "Unique Id for the train"
443
+ },
444
+ "StationFullName": {
445
+ "type": "string",
446
+ "title": "Stationfullname",
447
+ "description": "Long version of Station Name"
448
+ },
449
+ "StationCode": {
450
+ "type": "string",
451
+ "title": "Stationcode",
452
+ "description": "4-5 letter station abbreviation"
453
+ },
454
+ "QueryTime": {
455
+ "type": "string",
456
+ "title": "Querytime",
457
+ "description": "Time the query was made"
458
+ },
459
+ "TrainDate": {
460
+ "type": "string",
461
+ "title": "Traindate",
462
+ "description": "Service start date in the format DD Mon YYYY"
463
+ },
464
+ "Origin": {
465
+ "type": "string",
466
+ "title": "Origin",
467
+ "description": "Starting station"
468
+ },
469
+ "Destination": {
470
+ "type": "string",
471
+ "title": "Destination",
472
+ "description": "Final station"
473
+ },
474
+ "OriginTime": {
475
+ "type": "string",
476
+ "title": "Origintime",
477
+ "description": "Departure time from origin"
478
+ },
479
+ "DestinationTime": {
480
+ "type": "string",
481
+ "title": "Destinationtime",
482
+ "description": "Scheduled arrival at destination"
483
+ },
484
+ "Status": {
485
+ "type": "string",
486
+ "title": "Status",
487
+ "description": "Latest service information"
488
+ },
489
+ "LastLocation": {
490
+ "type": "string",
491
+ "title": "Lastlocation",
492
+ "description": "Format: Arrived|Departed StationName"
493
+ },
494
+ "DueIn": {
495
+ "type": "integer",
496
+ "title": "Duein",
497
+ "description": "Minutes until arrival"
498
+ },
499
+ "Late": {
500
+ "type": "integer",
501
+ "title": "Late",
502
+ "description": "Minutes delayed"
503
+ },
504
+ "ExpArrival": {
505
+ "type": "string",
506
+ "title": "Exparrival",
507
+ "description": "Expected arrival time. Note: Will show 00:00 for trains starting from query station"
508
+ },
509
+ "ExpDepart": {
510
+ "type": "string",
511
+ "title": "Expdepart",
512
+ "description": "Expected departure time. Note: Will show 00:00 for trains terminating at query station"
513
+ },
514
+ "SchArrival": {
515
+ "type": "string",
516
+ "title": "Scharrival",
517
+ "description": "Scheduled arrival time. Note: Will show 00:00 for trains starting from query station"
518
+ },
519
+ "SchDepart": {
520
+ "type": "string",
521
+ "title": "Schdepart",
522
+ "description": "Scheduled departure time. Note: Will show 00:00 for trains terminating at query station"
523
+ },
524
+ "Direction": {
525
+ "type": "string",
526
+ "title": "Direction",
527
+ "description": "Northbound, Southbound, or To Destination"
528
+ },
529
+ "TrainType": {
530
+ "type": "string",
531
+ "title": "Traintype",
532
+ "description": "DART, Intercity, etc."
533
+ },
534
+ "LocationType": {
535
+ "type": "string",
536
+ "title": "Locationtype",
537
+ "description": "O=Origin, D=Destination, S=Stop"
538
+ }
539
+ },
540
+ "type": "object",
541
+ "required": [
542
+ "ServerTime",
543
+ "TrainCode",
544
+ "StationFullName",
545
+ "StationCode",
546
+ "QueryTime",
547
+ "TrainDate",
548
+ "Origin",
549
+ "Destination",
550
+ "OriginTime",
551
+ "DestinationTime",
552
+ "Status",
553
+ "LastLocation",
554
+ "DueIn",
555
+ "Late",
556
+ "ExpArrival",
557
+ "ExpDepart",
558
+ "SchArrival",
559
+ "SchDepart",
560
+ "Direction",
561
+ "TrainType",
562
+ "LocationType"
563
+ ],
564
+ "title": "objStationData"
565
+ },
566
+ "objStationFilter": {
567
+ "xml": {
568
+ "name": "objStationFilter"
569
+ },
570
+ "properties": {
571
+ "StationDesc_sp": {
572
+ "type": "string",
573
+ "title": "Stationdesc Sp",
574
+ "description": "A special description of the station. May just be the name with some special characters included."
575
+ },
576
+ "StationDesc": {
577
+ "type": "string",
578
+ "title": "Stationdesc",
579
+ "description": "The name of the station"
580
+ },
581
+ "StationCode": {
582
+ "type": "string",
583
+ "title": "Stationcode",
584
+ "description": "The station code"
585
+ }
586
+ },
587
+ "type": "object",
588
+ "required": [
589
+ "StationDesc_sp",
590
+ "StationDesc",
591
+ "StationCode"
592
+ ],
593
+ "title": "objStationFilter"
594
+ },
595
+ "objTrainMovements": {
596
+ "xml": {
597
+ "name": "objTrainMovements"
598
+ },
599
+ "properties": {
600
+ "TrainCode": {
601
+ "type": "string",
602
+ "title": "Traincode",
603
+ "description": "Unique Id for the train"
604
+ },
605
+ "TrainDate": {
606
+ "type": "string",
607
+ "title": "Traindate",
608
+ "description": "Service start date in the format DD Mon YYYY"
609
+ },
610
+ "LocationCode": {
611
+ "type": "string",
612
+ "title": "Locationcode",
613
+ "description": "4-5 letter location abbreviation"
614
+ },
615
+ "LocationFullName": {
616
+ "type": "string",
617
+ "title": "Locationfullname",
618
+ "description": "Long version of location name"
619
+ },
620
+ "LocationOrder": {
621
+ "type": "integer",
622
+ "title": "Locationorder",
623
+ "description": "The stop order of the location"
624
+ },
625
+ "LocationType": {
626
+ "type": "string",
627
+ "title": "Locationtype",
628
+ "description": "Type of location (O=Origin S=Stop T=TimingPoint D=Destination)"
629
+ },
630
+ "TrainOrigin": {
631
+ "type": "string",
632
+ "title": "Trainorigin",
633
+ "description": "Starting station"
634
+ },
635
+ "TrainDestination": {
636
+ "type": "string",
637
+ "title": "Traindestination",
638
+ "description": "Final station"
639
+ },
640
+ "ScheduledArrival": {
641
+ "type": "string",
642
+ "title": "Scheduledarrival",
643
+ "description": "Scheduled arrival time. Note: Will show 00:00 for trains starting from query station"
644
+ },
645
+ "ScheduledDeparture": {
646
+ "type": "string",
647
+ "title": "Scheduleddeparture",
648
+ "description": "Scheduled departure time. Note: Will show 00:00 for trains terminating at query station"
649
+ },
650
+ "ExpectedArrival": {
651
+ "type": "string",
652
+ "title": "Expectedarrival",
653
+ "description": "Expected arrival time. Note: Will show 00:00 for trains starting from query station"
654
+ },
655
+ "ExpectedDeparture": {
656
+ "type": "string",
657
+ "title": "Expecteddeparture",
658
+ "description": "Expected departure time. Note: Will show 00:00 for trains terminating at query station"
659
+ },
660
+ "Arrival": {
661
+ "type": "string",
662
+ "title": "Arrival",
663
+ "description": "Actual arrival time"
664
+ },
665
+ "Departure": {
666
+ "type": "string",
667
+ "title": "Departure",
668
+ "description": "Actual departure time"
669
+ },
670
+ "AutoArrival": {
671
+ "type": "integer",
672
+ "title": "Autoarrival",
673
+ "description": "Was information automatically generated"
674
+ },
675
+ "AutoDeparture": {
676
+ "type": "integer",
677
+ "title": "Autodeparture",
678
+ "description": "Was information automatically generated"
679
+ },
680
+ "StopType": {
681
+ "type": "string",
682
+ "title": "Stoptype",
683
+ "description": "Stop Type (C=Current N=Next"
684
+ }
685
+ },
686
+ "type": "object",
687
+ "required": [
688
+ "TrainCode",
689
+ "TrainDate",
690
+ "LocationCode",
691
+ "LocationFullName",
692
+ "LocationOrder",
693
+ "LocationType",
694
+ "TrainOrigin",
695
+ "TrainDestination",
696
+ "ScheduledArrival",
697
+ "ScheduledDeparture",
698
+ "ExpectedArrival",
699
+ "ExpectedDeparture",
700
+ "Arrival",
701
+ "Departure",
702
+ "AutoArrival",
703
+ "AutoDeparture",
704
+ "StopType"
705
+ ],
706
+ "title": "objTrainMovements"
707
+ },
708
+ "objTrainPositions": {
709
+ "xml": {
710
+ "name": "objTrainPositions"
711
+ },
712
+ "properties": {
713
+ "TrainStatus": {
714
+ "type": "string",
715
+ "title": "Trainstatus",
716
+ "description": "Status of the train (N = Not yet running R = Running)"
717
+ },
718
+ "TrainLatitude": {
719
+ "type": "number",
720
+ "title": "Trainlatitude",
721
+ "description": "Latitude of the train"
722
+ },
723
+ "TrainLongitude": {
724
+ "type": "number",
725
+ "title": "Trainlongitude",
726
+ "description": "Longitude of the train"
727
+ },
728
+ "TrainCode": {
729
+ "type": "string",
730
+ "title": "Traincode",
731
+ "description": "The train code"
732
+ },
733
+ "TrainDate": {
734
+ "type": "string",
735
+ "title": "Traindate",
736
+ "description": "The date of the train in the format DD Mon YYYY"
737
+ },
738
+ "PublicMessage": {
739
+ "type": "string",
740
+ "title": "Publicmessage",
741
+ "description": "Public message to be displayed (i.e AA509\n11:00 - Waterford to Dublin (0 mins late)\nDeparted Waterford next stop Thomastown)"
742
+ },
743
+ "Direction": {
744
+ "type": "string",
745
+ "title": "Direction",
746
+ "description": "Direction of the train (Northbound/Southbound between Dundalk and Rosslare and between Dublin and Sligo, for all other locations it is the destination e.g To Cork"
747
+ }
748
+ },
749
+ "type": "object",
750
+ "required": [
751
+ "TrainStatus",
752
+ "TrainLatitude",
753
+ "TrainLongitude",
754
+ "TrainCode",
755
+ "TrainDate",
756
+ "PublicMessage",
757
+ "Direction"
758
+ ],
759
+ "title": "objTrainPositions"
760
+ }
761
+ },
762
+ "responses": {
763
+ "HTTP422Response": {
764
+ "description": "Validation Error",
765
+ "content": {
766
+ "application/xml": {
767
+ "schema": {
768
+ "$ref": "#/components/schemas/HTTPValidationError",
769
+ "xml": {
770
+ "name": "Error",
771
+ "namespace": "http://api.irishrail.ie/realtime/"
772
+ }
773
+ }
774
+ },
775
+ "application/json": {
776
+ "schema": {
777
+ "$ref": "#/components/schemas/HTTPValidationError"
778
+ }
779
+ }
780
+ }
781
+ },
782
+ "HTTP500Response": {
783
+ "description": "Internal Server Error (XML)",
784
+ "content": {
785
+ "application/xml": {
786
+ "schema": {
787
+ "type": "object",
788
+ "xml": {
789
+ "name": "Error",
790
+ "namespace": "http://api.irishrail.ie/realtime/"
791
+ },
792
+ "properties": {
793
+ "message": {
794
+ "type": "string"
795
+ },
796
+ "details": {
797
+ "type": "string"
798
+ }
799
+ }
800
+ },
801
+ "examples": {
802
+ "serverError": {
803
+ "summary": "Example error response",
804
+ "value": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Error>\n <Message>Failed to retrieve station data</Message>\n <Details>Unable to reach server</Details>\n</Error>"
805
+ }
806
+ }
807
+ }
808
+ }
809
+ }
810
+ }
811
+ }
812
+ }
813
+
814
+
@@ -0,0 +1,78 @@
1
+ import { oas3 } from '@stoplight/spectral-formats';
2
+ import { oasExample } from '@stoplight/spectral-rulesets/dist/oas/functions/index.js';
3
+ import oasRuleset from '@stoplight/spectral-rulesets/dist/oas/index.js';
4
+
5
+ // Custom wrapper around oasExample that skips XML-related examples
6
+ const oasExampleNonXml = (targetVal, opts, context) => {
7
+ const path = context.path || [];
8
+ const pathString = path.join('.');
9
+
10
+ // Case 1: Media Type Objects - filter by media type in path
11
+ // Path format: paths./station-timetables.get.responses[200].content.application/xml.examples
12
+ // Matches patterns like: .content.application/xml. or .content.image/svg+xml.
13
+ if (pathString.match(/\.content\.[^.]*(\/xml|\+xml)\b/i)) {
14
+ return [];
15
+ }
16
+
17
+ // Case 2 & 3: Header/Parameter Objects - check if their schema has xml property
18
+ // Path format: paths./stations.get.parameters[0].schema or paths./.headers.X-Custom.schema
19
+ // The targetVal is the object with schema and example/examples
20
+ if (targetVal && targetVal.schema && targetVal.schema.xml) {
21
+ return [];
22
+ }
23
+
24
+ // Otherwise, delegate to the original oasExample function
25
+ return oasExample(targetVal, opts, context);
26
+ };
27
+
28
+ export default {
29
+ extends: [oasRuleset],
30
+ rules: {
31
+ 'oas3-schema': 'error',
32
+
33
+ // --- MEDIA EXAMPLES ---
34
+ // Override to skip XML media type validation using custom wrapper function
35
+ 'oas3-valid-media-example': {
36
+ description: 'Examples must be valid against their defined schema (non-XML media only).',
37
+ message: '{{error}}',
38
+ severity: 'error',
39
+ formats: [oas3],
40
+ given: [
41
+ '$..content..[?(@ && @.schema && (@.example !== void 0 || @.examples))]',
42
+ '$..headers..[?(@ && @.schema && (@.example !== void 0 || @.examples))]',
43
+ '$..parameters..[?(@ && @.schema && (@.example !== void 0 || @.examples))]',
44
+ ],
45
+ then: {
46
+ function: oasExampleNonXml,
47
+ functionOptions: {
48
+ schemaField: 'schema',
49
+ oasVersion: 3,
50
+ type: 'media'
51
+ }
52
+ }
53
+ },
54
+
55
+ // --- SCHEMA EXAMPLES ---
56
+ // Override to skip schemas that have an xml property at the top level
57
+ 'oas3-valid-schema-example': {
58
+ description: 'Examples must be valid against their defined schema (skip schemas that declare XML mapping).',
59
+ message: '{{error}}',
60
+ severity: 'error',
61
+ formats: [oas3],
62
+ given: [
63
+ '$.components.schemas..[?(@property !== \'properties\' && @ && (@.example !== void 0 || @.default !== void 0) && (@.enum || @.type || @.format || @.$ref || @.properties || @.items) && !@.xml)]',
64
+ '$..content..[?(@property !== \'properties\' && @ && (@.example !== void 0 || @.default !== void 0) && (@.enum || @.type || @.format || @.$ref || @.properties || @.items) && !@.xml)]',
65
+ '$..headers..[?(@property !== \'properties\' && @ && (@.example !== void 0 || @.default !== void 0) && (@.enum || @.type || @.format || @.$ref || @.properties || @.items) && !@.xml)]',
66
+ '$..parameters..[?(@property !== \'properties\' && @ && (@.example !== void 0 || @.default !== void 0) && (@.enum || @.type || @.format || @.$ref || @.properties || @.items) && !@.xml)]'
67
+ ],
68
+ then: {
69
+ function: oasExample,
70
+ functionOptions: {
71
+ schemaField: '$',
72
+ oasVersion: 3,
73
+ type: 'schema'
74
+ }
75
+ }
76
+ }
77
+ }
78
+ };