diff --git a/extensions/linz/README.md b/extensions/linz/README.md index 91b1528e..1dbd21a7 100644 --- a/extensions/linz/README.md +++ b/extensions/linz/README.md @@ -63,28 +63,34 @@ See [ISO/IEC 13249-3:2016(en)](https://www.iso.org/obp/ui/#!iso:std:60343:en) fo ## Collection Fields -| Field Name | Type | Description | -| ---------------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| title | string | **REQUIRED**. Collection title. | -| linz:geospatial_type | string | **REQUIRED**. A general description of the type of content that can be found in the dataset. See the [list of accepted geospatial types](#geospatial-types). | -| linz:history | string | **REQUIRED**. A descriptive statement about the lineage/history of a dataset | -| linz:lifecycle | string | **REQUIRED**. Lifecycle Status of Collection. Must be one of `under development`, `preview`, `ongoing`, `completed`, `deprecated`. | -| linz:providers | LINZ Provider Object | **REQUIRED**. The object provides information about a provider with additional roles defined by Toitū Te Whenua LINZ. A provider is any of the organizations that captures or processes the content of the assets and therefore influences the data offered by the STAC implementation. See [LINZ Provider Object](#linz-provider-object). | -| providers | Provider Object | **REQUIRED**. The object provides information about a provider. A provider is any of the organizations that captures or processes the content of the assets and therefore influences the data offered by the STAC implementation. See [Provider Object](#provider-object). | -| linz:security_classification | string | **REQUIRED**. New Zealand Government [Security Classification](https://www.digital.govt.nz/standards-and-guidance/governance/managing-online-channels/security-and-privacy-for-websites/foundations/classify-information/). Must be one of `unclassified`, `in-confidence`, `sensitive`, `restricted`, `confidential`, `secret` or `top-secret`. | -| linz:update_frequency | string | Recommended. Indicates how frequently an updated dataset may be distributed to the publication platform. Must follow the format for durations as defined in [RFC3339](https://datatracker.ietf.org/doc/html/rfc3339#appendix-A). For example, `P3D` expresses a duration of 3 days. | -| processing:software | Map | Recommended. The software and versions which were used to generate the dataset. See [reference](https://github.com/stac-extensions/processing). | - -### Collection Summaries Object Fields +| Field Name | Type | Description | +| ---------------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| title | string | **REQUIRED**. Collection title. | +| linz:asset_summaries | Map> | **REQUIRED**. See [Custom Collection Summaries Object Fields](#custom-collection-summaries-object-fields). | +| linz:geospatial_type | string | **REQUIRED**. A general description of the type of content that can be found in the dataset. See the [list of accepted geospatial types](#geospatial-types). | +| linz:history | string | **REQUIRED**. A descriptive statement about the lineage/history of a dataset | +| linz:lifecycle | string | **REQUIRED**. Lifecycle Status of Collection. Must be one of `under development`, `preview`, `ongoing`, `completed`, `deprecated`. | +| linz:providers | LINZ Provider Object | **REQUIRED**. The object provides information about a provider with additional roles defined by Toitū Te Whenua LINZ. A provider is any of the organizations that captures or processes the content of the assets and therefore influences the data offered by the STAC implementation. See [LINZ Provider Object](#linz-provider-object). | +| providers | Provider Object | **REQUIRED**. The object provides information about a provider. A provider is any of the organizations that captures or processes the content of the assets and therefore influences the data offered by the STAC implementation. See [Provider Object](#provider-object). | +| linz:security_classification | string | **REQUIRED**. New Zealand Government [Security Classification](https://www.digital.govt.nz/standards-and-guidance/governance/managing-online-channels/security-and-privacy-for-websites/foundations/classify-information/). Must be one of `unclassified`, `in-confidence`, `sensitive`, `restricted`, `confidential`, `secret` or `top-secret`. | +| linz:update_frequency | string | Recommended. Indicates how frequently an updated dataset may be distributed to the publication platform. Must follow the format for durations as defined in [RFC3339](https://datatracker.ietf.org/doc/html/rfc3339#appendix-A). For example, `P3D` expresses a duration of 3 days. | +| processing:software | Map | Recommended. The software and versions which were used to generate the dataset. See [reference](https://github.com/stac-extensions/processing). | + +### Custom Collection Summaries Object Fields | Field Name | Type | Description | | --------------- | --------- | -------------------------------------------------------------------- | -| title | string | **REQUIRED**. Collection title. | | created/minimum | date-time | **REQUIRED**. Earliest [asset created value](#asset-fields), in UTC. | | created/maximum | date-time | **REQUIRED**. Latest [asset created value](#asset-fields), in UTC. | | updated/minimum | date-time | **REQUIRED**. Earliest [asset updated value](#asset-fields), in UTC. | | updated/maximum | date-time | **REQUIRED**. Latest [asset updated value](#asset-fields), in UTC. | +### Collection Summaries Object Fields + +| Field Name | Type | Description | +| ---------- | ------ | ------------------------------- | +| title | string | **REQUIRED**. Collection title. | + ### LINZ Provider Object Fields This expands on the [provider object in the STAC spec](https://github.com/radiantearth/stac-spec/blob/v1.0.0/item-spec/common-metadata.md#provider-object). Only differences from that definition are mentioned here. diff --git a/extensions/linz/examples/collection.json b/extensions/linz/examples/collection.json index 71e8fd94..9611b8df 100644 --- a/extensions/linz/examples/collection.json +++ b/extensions/linz/examples/collection.json @@ -12,6 +12,16 @@ "title": "A title", "description": "A description", "license": "Apache-2.0", + "linz:asset_summaries": { + "created": { + "minimum": "1999-01-01T00:00:00Z", + "maximum": "2010-01-01T00:00:00Z" + }, + "updated": { + "minimum": "1999-01-01T00:00:00Z", + "maximum": "2010-01-01T00:00:00Z" + } + }, "linz:lifecycle": "under development", "linz:providers": [ { diff --git a/extensions/linz/schema.json b/extensions/linz/schema.json index 7130e3dc..42e9e230 100644 --- a/extensions/linz/schema.json +++ b/extensions/linz/schema.json @@ -34,6 +34,7 @@ { "type": "object", "required": [ + "linz:asset_summaries", "linz:geospatial_type", "linz:history", "linz:lifecycle", @@ -131,15 +132,11 @@ "properties": { "created": { "title": "Creation time", - "type": "string", - "format": "date-time", - "pattern": "(\\+00:00|Z)$" + "$ref": "#/definitions/utc_datetime" }, "updated": { "title": "Last update time", - "type": "string", - "format": "date-time", - "pattern": "(\\+00:00|Z)$" + "$ref": "#/definitions/utc_datetime" }, "linz:language": { "title": "IETF RFC 5646 language tag", @@ -940,6 +937,37 @@ } } }, + "linz:asset_summaries": { + "required": ["created", "updated"], + "properties": { + "created": { + "required": ["minimum", "maximum"], + "properties": { + "minimum": { + "title": "Earliest asset creation time", + "$ref": "#/definitions/utc_datetime" + }, + "maximum": { + "title": "Latest asset creation time", + "$ref": "#/definitions/utc_datetime" + } + } + }, + "updated": { + "required": ["minimum", "maximum"], + "properties": { + "minimum": { + "title": "Earliest asset updated time", + "$ref": "#/definitions/utc_datetime" + }, + "maximum": { + "title": "Latest asset updated time", + "$ref": "#/definitions/utc_datetime" + } + } + } + } + }, "linz:geospatial_type": {}, "linz:history": { "title": "History", @@ -1047,16 +1075,12 @@ "required": ["minimum", "maximum"], "properties": { "minimum": { - "title": "Earliest asset creation time", - "type": "string", - "format": "date-time", - "pattern": "(\\+00:00|Z)$" + "title": "Earliest metadata creation time", + "$ref": "#/definitions/utc_datetime" }, "maximum": { - "title": "Latest asset creation time", - "type": "string", - "format": "date-time", - "pattern": "(\\+00:00|Z)$" + "title": "Latest metadata creation time", + "$ref": "#/definitions/utc_datetime" } } }, @@ -1065,16 +1089,12 @@ "required": ["minimum", "maximum"], "properties": { "minimum": { - "title": "Earliest asset updated time", - "type": "string", - "format": "date-time", - "pattern": "(\\+00:00|Z)$" + "title": "Earliest metadata updated time", + "$ref": "#/definitions/utc_datetime" }, "maximum": { - "title": "Latest asset updated time", - "type": "string", - "format": "date-time", - "pattern": "(\\+00:00|Z)$" + "title": "Latest metadata updated time", + "$ref": "#/definitions/utc_datetime" } } } @@ -1089,6 +1109,11 @@ "^(?!linz:)": {} }, "additionalProperties": false + }, + "utc_datetime": { + "type": "string", + "format": "date-time", + "pattern": "(\\+00:00|Z)$" } } } diff --git a/extensions/linz/tests/linz_collection.test.js b/extensions/linz/tests/linz_collection.test.js index 9dea747c..50a64c3e 100644 --- a/extensions/linz/tests/linz_collection.test.js +++ b/extensions/linz/tests/linz_collection.test.js @@ -205,6 +205,93 @@ o.spec('LINZ collection', () => { ).equals(true)(JSON.stringify(validate.errors)); }); + o("Example without the mandatory 'linz:asset_summaries' property should fail validation", async () => { + // given + const example = JSON.parse(await fs.readFile(examplePath)); + delete example['linz:asset_summaries']; + + // when + let valid = validate(example); + + // then + o(valid).equals(false); + o( + validate.errors.some( + (error) => error.instancePath === '' && error.message === "must have required property 'linz:asset_summaries'", + ), + ).equals(true)(JSON.stringify(validate.errors)); + }); + + o("Asset summary without the mandatory 'created'/'updated' properties should fail validation", async () => { + for (const property of ['created', 'updated']) { + // given + const example = JSON.parse(await fs.readFile(examplePath)); + delete example['linz:asset_summaries'][property]; + + // when + let valid = validate(example); + + // then + o(valid).equals(false); + o( + validate.errors.some( + (error) => + error.instancePath === '/linz:asset_summaries' && + error.message === `must have required property '${property}'`, + ), + ).equals(true)(JSON.stringify(validate.errors)); + } + }); + + o( + "Asset summary created/updated without the mandatory 'minimum'/'maximum' properties should fail validation", + async () => { + for (const outerProperty of ['created', 'updated']) { + for (const innerProperty of ['minimum', 'maximum']) { + // given + const example = JSON.parse(await fs.readFile(examplePath)); + delete example['linz:asset_summaries'][outerProperty][innerProperty]; + + // when + let valid = validate(example); + + // then + o(valid).equals(false); + o( + validate.errors.some( + (error) => + error.instancePath === `/linz:asset_summaries/${outerProperty}` && + error.message === `must have required property '${innerProperty}'`, + ), + ).equals(true)(JSON.stringify(validate.errors)); + } + } + }, + ); + + o("Asset summary created/updated with invalid 'minimum'/'maximum' value should fail validation", async () => { + for (const outerProperty of ['created', 'updated']) { + for (const innerProperty of ['minimum', 'maximum']) { + // given + const example = JSON.parse(await fs.readFile(examplePath)); + example['linz:asset_summaries'][outerProperty][innerProperty] = '1999-01-01T00:00:00'; + + // when + let valid = validate(example); + + // then + o(valid).equals(false); + o( + validate.errors.some( + (error) => + error.instancePath === `/linz:asset_summaries/${outerProperty}/${innerProperty}` && + error.message === 'must match pattern "(\\+00:00|Z)$"', + ), + ).equals(true)(JSON.stringify(validate.errors)); + } + } + }); + o("Example without the mandatory 'linz:history' field should fail validation", async () => { // given const example = JSON.parse(await fs.readFile(examplePath));