From e3d5f08b4efcc8c279bc1b32f3ec5c98b9bc23d4 Mon Sep 17 00:00:00 2001 From: Tom <1955774+tdelmas@users.noreply.github.com> Date: Thu, 29 Jun 2023 14:55:25 +0200 Subject: [PATCH] Add V3-RC validation (#104) * Git ignore for schemas subtree Signed-off-by: Tom <1955774+tdelmas@users.noreply.github.com> * Squashed 'gbfs-validator/versions/schemas/' changes from 5273f96..22b058d 22b058d fix: incorrect spec for "languages" property in system_information.json (#90) e9d891b Refer to later versions in gbfs_versions.json (#87) fbd3ced Add v3.0 to v3.0-RC schemas (#88) ef0d9a6 Fix schemas for v3.0-RC (#85) 2b6ad1e FIX: replaces name field with localized string (#82) abe71f2 Fix invalid json, no trailing commas (#83) f43170e V3.0-RC Schemas (#79) 680df70 Update pr-to-project.yml c2c30a2 Typo fix in geofencing_zones.json (#78) c9be765 Create pr-to-project.yml 0a17875 Create issues-to-project.yml 30e5a3a Update README.md git-subtree-dir: gbfs-validator/versions/schemas git-subtree-split: 22b058dc4a358baf451e006cde74775075929b3d * remove dot files in schemas Signed-off-by: Tom <1955774+tdelmas@users.noreply.github.com> * v3-RC validation Signed-off-by: Tom <1955774+tdelmas@users.noreply.github.com> * Remove unused file Signed-off-by: Tom <1955774+tdelmas@users.noreply.github.com> * Add `serverOpts` required in Windows Signed-off-by: Tom <1955774+tdelmas@users.noreply.github.com> * Fix typo `recommended` Signed-off-by: Tom <1955774+tdelmas@users.noreply.github.com> * update test for 3.0-RC , 3.0 and upcoming minor versions Signed-off-by: Tom <1955774+tdelmas@users.noreply.github.com> * fix after rebase Signed-off-by: Tom <1955774+tdelmas@users.noreply.github.com> * Recommended. Signed-off-by: Tom <1955774+tdelmas@users.noreply.github.com> * Squashed 'gbfs-validator/versions/schemas/' changes from 22b058d..5f12243 5f12243 Patch manifest.json (#91) git-subtree-dir: gbfs-validator/versions/schemas git-subtree-split: 5f12243d5735a608eef697212a627b9090eea31a --------- Signed-off-by: Tom <1955774+tdelmas@users.noreply.github.com> --- .gitignore | 4 + RULES.md | 2 +- .../__test__/fixtures/v3.0-RC/default.js | 207 +++ .../__test__/fixtures/v3.0-RC/exaustive.js | 757 ++++++++++ gbfs-validator/__test__/gbfs.test.js | 8 +- gbfs-validator/__test__/gbfs.v3.0-RC.test.js | 153 ++ gbfs-validator/gbfs.js | 93 +- .../required_vehicle_type_id.js | 0 .../required_vehicle_types_available.js | 0 .../required_store_uri.js | 0 .../required_vehicle_type_id.js | 0 .../required_vehicle_types_available.js | 0 .../required_store_uri.js | 0 .../required_vehicle_type_id.js | 0 .../required_vehicle_types_available.js | 0 .../required_store_uri.js | 0 .../{ => vehicle_types}/pricing_plan_id.js | 0 .../required_vehicle_types_available.js | 47 + .../system_information/required_store_uri.js | 61 + .../required_vehicle_type_id.js | 64 + .../v3.0-RC/vehicle_types/pricing_plan_id.js | 28 + .../.github/workflows/validate_json.yml | 18 - gbfs-validator/versions/schemas/.gitignore | 87 -- gbfs-validator/versions/schemas/README.md | 2 +- .../versions/schemas/v1.1/gbfs_versions.json | 8 +- .../versions/schemas/v2.0/gbfs_versions.json | 7 +- .../versions/schemas/v2.1/gbfs_versions.json | 6 +- .../schemas/v2.1/geofencing_zones.json | 3 +- .../versions/schemas/v2.2/gbfs_versions.json | 5 +- .../schemas/v2.2/geofencing_zones.json | 3 +- .../versions/schemas/v2.3/gbfs_versions.json | 4 +- .../schemas/v2.3/geofencing_zones.json | 5 +- .../versions/schemas/v3.0-RC/gbfs.json | 70 + .../schemas/v3.0-RC/gbfs_versions.json | 69 + .../schemas/v3.0-RC/geofencing_zones.json | 204 +++ .../versions/schemas/v3.0-RC/manifest.json | 84 ++ .../schemas/v3.0-RC/station_information.json | 275 ++++ .../schemas/v3.0-RC/station_status.json | 149 ++ .../schemas/v3.0-RC/system_alerts.json | 164 +++ .../schemas/v3.0-RC/system_information.json | 1229 +++++++++++++++++ .../schemas/v3.0-RC/system_pricing_plans.json | 204 +++ .../schemas/v3.0-RC/system_regions.json | 71 + .../schemas/v3.0-RC/vehicle_status.json | 155 +++ .../schemas/v3.0-RC/vehicle_types.json | 271 ++++ gbfs-validator/versions/v3.0-RC.js | 17 + website/src/components/SubResult.vue | 6 +- website/src/pages/Validator.vue | 1 + 47 files changed, 4398 insertions(+), 143 deletions(-) create mode 100644 gbfs-validator/__test__/fixtures/v3.0-RC/default.js create mode 100644 gbfs-validator/__test__/fixtures/v3.0-RC/exaustive.js create mode 100644 gbfs-validator/__test__/gbfs.v3.0-RC.test.js rename gbfs-validator/versions/partials/v2.1/{ => free_bike_status}/required_vehicle_type_id.js (100%) rename gbfs-validator/versions/partials/v2.1/{ => station_status}/required_vehicle_types_available.js (100%) rename gbfs-validator/versions/partials/v2.1/{ => system_information}/required_store_uri.js (100%) rename gbfs-validator/versions/partials/v2.2/{ => free_bike_status}/required_vehicle_type_id.js (100%) rename gbfs-validator/versions/partials/v2.2/{ => station_status}/required_vehicle_types_available.js (100%) rename gbfs-validator/versions/partials/v2.2/{ => system_information}/required_store_uri.js (100%) rename gbfs-validator/versions/partials/v2.3/{ => free_bike_status}/required_vehicle_type_id.js (100%) rename gbfs-validator/versions/partials/v2.3/{ => station_status}/required_vehicle_types_available.js (100%) rename gbfs-validator/versions/partials/v2.3/{ => system_information}/required_store_uri.js (100%) rename gbfs-validator/versions/partials/v2.3/{ => vehicle_types}/pricing_plan_id.js (100%) create mode 100644 gbfs-validator/versions/partials/v3.0-RC/station_status/required_vehicle_types_available.js create mode 100644 gbfs-validator/versions/partials/v3.0-RC/system_information/required_store_uri.js create mode 100644 gbfs-validator/versions/partials/v3.0-RC/vehicle_status/required_vehicle_type_id.js create mode 100644 gbfs-validator/versions/partials/v3.0-RC/vehicle_types/pricing_plan_id.js delete mode 100644 gbfs-validator/versions/schemas/.github/workflows/validate_json.yml delete mode 100644 gbfs-validator/versions/schemas/.gitignore create mode 100644 gbfs-validator/versions/schemas/v3.0-RC/gbfs.json create mode 100644 gbfs-validator/versions/schemas/v3.0-RC/gbfs_versions.json create mode 100644 gbfs-validator/versions/schemas/v3.0-RC/geofencing_zones.json create mode 100644 gbfs-validator/versions/schemas/v3.0-RC/manifest.json create mode 100644 gbfs-validator/versions/schemas/v3.0-RC/station_information.json create mode 100644 gbfs-validator/versions/schemas/v3.0-RC/station_status.json create mode 100644 gbfs-validator/versions/schemas/v3.0-RC/system_alerts.json create mode 100644 gbfs-validator/versions/schemas/v3.0-RC/system_information.json create mode 100644 gbfs-validator/versions/schemas/v3.0-RC/system_pricing_plans.json create mode 100644 gbfs-validator/versions/schemas/v3.0-RC/system_regions.json create mode 100644 gbfs-validator/versions/schemas/v3.0-RC/vehicle_status.json create mode 100644 gbfs-validator/versions/schemas/v3.0-RC/vehicle_types.json create mode 100644 gbfs-validator/versions/v3.0-RC.js diff --git a/.gitignore b/.gitignore index efcede4..5e5e7ce 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,7 @@ coverage functions/*.zip .env + +# schemas + +gbfs-validator/versions/schemas/.* diff --git a/RULES.md b/RULES.md index 1400203..22607d1 100644 --- a/RULES.md +++ b/RULES.md @@ -1,4 +1,4 @@ -This project validates feeds up to version 2.3-RC of the [JSON Schemas](https://github.com/MobilityData/gbfs-json-schema). +This project validates feeds up to version 3.0-RC of the [JSON Schemas](https://github.com/MobilityData/gbfs-json-schema). # Files presence The validator will flag any missing file. It will inform the user if the missing file is required or not, as per the conditions in the GBFS version that it detects. diff --git a/gbfs-validator/__test__/fixtures/v3.0-RC/default.js b/gbfs-validator/__test__/fixtures/v3.0-RC/default.js new file mode 100644 index 0000000..31919d2 --- /dev/null +++ b/gbfs-validator/__test__/fixtures/v3.0-RC/default.js @@ -0,0 +1,207 @@ +const fastify = require('fastify') + +const version = '3.0-RC' +const last_updated = 1566224400 +const last_updated_fresh = Math.floor(Date.now() / 1000) - 30 + +class MockRequests { + entry_points() { + return { + manifest: this.manifest, + gbfs: this.gbfs, + gbfs_versions: this.gbfs_versions, + system_information: this.system_information, + vehicle_types: this.vehicle_types, + station_status: this.station_status + } + } + + manifest({ basePath }) { + return { + last_updated, + ttl: 0, + version, + data: { + datasets: [ + { + system_id: 'example_berlin', + versions: [ + { + version: '3.0-RC', + url: `${basePath}/gbfs.json` + } + ] + } + ] + } + } + } + + gbfs({ basePath }) { + return { + last_updated, + ttl: 0, + version, + data: { + feeds: [ + { + name: 'system_information', + url: `${basePath}/system_information.json` + }, + { + name: 'vehicle_types', + url: `${basePath}/vehicle_types.json` + }, + { + name: 'vehicle_status', + url: `${basePath}/vehicle_status.json` + } + ] + } + } + } + + gbfs_versions({ basePath }) { + return { + last_updated, + ttl: 0, + version, + data: { + versions: [ + { + version: '3.0-RC', + url: `${basePath}/gbfs.json` + } + ] + } + } + } + + system_information() { + return { + last_updated, + ttl: 0, + version, + data: { + system_id: 'shared_bike', + languages: ['en'], + name: [ + { + text: 'Shared Bike USA', + language: 'en' + } + ], + timezone: 'Etc/UTC', + opening_hours: 'Mo-Su 00:00-23:59', + feed_contact_email: 'datafeed@example.com' + } + } + } + + vehicle_types() { + return { + last_updated, + ttl: 0, + version, + data: { + vehicle_types: [ + { + vehicle_type_id: 'biketype1', + form_factor: 'bicycle', + propulsion_type: 'human', + name: [ + { + text: 'Example Basic Bike', + language: 'en' + } + ], + default_reserve_time: 30, + return_type: ['any_station', 'free_floating'], + vehicle_assets: { + icon_url: 'https://www.example.com/assets/icon_bicycle.svg', + icon_url_dark: + 'https://www.example.com/assets/icon_bicycle_dark.svg', + icon_last_modified: '2021-06-15' + }, + default_pricing_plan_id: 'bike_plan_1', + pricing_plan_ids: ['bike_plan_1', 'bike_plan_2', 'bike_plan_3'] + }, + { + vehicle_type_id: 'cartype1', + form_factor: 'car', + propulsion_type: 'electric', + name: [ + { + text: 'Example Electric Car', + language: 'en' + } + ], + default_reserve_time: 30, + max_range_meters: 100, + return_type: ['any_station', 'free_floating'], + vehicle_assets: { + icon_url: 'https://www.example.com/assets/icon_car.svg', + icon_url_dark: 'https://www.example.com/assets/icon_car_dark.svg', + icon_last_modified: '2021-06-15' + }, + default_pricing_plan_id: 'car_plan_1', + pricing_plan_ids: ['car_plan_1', 'car_plan_2', 'car_plan_3'] + } + ] + } + } + } + + vehicle_status() { + return { + last_updated: last_updated_fresh, + ttl: 0, + version, + data: { + vehicles: [ + { + vehicle_id: 'bike1', + last_reported: 1609866109, + lat: 12.345678, + lon: 56.789012, + is_reserved: false, + is_disabled: false, + vehicle_type_id: 'biketype1' + }, + { + vehicle_id: 'car1', + last_reported: 1609866109, + lat: 12.345678, + lon: 56.789012, + is_reserved: false, + is_disabled: false, + vehicle_type_id: 'cartype1', + current_range_meters: 10 + } + ] + } + } + } + + build() { + const app = fastify() + + const data = this.entry_points() + + const keys = Object.keys(data) + + for (const key of keys) { + app.get(`/${key}.json`, async function(request) { + const basePath = `http://${request.hostname}` + + return data[key]({ request, basePath }) + }) + } + + return app + } +} + +module.exports = { + MockRequests +} diff --git a/gbfs-validator/__test__/fixtures/v3.0-RC/exaustive.js b/gbfs-validator/__test__/fixtures/v3.0-RC/exaustive.js new file mode 100644 index 0000000..ed1aaed --- /dev/null +++ b/gbfs-validator/__test__/fixtures/v3.0-RC/exaustive.js @@ -0,0 +1,757 @@ +const fastify = require('fastify') + +const version = '3.0-RC' +const last_updated = 1566224400 + +const last_updated_fresh = Math.floor(Date.now() / 1000) - 30 + +class MockRequests { + entry_points() { + return { + manifest: this.manifest, + gbfs: this.gbfs, + gbfs_versions: this.gbfs_versions, + system_information: this.system_information, + vehicle_types: this.vehicle_types, + vehicle_status: this.vehicle_status, + station_status: this.station_status, + station_information: this.station_information, + system_regions: this.system_regions, + system_pricing_plans: this.system_pricing_plans, + system_alerts: this.system_alerts, + geofencing_zones: this.geofencing_zones + } + } + + manifest({ basePath }) { + return { + last_updated, + ttl: 0, + version, + data: { + datasets: [ + { + system_id: 'example_berlin', + versions: [ + { + version: '3.0-RC', + url: `${basePath}/gbfs.json` + } + ] + } + ] + } + } + } + + gbfs({ basePath }) { + return { + last_updated, + ttl: 0, + version, + data: { + feeds: [ + { + name: 'system_information', + url: `${basePath}/system_information.json` + }, + { + name: 'gbfs_versions', + url: `${basePath}/gbfs_versions.json` + }, + { + name: 'vehicle_types', + url: `${basePath}/vehicle_types.json` + }, + { + name: 'vehicle_status', + url: `${basePath}/vehicle_status.json` + }, + { + name: 'station_information', + url: `${basePath}/station_information.json` + }, + { + name: 'station_status', + url: `${basePath}/station_status.json` + }, + { + name: 'system_regions', + url: `${basePath}/system_regions.json` + }, + { + name: 'system_pricing_plans', + url: `${basePath}/system_pricing_plans.json` + }, + { + name: 'system_alerts', + url: `${basePath}/system_alerts.json` + }, + { + name: 'geofencing_zones', + url: `${basePath}/geofencing_zones.json` + } + ] + } + } + } + + gbfs_versions({ basePath }) { + return { + last_updated, + ttl: 0, + version, + data: { + versions: [ + { + version: '3.0-RC', + url: `${basePath}/gbfs.json` + } + ] + } + } + } + + system_information({ basePath }) { + return { + last_updated, + ttl: 0, + version, + data: { + system_id: 'shared_bike', + languages: ['en'], + name: [ + { + text: 'Shared Bike USA', + language: 'en' + } + ], + opening_hours: 'Mo-Su 00:00-23:59', + short_name: [ + { + text: 'SB-USA', + language: 'en' + } + ], + operator: [ + { + text: 'Shared Bike', + language: 'en' + } + ], + url: 'https://www.example.com', + purchase_url: 'https://www.example.com', + start_date: '2019-01-13', + phone_number: '+18005551234', + email: 'customerservice@example.com', + feed_contact_email: 'datafeed@example.com', + manifest_url: `${basePath}/manifest.json`, + timezone: 'America/Chicago', + license_id: 'CC-BY-4.0', + attribution_organization_name: [ + { + text: 'Shared Bike', + language: 'en' + } + ], + attribution_url: 'https://www.example.com', + brand_assets: { + brand_last_modified: '2021-06-15', + brand_image_url: 'https://www.example.com/assets/brand_image.svg', + brand_image_url_dark: + 'https://www.example.com/assets/brand_image_dark.svg', + color: '#C2D32C', + brand_terms_url: 'https://www.example.com/assets/brand.pdf' + }, + terms_url: [ + { + text: 'https://www.example.com/en/terms', + language: 'en' + } + ], + terms_last_updated: '2021-06-21', + privacy_url: [ + { + text: 'https://www.example.com/en/privacy-policy', + language: 'en' + } + ], + privacy_last_updated: '2019-01-13', + rental_apps: { + android: { + store_uri: + 'https://play.google.com/store/apps/details?id=com.example.android', + discovery_uri: 'com.example.android://' + }, + ios: { + store_uri: 'https://apps.apple.com/app/apple-store/id123456789', + discovery_uri: 'com.example.ios://' + } + } + } + } + } + + vehicle_types() { + return { + last_updated, + ttl: 0, + version, + data: { + vehicle_types: [ + { + vehicle_type_id: 'biketype1', + form_factor: 'bicycle', + rider_capacity: 1, + propulsion_type: 'human', + name: [ + { + text: 'Example Basic Bike', + language: 'en' + } + ], + default_reserve_time: 30, + return_type: ['any_station', 'free_floating'], + vehicle_assets: { + icon_url: 'https://www.example.com/assets/icon_bicycle.svg', + icon_url_dark: + 'https://www.example.com/assets/icon_bicycle_dark.svg', + icon_last_modified: '2021-06-15' + }, + default_pricing_plan_id: 'plan2', + pricing_plan_ids: ['plan2', 'plan3'] + }, + { + vehicle_type_id: 'cartype1', + form_factor: 'car', + cargo_volume_capacity: 50, + cargo_load_capacity: 100, + propulsion_type: 'electric', + eco_label: [ + { + country_code: 'FR', + eco_sticker: 'critair_1' + } + ], + max_range_meters: 10000, + name: [ + { + text: 'Example Electric Car', + language: 'en' + } + ], + vehicle_accessories: ['automatic', 'air_conditioning'], + g_CO2_km: 0, + default_reserve_time: 30, + return_type: ['any_station', 'free_floating'], + vehicle_image: 'https://www.example.com/assets/car.jpg', + make: [ + { + text: 'Example Car Manufacturer', + language: 'en' + } + ], + model: [ + { + text: 'Example Car Model', + language: 'en' + } + ], + color: 'dark blue', + description: [ + { + text: 'Example Car Description', + language: 'en' + } + ], + wheel_count: 4, + max_permitted_speed: 200, + rated_power: 100, + default_reserve_time: 30, + return_constraint: 'hybrid', + vehicle_assets: { + icon_url: 'https://www.example.com/assets/icon_car.svg', + icon_url_dark: 'https://www.example.com/assets/icon_car_dark.svg', + icon_last_modified: '2021-06-15' + }, + default_pricing_plan_id: 'plan3', + pricing_plan_ids: ['plan3'] + } + ] + } + } + } + + station_information({ basePath }) { + return { + last_updated, + ttl: 0, + version, + data: { + stations: [ + { + station_id: 'pga', + name: [ + { + text: 'Parking garage A', + language: 'en' + } + ], + short_name: [ + { + text: 'PGA', + language: 'en' + } + ], + lat: 12.345678, + lon: 45.678901, + address: '1234 Main Street', + cross_street: '2nd Street', + region_id: 'region1', + post_code: '12345', + station_opening_hours: 'Su-Th 05:00-22:00; Fr-Sa 05:00-01:00', + rental_methods: ['creditcard', 'phone'], + is_virtual_station: true, + station_area: { + type: 'MultiPolygon', + coordinates: [ + [ + [ + [-122.655775, 45.516445], + [-122.655705, 45.516445], + [-122.655705, 45.516495], + [-122.655775, 45.516495], + [-122.655775, 45.516445] + ] + ] + ] + }, + parking_type: 'underground_parking', + parking_hoop: false, + contact_phone: '+33109874321', + capacity: 10, + vehicle_type_area_capacity: [ + { + vehicle_type_id: 'abc123', + count: 7 + }, + { + vehicle_type_id: 'def456', + count: 3 + } + ], + vehicle_type_dock_capacity: [ + { + vehicle_type_id: 'abc123', + count: 7 + }, + { + vehicle_type_id: 'def456', + count: 3 + } + ], + is_valet_station: true, + is_charging_station: true, + rental_uris: { + android: + 'https://www.example.com/app?sid=1234567890&platform=android', + ios: 'https://www.example.com/app?sid=1234567890&platform=ios', + web: 'https://www.example.com/app?sid=1234567890' + } + } + ] + } + } + } + + station_status({ basePath }) { + return { + last_updated: last_updated_fresh, + ttl: 0, + version, + data: { + stations: [ + { + station_id: 'station1', + is_installed: true, + is_renting: true, + is_returning: true, + last_reported: 1609866125, + num_docks_available: 3, + num_docks_disabled: 1, + vehicle_docks_available: [ + { + vehicle_type_ids: ['biketype1', 'cartype1'], + count: 2 + }, + { + vehicle_type_ids: ['cartype1'], + count: 1 + } + ], + num_vehicles_available: 1, + num_vehicles_disabled: 2, + vehicle_types_available: [ + { + vehicle_type_id: 'biketype1', + count: 1 + }, + { + vehicle_type_id: 'cartype1', + count: 0 + } + ] + }, + { + station_id: 'station2', + is_installed: true, + is_renting: true, + is_returning: true, + last_reported: 1609866106, + num_docks_available: 8, + num_docks_disabled: 1, + vehicle_docks_available: [ + { + vehicle_type_ids: ['biketype1'], + count: 6 + }, + { + vehicle_type_ids: ['cartype1'], + count: 2 + } + ], + num_vehicles_available: 6, + num_vehicles_disabled: 1, + vehicle_types_available: [ + { + vehicle_type_id: 'biketype1', + count: 2 + }, + { + vehicle_type_id: 'cartype1', + count: 4 + } + ] + } + ] + } + } + } + + vehicle_status() { + return { + last_updated: last_updated_fresh, + ttl: 0, + version, + data: { + vehicles: [ + { + vehicle_id: 'bike1', + lat: 12.345678, + lon: 56.789012, + is_reserved: false, + is_disabled: false, + rental_uris: { + android: + 'https://www.example.com/app?vehicle_id=973a5c94-c288-4a2b-afa6-de8aeb6ae2e5&platform=android&', + ios: + 'https://www.example.com/app?vehicle_id=973a5c94-c288-4a2b-afa6-de8aeb6ae2e5&platform=ios', + web: 'https://www.example.com/app?sid=1234567890' + }, + vehicle_type_id: 'biketype1', + last_reported: 1609866109 + }, + { + vehicle_id: 'car1', + lat: 12.345678, + lon: 56.789012, + is_reserved: false, + is_disabled: false, + vehicle_type_id: 'cartype1', + last_reported: 1609866109, + current_range_meters: 10000, + current_fuel_percent: 0.5, + station_id: 'station1', + home_station_id: 'station1', + pricing_plan_id: 'plan3', + vehicle_equipment: ['winter_tires'], + available_until: '2021-05-17T15:00:00Z' + } + ] + } + } + } + + system_regions({ basePath }) { + return { + last_updated, + ttl: 0, + version, + data: { + regions: [ + { + name: [ + { + text: 'North', + language: 'en' + } + ], + region_id: 'region3' + }, + { + name: [ + { + text: 'East', + language: 'en' + } + ], + region_id: 'region4' + }, + { + name: [ + { + text: 'South', + language: 'en' + } + ], + region_id: 'region5' + }, + { + name: [ + { + text: 'West', + language: 'en' + } + ], + region_id: 'region6' + } + ] + } + } + } + + system_pricing_plans({ basePath }) { + return { + last_updated, + ttl: 0, + version, + data: { + plans: [ + { + plan_id: 'plan2', + url: 'https://www.example.com/pricing/plan2', + name: [ + { + text: 'One-Way', + language: 'en' + } + ], + currency: 'USD', + price: 2.0, + reservation_price_per_min: 0.15, + is_taxable: false, + description: [ + { + text: 'Includes 10km, overage fees apply after 10km.', + language: 'en' + } + ], + per_km_pricing: [ + { + start: 10, + rate: 1.0, + interval: 1, + end: 25 + }, + { + start: 25, + rate: 0.5, + interval: 1 + }, + { + start: 25, + rate: 3.0, + interval: 5 + } + ], + surge_pricing: false + }, + { + plan_id: 'plan3', + name: [ + { + text: 'Simple Rate', + language: 'en' + } + ], + currency: 'CAD', + price: 3.0, + reservation_price_flat_rate: 3.0, + is_taxable: true, + description: [ + { + text: '$3 unlock fee, $0.25 per kilometer and 0.50 per minute.', + language: 'en' + } + ], + per_km_pricing: [ + { + start: 0, + rate: 0.25, + interval: 1 + } + ], + per_min_pricing: [ + { + start: 0, + rate: 0.5, + interval: 1 + } + ], + surge_pricing: false + } + ] + } + } + } + + system_alerts({ basePath }) { + return { + last_updated, + ttl: 0, + version, + data: { + alerts: [ + { + alert_id: '21', + type: 'station_closure', + times: [ + { + start: 1604448000, + end: 1604674800 + } + ], + station_ids: ['123', '456', '789'], + url: [ + { + text: 'https://example.com/more-info', + language: 'en' + } + ], + region_ids: ['region1', 'region2'], + url: [ + { + text: 'https://example.com/more-info', + language: 'en' + } + ], + summary: [ + { + text: 'Disruption of Service', + language: 'en' + } + ], + description: [ + { + text: + 'The three stations on Broadway will be out of service from 12:00am Nov 3 to 3:00pm Nov 6th to accommodate road work', + language: 'en' + } + ], + last_updated: 1604198100 + } + ] + } + } + } + + geofencing_zones({ basePath }) { + return { + last_updated, + ttl: 0, + version, + data: { + geofencing_zones: { + type: 'FeatureCollection', + features: [ + { + type: 'Feature', + geometry: { + type: 'MultiPolygon', + coordinates: [ + [ + [ + [-122.578067, 45.562982], + [-122.661838, 45.562741], + [-122.661151, 45.504542], + [-122.578926, 45.5046625], + [-122.578067, 45.562982] + ] + ], + [ + [ + [-122.65068, 45.548197], + [-122.650852, 45.534731], + [-122.630939, 45.535212], + [-122.630424, 45.548197], + [-122.65068, 45.548197] + ] + ] + ] + }, + properties: { + name: [ + { + text: 'NE 24th/NE Knott', + language: 'en' + } + ], + start: 1593878400, + end: 1593907260, + rules: [ + { + vehicle_type_id: ['moped1', 'car1'], + ride_start_allowed: false, + ride_end_allowed: false, + ride_through_allowed: true, + maximum_speed_kph: 10, + station_parking: true + } + ] + } + } + ] + }, + global_rules: [ + { + ride_start_allowed: false, + ride_end_allowed: false, + ride_through_allowed: true + } + ] + } + } + } + + build() { + const app = fastify() + + const data = this.entry_points() + + const keys = Object.keys(data) + + for (const key of keys) { + app.get(`/${key}.json`, async function(request) { + const basePath = `http://${request.hostname}` + + return data[key]({ request, basePath }) + }) + } + + return app + } +} + +module.exports = { + MockRequests +} diff --git a/gbfs-validator/__test__/gbfs.test.js b/gbfs-validator/__test__/gbfs.test.js index ce62920..2ef61d2 100644 --- a/gbfs-validator/__test__/gbfs.test.js +++ b/gbfs-validator/__test__/gbfs.test.js @@ -82,7 +82,7 @@ describe('checkAutodiscovery method', () => { exists: true, file: 'gbfs.json', hasErrors: false, - recommanded: true, + recommended: true, required: true, url: `http://${gbfsFeedServer.server.address().address}:${ gbfsFeedServer.server.address().port @@ -104,7 +104,7 @@ describe('checkAutodiscovery method', () => { exists: true, file: 'gbfs.json', hasErrors: false, - recommanded: true, + recommended: true, required: true, url: `http://${gbfsFeedServer.server.address().address}:${ gbfsFeedServer.server.address().port @@ -126,7 +126,7 @@ describe('checkAutodiscovery method', () => { exists: false, file: 'gbfs.json', hasErrors: false, - recommanded: true, + recommended: true, required: false, url: `http://${gbfsFeedServer.server.address().address}:${ gbfsFeedServer.server.address().port @@ -149,7 +149,7 @@ describe('checkAutodiscovery method', () => { exists: false, file: 'gbfs.json', hasErrors: false, - recommanded: true, + recommended: true, required: true, url: `http://${gbfsFeedServer.server.address().address}:${ gbfsFeedServer.server.address().port diff --git a/gbfs-validator/__test__/gbfs.v3.0-RC.test.js b/gbfs-validator/__test__/gbfs.v3.0-RC.test.js new file mode 100644 index 0000000..5aed3c8 --- /dev/null +++ b/gbfs-validator/__test__/gbfs.v3.0-RC.test.js @@ -0,0 +1,153 @@ +const GBFS = require('../gbfs') + +const serverOpts = { + port: 0, + host: '127.0.0.1', +} + +function get_errors(result) { + let errors = [] + + result.files?.map(f => { + if (f.errors) { + errors.push({ file: f.file, errors: f.errors }) + } + + f.languages?.map(l => { + if (l.errors) { + errors.push({ file: f.file, lang: l.lang, errors: l.errors }) + } + }) + }) + + return errors +} + +describe('default feed', () => { + let gbfsFeedServer + + beforeAll(async () => { + const { MockRequests } = require('./fixtures/v3.0-RC/default') + let mockRequests = new MockRequests() + + gbfsFeedServer = mockRequests.build() + + await gbfsFeedServer.listen(serverOpts) + }) + + afterAll(() => { + return gbfsFeedServer.close() + }) + + test('should validate feed', async () => { + const url = `http://${gbfsFeedServer.server.address().address}:${ + gbfsFeedServer.server.address().port + }` + const gbfs = new GBFS(`${url}/gbfs.json`) + + expect.assertions(1) + + return gbfs.validation().then(result => { + expect(result).toMatchObject({ + summary: expect.objectContaining({ + version: { detected: '3.0-RC', validated: '3.0-RC' }, + validatorVersion: '1.0.0', + hasErrors: false + }), + files: expect.any(Array) + }) + }) + }) +}) + +describe('invalid feed', () => { + let gbfsFeedServer + + beforeAll(async () => { + const { MockRequests } = require('./fixtures/v3.0-RC/default') + class InvalidMockRequests extends MockRequests { + system_information(...args) { + const json = super.system_information(...args) + + delete json.data.name + + return json + } + } + + let mockRequests = new InvalidMockRequests() + + gbfsFeedServer = mockRequests.build() + + await gbfsFeedServer.listen(serverOpts) + }) + + afterAll(() => { + return gbfsFeedServer.close() + }) + + test('should not validate feed', async () => { + const url = `http://${gbfsFeedServer.server.address().address}:${ + gbfsFeedServer.server.address().port + }` + const gbfs = new GBFS(`${url}/gbfs.json`) + + expect.assertions(2) + + return gbfs.validation().then(result => { + expect(result).toMatchObject({ + summary: expect.objectContaining({ + version: { detected: '3.0-RC', validated: '3.0-RC' }, + validatorVersion: '1.0.0', + hasErrors: true, + errorsCount: 1 + }), + files: expect.any(Array) + }) + + let error = result.files.find(f => f.file === 'system_information.json') + ?.languages?.[0].errors?.[0].schemaPath + expect(error).toBe('#/properties/data/required') + }) + }) +}) + +describe('exaustive feed', () => { + let gbfsFeedServer + + beforeAll(async () => { + const { MockRequests } = require('./fixtures/v3.0-RC/exaustive') + + let mockRequests = new MockRequests() + + gbfsFeedServer = mockRequests.build() + + await gbfsFeedServer.listen(serverOpts) + }) + + afterAll(() => { + return gbfsFeedServer.close() + }) + + test('should validate feed', async () => { + const url = `http://${gbfsFeedServer.server.address().address}:${ + gbfsFeedServer.server.address().port + }` + const gbfs = new GBFS(`${url}/gbfs.json`) + + expect.assertions(2) + + return gbfs.validation().then(result => { + expect(get_errors(result)).toEqual([]) + + expect(result).toMatchObject({ + summary: expect.objectContaining({ + version: { detected: '3.0-RC', validated: '3.0-RC' }, + validatorVersion: '1.0.0', + hasErrors: false + }), + files: expect.any(Array) + }) + }) + }) +}) diff --git a/gbfs-validator/gbfs.js b/gbfs-validator/gbfs.js index 29ed6b5..f3a2086 100644 --- a/gbfs-validator/gbfs.js +++ b/gbfs-validator/gbfs.js @@ -196,7 +196,7 @@ class GBFS { .then((body) => { if (typeof body !== 'object') { return { - recommanded: true, + recommended: true, required: isGBFSFileRequire(this.options.version), errors: false, exists: false, @@ -218,7 +218,7 @@ class GBFS { errors, url, version: body.version, - recommanded: true, + recommended: true, required: isGBFSFileRequire( this.options.version || body.version || '1.0' ), @@ -230,7 +230,7 @@ class GBFS { .catch(() => { return { url, - recommanded: true, + recommended: true, required: isGBFSFileRequire(this.options.version), errors: false, exists: false, @@ -264,7 +264,7 @@ class GBFS { errors, url: this.url, version: body.version || '1.0', - recommanded: true, + recommended: true, required: isGBFSFileRequire( this.options.version || body.version || '1.0' ), @@ -282,7 +282,7 @@ class GBFS { return { url: this.url, - recommanded: true, + recommended: true, required: isGBFSFileRequire(this.options.version), errors: false, exists: false, @@ -307,12 +307,22 @@ class GBFS { getFile(type, required) { if (this.autoDiscovery) { - const urls = Object.entries(this.autoDiscovery.data).map((key) => { - return Object.assign( - { lang: key[0] }, - this.autoDiscovery.data[key[0]].feeds.find((f) => f.name === type) - ) - }) + let urls + + let version = this.options.version || this.autoDiscovery.version + + // 3.0-RC , 3.0 and upcoming minor versions + if (/^3\.\d/.test(version)) { + urls = + this.autoDiscovery.data.feeds?.filter((f) => f.name === type) || [] + } else { + urls = Object.entries(this.autoDiscovery.data).map((key) => { + return Object.assign( + { lang: key[0] }, + this.autoDiscovery.data[key[0]].feeds.find((f) => f.name === type) + ) + }) + } return Promise.all( urls.map((lang) => @@ -462,9 +472,34 @@ class GBFS { const stationInformationFile = files.find( (a) => a.type === 'station_information' ) - const stationPricingPlans = files.find( + const systemPricingPlans = files.find( (a) => a.type === 'system_pricing_plans' ) + const systemInformation = files.find((a) => a.type === 'system_information') + + const manifestUrl = systemInformation?.body?.[0]?.body?.data?.manifest_url + + if (manifestUrl) { + try { + const body = await got.get(manifestUrl, this.gotOptions).json() + + files.push({ + body, + required: true, + type: 'manifest' + }) + } catch (error) { + files.push({ + url: manifestUrl, + recommended: true, + required: true, + errors: false, + exists: false, + file: `manifest.json`, + hasErrors: false + }) + } + } let vehicleTypes, pricingPlans, @@ -499,8 +534,8 @@ class GBFS { hasRentalUris(stationInformationFile, 'stations', 'android') } - if (fileExist(stationPricingPlans)) { - pricingPlans = getPricingPlans(stationPricingPlans) + if (fileExist(systemPricingPlans)) { + pricingPlans = getPricingPlans(systemPricingPlans) } files.forEach((f) => { @@ -512,7 +547,7 @@ class GBFS { if (vehicleTypes && vehicleTypes.length) { const partial = getPartialSchema( gbfsVersion, - 'required_vehicle_types_available', + 'station_status/required_vehicle_types_available', { vehicleTypes } @@ -526,7 +561,21 @@ class GBFS { if (vehicleTypes && vehicleTypes.length) { const partial = getPartialSchema( gbfsVersion, - 'required_vehicle_type_id', + 'free_bike_status/required_vehicle_type_id', + { + vehicleTypes + } + ) + if (partial) { + addSchema.push(partial) + } + } + break + case 'vehicle_status': + if (vehicleTypes && vehicleTypes.length) { + const partial = getPartialSchema( + gbfsVersion, + 'vehicle_status/required_vehicle_type_id', { vehicleTypes } @@ -541,9 +590,13 @@ class GBFS { required = true } if (pricingPlans && pricingPlans.length) { - const partial = getPartialSchema(gbfsVersion, 'pricing_plan_id', { - pricingPlans - }) + const partial = getPartialSchema( + gbfsVersion, + 'vehicle_types/pricing_plan_id', + { + pricingPlans + } + ) if (partial) { addSchema.push(partial) @@ -560,7 +613,7 @@ class GBFS { if (hasAndroidRentalUris || hasIosRentalUris) { const partial = getPartialSchema( gbfsVersion, - 'required_store_uri', + 'system_information/required_store_uri', { ios: hasIosRentalUris, android: hasAndroidRentalUris diff --git a/gbfs-validator/versions/partials/v2.1/required_vehicle_type_id.js b/gbfs-validator/versions/partials/v2.1/free_bike_status/required_vehicle_type_id.js similarity index 100% rename from gbfs-validator/versions/partials/v2.1/required_vehicle_type_id.js rename to gbfs-validator/versions/partials/v2.1/free_bike_status/required_vehicle_type_id.js diff --git a/gbfs-validator/versions/partials/v2.1/required_vehicle_types_available.js b/gbfs-validator/versions/partials/v2.1/station_status/required_vehicle_types_available.js similarity index 100% rename from gbfs-validator/versions/partials/v2.1/required_vehicle_types_available.js rename to gbfs-validator/versions/partials/v2.1/station_status/required_vehicle_types_available.js diff --git a/gbfs-validator/versions/partials/v2.1/required_store_uri.js b/gbfs-validator/versions/partials/v2.1/system_information/required_store_uri.js similarity index 100% rename from gbfs-validator/versions/partials/v2.1/required_store_uri.js rename to gbfs-validator/versions/partials/v2.1/system_information/required_store_uri.js diff --git a/gbfs-validator/versions/partials/v2.2/required_vehicle_type_id.js b/gbfs-validator/versions/partials/v2.2/free_bike_status/required_vehicle_type_id.js similarity index 100% rename from gbfs-validator/versions/partials/v2.2/required_vehicle_type_id.js rename to gbfs-validator/versions/partials/v2.2/free_bike_status/required_vehicle_type_id.js diff --git a/gbfs-validator/versions/partials/v2.2/required_vehicle_types_available.js b/gbfs-validator/versions/partials/v2.2/station_status/required_vehicle_types_available.js similarity index 100% rename from gbfs-validator/versions/partials/v2.2/required_vehicle_types_available.js rename to gbfs-validator/versions/partials/v2.2/station_status/required_vehicle_types_available.js diff --git a/gbfs-validator/versions/partials/v2.2/required_store_uri.js b/gbfs-validator/versions/partials/v2.2/system_information/required_store_uri.js similarity index 100% rename from gbfs-validator/versions/partials/v2.2/required_store_uri.js rename to gbfs-validator/versions/partials/v2.2/system_information/required_store_uri.js diff --git a/gbfs-validator/versions/partials/v2.3/required_vehicle_type_id.js b/gbfs-validator/versions/partials/v2.3/free_bike_status/required_vehicle_type_id.js similarity index 100% rename from gbfs-validator/versions/partials/v2.3/required_vehicle_type_id.js rename to gbfs-validator/versions/partials/v2.3/free_bike_status/required_vehicle_type_id.js diff --git a/gbfs-validator/versions/partials/v2.3/required_vehicle_types_available.js b/gbfs-validator/versions/partials/v2.3/station_status/required_vehicle_types_available.js similarity index 100% rename from gbfs-validator/versions/partials/v2.3/required_vehicle_types_available.js rename to gbfs-validator/versions/partials/v2.3/station_status/required_vehicle_types_available.js diff --git a/gbfs-validator/versions/partials/v2.3/required_store_uri.js b/gbfs-validator/versions/partials/v2.3/system_information/required_store_uri.js similarity index 100% rename from gbfs-validator/versions/partials/v2.3/required_store_uri.js rename to gbfs-validator/versions/partials/v2.3/system_information/required_store_uri.js diff --git a/gbfs-validator/versions/partials/v2.3/pricing_plan_id.js b/gbfs-validator/versions/partials/v2.3/vehicle_types/pricing_plan_id.js similarity index 100% rename from gbfs-validator/versions/partials/v2.3/pricing_plan_id.js rename to gbfs-validator/versions/partials/v2.3/vehicle_types/pricing_plan_id.js diff --git a/gbfs-validator/versions/partials/v3.0-RC/station_status/required_vehicle_types_available.js b/gbfs-validator/versions/partials/v3.0-RC/station_status/required_vehicle_types_available.js new file mode 100644 index 0000000..66c3f4e --- /dev/null +++ b/gbfs-validator/versions/partials/v3.0-RC/station_status/required_vehicle_types_available.js @@ -0,0 +1,47 @@ +module.exports = ({ vehicleTypes }) => { + return { + $id: 'required_vehicle_types_available.json#', + $merge: { + source: { + $ref: + 'https://github.com/NABSA/gbfs/blob/v3.0-RC/gbfs.md#station_statusjson' + }, + with: { + properties: { + data: { + properties: { + stations: { + items: { + properties: { + vehicle_types_available: { + items: { + properties: { + vehicle_type_id: { + enum: vehicleTypes.map(vt => vt.vehicle_type_id) + } + } + } + } + } + } + } + } + } + } + } + }, + $patch: { + source: { + $ref: + 'https://github.com/NABSA/gbfs/blob/v3.0-RC/gbfs.md#station_statusjson' + }, + with: [ + { + op: 'add', + path: '/properties/data/properties/stations/items/required/0', + value: 'vehicle_types_available' + } + ] + } + } +} diff --git a/gbfs-validator/versions/partials/v3.0-RC/system_information/required_store_uri.js b/gbfs-validator/versions/partials/v3.0-RC/system_information/required_store_uri.js new file mode 100644 index 0000000..ac1a136 --- /dev/null +++ b/gbfs-validator/versions/partials/v3.0-RC/system_information/required_store_uri.js @@ -0,0 +1,61 @@ +module.exports = ({ android = false, ios = false }) => { + const r = { + $id: 'required_ios_store_uri.json#', + $patch: { + source: { + $ref: + 'https://github.com/NABSA/gbfs/blob/v3.0-RC/gbfs.md#system_informationjson' + }, + with: [ + { + op: 'add', + path: '/properties/data/required/0', + value: 'rental_apps' + } + ] + }, + $merge: { + source: { + $ref: + 'https://github.com/NABSA/gbfs/blob/v3.0-RC/gbfs.md#system_informationjson' + }, + with: { + properties: { + data: { + properties: { + rental_apps: { + required: [], + properties: { + ios: { + required: [] + }, + android: { + required: [] + } + } + } + } + } + } + } + } + } + + if (ios) { + r.$merge.with.properties.data.properties.rental_apps.required.push('ios') + r.$merge.with.properties.data.properties.rental_apps.properties.ios.required.push( + 'store_uri' + ) + } + + if (android) { + r.$merge.with.properties.data.properties.rental_apps.required.push( + 'android' + ) + r.$merge.with.properties.data.properties.rental_apps.properties.android.required.push( + 'store_uri' + ) + } + + return r +} diff --git a/gbfs-validator/versions/partials/v3.0-RC/vehicle_status/required_vehicle_type_id.js b/gbfs-validator/versions/partials/v3.0-RC/vehicle_status/required_vehicle_type_id.js new file mode 100644 index 0000000..d09c59d --- /dev/null +++ b/gbfs-validator/versions/partials/v3.0-RC/vehicle_status/required_vehicle_type_id.js @@ -0,0 +1,64 @@ +module.exports = ({ vehicleTypes }) => { + const res = { + $id: 'required_vehicle_type_id.json#' + } + + const motorVehicleTypes = vehicleTypes.filter(vt => + ['electric_assist', 'electric', 'combustion'].includes(vt.propulsion_type) + ) + + if (motorVehicleTypes.length) { + res.$merge = { + source: { + $ref: + 'https://github.com/NABSA/gbfs/blob/v3.0-RC/gbfs.md#vehicle_statusjson' + }, + with: { + properties: { + data: { + properties: { + vehicles: { + items: { + errorMessage: { + required: { + vehicle_type_id: + "'vehicle_type_id' is required for this vehicle type" + } + }, + if: { + properties: { + vehicle_type_id: { + enum: motorVehicleTypes.map(vt => vt.vehicle_type_id) + } + }, + // "required" so it only trigger "then" when "vehicle_type_id" is present. + required: ['vehicle_type_id'] + }, + then: { + required: ['current_range_meters'] + } + } + } + } + } + } + } + } + } + + res.$patch = { + source: { + $ref: + 'https://github.com/NABSA/gbfs/blob/v3.0-RC/gbfs.md#vehicle_statusjson' + }, + with: [ + { + op: 'add', + path: '/properties/data/properties/vehicles/items/required/0', + value: 'vehicle_type_id' + } + ] + } + + return res +} diff --git a/gbfs-validator/versions/partials/v3.0-RC/vehicle_types/pricing_plan_id.js b/gbfs-validator/versions/partials/v3.0-RC/vehicle_types/pricing_plan_id.js new file mode 100644 index 0000000..ded5053 --- /dev/null +++ b/gbfs-validator/versions/partials/v3.0-RC/vehicle_types/pricing_plan_id.js @@ -0,0 +1,28 @@ +module.exports = ({ pricingPlans }) => { + return { + $id: 'pricing_plan_id.json#', + $merge: { + source: { + $ref: + 'https://github.com/NABSA/gbfs/blob/v3.0-RC/gbfs.md#vehicle_typesjson-added-in-v21-rc' + }, + with: { + properties: { + data: { + properties: { + vehicle_types: { + items: { + properties: { + default_pricing_plan_id: { + enum: pricingPlans.map(p => p.plan_id) + } + } + } + } + } + } + } + } + } + } +} diff --git a/gbfs-validator/versions/schemas/.github/workflows/validate_json.yml b/gbfs-validator/versions/schemas/.github/workflows/validate_json.yml deleted file mode 100644 index cd25db4..0000000 --- a/gbfs-validator/versions/schemas/.github/workflows/validate_json.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: Validate JSON Schema files -on: [push] -jobs: - test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Setup python - uses: actions/setup-python@v2 - with: - python-version: '3.x' - - name: Install jsonschema - run: pip install jsonschema==3.2.0 - - run: | - for i in **/*.json; do - echo "Validating $i" - jsonschema $i - done diff --git a/gbfs-validator/versions/schemas/.gitignore b/gbfs-validator/versions/schemas/.gitignore deleted file mode 100644 index 23c81e2..0000000 --- a/gbfs-validator/versions/schemas/.gitignore +++ /dev/null @@ -1,87 +0,0 @@ -# Ignore all IDEA files. Comment out the following line if you wish to ignore only particular files (listed below). -.idea/ - -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 - -# User-specific stuff -.idea/**/modules.xml -.idea/**/workspace.xml -.idea/**/tasks.xml -.idea/**/usage.statistics.xml -.idea/**/dictionaries -.idea/**/shelf - -# Generated files -.idea/**/contentModel.xml - -# Sensitive or high-churn files -.idea/**/dataSources/ -.idea/**/dataSources.ids -.idea/**/dataSources.local.xml -.idea/**/sqlDataSources.xml -.idea/**/dynamic.xml -.idea/**/uiDesigner.xml -.idea/**/dbnavigator.xml - -# Gradle -.idea/**/gradle.xml -.idea/**/libraries - -# Gradle and Maven with auto-import -# When using Gradle or Maven with auto-import, you should exclude module files, -# since they will be recreated, and may cause churn. Uncomment if using -# auto-import. -.idea/artifacts -.idea/compiler.xml -.idea/jarRepositories.xml -.idea/modules.xml -.idea/*.iml -.idea/modules -*.iml -*.ipr - -# CMake -cmake-build-*/ - -# Mongo Explorer plugin -.idea/**/mongoSettings.xml - -# File-based project format -*.iws - -# IntelliJ -out/ - -# mpeltonen/sbt-idea plugin -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Cursive Clojure plugin -.idea/replstate.xml - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties - -# Editor-based Rest Client -.idea/httpRequests - -# Android studio 3.1+ serialized cache file -.idea/caches/build_file_checksums.ser - -# Gradle -.gradle -build/ - -# Inputs and outputs -/input/ -/output/ - -# Mac -*/.DS_Store -.DS_Store \ No newline at end of file diff --git a/gbfs-validator/versions/schemas/README.md b/gbfs-validator/versions/schemas/README.md index e079302..cf6cde1 100644 --- a/gbfs-validator/versions/schemas/README.md +++ b/gbfs-validator/versions/schemas/README.md @@ -1,2 +1,2 @@ # gbfs-json-schema -JSON Schema for [General Bikeshare Feed](https://github.com/NABSA/gbfs/blob/master/gbfs.md) Specification(GBFS) feeds, managed by MobilityData. The [gbfs-validator](https://github.com/MobilityData/gbfs-validator) links directly to them. +JSON Schema for [General Bikeshare Feed Specification(GBFS)](https://github.com/MobilityData/gbfs/blob/master/gbfs.md) feeds, managed by MobilityData. The [gbfs-validator](https://github.com/MobilityData/gbfs-validator) links directly to them. diff --git a/gbfs-validator/versions/schemas/v1.1/gbfs_versions.json b/gbfs-validator/versions/schemas/v1.1/gbfs_versions.json index deb3cad..c16762e 100644 --- a/gbfs-validator/versions/schemas/v1.1/gbfs_versions.json +++ b/gbfs-validator/versions/schemas/v1.1/gbfs_versions.json @@ -41,7 +41,13 @@ "type": "string", "enum": [ "1.0", - "1.1" + "1.1", + "2.0", + "2.1", + "2.2", + "2.3", + "3.0-RC", + "3.0" ] }, "url": { diff --git a/gbfs-validator/versions/schemas/v2.0/gbfs_versions.json b/gbfs-validator/versions/schemas/v2.0/gbfs_versions.json index caceb98..e16d52d 100644 --- a/gbfs-validator/versions/schemas/v2.0/gbfs_versions.json +++ b/gbfs-validator/versions/schemas/v2.0/gbfs_versions.json @@ -42,7 +42,12 @@ "enum": [ "1.0", "1.1", - "2.0" + "2.0", + "2.1", + "2.2", + "2.3", + "3.0-RC", + "3.0" ] }, "url": { diff --git a/gbfs-validator/versions/schemas/v2.1/gbfs_versions.json b/gbfs-validator/versions/schemas/v2.1/gbfs_versions.json index e83b640..6b17207 100644 --- a/gbfs-validator/versions/schemas/v2.1/gbfs_versions.json +++ b/gbfs-validator/versions/schemas/v2.1/gbfs_versions.json @@ -43,7 +43,11 @@ "1.0", "1.1", "2.0", - "2.1" + "2.1", + "2.2", + "2.3", + "3.0-RC", + "3.0" ] }, "url": { diff --git a/gbfs-validator/versions/schemas/v2.1/geofencing_zones.json b/gbfs-validator/versions/schemas/v2.1/geofencing_zones.json index fc341de..4645df6 100644 --- a/gbfs-validator/versions/schemas/v2.1/geofencing_zones.json +++ b/gbfs-validator/versions/schemas/v2.1/geofencing_zones.json @@ -143,7 +143,8 @@ }, "required": ["type", "features"] } - } + }, + "required": ["geofencing_zones"] } }, "required": ["last_updated", "ttl", "version", "data"] diff --git a/gbfs-validator/versions/schemas/v2.2/gbfs_versions.json b/gbfs-validator/versions/schemas/v2.2/gbfs_versions.json index 5fe7582..c17c103 100644 --- a/gbfs-validator/versions/schemas/v2.2/gbfs_versions.json +++ b/gbfs-validator/versions/schemas/v2.2/gbfs_versions.json @@ -44,7 +44,10 @@ "1.1", "2.0", "2.1", - "2.2" + "2.2", + "2.3", + "3.0-RC", + "3.0" ] }, "url": { diff --git a/gbfs-validator/versions/schemas/v2.2/geofencing_zones.json b/gbfs-validator/versions/schemas/v2.2/geofencing_zones.json index 12975a6..bb41cca 100644 --- a/gbfs-validator/versions/schemas/v2.2/geofencing_zones.json +++ b/gbfs-validator/versions/schemas/v2.2/geofencing_zones.json @@ -143,7 +143,8 @@ }, "required": ["type", "features"] } - } + }, + "required": ["geofencing_zones"] } }, "required": ["last_updated", "ttl", "version", "data"] diff --git a/gbfs-validator/versions/schemas/v2.3/gbfs_versions.json b/gbfs-validator/versions/schemas/v2.3/gbfs_versions.json index 1488a3d..5bed52f 100644 --- a/gbfs-validator/versions/schemas/v2.3/gbfs_versions.json +++ b/gbfs-validator/versions/schemas/v2.3/gbfs_versions.json @@ -45,7 +45,9 @@ "2.0", "2.1", "2.2", - "2.3" + "2.3", + "3.0-RC", + "3.0" ] }, "url": { diff --git a/gbfs-validator/versions/schemas/v2.3/geofencing_zones.json b/gbfs-validator/versions/schemas/v2.3/geofencing_zones.json index 7cdedd2..7859637 100644 --- a/gbfs-validator/versions/schemas/v2.3/geofencing_zones.json +++ b/gbfs-validator/versions/schemas/v2.3/geofencing_zones.json @@ -77,7 +77,7 @@ "items": { "type": "string" } }, "ride_allowed": { - "description": "Is the undocked ride allowed to stat and end in this zone?", + "description": "Is the undocked ride allowed to start and end in this zone?", "type": "boolean" }, "ride_through_allowed": { @@ -137,7 +137,8 @@ }, "required": ["type", "features"] } - } + }, + "required": ["geofencing_zones"] } }, "required": ["last_updated", "ttl", "version", "data"] diff --git a/gbfs-validator/versions/schemas/v3.0-RC/gbfs.json b/gbfs-validator/versions/schemas/v3.0-RC/gbfs.json new file mode 100644 index 0000000..3656250 --- /dev/null +++ b/gbfs-validator/versions/schemas/v3.0-RC/gbfs.json @@ -0,0 +1,70 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "https://github.com/NABSA/gbfs/blob/v3.0-RC/gbfs.md#gbfsjson", + "description": + "Auto-discovery file that links to all of the other files published by the system.", + "type": "object", + "properties": { + "last_updated": { + "description": + "Last time the data in the feed was updated in POSIX time.", + "type": "integer", + "minimum": 1450155600 + }, + "ttl": { + "description": + "Number of seconds before the data in the feed will be updated again (0 if the data should always be refreshed).", + "type": "integer", + "minimum": 0 + }, + "version": { + "description": + "GBFS version number to which the feed conforms, according to the versioning framework (added in v1.1).", + "type": "string", + "enum": [ + "3.0-RC", + "3.0" + ] + }, + "data": { + "properties": { + "feeds": { + "description": "An array of all of the feeds that are published by the auto-discovery file. Each element in the array is an object with the keys below.", + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "description": "Key identifying the type of feed this is. The key must be the base file name defined in the spec for the corresponding feed type.", + "type": "string", + "enum": [ + "gbfs", + "gbfs_versions", + "system_information", + "vehicle_types", + "station_information", + "station_status", + "vehicle_status", + "system_alerts", + "system_regions", + "system_pricing_plans", + "geofencing_zones" + ] + }, + "url": { + "description": "URL for the feed.", + "type": "string", + "format": "uri" + } + }, + "required": ["name", "url"] + } + }, + "required": true + } + }, + "required": true + }, + "additionalProperties": false, + "required": ["last_updated", "ttl", "version", "data"] +} diff --git a/gbfs-validator/versions/schemas/v3.0-RC/gbfs_versions.json b/gbfs-validator/versions/schemas/v3.0-RC/gbfs_versions.json new file mode 100644 index 0000000..bd1d323 --- /dev/null +++ b/gbfs-validator/versions/schemas/v3.0-RC/gbfs_versions.json @@ -0,0 +1,69 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": + "https://github.com/NABSA/gbfs/blob/v3.0-RC/gbfs.md#gbfs_versionsjson-added-in-v11", + "description": "Lists all feed endpoints published according to version sof the GBFS documentation. (added in v1.1)", + "type": "object", + "properties": { + "last_updated": { + "description": + "Last time the data in the feed was updated in POSIX time.", + "type": "integer", + "minimum": 1450155600 + }, + "ttl": { + "description": + "Number of seconds before the data in the feed will be updated again (0 if the data should always be refreshed).", + "type": "integer", + "minimum": 0 + }, + "version": { + "description": + "GBFS version number to which the feed conforms, according to the versioning framework.", + "type": "string", + "enum": [ + "3.0-RC", + "3.0" + ] + }, + "data": { + "description": "Response data in the form of name:value pairs.", + "type": "object", + "properties": { + "versions": { + "description": + "Contains one object, as defined below, for each of the available versions of a feed. The array must be sorted by increasing MAJOR and MINOR version number.", + "type": "array", + "items": { + "type": "object", + "properties": { + "version": { + "description": "The semantic version of the feed in the form X.Y", + "type": "string", + "enum": [ + "1.0", + "1.1", + "2.0", + "2.1", + "2.2", + "2.3", + "3.0-RC", + "3.0" + ] + }, + "url": { + "description": "URL of the corresponding gbfs.json endpoint", + "type": "string", + "format": "uri" + } + }, + "required": ["version", "url"] + } + } + }, + "required": ["versions"], + "additionalProperties": false + } + }, + "required": ["last_updated", "ttl", "version", "data"] +} diff --git a/gbfs-validator/versions/schemas/v3.0-RC/geofencing_zones.json b/gbfs-validator/versions/schemas/v3.0-RC/geofencing_zones.json new file mode 100644 index 0000000..681127b --- /dev/null +++ b/gbfs-validator/versions/schemas/v3.0-RC/geofencing_zones.json @@ -0,0 +1,204 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": + "https://github.com/NABSA/gbfs/blob/v3.0-RC/gbfs.md#geofencing_zonesjson", + "description": + "Describes geofencing zones and their associated rules and attributes (added in v2.1-RC).", + "type": "object", + "properties": { + "last_updated": { + "description": + "Last time the data in the feed was updated in POSIX time.", + "type": "integer", + "minimum": 1450155600 + }, + "ttl": { + "description": "Number of seconds before the data in the feed will be updated again (0 if the data should always be refreshed).", + "type": "integer", + "minimum": 0 + }, + "version": { + "description": "GBFS version number to which the feed conforms, according to the versioning framework.", + "type": "string", + "enum": [ + "3.0-RC", + "3.0" + ] + }, + "data": { + "description": "Array that contains geofencing information for the system.", + "type": "object", + "properties": { + "geofencing_zones": { + "type": "object", + "description": "Each geofenced zone and its associated rules and attributes is described as an object within the array of features.", + "properties": { + "type": { + "description": "FeatureCollection as per IETF RFC 7946.", + "type": "string", + "enum": ["FeatureCollection"] + }, + "features": { + "description": "Array of objects.", + "type": "array", + "items": { + "title": "GeoJSON Feature", + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["Feature"] + }, + "properties": { + "description": + "Describing travel allowances and limitations.", + "type": "object", + "properties": { + "name": { + "description": "Public name of the geofencing zone.", + "type": "array", + "items": { + "type": "object", + "properties": { + "text": { + "description": "The translated text.", + "type": "string" + }, + "language": { + "description": "IETF BCP 47 language code.", + "type": "string", + "pattern": "^[a-z]{2,3}(-[A-Z]{2})?$" + } + }, + "required": ["text", "language"] + } + }, + "start": { + "description": "Start time of the geofencing zone in POSIX time.", + "type": "integer", + "minimum": 1450155600 + }, + "end": { + "description": "End time of the geofencing zone in POSIX time.", + "type": "integer", + "minimum": 1450155600 + }, + "rules": { + "description": "Array of Rule objects defining restrictions that apply within the area of the polygon.", + "type": "array", + "items": { + "type": "object", + "properties": { + "vehicle_type_id": { + "type": "array", + "description": "Array of vehicle type IDs for which these restrictions apply.", + "items": { "type": "string" } + }, + "ride_start_allowed": { + "description": "Is the ride allowed to start in this zone?", + "type": "boolean" + }, + "ride_end_allowed": { + "description": "Is the ride allowed to end in this zone?", + "type": "boolean" + }, + "ride_through_allowed": { + "description": + "Is the ride allowed to travel through this zone?", + "type": "boolean" + }, + "maximum_speed_kph": { + "description": "What is the maximum speed allowed, in kilometers per hour?", + "type": "integer", + "minimum": 0 + }, + "station_parking": { + "description": "Vehicle MUST be parked at stations defined in station_information.json within this geofence zone", + "type": "boolean" + } + }, + "required": ["ride_start_allowed", "ride_end_allowed", "ride_through_allowed"] + } + } + } + }, + "geometry": { + "description": + "A polygon that describes where rides might not be able to start, end, go through, or have other limitations. Must follow the right-hand rule.", + "title": "GeoJSON MultiPolygon", + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["MultiPolygon"] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + } + } + }, + "required": ["type", "coordinates"] + } + }, + "required": ["type", "geometry", "properties"] + } + } + }, + "required": ["type", "features"] + }, + "global_rules": { + "description": "Array of Rule objects defining restrictions that apply globally in all areas as the default restrictions, except where overridden with an explicit geofencing zone.", + "type": "array", + "items": { + "type": "object", + "properties": { + "vehicle_type_id": { + "type": "array", + "description": "Array of vehicle type IDs for which these restrictions apply.", + "items": { "type": "string" } + }, + "ride_start_allowed": { + "description": "Is the ride allowed to start in this zone?", + "type": "boolean" + }, + "ride_end_allowed": { + "description": "Is the ride allowed to end in this zone?", + "type": "boolean" + }, + "ride_through_allowed": { + "description": + "Is the ride allowed to travel through this zone?", + "type": "boolean" + }, + "maximum_speed_kph": { + "description": "What is the maximum speed allowed, in kilometers per hour?", + "type": "integer", + "minimum": 0 + }, + "station_parking": { + "description": "Vehicle MUST be parked at stations defined in station_information.json within this geofence zone", + "type": "boolean" + } + }, + "required": ["ride_start_allowed", "ride_end_allowed", "ride_through_allowed"] + } + } + }, + "required": ["geofencing_zones", "global_rules"] + } + }, + "required": ["last_updated", "ttl", "version", "data"] +} diff --git a/gbfs-validator/versions/schemas/v3.0-RC/manifest.json b/gbfs-validator/versions/schemas/v3.0-RC/manifest.json new file mode 100644 index 0000000..8398b4a --- /dev/null +++ b/gbfs-validator/versions/schemas/v3.0-RC/manifest.json @@ -0,0 +1,84 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "https://github.com/NABSA/gbfs/blob/v3.0-RC/gbfs.md#manifestjson", + "description": "An index of gbfs.json URLs for each GBFS data set produced by a publisher. A single instance of this file should be published at a single stable URL, for example: https://example.com/gbfs/manifest.json.", + "type": "object", + "properties": { + "last_updated": { + "description": "Last time the data in the feed was updated in POSIX time.", + "type": "integer", + "minimum": 1450155600 + }, + "ttl": { + "description": "Number of seconds before the data in the feed will be updated again (0 if the data should always be refreshed).", + "type": "integer", + "minimum": 0 + }, + "version": { + "description": "GBFS version number to which the feed conforms, according to the versioning framework (added in v1.1).", + "type": "string", + "enum": [ + "3.0-RC", + "3.0" + ] + }, + "data": { + "type": "object", + "properties": { + "datasets": { + "description": "An array of objects, each containing the keys below.", + "type": "array", + "items": { + "type": "object", + "properties": { + "system_id": { + "description": "The system_id from system_information.json for the corresponding data set(s).", + "type": "string" + }, + "versions": { + "description": "Contains one object, as defined below, for each of the available versions of a feed. The array MUST be sorted by increasing MAJOR and MINOR version number.", + "type": "array", + "items": { + "type": "object", + "properties": { + "version": { + "description": "The semantic version of the feed in the form X.Y", + "type": "string", + "enum": [ + "1.0", + "1.1", + "2.0", + "2.1", + "2.2", + "2.3", + "3.0-RC", + "3.0" + ] + }, + "url": { + "description": "URL of the corresponding gbfs.json endpoint", + "type": "string", + "format": "uri" + } + }, + "required": [ + "version", + "url" + ] + } + } + }, + "required": [ + "system_id", + "versions" + ] + } + } + }, + "required": ["datasets"], + "additionalProperties": false + } + }, + "required": ["last_updated", "ttl", "version", "data"] +} + diff --git a/gbfs-validator/versions/schemas/v3.0-RC/station_information.json b/gbfs-validator/versions/schemas/v3.0-RC/station_information.json new file mode 100644 index 0000000..fa2f28a --- /dev/null +++ b/gbfs-validator/versions/schemas/v3.0-RC/station_information.json @@ -0,0 +1,275 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": + "https://github.com/NABSA/gbfs/blob/v3.0-RC/gbfs.md#station_informationjson", + "description": + "Details including system operator, system location, year implemented, URL, contact info, time zone.", + "type": "object", + "properties": { + "last_updated": { + "description": + "Last time the data in the feed was updated in POSIX time.", + "type": "integer", + "minimum": 1450155600 + }, + "ttl": { + "description": + "Number of seconds before the data in the feed will be updated again (0 if the data should always be refreshed).", + "type": "integer", + "minimum": 0 + }, + "version": { + "description": + "GBFS version number to which the feed conforms, according to the versioning framework (added in v1.1).", + "type": "string", + "enum": [ + "3.0-RC", + "3.0" + ] + }, + "data": { + "description": + "Array that contains one object per station as defined below.", + "type": "object", + "properties": { + "stations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "station_id": { + "description": "Identifier of a station.", + "type": "string" + }, + "name": { + "description": "Public name of the station.", + "type": "array", + "items": { + "type": "object", + "properties": { + "text": { + "description": "The translated text.", + "type": "string" + }, + "language": { + "description": "IETF BCP 47 language code.", + "type": "string", + "pattern": "^[a-z]{2,3}(-[A-Z]{2})?$" + } + }, + "required": ["text", "language"] + } + }, + "short_name": { + "description": "Short name or other type of identifier.", + "type": "array", + "items": { + "type": "object", + "properties": { + "text": { + "description": "The translated text.", + "type": "string" + }, + "language": { + "description": "IETF BCP 47 language code.", + "type": "string", + "pattern": "^[a-z]{2,3}(-[A-Z]{2})?$" + } + }, + "required": ["text", "language"] + } + }, + "lat": { + "description": "The latitude of the station.", + "type": "number", + "minimum": -90, + "maximum": 90 + }, + "lon": { + "description": "The longitude fo the station.", + "type": "number", + "minimum": -180, + "maximum": 180 + }, + "address": { + "description": "Address where station is located.", + "type": "string" + }, + "cross_street": { + "description": + "Cross street or landmark where the station is located.", + "type": "string" + }, + "region_id": { + "description": + "Identifier of the region where the station is located.", + "type": "string" + }, + "post_code": { + "description": "Postal code where station is located.", + "type": "string" + }, + "station_opening_hours": { + "description": "Hours of operation for the station in OSM opening_hours format.", + "type": "string" + }, + "rental_methods": { + "description": "Payment methods accepted at this station.", + "type": "array", + "items": { + "type": "string", + "enum": [ + "key", + "creditcard", + "paypass", + "applepay", + "androidpay", + "transitcard", + "accountnumber", + "phone" + ] + }, + "minItems": 1 + }, + "is_virtual_station": { + "description": + "Is this station a location with or without physical infrastructure? (added in v2.1-RC)", + "type": "boolean" + }, + "station_area": { + "description": + "A multipolygon that describes the area of a virtual station (added in v2.1-RC).", + "type": "object", + "required": ["type", "coordinates"], + "properties": { + "type": { + "type": "string", + "enum": ["MultiPolygon"] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + } + } + } + }, + "parking_type": { + "description": "Type of parking station. Added in v2.3", + "type": "string", + "enum": [ + "parking_lot", + "street_parking", + "underground_parking", + "sidewalk_parking", + "other" + ] + }, + "parking_hoop": { + "description": "Are parking hoops present at this station? Added in v2.3", + "type": "boolean" + }, + "contact_phone": { + "description": "Contact phone of the station. Added in v2.3", + "type": "string" + }, + "capacity": { + "description": + "Number of total docking points installed at this station, both available and unavailable.", + "type": "integer", + "minimum": 0 + }, + "vehicle_type_area_capacity": { + "description": "An object where each key is a vehicle_type_id and the value is a number presenting the total number of vehicles of this type that can park within the station_area (added in v2.1-RC).", + "type": "array", + "items": { + "type": "object", + "properties": { + "vehicle_type_id": { + "description": "A vehicle_type_id, as defined in vehicle_types.json, that may park at the virtual station.", + "type": "string" + }, + "count": { + "description": "A number representing the total number of vehicles of the corresponding vehicle_type_id type that can park within the virtual station.", + "type": "integer", + "minimum": 0 + } + }, + "required": ["vehicle_type_id","count"] + } + }, + "vehicle_type_dock_capacity": { + "description": "This field's value is an array of objects containing the keys vehicle_type_id and count defined below. These objects are used to model the total docking capacity of a station, both available and unavailable, for each type of vehicle defined in vehicle_types.json.", + "type": "array", + "items": { + "type": "object", + "properties": { + "vehicle_type_id": { + "description": "A vehicle_type_id, as defined in vehicle_types.json, that may dock at the station.", + "type": "string" + }, + "count": { + "description": "The total number of docks at the station, both available and unavailable, that may accept the corresponding vehicle type as defined by its vehicle_type_id.", + "type": "integer", + "minimum": 0 + } + }, + "required": ["vehicle_type_id","count"] + } + }, + "is_valet_station": { + "description": + "Are valet services provided at this station? (added in v2.1-RC)", + "type": "boolean" + }, + "is_charging_station": { + "description": + "Does the station support charging of electric vehicles? (added in v2.3-RC)", + "type": "boolean" + }, + "rental_uris": { + "description": + "Contains rental uris for Android, iOS, and web in the android, ios, and web fields (added in v1.1).", + "type": "object", + "properties": { + "android": { + "description": + "URI that can be passed to an Android app with an intent (added in v1.1).", + "type": "string", + "format": "uri" + }, + "ios": { + "description": + "URI that can be used on iOS to launch the rental app for this station (added in v1.1).", + "type": "string", + "format": "uri" + }, + "web": { + "description": + "URL that can be used by a web browser to show more information about renting a vehicle at this station (added in v1.1).", + "type": "string", + "format": "uri" + } + } + } + }, + "required": ["station_id", "name", "lat", "lon"] + } + } + }, + "required": ["stations"] + } + }, + "required": ["last_updated", "ttl", "version", "data"] +} diff --git a/gbfs-validator/versions/schemas/v3.0-RC/station_status.json b/gbfs-validator/versions/schemas/v3.0-RC/station_status.json new file mode 100644 index 0000000..6b5014b --- /dev/null +++ b/gbfs-validator/versions/schemas/v3.0-RC/station_status.json @@ -0,0 +1,149 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "https://github.com/NABSA/gbfs/blob/v3.0-RC/gbfs.md#station_statusjson", + "description": + "Describes the capacity and rental availability of the station", + "type": "object", + "properties": { + "last_updated": { + "description": + "Last time the data in the feed was updated in POSIX time.", + "type": "integer", + "minimum": 1450155600 + }, + "ttl": { + "description": + "Number of seconds before the data in the feed will be updated again (0 if the data should always be refreshed).", + "type": "integer", + "minimum": 0 + }, + "version": { + "description": + "GBFS version number to which the feed conforms, according to the versioning framework (added in v1.1).", + "type": "string", + "enum": [ + "3.0-RC", + "3.0" + ] + }, + "data": { + "description": + "Array that contains one object per station as defined below.", + "type": "object", + "properties": { + "stations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "station_id": { + "description": "Identifier of a station.", + "type": "string" + }, + "num_vehicles_available": { + "description": + "Number of vehicles of any type physically available for rental at the station.", + "type": "integer", + "minimum": 0 + }, + "vehicle_types_available": { + "description": + "Array of objects displaying the total number of each vehicle type at the station (added in v2.1-RC).", + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "properties": { + "vehicle_type_id": { + "description": + "The vehicle_type_id of vehicle at the station (added in v2.1-RC).", + "type": "string" + }, + "count": { + "description": + "A number representing the total amount of this vehicle type at the station (added in v2.1-RC).", + "type": "integer", + "minimum": 0 + } + }, + "required": ["vehicle_type_id", "count"] + } + }, + "num_vehicles_disabled": { + "description": + "Number of disabled vehicles of any type at the station.", + "type": "integer", + "minimum": 0 + }, + "num_docks_available": { + "description": + "Number of functional docks physically at the station.", + "type": "integer", + "minimum": 0 + }, + "num_docks_disabled": { + "description": + "Number of empty but disabled docks at the station.", + "type": "integer", + "minimum": 0 + }, + "is_installed": { + "description": "Is the station currently on the street?", + "type": "boolean" + }, + "is_renting": { + "description": "Is the station currently renting vehicles?", + "type": "boolean" + }, + "is_returning": { + "description": "Is the station accepting vehicle returns?", + "type": "boolean" + }, + "last_reported": { + "description": + "The last time this station reported its status to the operator's backend in POSIX time.", + "type": "integer", + "minimum": 1450155600 + }, + "vehicle_docks_available": { + "description": + "Object displaying available docks by vehicle type (added in v2.1-RC).", + "type": "array", + "items": { + "type": "object", + "properties": { + "vehicle_type_ids": { + "description": + "An array of strings where each string represents a vehicle_type_id that is able to use a particular type of dock at the station (added in v2.1-RC).", + "type": "array", + "items": { + "type": "string" + } + }, + "count": { + "description": + "A number representing the total number of available docks for the defined vehicle type (added in v2.1-RC).", + "type": "integer", + "minimum": 0 + } + }, + "required": ["vehicle_type_ids", "count"] + } + } + }, + "required": [ + "station_id", + "num_vehicles_available", + "is_installed", + "is_renting", + "is_returning", + "last_reported" + ] + } + } + }, + "required": ["stations"] + } + }, + "required": ["last_updated", "ttl", "version", "data"] +} diff --git a/gbfs-validator/versions/schemas/v3.0-RC/system_alerts.json b/gbfs-validator/versions/schemas/v3.0-RC/system_alerts.json new file mode 100644 index 0000000..01facd8 --- /dev/null +++ b/gbfs-validator/versions/schemas/v3.0-RC/system_alerts.json @@ -0,0 +1,164 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "https://github.com/NABSA/gbfs/blob/v3.0-RC/gbfs.md#system_alertsjson", + "description": "Describes ad-hoc changes to the system.", + "type": "object", + "properties": { + "last_updated": { + "description": + "Last time the data in the feed was updated in POSIX time.", + "type": "integer", + "minimum": 1450155600 + }, + "ttl": { + "description": + "Number of seconds before the data in the feed will be updated again (0 if the data should always be refreshed).", + "type": "integer", + "minimum": 0 + }, + "version": { + "description": + "GBFS version number to which the feed conforms, according to the versioning framework (added in v1.1).", + "type": "string", + "enum": [ + "3.0-RC", + "3.0" + ] + }, + "data": { + "description": "Array that contains ad-hoc alerts for the system.", + "type": "object", + "properties": { + "alerts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "alert_id": { + "description": "Identifier for this alert.", + "type": "string" + }, + "type": { + "description": "Type of alert.", + "type": "string", + "enum": [ + "system_closure", + "station_closure", + "station_move", + "other" + ] + }, + "times": { + "description": + "Array of objects indicating when the alert is in effect.", + "type": "array", + "items": { + "type": "object", + "properties": { + "start": { + "description": "Start time of the alert.", + "type": "integer", + "minimum": 1450155600 + }, + "end": { + "description": "End time of the alert.", + "type": "integer", + "minimum": 1450155600 + } + } + }, + "additionalItems": false, + "required": ["start"] + }, + "station_ids": { + "description": + "Array of identifiers of the stations for which this alert applies.", + "type": "array", + "items": { + "type": "string" + } + }, + "region_ids": { + "description": + "Array of identifiers of the regions for which this alert applies.", + "type": "array", + "items": { + "type": "string" + } + }, + "url": { + "description": + "URL where the customer can learn more information about this alert.", + "type": "array", + "items": { + "type": "object", + "properties": { + "text": { + "description": "The translated text.", + "type": "string", + "format": "uri" + }, + "language": { + "description": "IETF BCP 47 language code.", + "type": "string", + "pattern": "^[a-z]{2,3}(-[A-Z]{2})?$" + } + }, + "required": ["text", "language"] + } + }, + "summary": { + "description": + "A short summary of this alert to be displayed to the customer.", + "type": "array", + "items": { + "type": "object", + "properties": { + "text": { + "description": "The translated text.", + "type": "string" + }, + "language": { + "description": "IETF BCP 47 language code.", + "type": "string", + "pattern": "^[a-z]{2,3}(-[A-Z]{2})?$" + } + }, + "required": ["text", "language"] + } + }, + "description": { + "description": "Detailed description of the alert.", + "type": "array", + "items": { + "type": "object", + "properties": { + "text": { + "description": "The translated text.", + "type": "string" + }, + "language": { + "description": "IETF BCP 47 language code.", + "type": "string", + "pattern": "^[a-z]{2,3}(-[A-Z]{2})?$" + } + }, + "required": ["text", "language"] + } + }, + "last_updated": { + "description": + "Indicates the last time the info for the alert was updated.", + "type": "number", + "minimum": 1450155600 + } + }, + "required": ["alert_id", "type", "summary"] + } + } + }, + "required": ["alerts"] + } + }, + "required": ["last_updated", "ttl", "version", "data"] +} diff --git a/gbfs-validator/versions/schemas/v3.0-RC/system_information.json b/gbfs-validator/versions/schemas/v3.0-RC/system_information.json new file mode 100644 index 0000000..035462f --- /dev/null +++ b/gbfs-validator/versions/schemas/v3.0-RC/system_information.json @@ -0,0 +1,1229 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": + "https://github.com/NABSA/gbfs/blob/v3.0-RC/gbfs.md#system_informationjson", + "description": + "Details including system operator, system location, year implemented, URL, contact info, time zone.", + "type": "object", + "properties": { + "last_updated": { + "description": + "Last time the data in the feed was updated in POSIX time.", + "type": "integer", + "minimum": 1450155600 + }, + "ttl": { + "description": + "Number of seconds before the data in the feed will be updated again (0 if the data should always be refreshed).", + "type": "integer", + "minimum": 0 + }, + "version": { + "description": + "GBFS version number to which the feed conforms, according to the versioning framework (added in v1.1).", + "type": "string", + "enum": [ + "3.0-RC", + "3.0" + ] + }, + "data": { + "description": "Response data in the form of name:value pairs.", + "type": "object", + "properties": { + "system_id": { + "description": + "Identifier for this vehicle share system. This should be globally unique (even between different systems).", + "type": "string" + }, + "languages": { + "description": + "List of languages used in translated strings. Each element in the list must be of type Language.", + "type": "array", + "items": { + "description": "IETF BCP 47 language code.", + "type": "string", + "pattern": "^[a-z]{2,3}(-[A-Z]{2})?$" + } + }, + "name": { + "description": "Name of the system to be displayed to customers. An array with one object per supported language with the following keys:", + "type": "array", + "items": { + "type": "object", + "properties": { + "text": { + "description": "The translated text.", + "type": "string" + }, + "language": { + "description": "IETF BCP 47 language code.", + "type": "string", + "pattern": "^[a-z]{2,3}(-[A-Z]{2})?$" + } + }, + "required": ["text", "language"] + } + }, + "opening_hours": { + "description": "Hours and dates of operation for the system in OSM opening_hours format. (added in v3.0-RC)", + "type": "string" + }, + "short_name": { + "description": "Abbreviation for a system. An array with one object per supported language with the following keys:", + "type": "array", + "items": { + "type": "object", + "properties": { + "text": { + "description": "The translated text.", + "type": "string" + }, + "language": { + "description": "IETF BCP 47 language code.", + "type": "string", + "pattern": "^[a-z]{2,3}(-[A-Z]{2})?$" + } + }, + "required": ["text", "language"] + } + }, + "operator": { + "description": "Name of the system operator. An array with one object per supported language with the following keys:", + "type": "array", + "items": { + "type": "object", + "properties": { + "text": { + "description": "The translated text.", + "type": "string" + }, + "language": { + "description": "IETF BCP 47 language code.", + "type": "string", + "pattern": "^[a-z]{2,3}(-[A-Z]{2})?$" + } + }, + "required": ["text", "language"] + } + }, + "url": { + "description": "The URL of the vehicle share system.", + "type": "string", + "format": "uri" + }, + "purchase_url": { + "description": "URL where a customer can purchase a membership.", + "type": "string", + "format": "uri" + }, + "start_date": { + "description": "Date that the system began operations.", + "type": "string", + "format": "date" + }, + "phone_number": { + "description": + "This OPTIONAL field SHOULD contain a single voice telephone number for the specified system’s customer service department. MUST be in E.164 format as defined in Field Types.", + "type": "string", + "pattern": "^\\+[1-9]\\d{1,14}$" + }, + "email": { + "description": + "Email address actively monitored by the operator's customer service department.", + "type": "string", + "format": "email" + }, + "feed_contact_email": { + "description": + "A single contact email address for consumers of this feed to report technical issues (added in v1.1).", + "type": "string", + "format": "email" + }, + "manifest_url":{ + "description": "REQUIRED if the producer publishes datasets for more than one system geography, for example Berlin and Paris. A fully qualified URL pointing to the manifest.json file for the publisher.", + "type": "string", + "format": "uri" + }, + "timezone": { + "description": "The time zone where the system is located.", + "type": "string", + "enum": [ + "Africa/Abidjan", + "Africa/Algiers", + "Africa/Bissau", + "Africa/Cairo", + "Africa/Casablanca", + "Africa/Ceuta", + "Africa/El_Aaiun", + "Africa/Johannesburg", + "Africa/Juba", + "Africa/Khartoum", + "Africa/Lagos", + "Africa/Maputo", + "Africa/Monrovia", + "Africa/Nairobi", + "Africa/Ndjamena", + "Africa/Sao_Tome", + "Africa/Tripoli", + "Africa/Tunis", + "Africa/Windhoek", + "America/Adak", + "America/Anchorage", + "America/Araguaina", + "America/Argentina/Buenos_Aires", + "America/Argentina/Catamarca", + "America/Argentina/Cordoba", + "America/Argentina/Jujuy", + "America/Argentina/La_Rioja", + "America/Argentina/Mendoza", + "America/Argentina/Rio_Gallegos", + "America/Argentina/Salta", + "America/Argentina/San_Juan", + "America/Argentina/San_Luis", + "America/Argentina/Tucuman", + "America/Argentina/Ushuaia", + "America/Asuncion", + "America/Bahia", + "America/Bahia_Banderas", + "America/Barbados", + "America/Belem", + "America/Belize", + "America/Boa_Vista", + "America/Bogota", + "America/Boise", + "America/Cambridge_Bay", + "America/Campo_Grande", + "America/Cancun", + "America/Caracas", + "America/Cayenne", + "America/Chicago", + "America/Chihuahua", + "America/Costa_Rica", + "America/Cuiaba", + "America/Danmarkshavn", + "America/Dawson", + "America/Dawson_Creek", + "America/Denver", + "America/Detroit", + "America/Edmonton", + "America/Eirunepe", + "America/El_Salvador", + "America/Fort_Nelson", + "America/Fortaleza", + "America/Glace_Bay", + "America/Goose_Bay", + "America/Grand_Turk", + "America/Guatemala", + "America/Guayaquil", + "America/Guyana", + "America/Halifax", + "America/Havana", + "America/Hermosillo", + "America/Indiana/Indianapolis", + "America/Indiana/Knox", + "America/Indiana/Marengo", + "America/Indiana/Petersburg", + "America/Indiana/Tell_City", + "America/Indiana/Vevay", + "America/Indiana/Vincennes", + "America/Indiana/Winamac", + "America/Inuvik", + "America/Iqaluit", + "America/Jamaica", + "America/Juneau", + "America/Kentucky/Louisville", + "America/Kentucky/Monticello", + "America/La_Paz", + "America/Lima", + "America/Los_Angeles", + "America/Maceio", + "America/Managua", + "America/Manaus", + "America/Martinique", + "America/Matamoros", + "America/Mazatlan", + "America/Menominee", + "America/Merida", + "America/Metlakatla", + "America/Mexico_City", + "America/Miquelon", + "America/Moncton", + "America/Monterrey", + "America/Montevideo", + "America/New_York", + "America/Nipigon", + "America/Nome", + "America/Noronha", + "America/North_Dakota/Beulah", + "America/North_Dakota/Center", + "America/North_Dakota/New_Salem", + "America/Nuuk", + "America/Ojinaga", + "America/Panama", + "America/Pangnirtung", + "America/Paramaribo", + "America/Phoenix", + "America/Port-au-Prince", + "America/Porto_Velho", + "America/Puerto_Rico", + "America/Punta_Arenas", + "America/Rainy_River", + "America/Rankin_Inlet", + "America/Recife", + "America/Regina", + "America/Resolute", + "America/Rio_Branco", + "America/Santarem", + "America/Santiago", + "America/Santo_Domingo", + "America/Sao_Paulo", + "America/Scoresbysund", + "America/Sitka", + "America/St_Johns", + "America/Swift_Current", + "America/Tegucigalpa", + "America/Thule", + "America/Thunder_Bay", + "America/Tijuana", + "America/Toronto", + "America/Vancouver", + "America/Whitehorse", + "America/Winnipeg", + "America/Yakutat", + "America/Yellowknife", + "Antarctica/Casey", + "Antarctica/Davis", + "Antarctica/Macquarie", + "Antarctica/Mawson", + "Antarctica/Palmer", + "Antarctica/Rothera", + "Antarctica/Troll", + "Antarctica/Vostok", + "Asia/Almaty", + "Asia/Amman", + "Asia/Anadyr", + "Asia/Aqtau", + "Asia/Aqtobe", + "Asia/Ashgabat", + "Asia/Atyrau", + "Asia/Baghdad", + "Asia/Baku", + "Asia/Bangkok", + "Asia/Barnaul", + "Asia/Beirut", + "Asia/Bishkek", + "Asia/Brunei", + "Asia/Chita", + "Asia/Choibalsan", + "Asia/Colombo", + "Asia/Damascus", + "Asia/Dhaka", + "Asia/Dili", + "Asia/Dubai", + "Asia/Dushanbe", + "Asia/Famagusta", + "Asia/Gaza", + "Asia/Hebron", + "Asia/Ho_Chi_Minh", + "Asia/Hong_Kong", + "Asia/Hovd", + "Asia/Irkutsk", + "Asia/Jakarta", + "Asia/Jayapura", + "Asia/Jerusalem", + "Asia/Kabul", + "Asia/Kamchatka", + "Asia/Karachi", + "Asia/Kathmandu", + "Asia/Khandyga", + "Asia/Kolkata", + "Asia/Krasnoyarsk", + "Asia/Kuala_Lumpur", + "Asia/Kuching", + "Asia/Macau", + "Asia/Magadan", + "Asia/Makassar", + "Asia/Manila", + "Asia/Nicosia", + "Asia/Novokuznetsk", + "Asia/Novosibirsk", + "Asia/Omsk", + "Asia/Oral", + "Asia/Pontianak", + "Asia/Pyongyang", + "Asia/Qatar", + "Asia/Qostanay", + "Asia/Qyzylorda", + "Asia/Riyadh", + "Asia/Sakhalin", + "Asia/Samarkand", + "Asia/Seoul", + "Asia/Shanghai", + "Asia/Singapore", + "Asia/Srednekolymsk", + "Asia/Taipei", + "Asia/Tashkent", + "Asia/Tbilisi", + "Asia/Tehran", + "Asia/Thimphu", + "Asia/Tokyo", + "Asia/Tomsk", + "Asia/Ulaanbaatar", + "Asia/Urumqi", + "Asia/Ust-Nera", + "Asia/Vladivostok", + "Asia/Yakutsk", + "Asia/Yangon", + "Asia/Yekaterinburg", + "Asia/Yerevan", + "Atlantic/Azores", + "Atlantic/Bermuda", + "Atlantic/Canary", + "Atlantic/Cape_Verde", + "Atlantic/Faroe", + "Atlantic/Madeira", + "Atlantic/Reykjavik", + "Atlantic/South_Georgia", + "Atlantic/Stanley", + "Australia/Adelaide", + "Australia/Brisbane", + "Australia/Broken_Hill", + "Australia/Darwin", + "Australia/Eucla", + "Australia/Hobart", + "Australia/Lindeman", + "Australia/Lord_Howe", + "Australia/Melbourne", + "Australia/Perth", + "Australia/Sydney", + "CET", + "CST6CDT", + "EET", + "EST", + "EST5EDT", + "Etc/GMT", + "Etc/GMT-1", + "Etc/GMT-10", + "Etc/GMT-11", + "Etc/GMT-12", + "Etc/GMT-13", + "Etc/GMT-14", + "Etc/GMT-2", + "Etc/GMT-3", + "Etc/GMT-4", + "Etc/GMT-5", + "Etc/GMT-6", + "Etc/GMT-7", + "Etc/GMT-8", + "Etc/GMT-9", + "Etc/GMT+1", + "Etc/GMT+10", + "Etc/GMT+11", + "Etc/GMT+12", + "Etc/GMT+2", + "Etc/GMT+3", + "Etc/GMT+4", + "Etc/GMT+5", + "Etc/GMT+6", + "Etc/GMT+7", + "Etc/GMT+8", + "Etc/GMT+9", + "Etc/UTC", + "Europe/Amsterdam", + "Europe/Andorra", + "Europe/Astrakhan", + "Europe/Athens", + "Europe/Belgrade", + "Europe/Berlin", + "Europe/Brussels", + "Europe/Bucharest", + "Europe/Budapest", + "Europe/Chisinau", + "Europe/Copenhagen", + "Europe/Dublin", + "Europe/Gibraltar", + "Europe/Helsinki", + "Europe/Istanbul", + "Europe/Kaliningrad", + "Europe/Kiev", + "Europe/Kirov", + "Europe/Lisbon", + "Europe/London", + "Europe/Luxembourg", + "Europe/Madrid", + "Europe/Malta", + "Europe/Minsk", + "Europe/Monaco", + "Europe/Moscow", + "Europe/Oslo", + "Europe/Paris", + "Europe/Prague", + "Europe/Riga", + "Europe/Rome", + "Europe/Samara", + "Europe/Saratov", + "Europe/Simferopol", + "Europe/Sofia", + "Europe/Stockholm", + "Europe/Tallinn", + "Europe/Tirane", + "Europe/Ulyanovsk", + "Europe/Uzhgorod", + "Europe/Vienna", + "Europe/Vilnius", + "Europe/Volgograd", + "Europe/Warsaw", + "Europe/Zaporozhye", + "Europe/Zurich", + "HST", + "Indian/Chagos", + "Indian/Christmas", + "Indian/Cocos", + "Indian/Kerguelen", + "Indian/Mahe", + "Indian/Maldives", + "Indian/Mauritius", + "Indian/Reunion", + "MET", + "MST", + "MST7MDT", + "Pacific/Apia", + "Pacific/Auckland", + "Pacific/Bougainville", + "Pacific/Chatham", + "Pacific/Chuuk", + "Pacific/Easter", + "Pacific/Efate", + "Pacific/Fakaofo", + "Pacific/Fiji", + "Pacific/Funafuti", + "Pacific/Galapagos", + "Pacific/Gambier", + "Pacific/Guadalcanal", + "Pacific/Guam", + "Pacific/Honolulu", + "Pacific/Kanton", + "Pacific/Kiritimati", + "Pacific/Kosrae", + "Pacific/Kwajalein", + "Pacific/Majuro", + "Pacific/Marquesas", + "Pacific/Nauru", + "Pacific/Niue", + "Pacific/Norfolk", + "Pacific/Noumea", + "Pacific/Pago_Pago", + "Pacific/Palau", + "Pacific/Pitcairn", + "Pacific/Pohnpei", + "Pacific/Port_Moresby", + "Pacific/Rarotonga", + "Pacific/Tahiti", + "Pacific/Tarawa", + "Pacific/Tongatapu", + "Pacific/Wake", + "Pacific/Wallis", + "PST8PDT", + "WET" + ] + }, + "license_id": { + "description": "REQUIRED if the dataset is provided under a standard license. An identifier for a standard license from the SPDX License List. Provide license_id rather than license_url if the license is included in the SPDX License List. See the GBFS wiki for a comparison of a subset of standard licenses. If the license_id and license_url fields are blank or omitted, this indicates that the feed is provided under the Creative Commons Universal Public Domain Dedication.", + "type": "string", + "enum": [ + "0BSD", + "AAL", + "Abstyles", + "AdaCore-doc", + "Adobe-2006", + "Adobe-Glyph", + "ADSL", + "AFL-1.1", + "AFL-1.2", + "AFL-2.0", + "AFL-2.1", + "AFL-3.0", + "Afmparse", + "AGPL-1.0-only", + "AGPL-1.0-or-later", + "AGPL-3.0-only", + "AGPL-3.0-or-later", + "Aladdin", + "AMDPLPA", + "AML", + "AMPAS", + "ANTLR-PD", + "ANTLR-PD-fallback", + "Apache-1.0", + "Apache-1.1", + "Apache-2.0", + "APAFML", + "APL-1.0", + "App-s2p", + "APSL-1.0", + "APSL-1.1", + "APSL-1.2", + "APSL-2.0", + "Arphic-1999", + "Artistic-1.0", + "Artistic-1.0-cl8", + "Artistic-1.0-Perl", + "Artistic-2.0", + "Baekmuk", + "Bahyph", + "Barr", + "Beerware", + "Bitstream-Charter", + "Bitstream-Vera", + "BitTorrent-1.0", + "BitTorrent-1.1", + "blessing", + "BlueOak-1.0.0", + "Borceux", + "Brian-Gladman-3-Clause", + "BSD-1-Clause", + "BSD-2-Clause", + "BSD-2-Clause-Patent", + "BSD-2-Clause-Views", + "BSD-3-Clause", + "BSD-3-Clause-Attribution", + "BSD-3-Clause-Clear", + "BSD-3-Clause-LBNL", + "BSD-3-Clause-Modification", + "BSD-3-Clause-No-Military-License", + "BSD-3-Clause-No-Nuclear-License", + "BSD-3-Clause-No-Nuclear-License-2014", + "BSD-3-Clause-No-Nuclear-Warranty", + "BSD-3-Clause-Open-MPI", + "BSD-4-Clause", + "BSD-4-Clause-Shortened", + "BSD-4-Clause-UC", + "BSD-4.3RENO", + "BSD-4.3TAHOE", + "BSD-Advertising-Acknowledgement", + "BSD-Attribution-HPND-disclaimer", + "BSD-Protection", + "BSD-Source-Code", + "BSL-1.0", + "BUSL-1.1", + "bzip2-1.0.6", + "C-UDA-1.0", + "CAL-1.0", + "CAL-1.0-Combined-Work-Exception", + "Caldera", + "CATOSL-1.1", + "CC-BY-1.0", + "CC-BY-2.0", + "CC-BY-2.5", + "CC-BY-2.5-AU", + "CC-BY-3.0", + "CC-BY-3.0-AT", + "CC-BY-3.0-DE", + "CC-BY-3.0-IGO", + "CC-BY-3.0-NL", + "CC-BY-3.0-US", + "CC-BY-4.0", + "CC-BY-NC-1.0", + "CC-BY-NC-2.0", + "CC-BY-NC-2.5", + "CC-BY-NC-3.0", + "CC-BY-NC-3.0-DE", + "CC-BY-NC-4.0", + "CC-BY-NC-ND-1.0", + "CC-BY-NC-ND-2.0", + "CC-BY-NC-ND-2.5", + "CC-BY-NC-ND-3.0", + "CC-BY-NC-ND-3.0-DE", + "CC-BY-NC-ND-3.0-IGO", + "CC-BY-NC-ND-4.0", + "CC-BY-NC-SA-1.0", + "CC-BY-NC-SA-2.0", + "CC-BY-NC-SA-2.0-DE", + "CC-BY-NC-SA-2.0-FR", + "CC-BY-NC-SA-2.0-UK", + "CC-BY-NC-SA-2.5", + "CC-BY-NC-SA-3.0", + "CC-BY-NC-SA-3.0-DE", + "CC-BY-NC-SA-3.0-IGO", + "CC-BY-NC-SA-4.0", + "CC-BY-ND-1.0", + "CC-BY-ND-2.0", + "CC-BY-ND-2.5", + "CC-BY-ND-3.0", + "CC-BY-ND-3.0-DE", + "CC-BY-ND-4.0", + "CC-BY-SA-1.0", + "CC-BY-SA-2.0", + "CC-BY-SA-2.0-UK", + "CC-BY-SA-2.1-JP", + "CC-BY-SA-2.5", + "CC-BY-SA-3.0", + "CC-BY-SA-3.0-AT", + "CC-BY-SA-3.0-DE", + "CC-BY-SA-4.0", + "CC-PDDC", + "CC0-1.0", + "CDDL-1.0", + "CDDL-1.1", + "CDL-1.0", + "CDLA-Permissive-1.0", + "CDLA-Permissive-2.0", + "CDLA-Sharing-1.0", + "CECILL-1.0", + "CECILL-1.1", + "CECILL-2.0", + "CECILL-2.1", + "CECILL-B", + "CECILL-C", + "CERN-OHL-1.1", + "CERN-OHL-1.2", + "CERN-OHL-P-2.0", + "CERN-OHL-S-2.0", + "CERN-OHL-W-2.0", + "CFITSIO", + "checkmk", + "ClArtistic", + "Clips", + "CMU-Mach", + "CNRI-Jython", + "CNRI-Python", + "CNRI-Python-GPL-Compatible", + "COIL-1.0", + "Community-Spec-1.0", + "Condor-1.1", + "copyleft-next-0.3.0", + "copyleft-next-0.3.1", + "Cornell-Lossless-JPEG", + "CPAL-1.0", + "CPL-1.0", + "CPOL-1.02", + "Crossword", + "CrystalStacker", + "CUA-OPL-1.0", + "Cube", + "curl", + "D-FSL-1.0", + "diffmark", + "DL-DE-BY-2.0", + "DOC", + "Dotseqn", + "DRL-1.0", + "DSDP", + "dvipdfm", + "ECL-1.0", + "ECL-2.0", + "EFL-1.0", + "EFL-2.0", + "eGenix", + "Elastic-2.0", + "Entessa", + "EPICS", + "EPL-1.0", + "EPL-2.0", + "ErlPL-1.1", + "etalab-2.0", + "EUDatagrid", + "EUPL-1.0", + "EUPL-1.1", + "EUPL-1.2", + "Eurosym", + "Fair", + "FDK-AAC", + "Frameworx-1.0", + "FreeBSD-DOC", + "FreeImage", + "FSFAP", + "FSFUL", + "FSFULLR", + "FSFULLRWD", + "FTL", + "GD", + "GFDL-1.1-invariants-only", + "GFDL-1.1-invariants-or-later", + "GFDL-1.1-no-invariants-only", + "GFDL-1.1-no-invariants-or-later", + "GFDL-1.1-only", + "GFDL-1.1-or-later", + "GFDL-1.2-invariants-only", + "GFDL-1.2-invariants-or-later", + "GFDL-1.2-no-invariants-only", + "GFDL-1.2-no-invariants-or-later", + "GFDL-1.2-only", + "GFDL-1.2-or-later", + "GFDL-1.3-invariants-only", + "GFDL-1.3-invariants-or-later", + "GFDL-1.3-no-invariants-only", + "GFDL-1.3-no-invariants-or-later", + "GFDL-1.3-only", + "GFDL-1.3-or-later", + "Giftware", + "GL2PS", + "Glide", + "Glulxe", + "GLWTPL", + "gnuplot", + "GPL-1.0-only", + "GPL-1.0-or-later", + "GPL-2.0-only", + "GPL-2.0-or-later", + "GPL-3.0-only", + "GPL-3.0-or-later", + "Graphics-Gems", + "gSOAP-1.3b", + "HaskellReport", + "Hippocratic-2.1", + "HP-1986", + "HPND", + "HPND-export-US", + "HPND-Markus-Kuhn", + "HPND-sell-variant", + "HPND-sell-variant-MIT-disclaimer", + "HTMLTIDY", + "IBM-pibs", + "ICU", + "IEC-Code-Components-EULA", + "IJG", + "IJG-short", + "ImageMagick", + "iMatix", + "Imlib2", + "Info-ZIP", + "Intel", + "Intel-ACPI", + "Interbase-1.0", + "IPA", + "IPL-1.0", + "ISC", + "Jam", + "JasPer-2.0", + "JPL-image", + "JPNIC", + "JSON", + "Kazlib", + "Knuth-CTAN", + "LAL-1.2", + "LAL-1.3", + "Latex2e", + "Leptonica", + "LGPL-2.0-only", + "LGPL-2.0-or-later", + "LGPL-2.1-only", + "LGPL-2.1-or-later", + "LGPL-3.0-only", + "LGPL-3.0-or-later", + "LGPLLR", + "Libpng", + "libpng-2.0", + "libselinux-1.0", + "libtiff", + "libutil-David-Nugent", + "LiLiQ-P-1.1", + "LiLiQ-R-1.1", + "LiLiQ-Rplus-1.1", + "Linux-man-pages-copyleft", + "Linux-OpenIB", + "LOOP", + "LPL-1.0", + "LPL-1.02", + "LPPL-1.0", + "LPPL-1.1", + "LPPL-1.2", + "LPPL-1.3a", + "LPPL-1.3c", + "LZMA-SDK-9.11-to-9.20", + "LZMA-SDK-9.22", + "MakeIndex", + "Martin-Birgmeier", + "Minpack", + "MirOS", + "MIT", + "MIT-0", + "MIT-advertising", + "MIT-CMU", + "MIT-enna", + "MIT-feh", + "MIT-Modern-Variant", + "MIT-open-group", + "MIT-Wu", + "MITNFA", + "Motosoto", + "mpi-permissive", + "mpich2", + "MPL-1.0", + "MPL-1.1", + "MPL-2.0", + "MPL-2.0-no-copyleft-exception", + "mplus", + "MS-LPL", + "MS-PL", + "MS-RL", + "MTLL", + "MulanPSL-1.0", + "MulanPSL-2.0", + "Multics", + "Mup", + "NAIST-2003", + "NASA-1.3", + "Naumen", + "NBPL-1.0", + "NCGL-UK-2.0", + "NCSA", + "Net-SNMP", + "NetCDF", + "Newsletr", + "NGPL", + "NICTA-1.0", + "NIST-PD", + "NIST-PD-fallback", + "NLOD-1.0", + "NLOD-2.0", + "NLPL", + "Nokia", + "NOSL", + "Noweb", + "NPL-1.0", + "NPL-1.1", + "NPOSL-3.0", + "NRL", + "NTP", + "NTP-0", + "O-UDA-1.0", + "OCCT-PL", + "OCLC-2.0", + "ODbL-1.0", + "ODC-By-1.0", + "OFFIS", + "OFL-1.0", + "OFL-1.0-no-RFN", + "OFL-1.0-RFN", + "OFL-1.1", + "OFL-1.1-no-RFN", + "OFL-1.1-RFN", + "OGC-1.0", + "OGDL-Taiwan-1.0", + "OGL-Canada-2.0", + "OGL-UK-1.0", + "OGL-UK-2.0", + "OGL-UK-3.0", + "OGTSL", + "OLDAP-1.1", + "OLDAP-1.2", + "OLDAP-1.3", + "OLDAP-1.4", + "OLDAP-2.0", + "OLDAP-2.0.1", + "OLDAP-2.1", + "OLDAP-2.2", + "OLDAP-2.2.1", + "OLDAP-2.2.2", + "OLDAP-2.3", + "OLDAP-2.4", + "OLDAP-2.5", + "OLDAP-2.6", + "OLDAP-2.7", + "OLDAP-2.8", + "OML", + "OpenPBS-2.3", + "OpenSSL", + "OPL-1.0", + "OPUBL-1.0", + "OSET-PL-2.1", + "OSL-1.0", + "OSL-1.1", + "OSL-2.0", + "OSL-2.1", + "OSL-3.0", + "Parity-6.0.0", + "Parity-7.0.0", + "PDDL-1.0", + "PHP-3.0", + "PHP-3.01", + "Plexus", + "PolyForm-Noncommercial-1.0.0", + "PolyForm-Small-Business-1.0.0", + "PostgreSQL", + "PSF-2.0", + "psfrag", + "psutils", + "Python-2.0", + "Python-2.0.1", + "Qhull", + "QPL-1.0", + "QPL-1.0-INRIA-2004", + "Rdisc", + "RHeCos-1.1", + "RPL-1.1", + "RPL-1.5", + "RPSL-1.0", + "RSA-MD", + "RSCPL", + "Ruby", + "SAX-PD", + "Saxpath", + "SCEA", + "SchemeReport", + "Sendmail", + "Sendmail-8.23", + "SGI-B-1.0", + "SGI-B-1.1", + "SGI-B-2.0", + "SHL-0.5", + "SHL-0.51", + "SimPL-2.0", + "SISSL", + "SISSL-1.2", + "Sleepycat", + "SMLNJ", + "SMPPL", + "SNIA", + "snprintf", + "Spencer-86", + "Spencer-94", + "Spencer-99", + "SPL-1.0", + "SSH-OpenSSH", + "SSH-short", + "SSPL-1.0", + "SugarCRM-1.1.3", + "SunPro", + "SWL", + "Symlinks", + "TAPR-OHL-1.0", + "TCL", + "TCP-wrappers", + "TMate", + "TORQUE-1.1", + "TOSL", + "TPDL", + "TPL-1.0", + "TTWL", + "TU-Berlin-1.0", + "TU-Berlin-2.0", + "UCAR", + "UCL-1.0", + "Unicode-DFS-2015", + "Unicode-DFS-2016", + "Unicode-TOU", + "Unlicense", + "UPL-1.0", + "Vim", + "VOSTROM", + "VSL-1.0", + "W3C", + "W3C-19980720", + "W3C-20150513", + "w3m", + "Watcom-1.0", + "Wsuipa", + "WTFPL", + "X11", + "X11-distribute-modifications-variant", + "Xerox", + "XFree86-1.1", + "xinetd", + "xlock", + "Xnet", + "xpp", + "XSkat", + "YPL-1.0", + "YPL-1.1", + "Zed", + "Zend-2.0", + "Zimbra-1.3", + "Zimbra-1.4", + "Zlib", + "zlib-acknowledgement", + "ZPL-1.1", + "ZPL-2.0", + "ZPL-2.1" + ] + }, + "license_url": { + "description": + "A fully qualified URL of a page that defines the license terms for the GBFS data for this system.", + "type": "string", + "format": "uri" + }, + "attribution_organization_name": { + "description": "If the feed license requires attribution, name of the organization to which attribution should be provided. An array with one object per supported language with the following keys:", + "type": "array", + "items": { + "type": "object", + "properties": { + "text": { + "description": "The translated text.", + "type": "string" + }, + "language": { + "description": "IETF BCP 47 language code.", + "type": "string", + "pattern": "^[a-z]{2,3}(-[A-Z]{2})?$" + } + }, + "required": ["text", "language"] + } + }, + "attribution_url": { + "description": "URL of the organization to which attribution should be provided.", + "type": "string", + "format": "uri" + }, + "brand_assets": { + "description": + "An object where each key defines one of the items listed below (added in v2.3-RC).", + "type": "object", + "properties": { + "brand_last_modified": { + "description": "Date that indicates the last time any included brand assets were updated (added in v2.3-RC).", + "type": "string", + "format": "date" + }, + "brand_terms_url": { + "description": "A fully qualified URL pointing to the location of a page that defines the license terms of brand icons, colors or other trademark information (added in v2.3-RC).", + "type": "string", + "format": "uri" + }, + "brand_image_url": { + "description": "A fully qualified URL pointing to the location of a graphic file representing the brand for the service (added in v2.3-RC). ", + "type": "string", + "format": "uri" + }, + "brand_image_url_dark": { + "description": "A fully qualified URL pointing to the location of a graphic file representing the brand for the service for use in dark mode (added in v2.3-RC).", + "type": "string", + "format": "uri" + }, + "color": { + "description": "Color used to represent the brand for the service (added in v2.3-RC)", + "type": "string", + "pattern": "^#([a-fA-F0-9]{6})$" + } + }, + "required": ["brand_last_modified", "brand_image_url"] + }, + "terms_url": { + "description": + "A fully qualified URL pointing to the terms of service (also often called \"terms of use\" or \"terms and conditions\") for the service. An array with one object per supported language with the following keys:", + "type": "array", + "items": { + "type": "object", + "properties": { + "text": { + "description": "The translated text.", + "type": "string", + "format": "uri" + }, + "language": { + "description": "IETF BCP 47 language code.", + "type": "string", + "pattern": "^[a-z]{2,3}(-[A-Z]{2})?$" + } + }, + "required": ["text", "language"] + } + }, + "terms_last_updated": { + "description": + "The date that the terms of service provided at terms_url were last updated (added in v2.3-RC)", + "type": "string", + "format": "date" + }, + "privacy_url": { + "description": + "A fully qualified URL pointing to the privacy policy for the service. An array with one object per supported language with the following keys:", + "type": "array", + "items": { + "type": "object", + "properties": { + "text": { + "description": "The translated text.", + "type": "string", + "format": "uri" + }, + "language": { + "description": "IETF BCP 47 language code.", + "type": "string", + "pattern": "^[a-z]{2,3}(-[A-Z]{2})?$" + } + }, + "required": ["text", "language"] + } + }, + "privacy_last_updated": { + "description": + "The date that the privacy policy provided at privacy_url was last updated (added in v2.3-RC).", + "type": "string", + "format": "date" + }, + "rental_apps": { + "description": + "Contains rental app information in the android and ios JSON objects (added in v1.1).", + "type": "object", + "properties": { + "android": { + "description": + "Contains rental app download and app discovery information for the Android platform. (added in v1.1)", + "type": "object", + "properties": { + "store_uri": { + "description": + "URI where the rental Android app can be downloaded from (added in v1.1).", + "type": "string", + "format": "uri" + }, + "discovery_uri": { + "description": + "URI that can be used to discover if the rental Android app is installed on the device (added in v1.1).", + "type": "string", + "format": "uri" + } + }, + "required": ["store_uri", "discovery_uri"] + }, + "ios": { + "description": + "Contains rental information for the iOS platform (added in v1.1).", + "type": "object", + "properties": { + "store_uri": { + "description": + "URI where the rental iOS app can be downloaded from (added in v1.1).", + "type": "string", + "format": "uri" + }, + "discovery_uri": { + "description": + "URI that can be used to discover if the rental iOS app is installed on the device (added in v1.1).", + "type": "string", + "format": "uri" + } + }, + "required": ["store_uri", "discovery_uri"] + } + } + } + }, + "oneOf": [ + { + "not": {"required" : ["license_url", "license_id"] } + }, + { + "required": ["license_id"], + "not": {"required": ["license_id"] } + }, + { + "required": ["license_url"], + "not": {"required": ["license_url"] } + } + ], + "required": ["system_id", "languages", "name", "opening_hours", "feed_contact_email", "timezone"], + "dependentRequired": { + "terms_url": ["terms_last_updated"], + "privacy_url": ["privacy_last_updated"] + }, + "additionalProperties": false + } + }, + "required": ["last_updated", "ttl", "version", "data"] +} diff --git a/gbfs-validator/versions/schemas/v3.0-RC/system_pricing_plans.json b/gbfs-validator/versions/schemas/v3.0-RC/system_pricing_plans.json new file mode 100644 index 0000000..e5b048e --- /dev/null +++ b/gbfs-validator/versions/schemas/v3.0-RC/system_pricing_plans.json @@ -0,0 +1,204 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": + "https://github.com/NABSA/gbfs/blob/v3.0-RC/gbfs.md#system_pricing_plansjson", + "description": "Describes the pricing schemes of the system.", + "type": "object", + "properties": { + "last_updated": { + "description": + "Last time the data in the feed was updated in POSIX time.", + "type": "integer", + "minimum": 1450155600 + }, + "ttl": { + "description": + "Number of seconds before the data in the feed will be updated again (0 if the data should always be refreshed).", + "type": "integer", + "minimum": 0 + }, + "version": { + "description": + "GBFS version number to which the feed conforms, according to the versioning framework (added in v1.1).", + "type": "string", + "enum": [ + "3.0-RC", + "3.0" + ] + }, + "data": { + "description": + "Array that contains one object per plan as defined below.", + "type": "object", + "properties": { + "plans": { + "type": "array", + "items": { + "type": "object", + "properties": { + "plan_id": { + "description": "Identifier of a pricing plan in the system.", + "type": "string" + }, + "url": { + "description": + "URL where the customer can learn more about this pricing plan.", + "type": "string", + "format": "uri" + }, + "name": { + "description": "Name of this pricing plan.", + "type": "array", + "items": { + "type": "object", + "properties": { + "text": { + "description": "The translated text.", + "type": "string" + }, + "language": { + "description": "IETF BCP 47 language code.", + "type": "string", + "pattern": "^[a-z]{2,3}(-[A-Z]{2})?$" + } + }, + "required": ["text", "language"] + } + }, + "currency": { + "description": + "Currency used to pay the fare in ISO 4217 code.", + "type": "string", + "pattern": "^\\w{3}$" + }, + "price": { + "description": "Fare price.", + "type": "number", + "minimum": 0 + }, + "reservation_price_per_min": { + "description": "The cost, described as per minute rate, to reserve the vehicle prior to beginning a rental.", + "type": "number", + "minimum": 0 + }, + "reservation_price_flat_rate": { + "description": "The cost, described as a flat rate, to reserve the vehicle prior to beginning a rental.", + "type": "number", + "minimum": 0 + }, + "is_taxable": { + "description": + "Will additional tax be added to the base price?", + "type": "boolean" + }, + "description": { + "description": + "Customer-readable description of the pricing plan.", + "type": "array", + "items": { + "type": "object", + "properties": { + "text": { + "description": "The translated text.", + "type": "string" + }, + "language": { + "description": "IETF BCP 47 language code.", + "type": "string", + "pattern": "^[a-z]{2,3}(-[A-Z]{2})?$" + } + }, + "required": ["text", "language"] + } + }, + "per_km_pricing": { + "description": + "Array of segments when the price is a function of distance travelled, displayed in kilometers (added in v2.1-RC2).", + "type": "array", + "items": { + "type": "object", + "properties": { + "start": { + "description": + "Number of kilometers that have to elapse before this segment starts applying (added in v2.1-RC2).", + "type": "integer", + "minimum": 0 + }, + "rate": { + "description": + "Rate that is charged for each kilometer interval after the start (added in v2.1-RC2).", + "type": "number" + }, + "interval": { + "description": + "Interval in kilometers at which the rate of this segment is either reapplied indefinitely, or if defined, up until (but not including) end kilometer (added in v2.1-RC2).", + "type": "integer", + "minimum": 0 + }, + "end": { + "description": + "The kilometer at which the rate will no longer apply (added in v2.1-RC2).", + "type": "integer", + "minimum": 0 + } + }, + "required": ["start", "rate", "interval"] + } + }, + "per_min_pricing": { + "description": + "Array of segments when the price is a function of time travelled, displayed in minutes (added in v2.1-RC2).", + "type": "array", + "items": { + "type": "object", + "properties": { + "start": { + "description": + "Number of minutes that have to elapse before this segment starts applying (added in v2.1-RC2).", + "type": "integer", + "minimum": 0 + }, + "rate": { + "description": + "Rate that is charged for each minute interval after the start (added in v2.1-RC2).", + "type": "number" + }, + "interval": { + "description": + "Interval in minutes at which the rate of this segment is either reapplied (added in v2.1-RC2).", + "type": "integer", + "minimum": 0 + }, + "end": { + "description": + "The minute at which the rate will no longer apply (added in v2.1-RC2).", + "type": "integer", + "minimum": 0 + } + }, + "required": ["start", "rate", "interval"] + } + }, + "surge_pricing": { + "description": + "Is there currently an increase in price in response to increased demand in this pricing plan? (added in v2.1-RC2)", + "type": "boolean" + } + }, + "required": [ + "plan_id", + "name", + "currency", + "price", + "is_taxable", + "description" + ] + } + } + }, + "required": ["plans"] + } + }, + "required": ["last_updated", "ttl", "version", "data"] +} + diff --git a/gbfs-validator/versions/schemas/v3.0-RC/system_regions.json b/gbfs-validator/versions/schemas/v3.0-RC/system_regions.json new file mode 100644 index 0000000..a5e7a8f --- /dev/null +++ b/gbfs-validator/versions/schemas/v3.0-RC/system_regions.json @@ -0,0 +1,71 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "https://github.com/NABSA/gbfs/blob/v3.0-RC/gbfs.md#system_regionsjson", + "description": + "Describes regions for a system that is broken up by geographic or political region.", + "type": "object", + "properties": { + "last_updated": { + "description": + "Last time the data in the feed was updated in POSIX time.", + "type": "integer", + "minimum": 1450155600 + }, + "ttl": { + "description": + "Number of seconds before the data in the feed will be updated again (0 if the data should always be refreshed).", + "type": "integer", + "minimum": 0 + }, + "version": { + "description": + "GBFS version number to which the feed conforms, according to the versioning framework (added in v1.1).", + "type": "string", + "enum": [ + "3.0-RC", + "3.0" + ] + }, + "data": { + "description": "Describe regions for a system that is broken up by geographic or political region.", + "type": "object", + "properties": { + "regions": { + "description": "Array of regions.", + "type": "array", + "items": { + "type": "object", + "properties": { + "region_id": { + "description": "identifier of the region.", + "type": "string" + }, + "name": { + "description": "Public name for this region.", + "type": "array", + "items": { + "type": "object", + "properties": { + "text": { + "description": "The translated text.", + "type": "string" + }, + "language": { + "description": "IETF BCP 47 language code.", + "type": "string", + "pattern": "^[a-z]{2,3}(-[A-Z]{2})?$" + } + }, + "required": ["text", "language"] + } + } + }, + "required": ["region_id", "name"] + } + } + }, + "required": ["regions"] + } + }, + "required": ["last_updated", "ttl", "version", "data"] +} diff --git a/gbfs-validator/versions/schemas/v3.0-RC/vehicle_status.json b/gbfs-validator/versions/schemas/v3.0-RC/vehicle_status.json new file mode 100644 index 0000000..72f507c --- /dev/null +++ b/gbfs-validator/versions/schemas/v3.0-RC/vehicle_status.json @@ -0,0 +1,155 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": + "https://github.com/NABSA/gbfs/blob/v3.0-RC/gbfs.md#vehicle_statusjson", + "description": + "Describes the vehicles that are available for rent (as of v3.0-RC, formerly free_bike_status).", + "type": "object", + "properties": { + "last_updated": { + "description": + "Last time the data in the feed was updated in POSIX time.", + "type": "integer", + "minimum": 1450155600 + }, + "ttl": { + "description": + "Number of seconds before the data in the feed will be updated again (0 if the data should always be refreshed).", + "type": "integer", + "minimum": 0 + }, + "version": { + "description": + "GBFS version number to which the feed conforms, according to the versioning framework (added in v1.1).", + "type": "string", + "enum": [ + "3.0-RC", + "3.0" + ] + }, + "data": { + "description": + "Array that contains one object per vehicle as defined below.", + "type": "object", + "properties": { + "vehicles": { + "type": "array", + "items": { + "type": "object", + "properties": { + "vehicle_id": { + "description": "Rotating (as of v2.0) identifier of a vehicle.", + "type": "string" + }, + "lat": { + "description": "The latitude of the vehicle.", + "type": "number", + "minimum": -90, + "maximum": 90 + }, + "lon": { + "description": "The longitude of the vehicle.", + "type": "number", + "minimum": -180, + "maximum": 180 + }, + "is_reserved": { + "description": "Is the vehicle currently reserved?", + "type": "boolean" + }, + "is_disabled": { + "description": "Is the vehicle currently disabled (broken)?", + "type": "boolean" + }, + "rental_uris": { + "description": "Contains rental uris for Android, iOS, and web in the android, ios, and web fields (added in v1.1).", + "type": "object", + "properties": { + "android": { + "description": "URI that can be passed to an Android app with an intent (added in v1.1).", + "type": "string", + "format": "uri" + }, + "ios": { + "description": "URI that can be used on iOS to launch the rental app for this vehicle (added in v1.1).", + "type": "string", + "format": "uri" + }, + "web": { + "description": "URL that can be used by a web browser to show more information about renting this vehicle (added in v1.1).", + "type": "string", + "format": "uri" + } + } + }, + "vehicle_type_id": { + "description": "The vehicle_type_id of this vehicle (added in v2.1-RC).", + "type": "string" + }, + "last_reported": { + "description": "The last time this vehicle reported its status to the operator's backend in POSIX time (added in v2.1-RC).", + "type": "number", + "minimum": 1450155600 + }, + "current_range_meters": { + "description": "The furthest distance in meters that the vehicle can travel without recharging or refueling with the vehicle's current charge or fuel (added in v2.1-RC).", + "type": "number", + "minimum": 0 + }, + "current_fuel_percent": { + "description": "This value represents the current percentage, expressed from 0 to 1, of fuel or battery power remaining in the vehicle. Added in v2.3-RC.", + "type": "number", + "minimum": 0 + }, + "station_id": { + "description": "Identifier referencing the station_id if the vehicle is currently at a station (added in v2.1-RC2).", + "type": "string" + }, + "home_station_id": { + "description": "The station_id of the station this vehicle must be returned to (added in v2.3-RC).", + "type": "string" + }, + "pricing_plan_id": { + "description": "The plan_id of the pricing plan this vehicle is eligible for (added in v2.2).", + "type": "string" + }, + "vehicle_equipment": { + "description": "List of vehicle equipment provided by the operator in addition to the accessories already provided in the vehicle. Added in v2.3.", + "type": "array", + "items": { + "enum": ["child_seat_a", "child_seat_b", "child_seat_c", "winter_tires", "snow_chains"] + } + }, + "available_until": { + "description": "The date and time when any rental of the vehicle must be completed. Added in v2.3.", + "type": "string", + "pattern": "^([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})([A-Z])$" + } + }, + "anyOf": [ + { + "required": ["lat", "lon"], + "errorMessage": "Both 'lat' and 'lon' are required." + }, + { + "required": ["station_id"], + "properties": { + "lat": { + "not": {} + }, + "lon": { + "not": {} + } + }, + "errorMessage": "'station_id' is required if 'lat' and 'lon' are not present." + } + ], + "required": ["vehicle_id", "is_reserved", "is_disabled"] + } + } + }, + "required": ["vehicles"] + } + }, + "required": ["last_updated", "ttl", "version", "data"] +} diff --git a/gbfs-validator/versions/schemas/v3.0-RC/vehicle_types.json b/gbfs-validator/versions/schemas/v3.0-RC/vehicle_types.json new file mode 100644 index 0000000..a6bc544 --- /dev/null +++ b/gbfs-validator/versions/schemas/v3.0-RC/vehicle_types.json @@ -0,0 +1,271 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": + "https://github.com/NABSA/gbfs/blob/v3.0-RC/gbfs.md#vehicle_typesjson-added-in-v21-rc", + "description": + "Describes the types of vehicles that System operator has available for rent (added in v2.1-RC).", + "type": "object", + "properties": { + "last_updated": { + "description": + "Last time the data in the feed was updated in POSIX time.", + "type": "integer", + "minimum": 1450155600 + }, + "ttl": { + "description": + "Number of seconds before the data in the feed will be updated again (0 if the data should always be refreshed).", + "type": "integer", + "minimum": 0 + }, + "version": { + "description": + "GBFS version number to which the feed conforms, according to the versioning framework.", + "type": "string", + "enum": [ + "3.0-RC", + "3.0" + ] + }, + "data": { + "description": "Response data in the form of name:value pairs.", + "type": "object", + "properties": { + "vehicle_types": { + "description": + "Array that contains one object per vehicle type in the system as defined below.", + "type": "array", + "items": { + "type": "object", + "properties": { + "vehicle_type_id": { + "description": "Unique identifier of a vehicle type.", + "type": "string" + }, + "form_factor": { + "description": "The vehicle's general form factor.", + "type": "string", + "enum": ["bicycle", "cargo_bicycle" ,"car", "moped", "scooter_standing", "scooter_seated", "other", "scooter"] + }, + "rider_capacity": { + "description": "The number of riders (driver included) the vehicle can legally accommodate", + "type": "integer", + "minimum": 0 + }, + "cargo_volume_capacity": { + "description": "Cargo volume available in the vehicle, expressed in liters.", + "type": "integer", + "minimum": 0 + }, + "cargo_load_capacity": { + "description": "The capacity of the vehicle cargo space (excluding passengers), expressed in kilograms.", + "type": "integer", + "minimum": 0 + }, + + "propulsion_type": { + "description": "The primary propulsion type of the vehicle. Updated in v2.3 to represent car-sharing", + "type": "string", + "enum": ["human", "electric_assist", "electric", "combustion", "combustion_diesel", "hybrid", "plug_in_hybrid", "hydrogen_fuel_cell"] + }, + "eco_label": { + "description": "Vehicle air quality certificate. added in v2.3.", + "type": "array", + "items": { + "type": "object", + "properties": { + "country_code": { + "description": " Country code following the ISO 3166-1 alpha-2 notation. Added in v2.3.", + "type": "string", + "pattern": "^[A-Z]{2}" + }, + "eco_sticker": { + "description": " Name of the eco label. Added in v2.3.", + "type": "string" + } + } + }, + "required": ["country_code", "eco_sticker"] + }, + "max_range_meters": { + "description": + "The furthest distance in meters that the vehicle can travel without recharging or refueling when it has the maximum amount of energy potential.", + "type": "number", + "minimum": 0 + }, + "name": { + "description": "The public name of this vehicle type. An array with one object per supported language with the following keys:", + "type": "array", + "items": { + "type": "object", + "properties": { + "text": { + "description": "The translated text.", + "type": "string" + }, + "language": { + "description": "IETF BCP 47 language code.", + "type": "string", + "pattern": "^[a-z]{2,3}(-[A-Z]{2})?$" + } + }, + "required": ["text", "language"] + } + }, + "vehicle_accessories": { + "description": "Description of accessories available in the vehicle.", + "type": "array", + "items": { + "enum": ["air_conditioning", "automatic", "manual", "convertible", "cruise_control", "doors_2", "doors_3", "doors_4", "doors_5", "navigation"] + } + }, + "g_CO2_km": { + "description": "Maximum quantity of CO2, in grams, emitted per kilometer, according to the WLTP. Added in v2.3", + "type": "integer", + "minimum": 0 + }, + "vehicle_image": { + "description": "URL to an image that would assist the user in identifying the vehicle. JPEG or PNG. Added in v2.3", + "type": "string", + "format": "uri" + }, + "make": { + "description": "The name of the vehicle manufacturer. An array with one object per supported language with the following keys:", + "type": "array", + "items": { + "type": "object", + "properties": { + "text": { + "description": "The translated text.", + "type": "string" + }, + "language": { + "description": "IETF BCP 47 language code.", + "type": "string", + "pattern": "^[a-z]{2,3}(-[A-Z]{2})?$" + } + }, + "required": ["text", "language"] + } + }, + "model": { + "description": "The name of the vehicle model. An array with one object per supported language with the following keys:", + "type": "array", + "items": { + "type": "object", + "properties": { + "text": { + "description": "The translated text.", + "type": "string" + }, + "language": { + "description": "IETF BCP 47 language code.", + "type": "string", + "pattern": "^[a-z]{2,3}(-[A-Z]{2})?$" + } + }, + "required": ["text", "language"] + } + }, + "color": { + "description": "The color of the vehicle. Added in v2.3", + "type": "string" + }, + "description": { + "description": "Customer-readable description of the vehicle type outlining special features or how-tos. An array with one object per supported language with the following keys:", + "type": "array", + "items": { + "type": "object", + "properties": { + "text": { + "description": "The translated text.", + "type": "string" + }, + "language": { + "description": "IETF BCP 47 language code.", + "type": "string", + "pattern": "^[a-z]{2,3}(-[A-Z]{2})?$" + } + }, + "required": ["text", "language"] + } + }, + "wheel_count": { + "description": "Number of wheels this vehicle type has. Added in v2.3", + "type": "integer", + "minimum": 0 + }, + "max_permitted_speed": { + "description": "The maximum speed in kilometers per hour this vehicle is permitted to reach in accordance with local permit and regulations. Added in v2.3", + "type": "integer", + "minimum": 0 + }, + "rated_power": { + "description": "The rated power of the motor for this vehicle type in watts. Added in v2.3", + "type": "integer", + "minimum": 0 + }, + "default_reserve_time": { + "description": "Maximum time in minutes that a vehicle can be reserved before a rental begins added in v2.3-RC.", + "type": "integer", + "minimum": 0 + }, + "return_constraint": { + "description": "The conditions for returning the vehicle at the end of the trip. Added in v2.3-RC as return_type, and updated to return_constraint in v2.3.", + "type": "string", + "enum": ["free_floating", "roundtrip_station", "any_station", "hybrid"] + }, + "vehicle_assets": { + "description": "An object where each key defines one of the items listed below added in v2.3-RC.", + "type": "object", + "properties": { + "icon_url": { + "description": "A fully qualified URL pointing to the location of a graphic icon file that MAY be used to represent this vehicle type on maps and in other applications added in v2.3-RC.", + "type": "string", + "format": "uri" + }, + "icon_url_dark": { + "description": "A fully qualified URL pointing to the location of a graphic icon file to be used to represent this vehicle type when in dark mode added in v2.3-RC.", + "type": "string", + "format": "uri" + }, + "icon_last_modified": { + "description": "Date that indicates the last time any included vehicle icon images were modified or updated added in v2.3-RC.", + "type": "string", + "format": "date" + } + }, + "required": ["icon_url", "icon_last_modified"] + }, + "default_pricing_plan_id": { + "description": "A plan_id as defined in system_pricing_plans.json added in v2.3-RC.", + "type": "string" + }, + "pricing_plan_ids": { + "description": "Array of all pricing plan IDs as defined in system_pricing_plans.json added in v2.3-RC.", + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": ["vehicle_type_id", "form_factor", "propulsion_type"], + "if": { + "properties": { + "propulsion_type": { + "enum": ["electric", "electric_assist", "combustion", "combustion_diesel", "hybrid", "plug_in_hybrid", "hydrogen_fuel_cell"] + } + } + }, + "then": { + "required": ["max_range_meters"] + } + } + } + }, + "required": ["vehicle_types"] + } + }, + "required": ["last_updated", "ttl", "version", "data"] +} + diff --git a/gbfs-validator/versions/v3.0-RC.js b/gbfs-validator/versions/v3.0-RC.js new file mode 100644 index 0000000..ac1ba25 --- /dev/null +++ b/gbfs-validator/versions/v3.0-RC.js @@ -0,0 +1,17 @@ +module.exports = { + gbfsRequired: true, + files: options => { + return [ + { file: 'gbfs_versions', required: false }, + { file: 'system_information', required: true }, + { file: 'vehicle_types', required: false }, + { file: 'station_information', required: options.docked }, + { file: 'station_status', required: options.docked }, + { file: 'vehicle_status', required: options.freefloating }, + { file: 'system_regions', required: false }, + { file: 'system_pricing_plans', required: false }, + { file: 'system_alerts', required: false }, + { file: 'geofencing_zones', required: false } + ] + } +} diff --git a/website/src/components/SubResult.vue b/website/src/components/SubResult.vue index 304d0182..0cd4f1f 100644 --- a/website/src/components/SubResult.vue +++ b/website/src/components/SubResult.vue @@ -17,13 +17,13 @@ function errorsCountFormated(value) {
{{ file.file }}
-
- Missing file. Recommanded +
+ Missing file. Recommended
diff --git a/website/src/pages/Validator.vue b/website/src/pages/Validator.vue index c271ad6..ccdf54c 100644 --- a/website/src/pages/Validator.vue +++ b/website/src/pages/Validator.vue @@ -24,6 +24,7 @@ const state = reactive({ }, versions: [ { value: null, text: 'auto-detection' }, + { value: '3.0-RC', text: 'v3.0-RC' }, { value: '2.3', text: 'v2.3' }, { value: '2.2', text: 'v2.2' }, { value: '2.1', text: 'v2.1' },