Skip to content

Instantly share code, notes, and snippets.

@tomkralidis
Last active May 31, 2023 11:19
Show Gist options
  • Save tomkralidis/cc674035706640d93a1f9527f18cb91a to your computer and use it in GitHub Desktop.
Save tomkralidis/cc674035706640d93a1f9527f18cb91a to your computer and use it in GitHub Desktop.
OGC API - Records validation

Testing the following workflow with check-jsonschema:

  1. Using Stoplight studio, export https://github.com/opengeospatial/ogcapi-records/blob/master/core/openapi/schemas/recordGeoJSON.yaml to a JSON Schema (bundled/dereferenced) (result is recordGeoJSON-bundled.json in this gist)
  2. Run the following on the below JSON instance:
check-jsonschema --schemafile recordGeoJSON-bundled.json test.json 
ok -- validation done
  1. Update the geometry definition in test.jsonto "geometry": null:

  2. Run the following on the below JSON instance:

check-jsonschema --schemafile recordGeoJSON-bundled.json test.json 
ok -- validation done
  1. Update the time definition in test.jsonto "time": null:

  2. Run the following on the below JSON instance:

check-jsonschema --schemafile recordGeoJSON-bundled.json test.json 
Schema validation errors were encountered.
  test.json::$.time: None is not of type 'object'
{
"type": "object",
"required": [
"id",
"type",
"time",
"geometry",
"properties",
"links"
],
"properties": {
"id": {
"type": "string",
"description": "A unique identifier of the catalogue record.",
"format": "uri"
},
"conformsTo": {
"type": "array",
"items": {
"type": "string"
}
},
"type": {
"type": "string",
"enum": [
"Feature"
]
},
"time": {
"type": "object",
"nullable": true,
"properties": {
"date": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$"
},
"timestamp": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?Z$"
},
"interval": {
"type": "array",
"minItems": 2,
"maxItems": 2,
"items": {
"oneOf": [
{
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$"
},
{
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?Z$"
},
{
"type": "string",
"enum": [
".."
]
}
]
}
},
"resolution": {
"type": "string",
"description": "Minimum time period resolvable in the dataset, as an ISO 8601 duration",
"example": [
"P1D"
]
}
}
},
"geometry": {
"oneOf": [
{
"enum": [
null
]
},
{
"oneOf": [
{
"type": "object",
"required": [
"type",
"coordinates"
],
"properties": {
"type": {
"type": "string",
"enum": [
"Point"
]
},
"coordinates": {
"type": "array",
"minItems": 2,
"items": {
"type": "number"
}
}
}
},
{
"type": "object",
"required": [
"type",
"coordinates"
],
"properties": {
"type": {
"type": "string",
"enum": [
"MultiPoint"
]
},
"coordinates": {
"type": "array",
"items": {
"type": "array",
"minItems": 2,
"items": {
"type": "number"
}
}
}
}
},
{
"type": "object",
"required": [
"type",
"coordinates"
],
"properties": {
"type": {
"type": "string",
"enum": [
"LineString"
]
},
"coordinates": {
"type": "array",
"minItems": 2,
"items": {
"type": "array",
"minItems": 2,
"items": {
"type": "number"
}
}
}
}
},
{
"type": "object",
"required": [
"type",
"coordinates"
],
"properties": {
"type": {
"type": "string",
"enum": [
"MultiLineString"
]
},
"coordinates": {
"type": "array",
"items": {
"type": "array",
"minItems": 2,
"items": {
"type": "array",
"minItems": 2,
"items": {
"type": "number"
}
}
}
}
}
},
{
"type": "object",
"required": [
"type",
"coordinates"
],
"properties": {
"type": {
"type": "string",
"enum": [
"Polygon"
]
},
"coordinates": {
"type": "array",
"items": {
"type": "array",
"minItems": 4,
"items": {
"type": "array",
"minItems": 2,
"items": {
"type": "number"
}
}
}
}
}
},
{
"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"
}
}
}
}
}
}
},
{
"type": "object",
"required": [
"type",
"geometries"
],
"properties": {
"type": {
"type": "string",
"enum": [
"GeometryCollection"
]
},
"geometries": {
"type": "array",
"items": {
"oneOf": [
{
"type": "object",
"required": [
"type",
"coordinates"
],
"properties": {
"type": {
"type": "string",
"enum": [
"Point"
]
},
"coordinates": {
"type": "array",
"minItems": 2,
"items": {
"type": "number"
}
}
}
},
{
"type": "object",
"required": [
"type",
"coordinates"
],
"properties": {
"type": {
"type": "string",
"enum": [
"MultiPoint"
]
},
"coordinates": {
"type": "array",
"items": {
"type": "array",
"minItems": 2,
"items": {
"type": "number"
}
}
}
}
},
{
"type": "object",
"required": [
"type",
"coordinates"
],
"properties": {
"type": {
"type": "string",
"enum": [
"LineString"
]
},
"coordinates": {
"type": "array",
"minItems": 2,
"items": {
"type": "array",
"minItems": 2,
"items": {
"type": "number"
}
}
}
}
},
{
"type": "object",
"required": [
"type",
"coordinates"
],
"properties": {
"type": {
"type": "string",
"enum": [
"MultiLineString"
]
},
"coordinates": {
"type": "array",
"items": {
"type": "array",
"minItems": 2,
"items": {
"type": "array",
"minItems": 2,
"items": {
"type": "number"
}
}
}
}
}
},
{
"type": "object",
"required": [
"type",
"coordinates"
],
"properties": {
"type": {
"type": "string",
"enum": [
"Polygon"
]
},
"coordinates": {
"type": "array",
"items": {
"type": "array",
"minItems": 4,
"items": {
"type": "array",
"minItems": 2,
"items": {
"type": "number"
}
}
}
}
}
},
{
"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"
}
}
}
}
}
}
},
{
"type": "object",
"required": [
"type",
"geometries"
],
"properties": {
"type": {
"type": "string",
"enum": [
"GeometryCollection"
]
},
"geometries": {
"type": "array",
"items": {
"$ref": "#/properties/geometry/oneOf/1/oneOf/6/properties/geometries/items"
}
}
}
}
]
}
}
}
}
]
}
]
},
"properties": {
"type": "object",
"required": [
"type",
"title"
],
"properties": {
"created": {
"type": "string",
"description": "Date of creation of this record.",
"format": "date-time"
},
"updated": {
"type": "string",
"description": "The most recent date on which the record was changed.",
"format": "date-time"
},
"type": {
"type": "string",
"description": "The nature or genre of the resource. The value should be a code, convenient for filtering records. Where available, a link to the canonical URI of the record type resource will be added to the 'links' property.",
"maxLength": 64
},
"title": {
"type": "string",
"description": "A human-readable name given to the resource."
},
"description": {
"type": "string",
"description": "A free-text account of the resource."
},
"keywords": {
"type": "array",
"description": "The topic or topics of the resource. Typically represented using free-form keywords, tags, key phrases, or classification codes.",
"items": {
"type": "string"
}
},
"language": {
"description": "The language used for textual values in this record representation.",
"type": "object",
"required": [
"code"
],
"properties": {
"code": {
"type": "string",
"description": "The language tag as per RFC-5646.",
"example": "el",
"name": {
"type": "string",
"minLength": 1,
"description": "The untranslated name of of the language.",
"example": "Ελληνικά"
},
"alternate": {
"type": "string",
"description": "The name of the language in another well-understood language, usually English.",
"example": "Greek"
},
"dir": {
"type": "string",
"description": "The direction for text in this language. The default, `ltr` (left-to-right), represents the most common situation. However, care should be taken to set the value of `dir` appropriately if the language direction is not `ltr`. Other values supported are `rtl` (right-to-left), `ttb` (top-to-bottom), and `btt` (bottom-to-top).",
"enum": [
"ltr",
"rtl",
"ttb",
"btt"
],
"default": "ltr"
}
}
}
},
"languages": {
"type": "array",
"description": "This list of languages in which this record is available.",
"items": {
"type": "object",
"description": "The language used for textual values in this record.",
"required": [
"code"
],
"properties": {
"code": {
"type": "string",
"description": "The language tag as per RFC-5646.",
"example": "el",
"name": {
"type": "string",
"minLength": 1,
"description": "The untranslated name of of the language.",
"example": "Ελληνικά"
},
"alternate": {
"type": "string",
"description": "The name of the language in another well-understood language, usually English.",
"example": "Greek"
},
"dir": {
"type": "string",
"description": "The direction for text in this language. The default, `ltr` (left-to-right), represents the most common situation. However, care should be taken to set the value of `dir` appropriately if the language direction is not `ltr`. Other values supported are `rtl` (right-to-left), `ttb` (top-to-bottom), and `btt` (bottom-to-top).",
"enum": [
"ltr",
"rtl",
"ttb",
"btt"
],
"default": "ltr"
}
}
}
}
},
"resourceLanguages": {
"type": "array",
"description": "The list of languages in which the resource described by this record is available.",
"items": {
"type": "object",
"description": "The language used for textual values in this record.",
"required": [
"code"
],
"properties": {
"code": {
"type": "string",
"description": "The language tag as per RFC-5646.",
"example": "el",
"name": {
"type": "string",
"minLength": 1,
"description": "The untranslated name of of the language.",
"example": "Ελληνικά"
},
"alternate": {
"type": "string",
"description": "The name of the language in another well-understood language, usually English.",
"example": "Greek"
},
"dir": {
"type": "string",
"description": "The direction for text in this language. The default, `ltr` (left-to-right), represents the most common situation. However, care should be taken to set the value of `dir` appropriately if the language direction is not `ltr`. Other values supported are `rtl` (right-to-left), `ttb` (top-to-bottom), and `btt` (bottom-to-top).",
"enum": [
"ltr",
"rtl",
"ttb",
"btt"
],
"default": "ltr"
}
}
}
}
},
"externalIds": {
"type": "array",
"description": "An identifier for the resource assigned by an external (to the catalogue) entity.",
"items": {
"type": "object",
"properties": {
"scheme": {
"type": "string",
"description": "A reference to an authority or identifier for a knowledge organization system from which the external identifier was obtained. It is recommended that the identifier be a resolvable URI."
},
"value": {
"type": "string",
"description": "The value of the identifier."
}
},
"required": [
"value"
]
}
},
"themes": {
"type": "array",
"description": "A knowledge organization system used to classify the resource.",
"minItems": 1,
"items": {
"type": "object",
"required": [
"concepts",
"scheme"
],
"properties": {
"concepts": {
"type": "array",
"description": "One or more entity/concept identifiers from this knowledge system. it is recommended that a resolvable URI be used for each entity/concept identifier.",
"minItems": 1,
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"description": "An identifier for the concept."
},
"title": {
"type": "string",
"description": "A human readable title for the concept."
},
"description": {
"type": "string",
"description": "A human readable description for the concept."
},
"url": {
"type": "string",
"format": "uri",
"description": "A URI providing further description of the concept."
}
}
}
},
"scheme": {
"type": "string",
"description": "An identifier for the knowledge organization system used to classify the resource. It is recommended that the identifier be a resolvable URI. The list of schemes used in a searchable catalogue can be determined by inspecting the server's OpenAPI document or, if the server implements CQL2, by exposing a queryable (e.g. named `scheme`) and enumerating the list of schemes in the queryable's schema definition."
}
}
}
},
"formats": {
"type": "array",
"description": "A list of available distributions of the resource.",
"items": {
"type": "string"
}
},
"contacts": {
"type": "array",
"description": "A list of contacts qualified by their role(s) in association to the record or the resource described by the record.",
"items": {
"type": "object",
"description": "Identification of, and means of communication with, person responsible\nfor the resource.",
"required": [
"name"
],
"properties": {
"identifier": {
"type": "string",
"description": "A value uniquely identifying a contact (individual or organization)"
},
"name": {
"type": "string",
"description": "The name of the organization or the individual."
},
"positionName": {
"type": "string",
"description": "The name of the role or position of the responsible person taken from the organization's formal organizational hierarchy or chart."
},
"organization": {
"type": "string",
"description": "Organization/affiliation of the individual/responsible person. In case of an organization, the name property should be used and this property is not to be used."
},
"logo": {
"description": "Graphic identifying a contact. The link relation should be `icon` and the media type should be an image media type.",
"allOf": [
{
"type": "object",
"required": [
"href"
],
"properties": {
"href": {
"type": "string",
"format": "url"
},
"rel": {
"type": "string"
},
"type": {
"type": "string"
},
"hreflang": {
"type": "string"
},
"title": {
"type": "string"
},
"templated": {
"type": "boolean"
},
"variables": {
"description": "This object contains one key per substitution variable in a templated\nhref. Each key defines the schema of one substitution variable using\na JSON Schema fragment and can thus include things like the data type\nof the variable, enumerations, minimum values, maximum values, etc.\nIn combination with a templated href, the variables section should\nprovide enough information to bind to the target resource (e.g. a WMS).",
"type": "object"
},
"created": {
"type": "string",
"description": "Date of creation of the resource pointed to by the link.",
"format": "date-time"
},
"updated": {
"type": "string",
"description": "Most recent date on which the resource pointed to by the link was changed.",
"format": "date-time"
}
}
},
{
"type": "object",
"required": [
"rel",
"type"
],
"properties": {
"rel": {
"enum": [
"icon"
]
}
}
}
]
},
"phones": {
"type": "array",
"description": "Telephone numbers at which contact can be made.",
"items": {
"type": "object",
"required": [
"value"
],
"properties": {
"value": {
"type": "string",
"description": "The value is the phone number itself.",
"pattern": "^\\+[1-9]{1}[0-9]{3,14}$",
"example": "+14165550142"
},
"roles": {
"description": "The type of phone number (e.g. home, work, fax, etc.).",
"type": "array",
"items": {
"type": "string"
}
}
}
}
},
"emails": {
"type": "array",
"description": "Email addresses at which contact can be made.",
"items": {
"type": "object",
"required": [
"value"
],
"properties": {
"value": {
"type": "string",
"description": "The value is the email number itself.",
"format": "email"
},
"roles": {
"description": "The type of email (e.g. home, work, etc.).",
"type": "array",
"items": {
"type": "string"
}
}
}
}
},
"addresses": {
"type": "array",
"description": "Physical location at which contact can be made.",
"items": {
"type": "object",
"properties": {
"deliveryPoint": {
"type": "array",
"description": "Address lines for the location.",
"items": {
"type": "string"
}
},
"city": {
"type": "string",
"description": "City for the location."
},
"administrativeArea": {
"type": "string",
"description": "State or province of the location."
},
"postalCode": {
"type": "string",
"description": "ZIP or other postal code."
},
"country": {
"type": "string",
"description": "Country of the physical address."
},
"roles": {
"description": "The type of address (e.g. office, home, etc.).",
"type": "array",
"items": {
"type": "string"
}
}
}
}
},
"links": {
"type": "array",
"description": "On-line information about the contact.",
"items": {
"allOf": [
{
"type": "object",
"required": [
"href"
],
"properties": {
"href": {
"type": "string",
"format": "url"
},
"rel": {
"type": "string"
},
"type": {
"type": "string"
},
"hreflang": {
"type": "string"
},
"title": {
"type": "string"
},
"templated": {
"type": "boolean"
},
"variables": {
"description": "This object contains one key per substitution variable in a templated\nhref. Each key defines the schema of one substitution variable using\na JSON Schema fragment and can thus include things like the data type\nof the variable, enumerations, minimum values, maximum values, etc.\nIn combination with a templated href, the variables section should\nprovide enough information to bind to the target resource (e.g. a WMS).",
"type": "object"
},
"created": {
"type": "string",
"description": "Date of creation of the resource pointed to by the link.",
"format": "date-time"
},
"updated": {
"type": "string",
"description": "Most recent date on which the resource pointed to by the link was changed.",
"format": "date-time"
}
}
},
{
"type": "object",
"required": [
"type"
]
}
]
}
},
"hoursOfService": {
"type": "string",
"description": "Time period when the contact can be contacted.",
"example": "Hours: Mo-Fr 10am-7pm Sa 10am-22pm Su 10am-21pm"
},
"contactInstructions": {
"type": "string",
"description": "Supplemental instructions on how or when to contact the\nresponsible party."
},
"roles": {
"description": "The set of named duties, job functions and/or permissions associated with this contact. (e.g. developer, administrator, etc.).",
"type": "array",
"items": {
"type": "string"
}
}
}
}
},
"license": {
"type": "string",
"description": "A legal document under which the resource is made available. If the resource is being made available under a common license then use an SPDX license id (https://spdx.org/licenses/). If the resource is being made available under multiple common licenses then use an SPDX license expression v2.3 string (https://spdx.github.io/spdx-spec/v2.3/SPDX-license-expressions/) If the resource is being made available under one or more licenses that haven't been assigned an SPDX identifier or one or more custom licenses then use a string value of 'other' and include one or more links (rel=\"license\") in the `link` section of the record to the file(s) that contains the text of the license(s). There is also the case of a resource that is private or unpublished and is thus unlicensed; in this case do not register such a resource in the catalogue in the first place since there is no point in making such a resource discoverable."
},
"rights": {
"type": "string",
"description": "A statement that concerns all rights not addressed by the license such as a copyright statement."
}
}
},
"links": {
"type": "array",
"description": "A list of links for accessing the resource (e.g. download link, access link) in one of the supported distribution formats and/or links to other resources associated with this resource. Also, a list of links for navigating the API (e.g. prev, next, etc.). Since the specification requires that at least the self link be present then the min items for this list should be one.",
"items": {
"type": "object",
"required": [
"href"
],
"properties": {
"href": {
"type": "string",
"format": "url"
},
"rel": {
"type": "string"
},
"type": {
"type": "string"
},
"hreflang": {
"type": "string"
},
"title": {
"type": "string"
},
"templated": {
"type": "boolean"
},
"variables": {
"description": "This object contains one key per substitution variable in a templated\nhref. Each key defines the schema of one substitution variable using\na JSON Schema fragment and can thus include things like the data type\nof the variable, enumerations, minimum values, maximum values, etc.\nIn combination with a templated href, the variables section should\nprovide enough information to bind to the target resource (e.g. a WMS).",
"type": "object"
},
"created": {
"type": "string",
"description": "Date of creation of the resource pointed to by the link.",
"format": "date-time"
},
"updated": {
"type": "string",
"description": "Most recent date on which the resource pointed to by the link was changed.",
"format": "date-time"
}
},
"minItems": 1
}
}
}
}
{
"id": "urn:x-wmo:md:can:eccc-msc:weather.observations.swob-realtime",
"conformsTo": [
"http://wis.wmo.int/spec/wcmp/2.0/conf/core"
],
"time": {
"interval": [
"2010-11-11T11:11:11Z",
".."
]
},
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-142,
28
],
[
-142,
82
],
[
-52,
82
],
[
-52,
28
],
[
-142,
28
]
]
]
},
"properties": {
"title": "Surface Weather Observations",
"description": "Surface Observations measured at the automatic and manual stations of the Environment and Climate Change Canada and partners networks, either for a single station, or for the stations of specific provinces and territories (last 30 days)",
"themes": [
{
"concepts": [
{
"id": "Weather"
},
{
"id": "Archives"
},
{
"id": "Precipitation"
},
{
"id": "Air temperature"
},
{
"id": "Humidity"
},
{
"id": "Snow"
},
{
"id": "Wind"
},
{
"id": "Meteorological data"
}
],
"scheme": "https://canada.multites.net/cst"
},
{
"concepts": [
{
"id": "weather"
}
],
"scheme": "https://github.com/wmo-im/wcmp2-codelists/blob/main/codelists/earth-system-domain.csv"
}
],
"contacts": [
{
"name": "Government of Canada; Environment and Climate Change Canada; Meteorological Service of Canada",
"positionName": "National Inquiry Response Team",
"phones": [
{
"value": "+18199972800"
}
],
"emails": [
{
"value": "enviroinfo@ec.gc.ca"
}
],
"addresses": [
{
"deliveryPoint": [
"77 Westmorland Street, suite 260"
],
"city": "Fredericton",
"administrativeArea": "NB",
"postalCode": "E3B 6Z4",
"country": "Canada"
}
],
"links": [
{
"rel": "canonical",
"type": "text/html",
"href": "https://www.canada.ca/en/environment-climate-change.html"
}
],
"roles": [
"pointOfContact",
"originator"
]
}
],
"type": "dataset",
"created": "2018-01-01",
"updated": "2022-06-22",
"wmo:dataPolicy": "core"
},
"links": [
{
"rel": "http://www.wmo.int/def/rel/wmdr/1.0/FacilitySet",
"href": "https://dd.weather.gc.ca/observations/doc/swob-xml_station_list.csv",
"type": "text/csv",
"title": "Stations associated with this dataset"
},
{
"rel": "download",
"href": "https://dd.weather.gc.ca/observations/swob-ml",
"type": "text/html",
"hreflang": "en",
"title": "Raw data download (CSV files)"
},
{
"rel": "items",
"href": "https://api.weather.gc.ca/collections/swob-realtime/items",
"type": "application/json",
"title": "Data access API interface"
},
{
"rel": "related",
"href": "https://eccc-msc.github.io/open-data/msc-data/obs_station/readme_obs_insitu_swobdatamart_en",
"type": "text/html",
"title": "Documentation"
},
{
"rel": "items",
"href": "mqtt://example.org:8883",
"channel": "origin/a/wis2/can/eccc-msc/data/core/weather/observations/surface-land/landFixed",
"type": "application/json",
"title": "Data notifications"
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment