Create your own rules
The rules logic
To define your rules, here is the logic to follow:
attribute/mandatory: the NeTEx resource must have the given attributename: name of the expected attribute
{
"rule": "attribute/mandatory",
"name": "name"
}
attribute/blank: the NeTEx resource must have a value for the given attribute (it cannot be blank)name: name of the attribute that cannot be blank
{
"rule": "attribute/blank",
"name": "quay_type"
}
attribute/value: the NeTEx resource must have a given value for the given attributename: name of the attribute/item testedvalue: the expected value
{
"rule": "attribute/value",
"name": "transport_mode",
"value": "bus"
}
attribute/match: the NeTEx resource must have a value for the given attribute, which matches a given expressionname: name of the attribute/item testedvalue: the expected expression (regular expression)
{
"rule": "attribute/match",
"name": "id",
"value": "/DE:.*:LOC/",
"message": "Identifier must use this format 'DE:*:LOC'"
}
resource/mandatory: the NeTEx resource must be present (it is mandatory to have it)
{
"rule_context": "resource/kind_of",
"resource_class": "Network",
"rules": [
{
"rule": "resource/mandatory",
"message": "A Network is mandatory"
}
]
}
resource/class: the NeTEx resource must have comply with the given class
{
"rule_context": "resource/kind_of",
"resource_class": "JourneyPattern",
"rules": [
{
"rule": "resource/class",
"expected_class": "ServiceJourneyPattern"
"message": "Use ServiceJourneyPattern (not JourneyPattern)"
}
]
}
collection/count: the NeTEx resource must have a compatible count of elements in the given collectioncollection: name of the tested collectionminimum_count: minimum count expected in the collection (optional)maximum_count: maximum count expected in the collection (optional)
{
"rule_context": "resource/kind_of",
"resource_class": "JourneyPattern",
"rules": [
{
"rule": "collection/count",
"collection": "points_in_sequence",
"minimum_count": 2,
"message": "A (Service)JourneyPattern has two or more pointsInSequence"
}
]
}
reference/exists: the NeTEx reference of the given class must match an existing resourcereferenced_class: the object class, that is tested
{
"rule": "reference/exist",
"referenced_class": "Quay",
"message": "All referenced Quays must defined"
}
reference/version: the NeTEx reference must have a version that matches the given valuereferenced_classes: the type of reference, that is testedvalue: expected value
{
"rule": "reference/version",
"referenced_classes": ["Quay", "StopPlace"],
"value": "any"
}For example, the rule below can test these cases:
<PassengerStopAssignment id="valid">
<!-- ... -->
<QuayRef version="any" ref="..."/>
</PassengerStopAssignment>
<PassengerStopAssignment id="unvalid">
<!-- ... -->
<QuayRef ref="..."/>
</PassengerStopAssignment>
tag/match: the NeTEx resource has a tag which matches a given expressionname: name of the tested tagvalue:the expected value (can be a regular expression)
{
"rule_context": "resource/kind_of",
"resource_class": "ServiceJourney",
"rules": [
{
"rule": "tag/match",
"name": "frame_id",
"value": "FR:GeneralFrame:NETEX_HORAIRE:LOC",
"message": "ServiceJourneys must be defined in NETEX_HORAIRE GeneralFrame"
}
]
}
service_journey/passing_times_chronology: checks that the passing times in ServiceJourneys are chronological
{
"rule": "service_journey/passing_times_chronology",
"message": "TimedPassingTimes use chronological passing times"
}The context logic
resource/kind_of: tests limited to the NeTEx resource with the given class(es)resource_classes: list of the classes that are permittedresource_class: alias for a single class that is permitted
{
"rule_context": "resource/kind_of",
"resource_classes": ["Quay", "StopPlace","StopPlaceEntrance"]
"rules": [
...
]
},
{
"rule_context": "resource/kind_of",
"resource_class": "Line",
"rules": [
...
]
}
attribute/match: tests limited to the given attributed, for which the match to a single value is checkedname: name of the tested attributedvalue:expected value (can be a regular expression)
{
"rule_context": "attribute/match",
"name": "id",
"value": "FR:GeneralFrame:NETEX_ARRET:LOC",
"rules": [
...
]
}
reduce/element: tests limited to a NeTEx elementelement: name of the tested element
{
"rule_context": "reduce/element",
"element": "presentation",
"rules": [
...
]
}
reduce/collection: test limited to the resources present in a NeTEx collectioncollection: name of the collection containgin the resources to be tested
{
"rule_context": "reduce/collection",
"element": "passingTimes",
"rules": [
...
]
}Creation your own rules
Using the command line
It is the easiest way to update your rules set.
Create and update a Ruleset :
secretary-client create ruleset --name="Test Ruleset" --slug=acmee:test sample.json
secretary-client patch ruleset acmee:test --definition sample.json
Fetch a Ruleset
secretary-client get ruleset acmee:test
secretary-client get ruleset --definition acmee:test
Edit simply one of your Rulesets :
secretary-client get ruleset --definition acmee:test > sample.json
# Edit sample.json
secretary-client update ruleset acmee:test --definition sample.json
Delete a Ruleset :
secretary-client delete ruleset acmee:test
Use of the API
The documentation is available in Postman.
Create and update a Ruleset :
➜ curl -s -X POST -H "Authorization: Token token=$TOKEN" -H "Content-Type: application/json; charset=UTF-8" https://chouette-valid.enroute.mobi/api/rulesets -d@- <<EOF
{
"ruleset": {
"name":"Test Ruleset",
"slug":"test",
"definition":"[
{
\"rule\": \"attribute/mandatory\",
\"name\": \"name\",
\"criticity\": \"error\",
\"code\": \"sample\",
\"message\": \"Phasellus vel nulla vel dolor semper tincidunt a vitae risus\"
}
]"
}
}
EOF➜ curl -s -X PUT -H "Authorization: Token token=$TOKEN" -H "Content-Type: application/json; charset=UTF-8" https://chouette-valid.enroute.mobi/api/rulesets/test -d@- <<EOF
{
"ruleset": {
"name":"Test Ruleset",
"slug":"test",
"definition":"[
{
\"rule\": \"attribute/mandatory\",
\"name\": \"name\",
\"criticity\": \"warning\",
\"code\": \"sample\",
\"message\": \"Fusce in risus ut nisi efficitur tempor et sed ligula\"
}
]"
}
}
EOF
Fetch a Ruleset
➜ curl -s -H "Authorization: Token token=$TOKEN" https://chouette-valid.enroute.mobi/api/rulesets/test | jq .Typical response:
{
"id": "b97e25fc-ebe0-4417-a8f4-7b1da843d645",
"name": "Test Ruleset",
"slug": "test",
"definition": "[ { \"rule\": \"attribute/mandatory\", \"name\": \"name\", \"criticity\": \"error\", \"code\": \"sample\", \"message\": \"Phasellus vel nulla vel dolor semper tincidunt a vitae risus\" } ]",
"created_at": "2025-09-08T07:36:43.076Z",
"updated_at": "2025-09-08T07:36:43.076Z"
}
Delete a Ruleset:
➜ curl -s -X DELETE -H "Authorization: Token token=$TOKEN" https://chouette-valid.enroute.mobi/api/rulesets/test
Examples of rules
Rules on attributes
Checks that each
Lineobject has an attributepresentationwith a criticity level “warning” and an error message that is “A Line should have a Presentation”.
{
"rule_context": "resource/kind_of",
"resource_classes": "Line",
"rules": [
{
"rule":"attribute/mandatory",
"name": "presentation",
"criticity": "warning",
"code": "line-presentation-mandatory",
"message": "A Line should have a Presentation"
}
]
}
Checks that each attribute
short_namein the objectsLineandStopPlacehas a maximum of 12 caracters with a criticity level “error”.
{
"rule_context": "resource/kind_of",
"resource_classes": ["StopPlace", "Line"],
"rules": [
{
"rule": "attribute/match",
"name": "short_name",
"value": "/^.{0,12}$/",
"criticity": "error",
"code": "shortname-length",
"message": "A ShortName must not exceed 12 characters"
}
]
}
Checks that there is an attribute
colourin the resourcespresentationforLineobjectfs with a criticity level “warning”.
{
"rule_context": "resource/kind_of",
"resource_class": "Line",
"rules": [
{
"rule_context": "reduce/element",
"element": "presentation",
"rules": [
{
"rule": "attribute/mandatory",
"name": "colour",
"criticity": "warning",
"code": "line-presentation-colour-mandatory",
"message": "A Line should have a Presentation color"
}
]
}
]
}
Rules of the file structure
Checks that there is a GeneralFrame “ARRET” in the file stop.xml of the NeTEx dataset with a criticity level “error”.
{
"rule_context": "resource/kind_of",
"resource_classes": ["GeneralFrame"],
"rules": [
{
"rule_context": "attribute/match",
"name": "id",
"value": "FR:GeneralFrame:NETEX_ARRET:LOC",
"rules": [
{
"rule": "tag/match",
"name": "filename",
"value": "stop.xml",
"criticity": "error",
"code": "general-frame-arret-filename",
"message": "GeneralFrame ARRET must be in stop.xml"
}
]
}
]
}
<!-- stop.xml -->
<?xml version="1.0" encoding="utf-8"?>
<PublicationDelivery …>
<PublicationTimestamp>2023-01-01T00:00:00.0Z</PublicationTimestamp>
<ParticipantRef>Exemple</ParticipantRef>
<dataObjects>
<GeneralFrame id="FR:GeneralFrame:NETEX_ARRET:LOC"
version="1.09:FR-NETEX-2.1-1.0">
<TypeOfFrameRef ref="FR:TypeOfFrame:NETEX_ARRET:"/>
<members>
…
</members>
</GeneralFrame>
</dataObjects>
</PublicationDelivery>
Chekcs that the objects
Quay,StopPlaceandStopPlaceEntranceare part of the a GeneralFrame “Arrêt”
{
"rule_context": "resource/kind_of",
"resource_classes": ["Quay", "StopPlace","StopPlaceEntrance"],
"rules": [
{
"rule": "tag/match",
"name": "frame_id",
"value": "FR:GeneralFrame:NETEX_ARRET:LOC",
"criticity": "error",
"code": "...",
"message": "..."
}
]
}
<?xml version="1.0" encoding="utf-8"?>
<PublicationDelivery …>
<PublicationTimestamp>2023-01-01T00:00:00.0Z</PublicationTimestamp>
<ParticipantRef>Exemple</ParticipantRef>
<dataObjects>
<GeneralFrame id="FR:GeneralFrame:NETEX_ARRET:LOC">
<members>
<!--
STOP PLACE
QUAY
TOPOGRAPHIC PLACE
STOP PLACE ENTRANCE
GENERAL GROUP OF ENTITIES
-->
</members>
</GeneralFrame>
</dataObjects>
</PublicationDelivery>