From 9cf00350ec2dc7210b0799dcbc935f6ec227d220 Mon Sep 17 00:00:00 2001 From: Ben Webb Date: Tue, 5 Feb 2019 14:01:43 +0900 Subject: [PATCH 01/15] Add fixture for testing better oneOf validation errors openownership/cove-bods#16 --- .../basic_statement_id_and_type_errors.json | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 tests/fixtures/api/basic_statement_id_and_type_errors.json diff --git a/tests/fixtures/api/basic_statement_id_and_type_errors.json b/tests/fixtures/api/basic_statement_id_and_type_errors.json new file mode 100644 index 0000000..d9a2b5f --- /dev/null +++ b/tests/fixtures/api/basic_statement_id_and_type_errors.json @@ -0,0 +1,73 @@ +[ + { + "statementType": "entityStatement", + "statementDate": "2017-11-18", + "entityType": "registeredEntity", + "name": "CHRINON LTD", + "foundingDate": "2010-11-18", + "identifiers": [ + { + "scheme": "GB-COH", + "id": "07444723" + } + ] + }, + { + "statementID": "shortID", + "statementType": "personStatement", + "statementDate": "2017-11-18", + "personType": "knownPerson", + "nationalities": [ + { + "code": "GB" + } + ], + "names": [ + { + "type": "individual", + "fullName": "Christopher Taggart", + "givenName": "Christopher", + "familyName": "Taggart" + }, + { + "type": "aka", + "fullName": "Chris Taggart" + } + ], + "birthDate": "1964-04", + "addresses": [ + { + "type": "service", + "address": "Aston House, Cornwall Avenue, London", + "country": "GB", + "postCode": "N3 1LF" + } + ] + }, + { + "statementType": "ownershipOrControlStatement", + "statementDate": "2017-11-18", + "subject": { + "describedByEntityStatement": "1dc0e987-5c57-4a1c-b3ad-61353b66a9b7" + }, + "interestedParty": { + "describedByPersonStatement": "019a93f1-e470-42e9-957b-03559861b2e2" + }, + "interests": [ + { + "type": "shareholding", + "interestLevel": "direct", + "beneficialOwnershipOrControl":true, + "startDate": "2016-04-06", + "share": { + "exact": 100 + } + } + ] + }, + { + }, + { + "statementType": "test" + } +] From c58d3aea7894655cd53c9b8d7044df4b0ef9f9e9 Mon Sep 17 00:00:00 2001 From: Ben Webb Date: Tue, 5 Feb 2019 14:51:36 +0900 Subject: [PATCH 02/15] Test the latest commit from lib-cove's cove-bods-16-oneof-validation https://github.com/OpenDataServices/lib-cove/pull/8 --- requirements.txt | 2 +- requirements_dev.txt | 2 +- tests/test_api.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index c0f24fa..44816cb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ -e git+https://github.com/OpenDataServices/flatten-tool.git@v0.5.0#egg=flattentool --e git+https://github.com/OpenDataServices/lib-cove.git@v0.3.1#egg=libcove +-e git+https://github.com/OpenDataServices/lib-cove.git@e1a6132fecdc2000ed03d82da9c2cc4dae3bd442#egg=libcove -e . diff --git a/requirements_dev.txt b/requirements_dev.txt index 9adb621..827c65e 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -1,5 +1,5 @@ -e git+https://github.com/OpenDataServices/flatten-tool.git@v0.5.0#egg=flattentool --e git+https://github.com/OpenDataServices/lib-cove.git@v0.3.1#egg=libcove +-e git+https://github.com/OpenDataServices/lib-cove.git@e1a6132fecdc2000ed03d82da9c2cc4dae3bd442#egg=libcove -e . pytest flake8 diff --git a/tests/test_api.py b/tests/test_api.py index 445fc56..b95887e 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -131,7 +131,7 @@ def test_basic_missing_statement_ids(): for validation_error, data in results['validation_errors']: validation_error_data = json.loads(validation_error) - assert 'is not valid under any of the given schemas' in validation_error_data['message'] + assert "'statementID' is missing but required" in validation_error_data['message'] def test_additional_fields_1(): From 104b79a38a3fa4a82581c1059aee5023ada98090 Mon Sep 17 00:00:00 2001 From: Ben Webb Date: Tue, 5 Feb 2019 15:54:19 +0900 Subject: [PATCH 03/15] Add a test for the new fixture, testing oneOf validation errors openownership/cove-bods#16 --- tests/test_api.py | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/tests/test_api.py b/tests/test_api.py index b95887e..1da6aaa 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -134,6 +134,53 @@ def test_basic_missing_statement_ids(): assert "'statementID' is missing but required" in validation_error_data['message'] +def test_basic_statement_id_and_type_errors(): + + cove_temp_folder = tempfile.mkdtemp(prefix='lib-cove-bods-tests-', dir=tempfile.gettempdir()) + json_filename = os.path.join(os.path.dirname( + os.path.realpath(__file__)), 'fixtures', 'api', 'basic_statement_id_and_type_errors.json' + ) + + results = bods_json_output(cove_temp_folder, json_filename) + + assert results['file_type'] == 'json' + assert results['validation_errors_count'] == 5 + assert results['additional_fields_count'] == 0 + assert results['additional_checks_count'] == 1 + assert results['statistics']['count_entity_statements'] == 1 + assert results['statistics']['count_person_statements'] == 1 + assert results['statistics']['count_ownership_or_control_statement'] == 1 + assert results['statistics']['count_ownership_or_control_statement_interested_party_with_person'] == 1 + assert results['statistics']['count_ownership_or_control_statement_interested_party_with_entity'] == 0 + + def unpack_validation_error(validation_error_result): + validation_error, data = validation_error_result + validation_error_data = json.loads(validation_error) + return validation_error_data, data + + validation_error_data, data = unpack_validation_error(results['validation_errors'][0]) + assert "'shortID' is too short" in validation_error_data['message'] + assert data[0]['path'] == '1/statementID' + assert data[0]['value'] == 'shortID' + + validation_error_data, data = unpack_validation_error(results['validation_errors'][1]) + assert "'statementID' is missing but required" in validation_error_data['message'] + assert data[0]['path'] == '0' + assert data[1]['path'] == '2' + + validation_error_data, data = unpack_validation_error(results['validation_errors'][2]) + assert "'statementType' is missing but required" in validation_error_data['message'] + assert data[0]['path'] == '3' + + validation_error_data, data = unpack_validation_error(results['validation_errors'][3]) + assert "Invalid code found in 'statementType'" in validation_error_data['message'] + assert data[0]['path'] == '4/statementType' + assert data[0]['value'] == 'test' + + assert results['additional_checks'][0]['type'] == 'person_statement_not_used_in_ownership_or_control_statement' + assert results['additional_checks'][0]['person_statement'] == 'shortID' + + def test_additional_fields_1(): cove_temp_folder = tempfile.mkdtemp(prefix='lib-cove-bods-tests-', dir=tempfile.gettempdir()) From 73db3b4b0cc6ec7b09ed0bc23c4ef235db4230dc Mon Sep 17 00:00:00 2001 From: Ben Webb Date: Fri, 15 Feb 2019 18:36:31 +0900 Subject: [PATCH 04/15] Start work on BODS fixture file that causes all validation msgs https://github.com/openownership/cove-bods/issues/16 --- .../api/badfile_all_validation_errors.json | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 tests/fixtures/api/badfile_all_validation_errors.json diff --git a/tests/fixtures/api/badfile_all_validation_errors.json b/tests/fixtures/api/badfile_all_validation_errors.json new file mode 100644 index 0000000..b43d7da --- /dev/null +++ b/tests/fixtures/api/badfile_all_validation_errors.json @@ -0,0 +1,21 @@ +[ +{}, +{"statementType": "bad statement type"}, +{"statementType": "personStatement"}, +{"statementType": {}}, +{"statementType": "personStatement", "statementID": 100}, +{"statementType": "personStatement", "statementID": "tooshort"}, +{"statementType": "personStatement", "statementID": "too long long long long long long long long long long long long long long long long"}, +{"statementType": "personStatement", "replacesStatements": ["tooshort"]}, +{"statementType": "personStatement", "replacesStatements": ["too long long long long long long long long long long long long long long long long"]}, +{"statementType": "personStatement", "replacesStatements": "not an array"}, +{"statementType": "personStatement", "statementDate": "not a date"}, +{"statementType": "personStatement", "statementID": "2f957a72-caaa-4363-bf45-1257f1b57db6", "personType": "bad person type"}, +{"statementType": "personStatement", "statementID": "2f957a72-caaa-4363-bf45-1257f1b57db6", "birthDate": "not a date"}, +{"statementType": "personStatement", "statementID": "2f957a72-caaa-4363-bf45-1257f1b57db6", "source":{"url": "not a uri"}}, +{"statementType": "personStatement", "statementID": "2f957a72-caaa-4363-bf45-1257f1b57db6", "source":{"retrievedAt": "not a date-time"}}, +{"statementType": "ownershipOrControlStatement", "statementID": "772099ea-419f-418b-b6b8-98492e5516bb", "interests": [{"share": {"exact": "not a number", "minimum":-1, "maximum":101}}]}, +{"statementType": "ownershipOrControlStatement", "statementID": "772099ea-419f-418b-b6b8-98492e5516bb", "interests": {"share": {"exact": "not a number", "minimum":-1, "maximum":101}}}, +{"statementType": "ownershipOrControlStatement", "statementID": "772099ea-419f-418b-b6b8-98492e5516bb", "interests": ["not an object"]}, +{"statementType": "ownershipOrControlStatement", "statementID": "772099ea-419f-418b-b6b8-98492e5516bb", "interests": [{"share": {"exclusiveMinimum": "not a bool"}}]} +] From a0658709b673d9cde07830123c62ecdcfdf2edd2 Mon Sep 17 00:00:00 2001 From: Ben Webb Date: Mon, 18 Feb 2019 14:46:07 +0900 Subject: [PATCH 05/15] Update the included schema to version 0.1 https://github.com/openownership/cove-bods/issues/16 --- data/schema.json | 2146 +++++++++++++++++++++++----------------------- 1 file changed, 1068 insertions(+), 1078 deletions(-) diff --git a/data/schema.json b/data/schema.json index 94718d6..cbeb0f5 100644 --- a/data/schema.json +++ b/data/schema.json @@ -1,18 +1,44 @@ { "id": "bods-package.json", "$schema": "http://json-schema.org/draft-04/schema#", + "version": "0.1", + "type": "array", "items": { "oneOf": [ { "id": "entity-statement.json", - "required": [ - "statementID", - "statementType", - "entityType" - ], + "$schema": "http://json-schema.org/draft-04/schema#", "version": "0.1", + "title": "Entity statement", + "description": "A statement identifying and describing the entity that is the subject of the ownership or control described in an ownership or control statement.", + "type": "object", "properties": { + "statementID": { + "title": "Statement Identifier", + "description": "A persistent globally unique identifier for this statement.", + "type": "string", + "minLength": 32, + "maxLenth": 64 + }, + "statementType": { + "title": "Statement type", + "description": " This should always be 'entityStatement.", + "type": "string", + "enum": [ + "entityStatement" + ], + "propertyOrder": 2 + }, + "statementDate": { + "title": "Statement date", + "description": "The date on which this statement was made.", + "type": "string", + "format": "date" + }, "entityType": { + "title": "Type", + "description": "From the [entityType codelist](#entitytype). What kind of entity is this? The 'registeredEntity' code covers any legal entity created through an act of official registration, usually resulting in an identifier being assigned to the entity. The 'legalEntity' code covers other bodies with distinct legal personality (government departments, international institutions etc.). The 'arrangement' code covers artificial entities, described in the data model for the purpose of associating one or more natural or legal persons together in an ownership or control relationship, but without implying that the parties to this arrangement have any other form of collective legal identity.", + "type": "string", "enum": [ "registeredEntity", "legalEntity", @@ -20,47 +46,193 @@ "anonymousEntity", "unknownEntity" ], - "openCodelist": false, - "title": "Type", - "type": "string", "codelist": "entityType.csv", - "description": "From the [entityType codelist](#entitytype). What kind of entity is this? The 'registeredEntity' code covers any legal entity created through an act of official registration, usually resulting in an identifier being assigned to the entity. The 'legalEntity' code covers other bodies with distinct legal personality (government departments, international institutions etc.). The 'arrangement' code covers artificial entities, described in the data model for the purpose of associating one or more natural or legal persons together in an ownership or control relationship, but without implying that the parties to this arrangement have any other form of collective legal identity.", + "openCodelist": false, "propertyOrder": 4 }, + "missingInfoReason": { + "title": "Missing information reason(s)", + "description": "For EntityStatements with the type 'anonymousEntity' or 'unknownEntity' this field should contain an explanation of the reason that detailed information on the entity is not provided. This may be a standard descriptive phrase from the source system, or a free-text justification.", + "type": "string", + "propertyOrder": 5 + }, + "name": { + "title": "Entity name", + "description": "The declared name of this entity.", + "type": "string", + "propertyOrder": 6 + }, + "alternateNames": { + "title": "Alternative names", + "description": "An array of other names this entity is known by.", + "type": "array", + "items": { + "type": "string", + "title": "Name", + "description": "A name this entity is known by." + }, + "propertyOrder": 7 + }, + "incorporatedInJurisdiction": { + "title": "Jurisdiction", + "description": "A jurisdiction MUST have a name. A jurisdiction SHOULD have an ISO ISO_3166-2 2-Digit country code, or ISO_3166-2 sub-division code.", + "type": "object", + "properties": { + "name": { + "title": "Name", + "description": "The name of the jurisdiction", + "type": "string" + }, + "code": { + "title": "Country code", + "description": "The ISO_3166-2 2-Digit country code, or ISO_3166-2 sub-division code of the jurisdiction", + "type": "string", + "maxLength": 6, + "minLength": 2 + } + } + }, + "identifiers": { + "title": "Identifiers", + "description": "One or more official identifiers for this entity. Where available, official registration numbers should be provided.", + "type": "array", + "items": { + "title": "Identifier", + "description": "An identifier that has been assigned to this person or entity. The scheme or list from which the identifier is drawn should be declared.", + "type": "object", + "properties": { + "id": { + "title": "ID", + "description": "The identifier for this person or entity as provided in the declared scheme.", + "type": "string" + }, + "scheme": { + "title": "Scheme", + "description": "For entity statements, the scheme should be a entry from the [org-id.guide](http://www.org-id.guide) codelist. For person statements, the scheme should have the pattern {JURISDICTION}-{TYPE} where JURISDICTION is an ISO 3-digit country code and TYPE is one of PASSPORT, TAXID or IDCARD.", + "type": "string" + }, + "schemeName": { + "title": "Scheme name", + "Description": "The name of this scheme, where the org-id code is unknown or only an unvalidated string is provided.", + "type": "string" + }, + "uri": { + "title": "URI", + "description": "Where this identifier has a [canonical URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) this may be included", + "type": "string", + "format": "uri" + } + }, + "anyOf": [ + { + "required": [ + "scheme" + ] + }, + { + "required": [ + "schemeName" + ] + }, + { + "required": [ + "scheme", + "schemeName" + ] + } + ] + }, + "propertyOrder": 20 + }, + "foundingDate": { + "title": "Founding date", + "description": "When was this entity founded, created or registered. Please provide as precise a date as possible in ISO 8601 format. When only the year or year and month is known, these can be given as YYYY or YYYY-MM.", + "type": "string", + "pattern": "^([\\+-]?\\d{4}(?!\\d{2}\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$", + "propertyOrder": 30 + }, "dissolutionDate": { - "description": "If this entity is no longer active, provide the date on which it was disolved or ceased. Please provide as precise a date as possible in ISO 8601 format. When only the year or year and month is known, these can be given as YYYY or YYYY-MM.", "title": "Dissolution date", + "description": "If this entity is no longer active, provide the date on which it was disolved or ceased. Please provide as precise a date as possible in ISO 8601 format. When only the year or year and month is known, these can be given as YYYY or YYYY-MM.", + "type": "string", "pattern": "^([\\+-]?\\d{4}(?!\\d{2}\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$", - "propertyOrder": 35, - "type": "string" + "propertyOrder": 35 + }, + "addresses": { + "title": "Addresses", + "description": "One or more addresses for this entity.", + "type": "array", + "items": { + "title": "Address", + "description": "A free text address string, providing as much address data as is relevant, suitable for processing using address parsing algorithms. For some uses (for example, Place of Birth) only a town and country are required.", + "type": "object", + "properties": { + "type": { + "title": "Type", + "description": "What type of address is this? See the [addressType](#addresstype) codelist.", + "type": "string", + "enum": [ + "placeOfBirth", + "home", + "residence", + "registered", + "service", + "alternative" + ], + "codelist": "addressType.csv", + "openCodelist": false + }, + "address": { + "title": "Address", + "description": "The address, with each line or component of the address separated by a line-break or comma. This field may also include the postal code. ", + "type": "string" + }, + "postCode": { + "title": "Postcode", + "description": "The postal code for this address.", + "type": "string" + }, + "country": { + "title": "Country", + "description": "The ISO 2-Digit county code for this address.", + "type": "string", + "minLength": 2, + "maxLength": 2 + } + } + }, + "propertyOrder": 40 + }, + "uri": { + "title": "URI", + "description": "Where a [persistent URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) is available for this entity this should be included.", + "type": "string", + "format": "uri", + "propertyOrder": 21 + }, + "replacesStatements": { + "title": "Replaces statement(s)", + "description": "If this statement replaces a previous statement or statements, provide the identifier(s) for the previous statement(s) here. Consuming applications are advised to mark the identified statements as no longer active.", + "type": "array", + "items": { + "title": "Statement identifier", + "description": "The identifier of a statement that is no longer active.", + "type": "string", + "minLength": 32, + "maxLength": 64 + } }, "source": { - "description": "The source of information about this entity.", "title": "Source", + "description": "The source object is used to explain where information in a statement originated from, and to link to supporting information.", "type": "object", - "propertyOrder": 89, "properties": { - "url": { - "description": "If this information was fetched from an external URL, or a machine or human readable web page is available that provides addition information on how this statement was sourced, provide the URL.", - "title": "Source URL", - "type": "string" - }, - "retrievedAt": { - "description": "If this statement was imported from some external system, include a timestamp indicating when this took place. The statement's own date should be set based on the source information. ", - "title": "Retrieved at", - "format": "datetime", - "type": "string" - }, - "description": { - "description": "Where required, additional free-text information about the source of this statement can be provided here.", - "title": "Description", - "type": "string" - }, "type": { - "description": "What type of source is this? Multiple tags can be combined. Values should come from the [source type](#sourcetype) codelist.", "title": "Source type", + "description": "What type of source is this? Multiple tags can be combined. Values should come from the [source type](#sourcetype) codelist.", "type": "array", "items": { + "type": "string", "enum": [ "selfDeclaration", "officialRegister", @@ -68,139 +240,293 @@ "primaryResearch", "verified" ], - "openCodelist": false, - "type": "string", - "codelist": "sourceType.csv" + "codelist": "sourceType.csv", + "openCodelist": false } }, + "description": { + "title": "Description", + "description": "Where required, additional free-text information about the source of this statement can be provided here.", + "type": "string" + }, + "url": { + "title": "Source URL", + "description": "If this information was fetched from an external URL, or a machine or human readable web page is available that provides additional information on how this statement was sourced, provide the URL.", + "type": "string" + }, + "retrievedAt": { + "title": "Retrieved at", + "description": "If this statement was imported from some external system, include a timestamp indicating when this took place. The statement's own date should be set based on the source information. ", + "type": "string", + "format": "date-time" + }, "assertedBy": { - "description": "Who is making this statement? This may be the name of the person or organisation making a self-declaration (in which case, please make sure the name field matches the organisation or person name field), or the name or description of some other party. If this statment has been verified, this may also include the name of the organisation providing verification.", "title": "Asserted by", + "description": "Who is making this statement? This may be the name of the person or organisation making a self-declaration (in which case, please make sure the name field matches the organisation or person name field), or the name or description of some other party. If this statement has been verified, this may also include the name of the organisation providing verification.", "type": "array", "items": { - "description": "An individual, organisation or other responsible agent making, or supporting, a given statement or annotation.", - "title": "Agent", "type": "object", + "title": "Agent", + "description": "An individual, organisation or other responsible agent making, or supporting, a given statement or annotation.", "properties": { - "uri": { - "description": "An optional URI to identify the agent.", - "title": "URI", - "format": "uri", - "type": "string" - }, "name": { - "description": "The name of the agent", "title": "Name", + "description": "The name of the agent", "type": "string" + }, + "uri": { + "title": "URI", + "description": "An optional URI to identify the agent.", + "type": "string", + "format": "uri" } } } } } }, - "addresses": { - "description": "One or more addresses for this entity.", - "title": "Addresses", + "annotations": { + "title": "Annotations", + "description": "Annotations about this statement or parts of this statement", "type": "array", - "propertyOrder": 40, "items": { - "description": "A free text address string, providing as much address data as is relevant, suitable for processing using address parsing algorithms. For some uses (for example, Place of Birth) only a town and country are required.", - "title": "Address", + "title": "Annotation", + "description": "An annotation provides additional information about ownership or control data being provided. Annotations can be provided in free-text, and can apply to a whole statement, an object or a single field. Additional extended properties can be included on the annotation object to provide structured data where required.", "type": "object", "properties": { - "address": { - "description": "The address, with each line or component of the address separated by a line-break or comma. This field may also include the postal code. ", - "title": "Address", + "statementPointerTarget": { + "title": "Statement Fragment Pointer", + "description": "An [RFC6901 JSON Pointer](https://tools.ietf.org/html/rfc6901) describing the target fragment of the statement that this annotation applies to, starting from the root of the statement. A value of '/' indicates that the annotation applies to the whole statement.", "type": "string" }, - "postCode": { - "description": "The postal code for this address.", - "title": "Postcode", - "type": "string" + "creationDate": { + "title": "Creation Date", + "description": "The date this annotation was created.", + "type": "string", + "format": "date" }, - "type": { + "createdBy": { + "title": "Created By", + "description": "The person, organisation or agent that created this annotation.", + "type": "object", + "properties": { + "name": { + "title": "Name", + "description": "The name of the person, organisation or agent that created this annotation.", + "type": "string" + }, + "uri": { + "title": "URI", + "description": "An optional URI to identify person, organisation or agent that created this annotation.", + "type": "string", + "format": "uri" + } + } + }, + "motivation": { + "title": "Motivation", + "description": "The motivation for this annotation, chosen from a codelist. See the [annotationMotivation](#annotationmotivation) codelist.", + "type": "string", "enum": [ - "placeOfBirth", - "home", - "residence", - "registered", - "service", - "alternative" + "commenting", + "correcting", + "identifying", + "linking", + "transformation" ], - "openCodelist": false, - "title": "Type", - "type": "string", - "codelist": "addressType.csv", - "description": "What type of address is this?" + "codelist": "annotationMotivation.csv", + "openCodelist": true }, - "country": { - "minLength": 2, - "description": "The ISO 2-Digit county code for this address.", - "title": "Country", - "maxLength": 2, + "description": { + "title": "Description", + "description": "A free-text description to annotate this statement or field.", "type": "string" + }, + "transformedContent": { + "type": "string", + "title": "Transformed content", + "description": "A representation of the annotation target after the transformation in the description field has been applied. This field SHOULD only be used when the motivation is transformation." + }, + "url": { + "title": "URL", + "description": "A linked resource that annotates, provides context for or enhances this statement. The content of the resource, or the relationship to the statement, MAY be described in the description field.", + "type": "string", + "format": "uri" } - } - } - }, - "missingInfoReason": { - "description": "For EntityStatements with the type 'anonymousEntity' or 'unknownEntity' this field should contain an explanation of the reason that detailed information on the entity is not provided. This may be a standard descriptive phrase from the source system, or a free-text justification.", - "title": "Missing information reason(s)", + }, + "anyOf": [ + { + "properties": { + "motivation": { + "enum": [ + "linking" + ] + } + }, + "required": [ + "statementPointerTarget", + "motivation" + ] + }, + { + "properties": { + "motivation": { + "enum": [ + "linking", + "identifying", + "commenting", + "correcting", + "transformation" + ] + } + }, + "required": [ + "statementPointerTarget", + "motivation" + ] + } + ] + }, + "propertyOrder": 90 + } + }, + "required": [ + "statementID", + "statementType", + "entityType" + ] + }, + { + "id": "person-statement.json", + "$schema": "http://json-schema.org/draft-04/schema#", + "version": "0.1", + "type": "object", + "title": "Person statement", + "description": "A person statement describes the information known about a natural person at a particular point in time, or from a given submission of information", + "properties": { + "statementID": { + "title": "Statement Identifier", + "description": "A persistent globally unique identifier for this statement.", "type": "string", - "propertyOrder": 5 + "minLength": 32, + "maxLenth": 64 }, - "name": { - "description": "The declared name of this entity.", - "title": "Entity name", + "statementType": { + "title": "Statement type", + "description": "This should always be 'personStatement.", "type": "string", - "propertyOrder": 6 + "enum": [ + "personStatement" + ], + "propertyOrder": 2, + "openCodelist": false, + "codelist": "statementType.csv" }, "statementDate": { - "description": "The date on which this statement was made.", "title": "Statement date", - "format": "date", + "description": "The date on which this statement was made.", "type": "string", - "propertyOrder": 3 + "format": "date" }, - "statementType": { + "personType": { + "title": "Person type", + "description": "Use the [personType codelist](#persontype). The ultimate beneficial owner of a legal entity is always a natural person. Where the beneficial owner has been identified, but information about them cannot be disclosed, use 'anonymousPerson'. Where the beneficial owner has not been clearly identified, use 'unknownPerson'. Where the beneficial owner has been identified use knownPerson.", + "type": "string", "enum": [ - "entityStatement" + "anonymousPerson", + "unknownPerson", + "knownPerson" ], - "description": " This should always be 'entityStatement.", - "title": "Statement type", - "type": "string", - "propertyOrder": 2 + "propertyOrder": 4, + "codelist": "personType.csv", + "openCodelist": false }, - "statementID": { - "minLength": 32, - "maxLenth": 64, - "title": "Statement Identifier", + "missingInfoReason": { + "title": "Missing information reason(s)", + "description": "For PersonStatements with the type 'anonymousPerson' or 'unknownPerson' this field should contain an explanation of the reason that detailed information on the person is not provided. This may be a standard descriptive phrase from the source system, or a free-text justification.", "type": "string", - "description": "A persistent globally unique identifier for this statement.", - "propertyOrder": 1 + "propertyOrder": 5 }, - "replacesStatements": { - "description": "If this statement replaces a previous statement or statements, provide the identifier(s) for the previous statement(s) here. Consuming applications are advised to mark the identified statements as no longer active.", - "title": "Replaces statement(s)", + "names": { + "title": "Names", + "description": "One or more known names for this individual.", "type": "array", - "propertyOrder": 100, "items": { - "minLength": 32, - "description": "The identifier of a statement that is no longer active.", - "title": "Statement identifier", - "maxLength": 64, - "type": "string" - } + "title": "Name", + "description": "An name by which this individual is known. Names should be provided in fullName, and may optionally be broken down in the familyName, givenName and patronymicName fields, based on the [EC ISA Core Person Vocabulary](https://joinup.ec.europa.eu/solution/e-government-core-vocabularies) definitions.", + "type": "object", + "properties": { + "type": { + "title": "Type", + "description": "What kind of name is this? See the [nameType](#nametype) codelist.", + "type": "string", + "enum": [ + "individual", + "translation", + "former", + "alias", + "aka", + "nick", + "birth" + ], + "codelist": "nameType.csv", + "openCodelist": false + }, + "fullName": { + "title": "Full name", + "description": "The full name contains the complete name of a person as one string.", + "type": "string" + }, + "familyName": { + "title": "Family name", + "description": "A family name is usually shared by members of a family. This attribute also carries prefixes or suffixes which are part of the Family Name, e.g. 'de Boer', 'van de Putte', 'von und zu Orlow'. Multiple family names, such as are commonly found in Hispanic countries, are recorded in the single Family Name field so that, for example, Miguel de Cervantes Saavedra's Family Name would be recorded as 'Cervantes Saavedra.'", + "type": "string" + }, + "givenName": { + "title": "Given names", + "description": "A given name, or multiple given names, are the denominator(s) that identify an individual within a family. These are given to a person by his or her parents at birth or may be legally recognised as 'given names' through a formal process. All given names are ordered in one field so that, for example, the given name for Johann Sebastian Bach is 'Johann Sebastian.'", + "type": "string" + }, + "patronymicName": { + "title": "Patronymic Name", + "description": "Patronymic names are important in some countries. Iceland does not have a concept of family name in the way that many other European countries do, for example. In Bulgaria and Russia, patronymic names are in every day usage, for example, the 'Sergeyevich' in 'Mikhail Sergeyevich Gorbachev'", + "type": "string" + } + } + }, + "propertyOrder": 10 }, "identifiers": { - "description": "One or more official identifiers for this entity. Where available, official registration numbers should be provided.", "title": "Identifiers", + "description": "One or more official identifiers for this perrson. Where available, official registration numbers should be provided.", "type": "array", - "propertyOrder": 20, "items": { - "description": "An identifier that has been assigned to this entity. The scheme or list from which the identifier is drawn should be declared.", "title": "Identifier", + "description": "An identifier that has been assigned to this person or entity. The scheme or list from which the identifier is drawn should be declared.", "type": "object", + "properties": { + "id": { + "title": "ID", + "description": "The identifier for this person or entity as provided in the declared scheme.", + "type": "string" + }, + "scheme": { + "title": "Scheme", + "description": "For entity statements, the scheme should be a entry from the [org-id.guide](http://www.org-id.guide) codelist. For person statements, the scheme should have the pattern {JURISDICTION}-{TYPE} where JURISDICTION is an ISO 3-digit country code and TYPE is one of PASSPORT, TAXID or IDCARD.", + "type": "string" + }, + "schemeName": { + "title": "Scheme name", + "Description": "The name of this scheme, where the org-id code is unknown or only an unvalidated string is provided.", + "type": "string" + }, + "uri": { + "title": "URI", + "description": "Where this identifier has a [canonical URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) this may be included", + "type": "string", + "format": "uri" + } + }, "anyOf": [ { "required": [ @@ -218,461 +544,97 @@ "schemeName" ] } - ], + ] + }, + "propertyOrder": 20 + }, + "nationalities": { + "title": "Nationality", + "description": "An array of ISO 2-Digit country codes representing nationalities held by this individual.", + "type": "array", + "items": { + "title": "Country", + "description": "A country MUST have a name. A country SHOULD have an ISO 2-Digit county code.", + "type": "object", "properties": { - "id": { - "description": "The identifier for this entity as provided in the declared scheme.", - "title": "ID", - "type": "string" - }, - "scheme": { - "description": "For entity statements, the scheme should be a entry from the [org-id.guide](http://www.org-id.guide) codelist. For person statements, recognised values include 'passport', 'internal' and 'id-card'.", - "title": "Scheme", - "type": "string" - }, - "schemeName": { - "Description": "The name of this scheme, where the org-id code is unknown or only an unvalidated string is provided.", - "title": "Scheme name", + "name": { + "title": "Name", + "description": "The name of the country", "type": "string" }, - "uri": { - "description": "Where this identifier has a [canonical URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) this may be included", - "title": "URI", - "format": "uri", - "type": "string" + "code": { + "title": "Country code", + "description": "The ISO 2-digit code for the country.", + "type": "string", + "maxLength": 2, + "minLength": 2 } } - } + }, + "propertyOrder": 30 }, - "incorporatedInJurisdiction": { - "description": "Details on where this legal entity is incorporated", - "title": "Incorporated In Jurisdiction", + "placeOfBirth": { + "title": "Address", + "description": "A free text address string, providing as much address data as is relevant, suitable for processing using address parsing algorithms. For some uses (for example, Place of Birth) only a town and country are required.", "type": "object", - "propertyOrder": 10, "properties": { - "code": { - "minLength": 2, - "description": "The ISO_3166-2 2-Digit country code, or ISO_3166-2 sub-division code of the jurisdiction", - "title": "Country code", - "maxLength": 6, - "type": "string" - }, - "name": { - "description": "The name of the jurisdiction", - "title": "Name", - "type": "string" - } - } - }, - "alternateNames": { - "description": "An array of other names this entity is known by.", - "title": "Alternative names", - "type": "array", - "propertyOrder": 7, - "items": { - "description": "A name this entity is known by.", - "title": "Name", - "type": "string" - } - }, - "foundingDate": { - "description": "When was this entity founded, created or registered. Please provide as precise a date as possible in ISO 8601 format. When only the year or year and month is known, these can be given as YYYY or YYYY-MM.", - "title": "Founding date", - "pattern": "^([\\+-]?\\d{4}(?!\\d{2}\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$", - "propertyOrder": 30, - "type": "string" - }, - "annotations": { - "description": "Annotations about this statement or parts of this statement", - "title": "Annotations", - "type": "array", - "propertyOrder": 90, - "items": { - "description": "An annotation provides additional information about ownership or control data being provided. Annotations can be provided in free-text, and can apply to a whole statement, an object or a single field. Additional extended properties can be included on the annotation object to provide structured data where required.", - "title": "Annotation", - "type": "object", - "anyOf": [ - { - "required": [ - "statementPointerTarget", - "motivation" - ], - "properties": { - "motivation": { - "enum": [ - "linking" - ] - } - } - }, - { - "required": [ - "statementPointerTarget", - "motivation" - ], - "properties": { - "motivation": { - "enum": [ - "linking", - "identifying", - "commenting", - "correcting", - "transformation" - ] - } - } - } - ], - "properties": { - "motivation": { - "enum": [ - "commenting", - "correcting", - "identifying", - "linking", - "transformation" - ], - "openCodelist": true, - "title": "Motivation", - "type": "string", - "codelist": "annotationMotivation.csv", - "description": "The motivation for this annotation, chosen from a codelist. See the [annotationMotivation](#annotationmotivation) codelist." - }, - "transformedContent": { - "description": "A representation of the annotation target after the transformation in the description field has been applied. This field SHOULD only be used when the motivation is transformation.", - "title": "Transformed content", - "type": "string" - }, - "statementPointerTarget": { - "description": "An [RFC6901 JSON Pointer](https://tools.ietf.org/html/rfc6901) describing the target fragment of the statement that this annotation applies to, starting from the root of the statement. A value of '/' indicates that the annotation applies to the whole statement.", - "title": "Statement Fragment Pointer", - "type": "string" - }, - "url": { - "description": "A linked resource that annotates, provides context for or enhances this statement. The content of the resource, or the relationship to the statement, MAY be described in the description field.", - "title": "URL", - "format": "uri", - "type": "string" - }, - "creationDate": { - "description": "The date this annotation was created.", - "title": "Creation Date", - "format": "date", - "type": "string" - }, - "createdBy": { - "description": "The person, organisation or agent that created this annotation.", - "title": "Created By", - "type": "object", - "properties": { - "uri": { - "description": "An optional URI to identify person, organisation or agent that created this annotation.", - "title": "URI", - "format": "uri", - "type": "string" - }, - "name": { - "description": "The name of the person, organisation or agent that created this annotation.", - "title": "Name", - "type": "string" - } - } - }, - "description": { - "description": "A free-text description to annotate this statment or field.", - "title": "Description", - "type": "string" - } - } - } - }, - "uri": { - "description": "Where a [persistent URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) is available for this entity this should be included.", - "title": "URI", - "format": "uri", - "type": "string", - "propertyOrder": 21 - } - }, - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "A statement identifying and describing the entity that is the subject of the ownership or control described in an ownership or control statement.", - "type": "object", - "title": "Entity statement" - }, - { - "id": "person-statment.json", - "required": [ - "statementID", - "statementType" - ], - "dependencies": { - "missingPersonReason": [ - "missingPersonType" - ] - }, - "version": "0.1", - "title": "Person statement", - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "A person statement describes the information known about a natural person at a particular point in time, or from a given submission of information", - "type": "object", - "properties": { - "names": { - "description": "One or more known names for this individual.", - "title": "Names", - "type": "array", - "propertyOrder": 10, - "items": { - "description": "An name by which this individual is known. Names should be provided in fullName, and may optionally be broken down in the familyName, givenName and patronymicName fields, based on the [EC ISA Core Person Vocabulary](https://joinup.ec.europa.eu/solution/e-government-core-vocabularies) definitions.", - "title": "Name", - "type": "object", - "properties": { - "fullName": { - "description": "The full name contains the complete name of a person as one string.", - "title": "Full name", - "type": "string" - }, - "type": { - "enum": [ - "individual", - "translation", - "former", - "alias", - "aka", - "nick", - "birth" - ], - "openCodelist": false, - "title": "Type", - "type": "string", - "codelist": "nameType.csv", - "description": "What kind of name is this? See the [nameType](#nametype) codelist." - }, - "givenName": { - "description": "A given name, or multiple given names, are the denominator(s) that identify an individual within a family. These are given to a person by his or her parents at birth or may be legally recognised as 'given names' through a formal process. All given names are ordered in one field so that, for example, the given name for Johann Sebastian Bach is 'Johann Sebastian.'", - "title": "Given names", - "type": "string" - }, - "familyName": { - "description": "A family name is usually shared by members of a family. This attribute also carries prefixes or suffixes which are part of the Family Name, e.g. 'de Boer', 'van de Putte', 'von und zu Orlow'. Multiple family names, such as are commonly found in Hispanic countries, are recorded in the single Family Name field so that, for example, Miguel de Cervantes Saavedra's Family Name would be recorded as 'Cervantes Saavedra.'", - "title": "Family name", - "type": "string" - }, - "patronymicName": { - "description": "Patronymic names are important in some countries. Iceland does not have a concept of family name in the way that many other European countries do, for example. In Bulgaria and Russia, patronymic names are in every day usage, for example, the 'Sergeyevich' in 'Mikhail Sergeyevich Gorbachev'", - "title": "Patronymic Name", - "type": "string" - } - } - } - }, - "source": { - "description": "The source of the information that links the entity and the interested party, or that supports a null statement.", - "title": "Source", - "type": "object", - "propertyOrder": 89, - "properties": { - "url": { - "description": "If this information was fetched from an external URL, or a machine or human readable web page is available that provides addition information on how this statement was sourced, provide the URL.", - "title": "Source URL", - "type": "string" - }, - "retrievedAt": { - "description": "If this statement was imported from some external system, include a timestamp indicating when this took place. The statement's own date should be set based on the source information. ", - "title": "Retrieved at", - "format": "datetime", - "type": "string" - }, - "description": { - "description": "Where required, additional free-text information about the source of this statement can be provided here.", - "title": "Description", - "type": "string" - }, - "type": { - "description": "What type of source is this? Multiple tags can be combined. Values should come from the [source type](#sourcetype) codelist.", - "title": "Source type", - "type": "array", - "items": { - "enum": [ - "selfDeclaration", - "officialRegister", - "thirdParty", - "primaryResearch", - "verified" - ], - "openCodelist": false, - "type": "string", - "codelist": "sourceType.csv" - } - }, - "assertedBy": { - "description": "Who is making this statement? This may be the name of the person or organisation making a self-declaration (in which case, please make sure the name field matches the organisation or person name field), or the name or description of some other party. If this statment has been verified, this may also include the name of the organisation providing verification.", - "title": "Asserted by", - "type": "array", - "items": { - "description": "An individual, organisation or other responsible agent making, or supporting, a given statement or annotation.", - "title": "Agent", - "type": "object", - "properties": { - "uri": { - "description": "An optional URI to identify the agent.", - "title": "URI", - "format": "uri", - "type": "string" - }, - "name": { - "description": "The name of the agent", - "title": "Name", - "type": "string" - } - } - } - } - } - }, - "personType": { - "enum": [ - "anonymousPerson", - "unknownPerson", - "knownPerson" - ], - "openCodelist": false, - "title": "Person type", - "type": "string", - "codelist": "personType.csv", - "description": "Use the [personType codelist](#persontype). The ultimate beneficial owner of a legal entity is always a natural person. Where the beneficial owner has been identified, but information about them cannot be disclosed, use 'anonymousPerson'. Where the beneficial owner has not been clearly identified, use 'unknownPerson'. Where the beneficial owner has been identified use knownPerson.", - "propertyOrder": 4 - }, - "pepStatus": { - "description": "One or more descriptions of this person's Politically-Exposed Person (PEP) status.", - "title": "Politically Exposed Person Status", - "type": "array", - "propertyOrder": 80, - "items": { - "description": "A description of a politically-exposed person status.", - "title": "PEP Status", - "type": "object", - "properties": { - "reason": { - "description": "The reason for this person being declared a politically-exposed person.", - "title": "Reason", - "type": "string" - }, - "endDate": { - "description": "When did this PEP status end. Please provide as precise a date as possible in ISO 8601 format. When only the year or year and month is known, these can be given as YYYY or YYYY-MM.", - "title": "End date", - "type": "string", - "pattern": "^([\\+-]?\\d{4}(?!\\d{2}\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$" - }, - "startDate": { - "description": "When did this PEP status begin. Please provide as precise a date as possible in ISO 8601 format. When only the year or year and month is known, these can be given as YYYY or YYYY-MM.", - "title": "State date", - "type": "string", - "pattern": "^([\\+-]?\\d{4}(?!\\d{2}\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$" - }, - "jurisdiction": { - "description": "A jurisdiction MUST have a name. A jurisdiction SHOULD have an ISO ISO_3166-2 2-Digit country code, or ISO_3166-2 sub-division code.", - "title": "Jurisdiction", - "type": "object", - "properties": { - "code": { - "minLength": 2, - "description": "The ISO_3166-2 2-Digit country code, or ISO_3166-2 sub-division code of the jurisdiction", - "title": "Country code", - "maxLength": 6, - "type": "string" - }, - "name": { - "description": "The name of the jurisdiction", - "title": "Name", - "type": "string" - } - } - } - } - } - }, - "addresses": { - "description": "One or more addresses for this entity.", - "title": "Addresses", - "type": "array", - "propertyOrder": 60, - "items": { - "description": "A free text address string, providing as much address data as is relevant, suitable for processing using address parsing algorithms. For some uses (for example, Place of Birth) only a town and country are required.", - "title": "Address", - "type": "object", - "properties": { - "address": { - "description": "The address, with each line or component of the address separated by a line-break or comma. This field may also include the postal code. ", - "title": "Address", - "type": "string" - }, - "postCode": { - "description": "The postal code for this address.", - "title": "Postcode", - "type": "string" - }, - "type": { - "enum": [ - "placeOfBirth", - "home", - "residence", - "registered", - "service", - "alternative" - ], - "openCodelist": false, - "title": "Type", - "type": "string", - "codelist": "addressType.csv", - "description": "What type of address is this?" - }, - "country": { - "minLength": 2, - "description": "The ISO 2-Digit county code for this address.", - "title": "Country", - "maxLength": 2, - "type": "string" - } + "type": { + "title": "Type", + "description": "What type of address is this? See the [addressType](#addresstype) codelist.", + "type": "string", + "enum": [ + "placeOfBirth", + "home", + "residence", + "registered", + "service", + "alternative" + ], + "codelist": "addressType.csv", + "openCodelist": false + }, + "address": { + "title": "Address", + "description": "The address, with each line or component of the address separated by a line-break or comma. This field may also include the postal code. ", + "type": "string" + }, + "postCode": { + "title": "Postcode", + "description": "The postal code for this address.", + "type": "string" + }, + "country": { + "title": "Country", + "description": "The ISO 2-Digit county code for this address.", + "type": "string", + "minLength": 2, + "maxLength": 2 } } }, - "missingInfoReason": { - "description": "For PersonStatements with the type 'anonymousPerson' or 'unknownPerson' this field should contain an explanation of the reason that detailed information on the person is not provided. This may be a standard descriptive phrase from the source system, or a free-text justification.", - "title": "Missing information reason(s)", + "birthDate": { + "title": "Date of birth", + "description": "The date of birth for this individual. Please provide as precise a date as possible in ISO 8601 format. When only the year or year and month is known, these can be given as YYYY or YYYY-MM.", "type": "string", - "propertyOrder": 5 + "pattern": "^([\\+-]?\\d{4}(?!\\d{2}\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$", + "propertyOrder": 35 }, "deathDate": { - "description": "If this individual is no longer alive, provide their date of death. Please provide as precise a date as possible in ISO 8601 format. When only the year or year and month is known, these can be given as YYYY or YYYY-MM.", "title": "Death date", - "pattern": "^([\\+-]?\\d{4}(?!\\d{2}\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$", - "propertyOrder": 36, - "type": "string" - }, - "statementDate": { - "description": "The date on which this statement was made.", - "title": "Statement date", - "format": "date", + "description": "If this individual is no longer alive, provide their date of death. Please provide as precise a date as possible in ISO 8601 format. When only the year or year and month is known, these can be given as YYYY or YYYY-MM.", "type": "string", - "propertyOrder": 3 + "pattern": "^([\\+-]?\\d{4}(?!\\d{2}\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$", + "propertyOrder": 36 }, - "placeOfBirth": { + "placeOfResidence": { + "title": "Address", "description": "A free text address string, providing as much address data as is relevant, suitable for processing using address parsing algorithms. For some uses (for example, Place of Birth) only a town and country are required.", - "title": "Place of birth", "type": "object", - "propertyOrder": 40, "properties": { - "address": { - "description": "The address, with each line or component of the address separated by a line-break or comma. This field may also include the postal code. ", - "title": "Address", - "type": "string" - }, - "postCode": { - "description": "The postal code for this address.", - "title": "Postcode", - "type": "string" - }, "type": { + "title": "Type", + "description": "What type of address is this? See the [addressType](#addresstype) codelist.", + "type": "string", "enum": [ "placeOfBirth", "home", @@ -681,219 +643,227 @@ "service", "alternative" ], - "openCodelist": false, - "title": "Type", - "type": "string", "codelist": "addressType.csv", - "description": "What type of address is this?" + "openCodelist": false + }, + "address": { + "title": "Address", + "description": "The address, with each line or component of the address separated by a line-break or comma. This field may also include the postal code. ", + "type": "string" + }, + "postCode": { + "title": "Postcode", + "description": "The postal code for this address.", + "type": "string" }, "country": { - "minLength": 2, - "description": "The ISO 2-Digit county code for this address.", "title": "Country", - "maxLength": 2, - "type": "string" + "description": "The ISO 2-Digit county code for this address.", + "type": "string", + "minLength": 2, + "maxLength": 2 } } }, - "statementType": { - "enum": [ - "personStatement" - ], - "openCodelist": false, - "title": "Statement type", - "type": "string", - "codelist": "statementType.csv", - "description": "This should always be 'personStatement.", - "propertyOrder": 2 - }, - "statementID": { - "minLength": 32, - "maxLenth": 64, - "title": "Statement Identifier", - "type": "string", - "description": "A persistent globally unique identifier for this statement.", - "propertyOrder": 1 - }, - "replacesStatements": { - "description": "If this statement replaces a previous statement or statements, provide the identifier(s) for the previous statement(s) here. Consuming applications are advised to mark the identified statements as no longer active.", - "title": "Replaces statement(s)", - "type": "array", - "items": { - "minLength": 32, - "description": "The identifier of a statement that is no longer active.", - "title": "Statement identifier", - "maxLength": 64, - "type": "string" - } - }, - "identifiers": { - "description": "One or more official identifiers for this perrson. Where available, official registration numbers should be provided.", - "title": "Identifiers", + "addresses": { + "title": "Addresses", + "description": "One or more addresses for this entity.", "type": "array", - "propertyOrder": 20, "items": { - "description": "An identifier that has been assigned to this entity. The scheme or list from which the identifier is drawn should be declared.", - "title": "Identifier", + "title": "Address", + "description": "A free text address string, providing as much address data as is relevant, suitable for processing using address parsing algorithms. For some uses (for example, Place of Birth) only a town and country are required.", "type": "object", - "anyOf": [ - { - "required": [ - "scheme" - ] + "properties": { + "type": { + "title": "Type", + "description": "What type of address is this? See the [addressType](#addresstype) codelist.", + "type": "string", + "enum": [ + "placeOfBirth", + "home", + "residence", + "registered", + "service", + "alternative" + ], + "codelist": "addressType.csv", + "openCodelist": false }, - { - "required": [ - "schemeName" - ] + "address": { + "title": "Address", + "description": "The address, with each line or component of the address separated by a line-break or comma. This field may also include the postal code. ", + "type": "string" }, - { - "required": [ - "scheme", - "schemeName" - ] + "postCode": { + "title": "Postcode", + "description": "The postal code for this address.", + "type": "string" + }, + "country": { + "title": "Country", + "description": "The ISO 2-Digit county code for this address.", + "type": "string", + "minLength": 2, + "maxLength": 2 } - ], + } + }, + "propertyOrder": 60 + }, + "pepStatus": { + "title": "Politically Exposed Person Status", + "description": "One or more descriptions of this person's Politically-Exposed Person (PEP) status.", + "type": "array", + "items": { + "title": "PEP Status", + "description": "A description of a politically-exposed person status.", + "type": "object", "properties": { - "id": { - "description": "The identifier for this entity as provided in the declared scheme.", - "title": "ID", + "reason": { + "title": "Reason", + "description": "The reason for this person being declared a politically-exposed person.", "type": "string" }, - "scheme": { - "description": "For entity statements, the scheme should be a entry from the [org-id.guide](http://www.org-id.guide) codelist. For person statements, recognised values include 'passport', 'internal' and 'id-card'.", - "title": "Scheme", - "type": "string" + "jurisdiction": { + "title": "Jurisdiction", + "description": "A jurisdiction MUST have a name. A jurisdiction SHOULD have an ISO ISO_3166-2 2-Digit country code, or ISO_3166-2 sub-division code.", + "type": "object", + "properties": { + "name": { + "title": "Name", + "description": "The name of the jurisdiction", + "type": "string" + }, + "code": { + "title": "Country code", + "description": "The ISO_3166-2 2-Digit country code, or ISO_3166-2 sub-division code of the jurisdiction", + "type": "string", + "maxLength": 6, + "minLength": 2 + } + } }, - "schemeName": { - "Description": "The name of this scheme, where the org-id code is unknown or only an unvalidated string is provided.", - "title": "Scheme name", - "type": "string" + "startDate": { + "title": "State date", + "description": "When did this PEP status begin. Please provide as precise a date as possible in ISO 8601 format. When only the year or year and month is known, these can be given as YYYY or YYYY-MM.", + "type": "string", + "pattern": "^([\\+-]?\\d{4}(?!\\d{2}\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$" }, - "uri": { - "description": "Where this identifier has a [canonical URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) this may be included", - "title": "URI", - "format": "uri", - "type": "string" + "endDate": { + "title": "End date", + "description": "When did this PEP status end. Please provide as precise a date as possible in ISO 8601 format. When only the year or year and month is known, these can be given as YYYY or YYYY-MM.", + "type": "string", + "pattern": "^([\\+-]?\\d{4}(?!\\d{2}\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$" } } - } - }, - "birthDate": { - "description": "The date of birth for this individual. Please provide as precise a date as possible in ISO 8601 format. When only the year or year and month is known, these can be given as YYYY or YYYY-MM.", - "title": "Date of birth", - "pattern": "^([\\+-]?\\d{4}(?!\\d{2}\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$", - "propertyOrder": 35, - "type": "string" + }, + "propertyOrder": 80 }, - "placeOfResidence": { - "description": "A free text address string, providing as much address data as is relevant, suitable for processing using address parsing algorithms. For some uses (for example, Place of Birth) only a town and country are required.", - "title": "Place of residence", + "source": { + "title": "Source", + "description": "The source object is used to explain where information in a statement originated from, and to link to supporting information.", "type": "object", - "propertyOrder": 50, "properties": { - "address": { - "description": "The address, with each line or component of the address separated by a line-break or comma. This field may also include the postal code. ", - "title": "Address", + "type": { + "title": "Source type", + "description": "What type of source is this? Multiple tags can be combined. Values should come from the [source type](#sourcetype) codelist.", + "type": "array", + "items": { + "type": "string", + "enum": [ + "selfDeclaration", + "officialRegister", + "thirdParty", + "primaryResearch", + "verified" + ], + "codelist": "sourceType.csv", + "openCodelist": false + } + }, + "description": { + "title": "Description", + "description": "Where required, additional free-text information about the source of this statement can be provided here.", "type": "string" }, - "postCode": { - "description": "The postal code for this address.", - "title": "Postcode", + "url": { + "title": "Source URL", + "description": "If this information was fetched from an external URL, or a machine or human readable web page is available that provides additional information on how this statement was sourced, provide the URL.", "type": "string" }, - "type": { - "enum": [ - "placeOfBirth", - "home", - "residence", - "registered", - "service", - "alternative" - ], - "openCodelist": false, - "title": "Type", + "retrievedAt": { + "title": "Retrieved at", + "description": "If this statement was imported from some external system, include a timestamp indicating when this took place. The statement's own date should be set based on the source information. ", "type": "string", - "codelist": "addressType.csv", - "description": "What type of address is this?" + "format": "date-time" }, - "country": { - "minLength": 2, - "description": "The ISO 2-Digit county code for this address.", - "title": "Country", - "maxLength": 2, - "type": "string" - } - } - }, - "nationalities": { - "description": "An array of ISO 2-Digit country codes representing nationalities held by this individual.", - "title": "Nationality", - "type": "array", - "propertyOrder": 30, - "items": { - "description": "A country MUST have a name. A country SHOULD have an ISO 2-Digit county code.", - "title": "Country", - "type": "object", - "properties": { - "code": { - "minLength": 2, - "description": "The ISO 2-digit code for the country.", - "title": "Country code", - "maxLength": 2, - "type": "string" - }, - "name": { - "description": "The name of the country", - "title": "Name", - "type": "string" + "assertedBy": { + "title": "Asserted by", + "description": "Who is making this statement? This may be the name of the person or organisation making a self-declaration (in which case, please make sure the name field matches the organisation or person name field), or the name or description of some other party. If this statement has been verified, this may also include the name of the organisation providing verification.", + "type": "array", + "items": { + "type": "object", + "title": "Agent", + "description": "An individual, organisation or other responsible agent making, or supporting, a given statement or annotation.", + "properties": { + "name": { + "title": "Name", + "description": "The name of the agent", + "type": "string" + }, + "uri": { + "title": "URI", + "description": "An optional URI to identify the agent.", + "type": "string", + "format": "uri" + } + } } } } }, "annotations": { - "description": "Annotations about this statement or parts of this statement", "title": "Annotations", + "description": "Annotations about this statement or parts of this statement", "type": "array", - "propertyOrder": 90, "items": { - "description": "An annotation provides additional information about ownership or control data being provided. Annotations can be provided in free-text, and can apply to a whole statement, an object or a single field. Additional extended properties can be included on the annotation object to provide structured data where required.", "title": "Annotation", + "description": "An annotation provides additional information about ownership or control data being provided. Annotations can be provided in free-text, and can apply to a whole statement, an object or a single field. Additional extended properties can be included on the annotation object to provide structured data where required.", "type": "object", - "anyOf": [ - { - "required": [ - "statementPointerTarget", - "motivation" - ], - "properties": { - "motivation": { - "enum": [ - "linking" - ] - } - } + "properties": { + "statementPointerTarget": { + "title": "Statement Fragment Pointer", + "description": "An [RFC6901 JSON Pointer](https://tools.ietf.org/html/rfc6901) describing the target fragment of the statement that this annotation applies to, starting from the root of the statement. A value of '/' indicates that the annotation applies to the whole statement.", + "type": "string" }, - { - "required": [ - "statementPointerTarget", - "motivation" - ], + "creationDate": { + "title": "Creation Date", + "description": "The date this annotation was created.", + "type": "string", + "format": "date" + }, + "createdBy": { + "title": "Created By", + "description": "The person, organisation or agent that created this annotation.", + "type": "object", "properties": { - "motivation": { - "enum": [ - "linking", - "identifying", - "commenting", - "correcting", - "transformation" - ] + "name": { + "title": "Name", + "description": "The name of the person, organisation or agent that created this annotation.", + "type": "string" + }, + "uri": { + "title": "URI", + "description": "An optional URI to identify person, organisation or agent that created this annotation.", + "type": "string", + "format": "uri" } } - } - ], - "properties": { + }, "motivation": { + "title": "Motivation", + "description": "The motivation for this annotation, chosen from a codelist. See the [annotationMotivation](#annotationmotivation) codelist.", + "type": "string", "enum": [ "commenting", "correcting", @@ -901,84 +871,152 @@ "linking", "transformation" ], - "openCodelist": true, - "title": "Motivation", - "type": "string", "codelist": "annotationMotivation.csv", - "description": "The motivation for this annotation, chosen from a codelist. See the [annotationMotivation](#annotationmotivation) codelist." + "openCodelist": true }, - "transformedContent": { - "description": "A representation of the annotation target after the transformation in the description field has been applied. This field SHOULD only be used when the motivation is transformation.", - "title": "Transformed content", + "description": { + "title": "Description", + "description": "A free-text description to annotate this statement or field.", "type": "string" }, - "statementPointerTarget": { - "description": "An [RFC6901 JSON Pointer](https://tools.ietf.org/html/rfc6901) describing the target fragment of the statement that this annotation applies to, starting from the root of the statement. A value of '/' indicates that the annotation applies to the whole statement.", - "title": "Statement Fragment Pointer", - "type": "string" + "transformedContent": { + "type": "string", + "title": "Transformed content", + "description": "A representation of the annotation target after the transformation in the description field has been applied. This field SHOULD only be used when the motivation is transformation." }, "url": { - "description": "A linked resource that annotates, provides context for or enhances this statement. The content of the resource, or the relationship to the statement, MAY be described in the description field.", "title": "URL", - "format": "uri", - "type": "string" - }, - "creationDate": { - "description": "The date this annotation was created.", - "title": "Creation Date", - "format": "date", - "type": "string" - }, - "createdBy": { - "description": "The person, organisation or agent that created this annotation.", - "title": "Created By", - "type": "object", + "description": "A linked resource that annotates, provides context for or enhances this statement. The content of the resource, or the relationship to the statement, MAY be described in the description field.", + "type": "string", + "format": "uri" + } + }, + "anyOf": [ + { "properties": { - "uri": { - "description": "An optional URI to identify person, organisation or agent that created this annotation.", - "title": "URI", - "format": "uri", - "type": "string" - }, - "name": { - "description": "The name of the person, organisation or agent that created this annotation.", - "title": "Name", - "type": "string" + "motivation": { + "enum": [ + "linking" + ] } - } + }, + "required": [ + "statementPointerTarget", + "motivation" + ] }, - "description": { - "description": "A free-text description to annotate this statment or field.", - "title": "Description", - "type": "string" + { + "properties": { + "motivation": { + "enum": [ + "linking", + "identifying", + "commenting", + "correcting", + "transformation" + ] + } + }, + "required": [ + "statementPointerTarget", + "motivation" + ] } - } + ] + }, + "propertyOrder": 90 + }, + "replacesStatements": { + "title": "Replaces statement(s)", + "description": "If this statement replaces a previous statement or statements, provide the identifier(s) for the previous statement(s) here. Consuming applications are advised to mark the identified statements as no longer active.", + "type": "array", + "items": { + "title": "Statement identifier", + "description": "The identifier of a statement that is no longer active.", + "type": "string", + "minLength": 32, + "maxLength": 64 } } + }, + "required": [ + "statementID", + "statementType" + ], + "dependencies": { + "missingPersonReason": [ + "missingPersonType" + ] } }, { "id": "ownership-or-control-statement.json", - "definitions": { - "InterestedParty": { - "description": "The interested party has some level of ownership or control over the entity referenced in this ownership or control statement. This should be described with reference to either an entity statement or person statement, or, where the interested party is unknown, details of why. ", + "$schema": "http://json-schema.org/draft-04/schema#", + "version": "0.1", + "title": "Ownership or control Statement", + "description": "An ownership or control statement is made up of an entity, an interested party (a reference to an entity, natural person, arrangement or trust), details of the interest and provenance information for the statement.", + "type": "object", + "properties": { + "statementID": { + "title": "Statement Identifier", + "description": "A persistent globally unique identifier for this statement.", + "type": "string", + "minLength": 32, + "maxLenth": 64 + }, + "statementType": { + "title": "Statement type", + "description": "This should always be set to `ownershipOrControlStatement`.", + "type": "string", + "enum": [ + "ownershipOrControlStatement" + ] + }, + "statementDate": { + "title": "Statement date", + "description": "The date on which this statement was made.", + "type": "string", + "format": "date" + }, + "subject": { + "title": "Subject", + "description": "The subject of an ownership or control relationship.", + "type": "object", + "properties": { + "describedByEntityStatement": { + "title": "Described by entity statement", + "description": "Provide the identifier of the statement which describes the entity that the subject of an ownership or control interest.", + "type": "string" + } + }, + "required": [ + "describedByEntityStatement" + ] + }, + "interestedParty": { "title": "Interested party", + "description": "The interested party has some level of ownership or control over the entity referenced in this ownership or control statement. This should be described with reference to either an entity statement or person statement, or, where the interested party is unknown, details of why. ", "type": "object", "properties": { "describedByEntityStatement": { - "description": "A reference to a statement describing a registered entity, trust or arrangement that has an ownership or control interest in the subject of this statement. An entityStatement should be used when the direct interests to be described represents known control or ownership by anyone other than a natural person.", "title": "Described by entity statement", + "description": "A reference to a statement describing a registered entity, trust or arrangement that has an ownership or control interest in the subject of this statement. An entityStatement should be used when the direct interests to be described represents known control or ownership by anyone other than a natural person.", + "type": "string" + }, + "describedByPersonStatement": { + "title": "Described by person statement", + "description": "A reference to a statement describing a natural person who has an ownership or control interest in the subject of this statement.", "type": "string" }, "unspecified": { - "required": [ - "reason" - ], - "description": "When confirmation has been provided that no interested party exists, where ownership and control information does not need to be provided, or where details of ownership and control are unknown, information explaining this should be given using the unspecified reason and description. Where there is a natural person with ownership or control, but their name or details are not known, or cannot be disclosed for some reason, unspecified should not be used, but instead a reference to a personStatement should be provided but identifying details of the person left blank.", "title": "Unspecified or unknown ownership and control", + "description": "When confirmation has been provided that no interested party exists, where ownership and control information does not need to be provided, or where details of ownership and control are unknown, information explaining this should be given using the unspecified reason and description. Where there is a natural person with ownership or control, but their name or details are not known, or cannot be disclosed for some reason, unspecified should not be used, but instead a reference to a personStatement should be provided but identifying details of the person left blank.", "type": "object", "properties": { "reason": { + "title": "Reason", + "description": "The reason that an interested party cannot be specified. From the [unspecifiedReason codelist](#unspecifiedreason).", + "type": "string", "enum": [ "no-beneficial-owners", "subject-unable-to-confirm-or-identify-beneficial-owner", @@ -987,87 +1025,139 @@ "interested-party-exempt-from-disclosure", "unknown" ], - "openCodelist": false, - "title": "Reason", - "type": "string", "codelist": "unspecifiedReason.csv", - "description": "The reason that an interested party cannot be specified. From the [unspecifiedReason codelist](#unspecifiedreason)." + "openCodelist": false }, "description": { - "description": "Any supporting information about the absence of a confirmed beneficial owner. This field may be used to provide set phrases from a source system, or for a free-text explanation.", "title": "Description", + "description": "Any supporting information about the absence of a confirmed beneficial owner. This field may be used to provide set phrases from a source system, or for a free-text explanation.", "type": "string" } - } - }, - "describedByPersonStatement": { - "description": "A reference to a statement describing a natural person who has an ownership or control interest in the subject of this statement.", - "title": "Described by person statement", - "type": "string" + }, + "required": [ + "reason" + ] } } - } - }, - "required": [ - "statementID", - "statementType", - "subject", - "interestedParty" - ], - "version": "0.2-beta", - "title": "Ownership or control Statement", - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "An ownership or control statement is made up of an entity, an interested party (a reference to an entity, natural person, arrangement or trust), details of the interest and provenance information for the statement.", - "type": "object", - "properties": { - "subject": { - "required": [ - "describedByEntityStatement" - ], - "description": "The subject of an ownership or control relationship.", - "title": "Subject", - "type": "object", - "properties": { - "describedByEntityStatement": { - "description": "Provide the identifier of the statement which describes the entity that the subject of an ownership or control interest.", - "title": "Described by entity statement", - "type": "string" + }, + "interests": { + "title": "Interests", + "description": "A description of the interests held by the interestedParty covered by this statement in the entity covered by this statement.", + "type": "array", + "items": { + "title": "Interest", + "description": "A description of the interest held by an interestedParty in another entity.", + "type": "object", + "properties": { + "type": { + "title": "Type of interest", + "description": "A codelist value indicating the nature of the interest. See the [interestType codelist](#interesttype)", + "type": "string", + "enum": [ + "shareholding", + "voting-rights", + "appointment-of-board", + "influence-or-control", + "senior-managing-official", + "settlor-of-trust", + "trustee-of-trust", + "protector-of-trust", + "beneficiary-of-trust", + "other-influence-or-control-of-trust", + "rights-to-surplus-assets", + "rights-to-profit-or-income" + ], + "codelist": "interestType.csv", + "openCodelist": false + }, + "interestLevel": { + "title": "Interest level", + "description": "Is this interest held directly or indirectly?", + "type": "string", + "enum": [ + "direct", + "indirect", + "unknown" + ], + "codelist": "interestLevel.csv", + "openCodelist": false + }, + "beneficialOwnershipOrControl": { + "title": "Beneficial ownership or control", + "description": "Does this statement assert this as a beneficial ownership or control interest? A beneficial ownership or control interest is always between a natural person and some entity, and exists where the person ultimately benefits from, or has a degree of control over, the entity. There may be cases where a person has an interest in an entity, but where there are arrangements or other conditions that mean this interest does not constitute beneficial ownership or control.", + "type": "boolean" + }, + "details": { + "title": "Details", + "description": "This field may be used to provide the local name given to this kind of interest, or any further semi-structured or unstructured information to clarify the nature of the interest held.", + "type": "string" + }, + "share": { + "title": "Percentage share", + "description": "Where an exact percentage is available, this should be given, and maximum and minimum values set to the same as the exact percentage. Otherwise, maximum and minimum can be used to record the range into which the share of this kind of interest falls.", + "type": "object", + "properties": { + "exact": { + "title": "Exact share", + "description": "The exact share of this interest held (where available).", + "type": "number", + "maximum": 100, + "minimum": 0 + }, + "maximum": { + "title": "Maximum share", + "description": "The upper bound of the share of this interest known to be held.", + "type": "number", + "maximum": 100, + "minimum": 0 + }, + "minimum": { + "title": "Minimum share", + "description": "The lower bound of the share of this interest known to be held.", + "type": "number", + "maximum": 100, + "minimum": 0 + }, + "exclusiveMinimum": { + "title": "Exclusive minimum", + "description": "If exclusiveMinimum is true, then the share is at least greater than the minimum value given. E.g. if minimum is 25, the share is at least 25.1, and not simply 25.", + "type": "boolean", + "default": false + }, + "exclusiveMaximum": { + "title": "Exclusive maximum", + "description": "If exclusiveMaximum is true, then the share is at least less than the maximum value given. E.g. if maximum is 50, the share is less than 49.999, and not simply 50.", + "type": "boolean", + "default": true + } + } + }, + "startDate": { + "title": "State date", + "description": "When did this interest first occur. Please provide as precise a date as possible in ISO 8601 format. When only the year or year and month is known, these can be given as YYYY or YYYY-MM.", + "type": "string", + "pattern": "^([\\+-]?\\d{4}(?!\\d{2}\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$" + }, + "endDate": { + "title": "End date", + "description": "When did this interest cease. Please provide as precise a date as possible in ISO 8601 format. When only the year or year and month is known, these can be given as YYYY or YYYY-MM.", + "type": "string", + "pattern": "^([\\+-]?\\d{4}(?!\\d{2}\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$" + } } } }, - "statementID": { - "minLength": 32, - "description": "A persistent globally unique identifier for this statement.", - "title": "Statement Identifier", - "type": "string", - "maxLenth": 64 - }, "source": { - "description": "The source of the information that links the entity and the interested party, or that supports a null statement.", "title": "Source", + "description": "The source object is used to explain where information in a statement originated from, and to link to supporting information.", "type": "object", "properties": { - "url": { - "description": "If this information was fetched from an external URL, or a machine or human readable web page is available that provides addition information on how this statement was sourced, provide the URL.", - "title": "Source URL", - "type": "string" - }, - "retrievedAt": { - "description": "If this statement was imported from some external system, include a timestamp indicating when this took place. The statement's own date should be set based on the source information. ", - "title": "Retrieved at", - "format": "datetime", - "type": "string" - }, - "description": { - "description": "Where required, additional free-text information about the source of this statement can be provided here.", - "title": "Description", - "type": "string" - }, "type": { - "description": "What type of source is this? Multiple tags can be combined. Values should come from the [source type](#sourcetype) codelist.", "title": "Source type", + "description": "What type of source is this? Multiple tags can be combined. Values should come from the [source type](#sourcetype) codelist.", "type": "array", "items": { + "type": "string", "enum": [ "selfDeclaration", "officialRegister", @@ -1075,174 +1165,198 @@ "primaryResearch", "verified" ], - "openCodelist": false, - "type": "string", - "codelist": "sourceType.csv" + "codelist": "sourceType.csv", + "openCodelist": false } }, + "description": { + "title": "Description", + "description": "Where required, additional free-text information about the source of this statement can be provided here.", + "type": "string" + }, + "url": { + "title": "Source URL", + "description": "If this information was fetched from an external URL, or a machine or human readable web page is available that provides additional information on how this statement was sourced, provide the URL.", + "type": "string" + }, + "retrievedAt": { + "title": "Retrieved at", + "description": "If this statement was imported from some external system, include a timestamp indicating when this took place. The statement's own date should be set based on the source information. ", + "type": "string", + "format": "date-time" + }, "assertedBy": { - "description": "Who is making this statement? This may be the name of the person or organisation making a self-declaration (in which case, please make sure the name field matches the organisation or person name field), or the name or description of some other party. If this statment has been verified, this may also include the name of the organisation providing verification.", "title": "Asserted by", + "description": "Who is making this statement? This may be the name of the person or organisation making a self-declaration (in which case, please make sure the name field matches the organisation or person name field), or the name or description of some other party. If this statement has been verified, this may also include the name of the organisation providing verification.", "type": "array", "items": { - "description": "An individual, organisation or other responsible agent making, or supporting, a given statement or annotation.", - "title": "Agent", "type": "object", + "title": "Agent", + "description": "An individual, organisation or other responsible agent making, or supporting, a given statement or annotation.", "properties": { - "uri": { - "description": "An optional URI to identify the agent.", - "title": "URI", - "format": "uri", - "type": "string" - }, "name": { - "description": "The name of the agent", "title": "Name", + "description": "The name of the agent", "type": "string" + }, + "uri": { + "title": "URI", + "description": "An optional URI to identify the agent.", + "type": "string", + "format": "uri" } } } } } }, - "replacesStatements": { - "description": "If this statement replaces a previous statement or statements, provide the identifier(s) for the previous statement(s) here. Consuming applications are advised to mark the identified statements as no longer active.", - "title": "Replaces statement(s)", - "type": "array", - "items": { - "minLength": 32, - "description": "The identifier of a statement that is no longer active.", - "title": "Statement identifier", - "maxLength": 64, - "type": "string" - } - }, - "interests": { - "description": "A description of the interests held by the interestedParty covered by this statement in the entity covered by this statement.", - "title": "Interests", + "annotations": { + "title": "Annotations", + "description": "Annotations about this statement or parts of this statement", "type": "array", "items": { - "description": "A description of the interest held by an interestedParty in another entity.", - "title": "Interest", + "title": "Annotation", + "description": "An annotation provides additional information about ownership or control data being provided. Annotations can be provided in free-text, and can apply to a whole statement, an object or a single field. Additional extended properties can be included on the annotation object to provide structured data where required.", "type": "object", "properties": { - "type": { - "enum": [ - "shareholding", - "voting-rights", - "appointment-of-board", - "influence-or-control", - "senior-managing-official", - "settlor-of-trust", - "trustee-of-trust", - "protector-of-trust", - "beneficiary-of-trust", - "other-influence-or-control-of-trust", - "rights-to-surplus-assets", - "rights-to-profit-or-income" - ], - "openCodelist": false, - "title": "Type of interest", - "type": "string", - "codelist": "interestType.csv", - "description": "A codelist value indicating the nature of the interest. See the [interestType codelist](#interesttype)" - }, - "interestLevel": { - "enum": [ - "direct", - "indirect", - "unknown" - ], - "openCodelist": false, - "title": "Interest level", - "type": "string", - "codelist": "interestLevel.csv", - "description": "Is this interest held directly or indirectly?" - }, - "details": { - "description": "This field may be used to provide the local name given to this kind of interest, or any further semi-structured or unstructured information to clarify the nature of the interest held.", - "title": "Details", + "statementPointerTarget": { + "title": "Statement Fragment Pointer", + "description": "An [RFC6901 JSON Pointer](https://tools.ietf.org/html/rfc6901) describing the target fragment of the statement that this annotation applies to, starting from the root of the statement. A value of '/' indicates that the annotation applies to the whole statement.", "type": "string" }, - "share": { - "description": "Where an exact percentage is available, this should be given, and maximum and minimum values set to the same as the exact percentage. Otherwise, maximum and minimum can be used to record the range into which the share of this kind of interest falls.", - "title": "Percentage share", + "creationDate": { + "title": "Creation Date", + "description": "The date this annotation was created.", + "type": "string", + "format": "date" + }, + "createdBy": { + "title": "Created By", + "description": "The person, organisation or agent that created this annotation.", "type": "object", "properties": { - "exclusiveMinimum": { - "description": "If exclusiveMinimum is true, then the share is at least greater than the minimum value given. E.g. if minimum is 25, the share is at least 25.1, and not simply 25.", - "title": "Exclusive minimum", - "type": "boolean", - "default": false - }, - "exclusiveMaximum": { - "description": "If exclusiveMaximum is true, then the share is at least less than the maximum value given. E.g. if maximum is 50, the share is less than 49.999, and not simply 50.", - "title": "Exclusive maximum", - "type": "boolean", - "default": true - }, - "maximum": { - "description": "The upper bound of the share of this interest known to be held.", - "title": "Maximum share", - "minimum": 0, - "type": "number", - "maximum": 100 - }, - "minimum": { - "description": "The lower bound of the share of this interest known to be held.", - "title": "Minimum share", - "minimum": 0, - "type": "number", - "maximum": 100 + "name": { + "title": "Name", + "description": "The name of the person, organisation or agent that created this annotation.", + "type": "string" }, - "exact": { - "description": "The exact share of this interest held (where available).", - "title": "Exact share", - "minimum": 0, - "type": "number", - "maximum": 100 + "uri": { + "title": "URI", + "description": "An optional URI to identify person, organisation or agent that created this annotation.", + "type": "string", + "format": "uri" } } }, - "endDate": { - "description": "When did this interest cease. Please provide as precise a date as possible in ISO 8601 format. When only the year or year and month is known, these can be given as YYYY or YYYY-MM.", - "title": "End date", + "motivation": { + "title": "Motivation", + "description": "The motivation for this annotation, chosen from a codelist. See the [annotationMotivation](#annotationmotivation) codelist.", "type": "string", - "pattern": "^([\\+-]?\\d{4}(?!\\d{2}\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$" + "enum": [ + "commenting", + "correcting", + "identifying", + "linking", + "transformation" + ], + "codelist": "annotationMotivation.csv", + "openCodelist": true }, - "startDate": { - "description": "When did this interest first occur. Please provide as precise a date as possible in ISO 8601 format. When only the year or year and month is known, these can be given as YYYY or YYYY-MM.", - "title": "State date", + "description": { + "title": "Description", + "description": "A free-text description to annotate this statement or field.", + "type": "string" + }, + "transformedContent": { "type": "string", - "pattern": "^([\\+-]?\\d{4}(?!\\d{2}\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$" + "title": "Transformed content", + "description": "A representation of the annotation target after the transformation in the description field has been applied. This field SHOULD only be used when the motivation is transformation." }, - "beneficialOwnershipOrControl": { - "description": "Does this statement assert this as a beneficial ownership or control interest? A beneficial ownership or control interest is always between a natural person and some entity, and exists where the person ultimately benefits from, or has a degree of control over, the entity. There may be cases where a person has an interest in an entity, but where there are arrangements or other conditions that mean this interest does not constitute beneficial ownership or control.", - "title": "Beneficial ownership or control", - "type": "boolean" + "url": { + "title": "URL", + "description": "A linked resource that annotates, provides context for or enhances this statement. The content of the resource, or the relationship to the statement, MAY be described in the description field.", + "type": "string", + "format": "uri" } - } + }, + "anyOf": [ + { + "properties": { + "motivation": { + "enum": [ + "linking" + ] + } + }, + "required": [ + "statementPointerTarget", + "motivation" + ] + }, + { + "properties": { + "motivation": { + "enum": [ + "linking", + "identifying", + "commenting", + "correcting", + "transformation" + ] + } + }, + "required": [ + "statementPointerTarget", + "motivation" + ] + } + ] } }, - "interestedParty": { - "description": "The interested party has some level of ownership or control over the entity referenced in this ownership or control statement. This should be described with reference to either an entity statement or person statement, or, where the interested party is unknown, details of why. ", + "replacesStatements": { + "title": "Replaces statement(s)", + "description": "If this statement replaces a previous statement or statements, provide the identifier(s) for the previous statement(s) here. Consuming applications are advised to mark the identified statements as no longer active.", + "type": "array", + "items": { + "title": "Statement identifier", + "description": "The identifier of a statement that is no longer active.", + "type": "string", + "minLength": 32, + "maxLength": 64 + } + } + }, + "required": [ + "statementID", + "statementType", + "subject", + "interestedParty" + ], + "definitions": { + "InterestedParty": { "title": "Interested party", + "description": "The interested party has some level of ownership or control over the entity referenced in this ownership or control statement. This should be described with reference to either an entity statement or person statement, or, where the interested party is unknown, details of why. ", "type": "object", "properties": { "describedByEntityStatement": { - "description": "A reference to a statement describing a registered entity, trust or arrangement that has an ownership or control interest in the subject of this statement. An entityStatement should be used when the direct interests to be described represents known control or ownership by anyone other than a natural person.", "title": "Described by entity statement", + "description": "A reference to a statement describing a registered entity, trust or arrangement that has an ownership or control interest in the subject of this statement. An entityStatement should be used when the direct interests to be described represents known control or ownership by anyone other than a natural person.", + "type": "string" + }, + "describedByPersonStatement": { + "title": "Described by person statement", + "description": "A reference to a statement describing a natural person who has an ownership or control interest in the subject of this statement.", "type": "string" }, "unspecified": { - "required": [ - "reason" - ], - "description": "When confirmation has been provided that no interested party exists, where ownership and control information does not need to be provided, or where details of ownership and control are unknown, information explaining this should be given using the unspecified reason and description. Where there is a natural person with ownership or control, but their name or details are not known, or cannot be disclosed for some reason, unspecified should not be used, but instead a reference to a personStatement should be provided but identifying details of the person left blank.", "title": "Unspecified or unknown ownership and control", + "description": "When confirmation has been provided that no interested party exists, where ownership and control information does not need to be provided, or where details of ownership and control are unknown, information explaining this should be given using the unspecified reason and description. Where there is a natural person with ownership or control, but their name or details are not known, or cannot be disclosed for some reason, unspecified should not be used, but instead a reference to a personStatement should be provided but identifying details of the person left blank.", "type": "object", "properties": { "reason": { + "title": "Reason", + "description": "The reason that an interested party cannot be specified. From the [unspecifiedReason codelist](#unspecifiedreason).", + "type": "string", "enum": [ "no-beneficial-owners", "subject-unable-to-confirm-or-identify-beneficial-owner", @@ -1251,147 +1365,23 @@ "interested-party-exempt-from-disclosure", "unknown" ], - "openCodelist": false, - "title": "Reason", - "type": "string", "codelist": "unspecifiedReason.csv", - "description": "The reason that an interested party cannot be specified. From the [unspecifiedReason codelist](#unspecifiedreason)." + "openCodelist": false }, "description": { - "description": "Any supporting information about the absence of a confirmed beneficial owner. This field may be used to provide set phrases from a source system, or for a free-text explanation.", "title": "Description", + "description": "Any supporting information about the absence of a confirmed beneficial owner. This field may be used to provide set phrases from a source system, or for a free-text explanation.", "type": "string" } - } - }, - "describedByPersonStatement": { - "description": "A reference to a statement describing a natural person who has an ownership or control interest in the subject of this statement.", - "title": "Described by person statement", - "type": "string" - } - } - }, - "statementDate": { - "description": "The date on which this statement was made.", - "title": "Statement date", - "format": "date", - "type": "string" - }, - "annotations": { - "description": "Annotations about this statement or parts of this statement", - "title": "Annotations", - "type": "array", - "items": { - "description": "An annotation provides additional information about ownership or control data being provided. Annotations can be provided in free-text, and can apply to a whole statement, an object or a single field. Additional extended properties can be included on the annotation object to provide structured data where required.", - "title": "Annotation", - "type": "object", - "anyOf": [ - { - "required": [ - "statementPointerTarget", - "motivation" - ], - "properties": { - "motivation": { - "enum": [ - "linking" - ] - } - } - }, - { - "required": [ - "statementPointerTarget", - "motivation" - ], - "properties": { - "motivation": { - "enum": [ - "linking", - "identifying", - "commenting", - "correcting", - "transformation" - ] - } - } - } - ], - "properties": { - "motivation": { - "enum": [ - "commenting", - "correcting", - "identifying", - "linking", - "transformation" - ], - "openCodelist": true, - "title": "Motivation", - "type": "string", - "codelist": "annotationMotivation.csv", - "description": "The motivation for this annotation, chosen from a codelist. See the [annotationMotivation](#annotationmotivation) codelist." - }, - "transformedContent": { - "description": "A representation of the annotation target after the transformation in the description field has been applied. This field SHOULD only be used when the motivation is transformation.", - "title": "Transformed content", - "type": "string" - }, - "statementPointerTarget": { - "description": "An [RFC6901 JSON Pointer](https://tools.ietf.org/html/rfc6901) describing the target fragment of the statement that this annotation applies to, starting from the root of the statement. A value of '/' indicates that the annotation applies to the whole statement.", - "title": "Statement Fragment Pointer", - "type": "string" - }, - "url": { - "description": "A linked resource that annotates, provides context for or enhances this statement. The content of the resource, or the relationship to the statement, MAY be described in the description field.", - "title": "URL", - "format": "uri", - "type": "string" - }, - "creationDate": { - "description": "The date this annotation was created.", - "title": "Creation Date", - "format": "date", - "type": "string" - }, - "createdBy": { - "description": "The person, organisation or agent that created this annotation.", - "title": "Created By", - "type": "object", - "properties": { - "uri": { - "description": "An optional URI to identify person, organisation or agent that created this annotation.", - "title": "URI", - "format": "uri", - "type": "string" - }, - "name": { - "description": "The name of the person, organisation or agent that created this annotation.", - "title": "Name", - "type": "string" - } - } }, - "description": { - "description": "A free-text description to annotate this statment or field.", - "title": "Description", - "type": "string" - } + "required": [ + "reason" + ] } } - }, - "statementType": { - "enum": [ - "ownershipOrControlStatement" - ], - "description": "This should always be set to `ownershipOrControlStatement`.", - "title": "Statement type", - "type": "string" } } } ] - }, - "type": "array", - "version": "0.1" -} + } +} \ No newline at end of file From dbb3cd4f4dc1ba08dcf5d3e987decfc02fa57096 Mon Sep 17 00:00:00 2001 From: Ben Webb Date: Mon, 18 Feb 2019 14:51:02 +0900 Subject: [PATCH 06/15] Finish work on BODS fixture file that causes all validation msgs https://github.com/openownership/cove-bods/issues/16 --- data/schema.json | 2 +- tests/fixtures/api/badfile_all_validation_errors.json | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/data/schema.json b/data/schema.json index cbeb0f5..e18a4ed 100644 --- a/data/schema.json +++ b/data/schema.json @@ -1384,4 +1384,4 @@ } ] } -} \ No newline at end of file +} diff --git a/tests/fixtures/api/badfile_all_validation_errors.json b/tests/fixtures/api/badfile_all_validation_errors.json index b43d7da..cf294c0 100644 --- a/tests/fixtures/api/badfile_all_validation_errors.json +++ b/tests/fixtures/api/badfile_all_validation_errors.json @@ -12,10 +12,13 @@ {"statementType": "personStatement", "statementDate": "not a date"}, {"statementType": "personStatement", "statementID": "2f957a72-caaa-4363-bf45-1257f1b57db6", "personType": "bad person type"}, {"statementType": "personStatement", "statementID": "2f957a72-caaa-4363-bf45-1257f1b57db6", "birthDate": "not a date"}, -{"statementType": "personStatement", "statementID": "2f957a72-caaa-4363-bf45-1257f1b57db6", "source":{"url": "not a uri"}}, {"statementType": "personStatement", "statementID": "2f957a72-caaa-4363-bf45-1257f1b57db6", "source":{"retrievedAt": "not a date-time"}}, +{"statementType": "personStatement", "statementID": "2f957a72-caaa-4363-bf45-1257f1b57db6", "missingPersonReason": "no missingPersonType"}, +{"statementType": "personStatement", "statementID": "2f957a72-caaa-4363-bf45-1257f1b57db6", "identifiers":[{}]}, {"statementType": "ownershipOrControlStatement", "statementID": "772099ea-419f-418b-b6b8-98492e5516bb", "interests": [{"share": {"exact": "not a number", "minimum":-1, "maximum":101}}]}, {"statementType": "ownershipOrControlStatement", "statementID": "772099ea-419f-418b-b6b8-98492e5516bb", "interests": {"share": {"exact": "not a number", "minimum":-1, "maximum":101}}}, {"statementType": "ownershipOrControlStatement", "statementID": "772099ea-419f-418b-b6b8-98492e5516bb", "interests": ["not an object"]}, -{"statementType": "ownershipOrControlStatement", "statementID": "772099ea-419f-418b-b6b8-98492e5516bb", "interests": [{"share": {"exclusiveMinimum": "not a bool"}}]} +{"statementType": "ownershipOrControlStatement", "statementID": "772099ea-419f-418b-b6b8-98492e5516bb", "interests": [{"share": {"exclusiveMinimum": "not a bool"}}]}, +{"statementType": "ownershipOrControlStatement", "statementID": "772099ea-419f-418b-b6b8-98492e5516bb", "annotations":[{"motivation": "not on open list"}]}, +{"statementType": "entityStatement", "statementID": "a484ec70-ef24-45ca-ae2a-52453ca2fe9b", "uri": "not a uri"} ] From ba170c59fabb4ffbb22ec4b7552165e0cf25e88e Mon Sep 17 00:00:00 2001 From: Ben Webb Date: Mon, 25 Feb 2019 19:04:54 +0900 Subject: [PATCH 07/15] Test the output from validating file that causes all validation messages https://github.com/openownership/cove-bods/issues/16 --- requirements.txt | 2 +- requirements_dev.txt | 2 +- tests/test_api.py | 72 +++++++++++++++++++++++++++++++++++++++----- 3 files changed, 66 insertions(+), 10 deletions(-) diff --git a/requirements.txt b/requirements.txt index 44816cb..cf50894 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ -e git+https://github.com/OpenDataServices/flatten-tool.git@v0.5.0#egg=flattentool --e git+https://github.com/OpenDataServices/lib-cove.git@e1a6132fecdc2000ed03d82da9c2cc4dae3bd442#egg=libcove +-e git+https://github.com/OpenDataServices/lib-cove.git@fe0ec370280e2986e6d47dff458a08313acceaef#egg=libcove -e . diff --git a/requirements_dev.txt b/requirements_dev.txt index 827c65e..213e078 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -1,5 +1,5 @@ -e git+https://github.com/OpenDataServices/flatten-tool.git@v0.5.0#egg=flattentool --e git+https://github.com/OpenDataServices/lib-cove.git@e1a6132fecdc2000ed03d82da9c2cc4dae3bd442#egg=libcove +-e git+https://github.com/OpenDataServices/lib-cove.git@fe0ec370280e2986e6d47dff458a08313acceaef#egg=libcove -e . pytest flake8 diff --git a/tests/test_api.py b/tests/test_api.py index 1da6aaa..6c33b9a 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -4,6 +4,42 @@ from libcovebods.api import bods_json_output +BADFILE_RESULTS = [ + ({'message': "'entityType' is missing but required. Check that the field is included and correctly spelled.", 'message_safe': 'entityType is missing but required. Check that the field is included and correctly spelled.', 'message_type': 'required', 'path_no_number': ''}, [{'path': '21'}]), # noqa + ({'message': "'exact' should be a number. Check that the value is not null, and doesn’t contain any characters other than 0-9 and dot ('.'). Number values should not be in quotes. ", 'message_safe': 'exact should be a number. Check that the value is not null, and doesn’t contain any characters other than 0-9 and dot (.). Number values should not be in quotes. ', 'message_type': 'number', 'path_no_number': 'interests/share/exact'}, [{'path': '16/interests/0/share/exact', 'value': 'not a number'}]), # noqa + ({'message': "'exclusiveMinimum' should be a JSON boolean, 'true' or 'false'.", 'message_safe': 'exclusiveMinimum should be a JSON boolean, true or false.', 'message_type': 'boolean', 'path_no_number': 'interests/share/exclusiveMinimum'}, [{'path': '19/interests/0/share/exclusiveMinimum', 'value': 'not a bool'}]), # noqa + ({'message': "'interestedParty' is missing but required. Check that the field is included and correctly spelled.", 'message_safe': 'interestedParty is missing but required. Check that the field is included and correctly spelled.', 'message_type': 'required', 'path_no_number': ''}, [{'path': '16'}, {'path': '17'}, {'path': '18'}, {'path': '19'}, {'path': '20'}]), # noqa + ({'message': "'interests' should be a JSON array. Check that value(s) appear within square brackets, [...]", 'message_safe': 'interests should be a JSON array. Check that value(s) appear within square brackets, [...]', 'message_type': 'array', 'path_no_number': 'interests'}, [{'path': '17/interests'}]), # noqa + ({'message': "'interests/0' should be a JSON object", 'message_safe': 'interests/0 should be a JSON object', 'message_type': 'object', 'path_no_number': 'interests'}, [{'path': '18/interests/0', 'value': 'not an object'}]), # noqa + ({'message': "'missingPersonType' is a dependency of 'missingPersonReason'", 'message_safe': ''missingPersonType' is a dependency of 'missingPersonReason'', 'message_type': 'dependencies', 'path_no_number': ''}, [{'path': '14'}]), # noqa + ({'message': "'motivation' contains an unrecognised value. Check the related codelist for allowed code values.", 'message_safe': 'motivation contains an unrecognised value. Check the related codelist for allowed code values.', 'message_type': 'enum', 'path_no_number': 'annotations/motivation'}, [{'path': '20/annotations/0/motivation', 'value': 'not on open list'}]), # noqa + ({'message': "'not a date' does not match '^([\\\\+-]?\\\\d{4}(?!\\\\d{2}\\x08))((-?)((0[1-9]|1[0-2])(\\\\3([12]\\\\d|0[1-9]|3[01]))?|W([0-4]\\\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\\\d|[12]\\\\d{2}|3([0-5]\\\\d|6[1-6])))([T\\\\s]((([01]\\\\d|2[0-3])((:?)[0-5]\\\\d)?|24\\\\:?00)([\\\\.,]\\\\d+(?!:))?)?(\\\\17[0-5]\\\\d([\\\\.,]\\\\d+)?)?([zZ]|([\\\\+-])([01]\\\\d|2[0-3]):?([0-5]\\\\d)?)?)?)?$'", 'message_safe': 'birthDate does not match the regex ^([\\+-]?\\d{4}(?!\\d{2}\x08))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$', 'message_type': 'pattern', 'path_no_number': 'birthDate'}, [{'path': '12/birthDate', 'value': 'not a date'}]), # noqa + ({'message': "'personType' contains an unrecognised value. Check the related codelist for allowed code values.", 'message_safe': 'personType contains an unrecognised value. Check the related codelist for allowed code values.', 'message_type': 'enum', 'path_no_number': 'personType'}, [{'path': '11/personType', 'value': 'bad person type'}]), # noqa + ({'message': "'replacesStatements' should be a JSON array. Check that value(s) appear within square brackets, [...]", 'message_safe': 'replacesStatements should be a JSON array. Check that value(s) appear within square brackets, [...]', 'message_type': 'array', 'path_no_number': 'replacesStatements'}, [{'path': '9/replacesStatements', 'value': 'not an array'}]), # noqa + ({'message': "'statementID' is missing but required. Check that the field is included and correctly spelled.", 'message_safe': 'statementID is missing but required. Check that the field is included and correctly spelled.', 'message_type': 'required', 'path_no_number': ''}, [{'path': '2'}, {'path': '7'}, {'path': '8'}, {'path': '9'}, {'path': '10'}]), # noqa + ({'message': "'statementID' should be a string. Check that the value is not null, and has quotes at the start and end. Escape any quotes in the value with '\\'", 'message_safe': 'statementID should be a string. Check that the value is not null, and has quotes at the start and end. Escape any quotes in the value with \\', 'message_type': 'string', 'path_no_number': 'statementID'}, [{'path': '4/statementID', 'value': 100}]), # noqa + ({'message': "'statementType' contains an unrecognised value. Check the related codelist for allowed code values.", 'message_safe': 'statementType contains an unrecognised value. Check the related codelist for allowed code values.', 'message_type': 'enum', 'path_no_number': 'statementType'}, [{'path': '1/statementType', 'value': 'bad statement type'}, {'path': '3/statementType'}]), # noqa + ({'message': "'statementType' is missing but required. Check that the field is included and correctly spelled.", 'message_safe': 'statementType is missing but required. Check that the field is included and correctly spelled.', 'message_type': 'required', 'path_no_number': ''}, [{'path': '0'}]), # noqa + ({'message': "'subject' is missing but required. Check that the field is included and correctly spelled.", 'message_safe': 'subject is missing but required. Check that the field is included and correctly spelled.', 'message_type': 'required', 'path_no_number': ''}, [{'path': '16'}, {'path': '17'}, {'path': '18'}, {'path': '19'}, {'path': '20'}]), # noqa + ({'message': "'too long long long long long long long long long long long long long long long long' is too long", 'message_safe': 'replacesStatements/0 is too long. It should not exceed 64 characters.', 'message_type': 'maxLength', 'path_no_number': 'replacesStatements'}, [{'path': '8/replacesStatements/0', 'value': 'too long long long long long long long long long long long long long long long long'}]), # noqa + ({'message': "'tooshort' is too short", 'message_safe': 'replacesStatements/0 is too short. It should be at least 32 characters.', 'message_type': 'minLength', 'path_no_number': 'replacesStatements'}, [{'path': '7/replacesStatements/0', 'value': 'tooshort'}]), # noqa + ({'message': "'tooshort' is too short", 'message_safe': 'statementID is too short. It should be at least 32 characters.', 'message_type': 'minLength', 'path_no_number': 'statementID'}, [{'path': '5/statementID', 'value': 'tooshort'}]), # noqa + ({'message': '-1 is less than the minimum of 0', 'message_safe': 'minimum is too small. The minimum allowed value is 0.', 'message_type': 'minimum', 'path_no_number': 'interests/share/minimum'}, [{'path': '16/interests/0/share/minimum', 'value': -1}]), # noqa + ({'message': '101 is greater than the maximum of 100', 'message_safe': 'maximum is too large. The maximum allowed value is 100.', 'message_type': 'maximum', 'path_no_number': 'interests/share/maximum'}, [{'path': '16/interests/0/share/maximum', 'value': 101}]), # noqa + ({'message': 'Date is not in the correct format. The correct format is YYYY-MM-DD.', 'message_safe': 'Date is not in the correct format. The correct format is YYYY-MM-DD.', 'message_type': 'date', 'path_no_number': 'statementDate'}, [{'path': '10/statementDate', 'value': 'not a date'}]), # noqa + ({'message': 'Date is not in the correct format. The correct format is YYYY-MM-DDThh:mm:ssZ.', 'message_safe': 'Date is not in the correct format. The correct format is YYYY-MM-DDT00:00:00Z.', 'message_type': 'date-time', 'path_no_number': 'source/retrievedAt'}, [{'path': '13/source/retrievedAt', 'value': 'not a date-time'}]), # noqa + ({'message': 'Invalid uri found', 'message_safe': 'Invalid uri found', 'message_type': 'uri', 'path_no_number': 'uri'}, [{'path': '21/uri', 'value': 'not a uri'}]), # noqa + ({'message': "{'motivation': 'not on open list'} is not valid under any of the given schemas", 'message_safe': '{'motivation': 'not on open list'} is not valid under any of the given schemas', 'message_type': 'anyOf', 'path_no_number': 'annotations'}, [{'path': '20/annotations/0'}]), # noqa + ({'message': '{} is not valid under any of the given schemas', 'message_safe': '{} is not valid under any of the given schemas', 'message_type': 'anyOf', 'path_no_number': 'identifiers'}, [{'path': '15/identifiers/0'}]), # noqa +] + + +def unpack_validation_error(validation_error_result): + validation_error, data = validation_error_result + validation_error_data = json.loads(validation_error) + return validation_error_data, data + + def test_basic_1(): cove_temp_folder = tempfile.mkdtemp(prefix='lib-cove-bods-tests-', dir=tempfile.gettempdir()) @@ -153,11 +189,6 @@ def test_basic_statement_id_and_type_errors(): assert results['statistics']['count_ownership_or_control_statement_interested_party_with_person'] == 1 assert results['statistics']['count_ownership_or_control_statement_interested_party_with_entity'] == 0 - def unpack_validation_error(validation_error_result): - validation_error, data = validation_error_result - validation_error_data = json.loads(validation_error) - return validation_error_data, data - validation_error_data, data = unpack_validation_error(results['validation_errors'][0]) assert "'shortID' is too short" in validation_error_data['message'] assert data[0]['path'] == '1/statementID' @@ -168,12 +199,12 @@ def unpack_validation_error(validation_error_result): assert data[0]['path'] == '0' assert data[1]['path'] == '2' - validation_error_data, data = unpack_validation_error(results['validation_errors'][2]) + validation_error_data, data = unpack_validation_error(results['validation_errors'][3]) assert "'statementType' is missing but required" in validation_error_data['message'] assert data[0]['path'] == '3' - validation_error_data, data = unpack_validation_error(results['validation_errors'][3]) - assert "Invalid code found in 'statementType'" in validation_error_data['message'] + validation_error_data, data = unpack_validation_error(results['validation_errors'][2]) + assert "'statementType' contains an unrecognised value. Check the related codelist for allowed code values." in validation_error_data['message'] # noqa assert data[0]['path'] == '4/statementType' assert data[0]['value'] == 'test' @@ -374,3 +405,28 @@ def test_basic_bad_identifier_scheme(): assert results['additional_checks'][0]['type'] == 'entity_identifier_scheme_not_known' assert results['additional_checks'][0]['scheme'] == 'GB-COH-THIS-SCHEME-IS-NOT-REAL-I-JUST-MADE-IT-UP-MWAHAHAHAHA' assert results['additional_checks'][0]['entity_statement'] == '1dc0e987-5c57-4a1c-b3ad-61353b66a9b7' + + +def test_badfile_all_validation_errors(): + + cove_temp_folder = tempfile.mkdtemp(prefix='lib-cove-bods-tests-', dir=tempfile.gettempdir()) + json_filename = os.path.join(os.path.dirname( + os.path.realpath(__file__)), 'fixtures', 'api', 'badfile_all_validation_errors.json' + ) + + results = bods_json_output(cove_temp_folder, json_filename) + + assert results['validation_errors_count'] == 39 + assert results['additional_fields_count'] == 1 + assert results['additional_checks_count'] == 9 + assert results['file_type'] == 'json' + + for (i, (expected_error, expected_values)) in enumerate(BADFILE_RESULTS): + error, values = unpack_validation_error(results['validation_errors'][i]) + assert error['message'] == expected_error['message'] + assert error['message_safe'] == expected_error['message_safe'] + assert error['message_type'] == expected_error['message_type'] + assert error['path_no_number'] == expected_error['path_no_number'] + + for j, value in enumerate(expected_values): + assert value['path'] == expected_values[j]['path'] From 59d855177e9fe2f2ad9eb1e449df3c135fb81427 Mon Sep 17 00:00:00 2001 From: Ben Webb Date: Mon, 25 Feb 2019 19:05:12 +0900 Subject: [PATCH 08/15] Most recent jsonschema causes issues for us on Python 3.6 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 515136b..f32cf92 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ long_description='A data review library', install_requires=[ 'jsonref', - 'jsonschema', + 'jsonschema<=2.6.0', 'CommonMark', 'Django', 'bleach', From 71cc69a7328887bbe4242cb4b4ef0c1a1a93b093 Mon Sep 17 00:00:00 2001 From: Ben Webb Date: Wed, 27 Feb 2019 16:01:33 +0900 Subject: [PATCH 09/15] Install extra packages required for jsonschema format validation See the bottom of https://python-jsonschema.readthedocs.io/en/latest/validate/ --- setup.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/setup.py b/setup.py index f32cf92..540a35b 100644 --- a/setup.py +++ b/setup.py @@ -12,6 +12,10 @@ install_requires=[ 'jsonref', 'jsonschema<=2.6.0', + # Required for jsonschema to validate URIs + 'rfc3987', + # Required for jsonschema to validate date-time + 'strict-rfc3339', 'CommonMark', 'Django', 'bleach', From 09a8d0b714623f742ca17f1b9391ea9e7b05adf0 Mon Sep 17 00:00:00 2001 From: Ben Webb Date: Fri, 22 Mar 2019 16:14:48 +0900 Subject: [PATCH 10/15] Revert "Update the included schema to version 0.1" This reverts commit a0658709b673d9cde07830123c62ecdcfdf2edd2. This will be replaced with the version 0.1 schema on the master branch when that is merged in. --- data/schema.json | 2138 +++++++++++++++++++++++----------------------- 1 file changed, 1073 insertions(+), 1065 deletions(-) diff --git a/data/schema.json b/data/schema.json index e18a4ed..98917f1 100644 --- a/data/schema.json +++ b/data/schema.json @@ -1,44 +1,18 @@ { "id": "bods-package.json", "$schema": "http://json-schema.org/draft-04/schema#", - "version": "0.1", - "type": "array", "items": { "oneOf": [ { "id": "entity-statement.json", - "$schema": "http://json-schema.org/draft-04/schema#", + "required": [ + "statementID", + "statementType", + "entityType" + ], "version": "0.1", - "title": "Entity statement", - "description": "A statement identifying and describing the entity that is the subject of the ownership or control described in an ownership or control statement.", - "type": "object", "properties": { - "statementID": { - "title": "Statement Identifier", - "description": "A persistent globally unique identifier for this statement.", - "type": "string", - "minLength": 32, - "maxLenth": 64 - }, - "statementType": { - "title": "Statement type", - "description": " This should always be 'entityStatement.", - "type": "string", - "enum": [ - "entityStatement" - ], - "propertyOrder": 2 - }, - "statementDate": { - "title": "Statement date", - "description": "The date on which this statement was made.", - "type": "string", - "format": "date" - }, "entityType": { - "title": "Type", - "description": "From the [entityType codelist](#entitytype). What kind of entity is this? The 'registeredEntity' code covers any legal entity created through an act of official registration, usually resulting in an identifier being assigned to the entity. The 'legalEntity' code covers other bodies with distinct legal personality (government departments, international institutions etc.). The 'arrangement' code covers artificial entities, described in the data model for the purpose of associating one or more natural or legal persons together in an ownership or control relationship, but without implying that the parties to this arrangement have any other form of collective legal identity.", - "type": "string", "enum": [ "registeredEntity", "legalEntity", @@ -46,193 +20,47 @@ "anonymousEntity", "unknownEntity" ], - "codelist": "entityType.csv", "openCodelist": false, - "propertyOrder": 4 - }, - "missingInfoReason": { - "title": "Missing information reason(s)", - "description": "For EntityStatements with the type 'anonymousEntity' or 'unknownEntity' this field should contain an explanation of the reason that detailed information on the entity is not provided. This may be a standard descriptive phrase from the source system, or a free-text justification.", - "type": "string", - "propertyOrder": 5 - }, - "name": { - "title": "Entity name", - "description": "The declared name of this entity.", - "type": "string", - "propertyOrder": 6 - }, - "alternateNames": { - "title": "Alternative names", - "description": "An array of other names this entity is known by.", - "type": "array", - "items": { - "type": "string", - "title": "Name", - "description": "A name this entity is known by." - }, - "propertyOrder": 7 - }, - "incorporatedInJurisdiction": { - "title": "Jurisdiction", - "description": "A jurisdiction MUST have a name. A jurisdiction SHOULD have an ISO ISO_3166-2 2-Digit country code, or ISO_3166-2 sub-division code.", - "type": "object", - "properties": { - "name": { - "title": "Name", - "description": "The name of the jurisdiction", - "type": "string" - }, - "code": { - "title": "Country code", - "description": "The ISO_3166-2 2-Digit country code, or ISO_3166-2 sub-division code of the jurisdiction", - "type": "string", - "maxLength": 6, - "minLength": 2 - } - } - }, - "identifiers": { - "title": "Identifiers", - "description": "One or more official identifiers for this entity. Where available, official registration numbers should be provided.", - "type": "array", - "items": { - "title": "Identifier", - "description": "An identifier that has been assigned to this person or entity. The scheme or list from which the identifier is drawn should be declared.", - "type": "object", - "properties": { - "id": { - "title": "ID", - "description": "The identifier for this person or entity as provided in the declared scheme.", - "type": "string" - }, - "scheme": { - "title": "Scheme", - "description": "For entity statements, the scheme should be a entry from the [org-id.guide](http://www.org-id.guide) codelist. For person statements, the scheme should have the pattern {JURISDICTION}-{TYPE} where JURISDICTION is an ISO 3-digit country code and TYPE is one of PASSPORT, TAXID or IDCARD.", - "type": "string" - }, - "schemeName": { - "title": "Scheme name", - "Description": "The name of this scheme, where the org-id code is unknown or only an unvalidated string is provided.", - "type": "string" - }, - "uri": { - "title": "URI", - "description": "Where this identifier has a [canonical URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) this may be included", - "type": "string", - "format": "uri" - } - }, - "anyOf": [ - { - "required": [ - "scheme" - ] - }, - { - "required": [ - "schemeName" - ] - }, - { - "required": [ - "scheme", - "schemeName" - ] - } - ] - }, - "propertyOrder": 20 - }, - "foundingDate": { - "title": "Founding date", - "description": "When was this entity founded, created or registered. Please provide as precise a date as possible in ISO 8601 format. When only the year or year and month is known, these can be given as YYYY or YYYY-MM.", + "title": "Type", "type": "string", - "pattern": "^([\\+-]?\\d{4}(?!\\d{2}\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$", - "propertyOrder": 30 + "codelist": "entityType.csv", + "description": "From the [entityType codelist](#entitytype). What kind of entity is this? The 'registeredEntity' code covers any legal entity created through an act of official registration, usually resulting in an identifier being assigned to the entity. The 'legalEntity' code covers other bodies with distinct legal personality (government departments, international institutions etc.). The 'arrangement' code covers artificial entities, described in the data model for the purpose of associating one or more natural or legal persons together in an ownership or control relationship, but without implying that the parties to this arrangement have any other form of collective legal identity.", + "propertyOrder": 4 }, "dissolutionDate": { - "title": "Dissolution date", "description": "If this entity is no longer active, provide the date on which it was disolved or ceased. Please provide as precise a date as possible in ISO 8601 format. When only the year or year and month is known, these can be given as YYYY or YYYY-MM.", - "type": "string", + "title": "Dissolution date", "pattern": "^([\\+-]?\\d{4}(?!\\d{2}\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$", - "propertyOrder": 35 - }, - "addresses": { - "title": "Addresses", - "description": "One or more addresses for this entity.", - "type": "array", - "items": { - "title": "Address", - "description": "A free text address string, providing as much address data as is relevant, suitable for processing using address parsing algorithms. For some uses (for example, Place of Birth) only a town and country are required.", - "type": "object", - "properties": { - "type": { - "title": "Type", - "description": "What type of address is this? See the [addressType](#addresstype) codelist.", - "type": "string", - "enum": [ - "placeOfBirth", - "home", - "residence", - "registered", - "service", - "alternative" - ], - "codelist": "addressType.csv", - "openCodelist": false - }, - "address": { - "title": "Address", - "description": "The address, with each line or component of the address separated by a line-break or comma. This field may also include the postal code. ", - "type": "string" - }, - "postCode": { - "title": "Postcode", - "description": "The postal code for this address.", - "type": "string" - }, - "country": { - "title": "Country", - "description": "The ISO 2-Digit county code for this address.", - "type": "string", - "minLength": 2, - "maxLength": 2 - } - } - }, - "propertyOrder": 40 - }, - "uri": { - "title": "URI", - "description": "Where a [persistent URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) is available for this entity this should be included.", - "type": "string", - "format": "uri", - "propertyOrder": 21 - }, - "replacesStatements": { - "title": "Replaces statement(s)", - "description": "If this statement replaces a previous statement or statements, provide the identifier(s) for the previous statement(s) here. Consuming applications are advised to mark the identified statements as no longer active.", - "type": "array", - "items": { - "title": "Statement identifier", - "description": "The identifier of a statement that is no longer active.", - "type": "string", - "minLength": 32, - "maxLength": 64 - } + "propertyOrder": 35, + "type": "string" }, "source": { + "description": "The source of information about this entity.", "title": "Source", - "description": "The source object is used to explain where information in a statement originated from, and to link to supporting information.", "type": "object", + "propertyOrder": 89, "properties": { + "url": { + "description": "If this information was fetched from an external URL, or a machine or human readable web page is available that provides addition information on how this statement was sourced, provide the URL.", + "title": "Source URL", + "type": "string" + }, + "retrievedAt": { + "description": "If this statement was imported from some external system, include a timestamp indicating when this took place. The statement's own date should be set based on the source information. ", + "title": "Retrieved at", + "format": "datetime", + "type": "string" + }, + "description": { + "description": "Where required, additional free-text information about the source of this statement can be provided here.", + "title": "Description", + "type": "string" + }, "type": { - "title": "Source type", "description": "What type of source is this? Multiple tags can be combined. Values should come from the [source type](#sourcetype) codelist.", + "title": "Source type", "type": "array", "items": { - "type": "string", "enum": [ "selfDeclaration", "officialRegister", @@ -240,293 +68,139 @@ "primaryResearch", "verified" ], - "codelist": "sourceType.csv", - "openCodelist": false + "openCodelist": false, + "type": "string", + "codelist": "sourceType.csv" } }, - "description": { - "title": "Description", - "description": "Where required, additional free-text information about the source of this statement can be provided here.", - "type": "string" - }, - "url": { - "title": "Source URL", - "description": "If this information was fetched from an external URL, or a machine or human readable web page is available that provides additional information on how this statement was sourced, provide the URL.", - "type": "string" - }, - "retrievedAt": { - "title": "Retrieved at", - "description": "If this statement was imported from some external system, include a timestamp indicating when this took place. The statement's own date should be set based on the source information. ", - "type": "string", - "format": "date-time" - }, "assertedBy": { + "description": "Who is making this statement? This may be the name of the person or organisation making a self-declaration (in which case, please make sure the name field matches the organisation or person name field), or the name or description of some other party. If this statment has been verified, this may also include the name of the organisation providing verification.", "title": "Asserted by", - "description": "Who is making this statement? This may be the name of the person or organisation making a self-declaration (in which case, please make sure the name field matches the organisation or person name field), or the name or description of some other party. If this statement has been verified, this may also include the name of the organisation providing verification.", "type": "array", "items": { - "type": "object", - "title": "Agent", "description": "An individual, organisation or other responsible agent making, or supporting, a given statement or annotation.", + "title": "Agent", + "type": "object", "properties": { + "uri": { + "description": "An optional URI to identify the agent.", + "title": "URI", + "format": "uri", + "type": "string" + }, "name": { - "title": "Name", "description": "The name of the agent", + "title": "Name", "type": "string" - }, - "uri": { - "title": "URI", - "description": "An optional URI to identify the agent.", - "type": "string", - "format": "uri" } } } } } }, - "annotations": { - "title": "Annotations", - "description": "Annotations about this statement or parts of this statement", + "addresses": { + "description": "One or more addresses for this entity.", + "title": "Addresses", "type": "array", + "propertyOrder": 40, "items": { - "title": "Annotation", - "description": "An annotation provides additional information about ownership or control data being provided. Annotations can be provided in free-text, and can apply to a whole statement, an object or a single field. Additional extended properties can be included on the annotation object to provide structured data where required.", + "description": "A free text address string, providing as much address data as is relevant, suitable for processing using address parsing algorithms. For some uses (for example, Place of Birth) only a town and country are required.", + "title": "Address", "type": "object", "properties": { - "statementPointerTarget": { - "title": "Statement Fragment Pointer", - "description": "An [RFC6901 JSON Pointer](https://tools.ietf.org/html/rfc6901) describing the target fragment of the statement that this annotation applies to, starting from the root of the statement. A value of '/' indicates that the annotation applies to the whole statement.", + "address": { + "description": "The address, with each line or component of the address separated by a line-break or comma. This field may also include the postal code. ", + "title": "Address", "type": "string" }, - "creationDate": { - "title": "Creation Date", - "description": "The date this annotation was created.", - "type": "string", - "format": "date" + "postCode": { + "description": "The postal code for this address.", + "title": "Postcode", + "type": "string" }, - "createdBy": { - "title": "Created By", - "description": "The person, organisation or agent that created this annotation.", - "type": "object", - "properties": { - "name": { - "title": "Name", - "description": "The name of the person, organisation or agent that created this annotation.", - "type": "string" - }, - "uri": { - "title": "URI", - "description": "An optional URI to identify person, organisation or agent that created this annotation.", - "type": "string", - "format": "uri" - } - } - }, - "motivation": { - "title": "Motivation", - "description": "The motivation for this annotation, chosen from a codelist. See the [annotationMotivation](#annotationmotivation) codelist.", - "type": "string", + "type": { "enum": [ - "commenting", - "correcting", - "identifying", - "linking", - "transformation" + "placeOfBirth", + "home", + "residence", + "registered", + "service", + "alternative" ], - "codelist": "annotationMotivation.csv", - "openCodelist": true - }, - "description": { - "title": "Description", - "description": "A free-text description to annotate this statement or field.", - "type": "string" - }, - "transformedContent": { - "type": "string", - "title": "Transformed content", - "description": "A representation of the annotation target after the transformation in the description field has been applied. This field SHOULD only be used when the motivation is transformation." - }, - "url": { - "title": "URL", - "description": "A linked resource that annotates, provides context for or enhances this statement. The content of the resource, or the relationship to the statement, MAY be described in the description field.", + "openCodelist": false, + "title": "Type", "type": "string", - "format": "uri" - } - }, - "anyOf": [ - { - "properties": { - "motivation": { - "enum": [ - "linking" - ] - } - }, - "required": [ - "statementPointerTarget", - "motivation" - ] + "codelist": "addressType.csv", + "description": "What type of address is this?" }, - { - "properties": { - "motivation": { - "enum": [ - "linking", - "identifying", - "commenting", - "correcting", - "transformation" - ] - } - }, - "required": [ - "statementPointerTarget", - "motivation" - ] + "country": { + "minLength": 2, + "description": "The ISO 2-Digit county code for this address.", + "title": "Country", + "maxLength": 2, + "type": "string" } - ] - }, - "propertyOrder": 90 - } - }, - "required": [ - "statementID", - "statementType", - "entityType" - ] - }, - { - "id": "person-statement.json", - "$schema": "http://json-schema.org/draft-04/schema#", - "version": "0.1", - "type": "object", - "title": "Person statement", - "description": "A person statement describes the information known about a natural person at a particular point in time, or from a given submission of information", - "properties": { - "statementID": { - "title": "Statement Identifier", - "description": "A persistent globally unique identifier for this statement.", + } + } + }, + "missingInfoReason": { + "description": "For EntityStatements with the type 'anonymousEntity' or 'unknownEntity' this field should contain an explanation of the reason that detailed information on the entity is not provided. This may be a standard descriptive phrase from the source system, or a free-text justification.", + "title": "Missing information reason(s)", "type": "string", - "minLength": 32, - "maxLenth": 64 + "propertyOrder": 5 }, - "statementType": { - "title": "Statement type", - "description": "This should always be 'personStatement.", + "name": { + "description": "The declared name of this entity.", + "title": "Entity name", "type": "string", - "enum": [ - "personStatement" - ], - "propertyOrder": 2, - "openCodelist": false, - "codelist": "statementType.csv" + "propertyOrder": 6 }, "statementDate": { - "title": "Statement date", "description": "The date on which this statement was made.", + "title": "Statement date", + "format": "date", "type": "string", - "format": "date" + "propertyOrder": 3 }, - "personType": { - "title": "Person type", - "description": "Use the [personType codelist](#persontype). The ultimate beneficial owner of a legal entity is always a natural person. Where the beneficial owner has been identified, but information about them cannot be disclosed, use 'anonymousPerson'. Where the beneficial owner has not been clearly identified, use 'unknownPerson'. Where the beneficial owner has been identified use knownPerson.", - "type": "string", + "statementType": { "enum": [ - "anonymousPerson", - "unknownPerson", - "knownPerson" + "entityStatement" ], - "propertyOrder": 4, - "codelist": "personType.csv", - "openCodelist": false + "description": " This should always be 'entityStatement.", + "title": "Statement type", + "type": "string", + "propertyOrder": 2 }, - "missingInfoReason": { - "title": "Missing information reason(s)", - "description": "For PersonStatements with the type 'anonymousPerson' or 'unknownPerson' this field should contain an explanation of the reason that detailed information on the person is not provided. This may be a standard descriptive phrase from the source system, or a free-text justification.", + "statementID": { + "minLength": 32, + "maxLenth": 64, + "title": "Statement Identifier", "type": "string", - "propertyOrder": 5 + "description": "A persistent globally unique identifier for this statement.", + "propertyOrder": 1 }, - "names": { - "title": "Names", - "description": "One or more known names for this individual.", + "replacesStatements": { + "description": "If this statement replaces a previous statement or statements, provide the identifier(s) for the previous statement(s) here. Consuming applications are advised to mark the identified statements as no longer active.", + "title": "Replaces statement(s)", "type": "array", + "propertyOrder": 100, "items": { - "title": "Name", - "description": "An name by which this individual is known. Names should be provided in fullName, and may optionally be broken down in the familyName, givenName and patronymicName fields, based on the [EC ISA Core Person Vocabulary](https://joinup.ec.europa.eu/solution/e-government-core-vocabularies) definitions.", - "type": "object", - "properties": { - "type": { - "title": "Type", - "description": "What kind of name is this? See the [nameType](#nametype) codelist.", - "type": "string", - "enum": [ - "individual", - "translation", - "former", - "alias", - "aka", - "nick", - "birth" - ], - "codelist": "nameType.csv", - "openCodelist": false - }, - "fullName": { - "title": "Full name", - "description": "The full name contains the complete name of a person as one string.", - "type": "string" - }, - "familyName": { - "title": "Family name", - "description": "A family name is usually shared by members of a family. This attribute also carries prefixes or suffixes which are part of the Family Name, e.g. 'de Boer', 'van de Putte', 'von und zu Orlow'. Multiple family names, such as are commonly found in Hispanic countries, are recorded in the single Family Name field so that, for example, Miguel de Cervantes Saavedra's Family Name would be recorded as 'Cervantes Saavedra.'", - "type": "string" - }, - "givenName": { - "title": "Given names", - "description": "A given name, or multiple given names, are the denominator(s) that identify an individual within a family. These are given to a person by his or her parents at birth or may be legally recognised as 'given names' through a formal process. All given names are ordered in one field so that, for example, the given name for Johann Sebastian Bach is 'Johann Sebastian.'", - "type": "string" - }, - "patronymicName": { - "title": "Patronymic Name", - "description": "Patronymic names are important in some countries. Iceland does not have a concept of family name in the way that many other European countries do, for example. In Bulgaria and Russia, patronymic names are in every day usage, for example, the 'Sergeyevich' in 'Mikhail Sergeyevich Gorbachev'", - "type": "string" - } - } - }, - "propertyOrder": 10 + "minLength": 32, + "description": "The identifier of a statement that is no longer active.", + "title": "Statement identifier", + "maxLength": 64, + "type": "string" + } }, "identifiers": { + "description": "One or more official identifiers for this entity. Where available, official registration numbers should be provided.", "title": "Identifiers", - "description": "One or more official identifiers for this perrson. Where available, official registration numbers should be provided.", "type": "array", + "propertyOrder": 20, "items": { + "description": "An identifier that has been assigned to this entity. The scheme or list from which the identifier is drawn should be declared.", "title": "Identifier", - "description": "An identifier that has been assigned to this person or entity. The scheme or list from which the identifier is drawn should be declared.", "type": "object", - "properties": { - "id": { - "title": "ID", - "description": "The identifier for this person or entity as provided in the declared scheme.", - "type": "string" - }, - "scheme": { - "title": "Scheme", - "description": "For entity statements, the scheme should be a entry from the [org-id.guide](http://www.org-id.guide) codelist. For person statements, the scheme should have the pattern {JURISDICTION}-{TYPE} where JURISDICTION is an ISO 3-digit country code and TYPE is one of PASSPORT, TAXID or IDCARD.", - "type": "string" - }, - "schemeName": { - "title": "Scheme name", - "Description": "The name of this scheme, where the org-id code is unknown or only an unvalidated string is provided.", - "type": "string" - }, - "uri": { - "title": "URI", - "description": "Where this identifier has a [canonical URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) this may be included", - "type": "string", - "format": "uri" - } - }, "anyOf": [ { "required": [ @@ -544,97 +218,461 @@ "schemeName" ] } - ] - }, - "propertyOrder": 20 - }, - "nationalities": { - "title": "Nationality", - "description": "An array of ISO 2-Digit country codes representing nationalities held by this individual.", - "type": "array", - "items": { - "title": "Country", - "description": "A country MUST have a name. A country SHOULD have an ISO 2-Digit county code.", - "type": "object", + ], "properties": { - "name": { - "title": "Name", - "description": "The name of the country", + "id": { + "description": "The identifier for this entity as provided in the declared scheme.", + "title": "ID", "type": "string" }, - "code": { - "title": "Country code", - "description": "The ISO 2-digit code for the country.", - "type": "string", - "maxLength": 2, - "minLength": 2 + "scheme": { + "description": "For entity statements, the scheme should be a entry from the [org-id.guide](http://www.org-id.guide) codelist. For person statements, recognised values include 'passport', 'internal' and 'id-card'.", + "title": "Scheme", + "type": "string" + }, + "schemeName": { + "Description": "The name of this scheme, where the org-id code is unknown or only an unvalidated string is provided.", + "title": "Scheme name", + "type": "string" + }, + "uri": { + "description": "Where this identifier has a [canonical URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) this may be included", + "title": "URI", + "format": "uri", + "type": "string" } } - }, - "propertyOrder": 30 + } }, - "placeOfBirth": { - "title": "Address", - "description": "A free text address string, providing as much address data as is relevant, suitable for processing using address parsing algorithms. For some uses (for example, Place of Birth) only a town and country are required.", + "incorporatedInJurisdiction": { + "description": "Details on where this legal entity is incorporated", + "title": "Incorporated In Jurisdiction", "type": "object", + "propertyOrder": 10, "properties": { - "type": { - "title": "Type", - "description": "What type of address is this? See the [addressType](#addresstype) codelist.", - "type": "string", - "enum": [ - "placeOfBirth", - "home", - "residence", - "registered", - "service", - "alternative" - ], - "codelist": "addressType.csv", - "openCodelist": false + "code": { + "minLength": 2, + "description": "The ISO_3166-2 2-Digit country code, or ISO_3166-2 sub-division code of the jurisdiction", + "title": "Country code", + "maxLength": 6, + "type": "string" }, - "address": { - "title": "Address", - "description": "The address, with each line or component of the address separated by a line-break or comma. This field may also include the postal code. ", + "name": { + "description": "The name of the jurisdiction", + "title": "Name", + "type": "string" + } + } + }, + "alternateNames": { + "description": "An array of other names this entity is known by.", + "title": "Alternative names", + "type": "array", + "propertyOrder": 7, + "items": { + "description": "A name this entity is known by.", + "title": "Name", + "type": "string" + } + }, + "foundingDate": { + "description": "When was this entity founded, created or registered. Please provide as precise a date as possible in ISO 8601 format. When only the year or year and month is known, these can be given as YYYY or YYYY-MM.", + "title": "Founding date", + "pattern": "^([\\+-]?\\d{4}(?!\\d{2}\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$", + "propertyOrder": 30, + "type": "string" + }, + "annotations": { + "description": "Annotations about this statement or parts of this statement", + "title": "Annotations", + "type": "array", + "propertyOrder": 90, + "items": { + "description": "An annotation provides additional information about ownership or control data being provided. Annotations can be provided in free-text, and can apply to a whole statement, an object or a single field. Additional extended properties can be included on the annotation object to provide structured data where required.", + "title": "Annotation", + "type": "object", + "anyOf": [ + { + "required": [ + "statementPointerTarget", + "motivation" + ], + "properties": { + "motivation": { + "enum": [ + "linking" + ] + } + } + }, + { + "required": [ + "statementPointerTarget", + "motivation" + ], + "properties": { + "motivation": { + "enum": [ + "linking", + "identifying", + "commenting", + "correcting", + "transformation" + ] + } + } + } + ], + "properties": { + "motivation": { + "enum": [ + "commenting", + "correcting", + "identifying", + "linking", + "transformation" + ], + "openCodelist": true, + "title": "Motivation", + "type": "string", + "codelist": "annotationMotivation.csv", + "description": "The motivation for this annotation, chosen from a codelist. See the [annotationMotivation](#annotationmotivation) codelist." + }, + "transformedContent": { + "description": "A representation of the annotation target after the transformation in the description field has been applied. This field SHOULD only be used when the motivation is transformation.", + "title": "Transformed content", + "type": "string" + }, + "statementPointerTarget": { + "description": "An [RFC6901 JSON Pointer](https://tools.ietf.org/html/rfc6901) describing the target fragment of the statement that this annotation applies to, starting from the root of the statement. A value of '/' indicates that the annotation applies to the whole statement.", + "title": "Statement Fragment Pointer", + "type": "string" + }, + "url": { + "description": "A linked resource that annotates, provides context for or enhances this statement. The content of the resource, or the relationship to the statement, MAY be described in the description field.", + "title": "URL", + "format": "uri", + "type": "string" + }, + "creationDate": { + "description": "The date this annotation was created.", + "title": "Creation Date", + "format": "date", + "type": "string" + }, + "createdBy": { + "description": "The person, organisation or agent that created this annotation.", + "title": "Created By", + "type": "object", + "properties": { + "uri": { + "description": "An optional URI to identify person, organisation or agent that created this annotation.", + "title": "URI", + "format": "uri", + "type": "string" + }, + "name": { + "description": "The name of the person, organisation or agent that created this annotation.", + "title": "Name", + "type": "string" + } + } + }, + "description": { + "description": "A free-text description to annotate this statment or field.", + "title": "Description", + "type": "string" + } + } + } + }, + "uri": { + "description": "Where a [persistent URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) is available for this entity this should be included.", + "title": "URI", + "format": "uri", + "type": "string", + "propertyOrder": 21 + } + }, + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "A statement identifying and describing the entity that is the subject of the ownership or control described in an ownership or control statement.", + "type": "object", + "title": "Entity statement" + }, + { + "id": "person-statment.json", + "required": [ + "statementID", + "statementType" + ], + "dependencies": { + "missingPersonReason": [ + "missingPersonType" + ] + }, + "version": "0.1", + "title": "Person statement", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "A person statement describes the information known about a natural person at a particular point in time, or from a given submission of information", + "type": "object", + "properties": { + "names": { + "description": "One or more known names for this individual.", + "title": "Names", + "type": "array", + "propertyOrder": 10, + "items": { + "description": "An name by which this individual is known. Names should be provided in fullName, and may optionally be broken down in the familyName, givenName and patronymicName fields, based on the [EC ISA Core Person Vocabulary](https://joinup.ec.europa.eu/solution/e-government-core-vocabularies) definitions.", + "title": "Name", + "type": "object", + "properties": { + "fullName": { + "description": "The full name contains the complete name of a person as one string.", + "title": "Full name", + "type": "string" + }, + "type": { + "enum": [ + "individual", + "translation", + "former", + "alias", + "aka", + "nick", + "birth" + ], + "openCodelist": false, + "title": "Type", + "type": "string", + "codelist": "nameType.csv", + "description": "What kind of name is this? See the [nameType](#nametype) codelist." + }, + "givenName": { + "description": "A given name, or multiple given names, are the denominator(s) that identify an individual within a family. These are given to a person by his or her parents at birth or may be legally recognised as 'given names' through a formal process. All given names are ordered in one field so that, for example, the given name for Johann Sebastian Bach is 'Johann Sebastian.'", + "title": "Given names", + "type": "string" + }, + "familyName": { + "description": "A family name is usually shared by members of a family. This attribute also carries prefixes or suffixes which are part of the Family Name, e.g. 'de Boer', 'van de Putte', 'von und zu Orlow'. Multiple family names, such as are commonly found in Hispanic countries, are recorded in the single Family Name field so that, for example, Miguel de Cervantes Saavedra's Family Name would be recorded as 'Cervantes Saavedra.'", + "title": "Family name", + "type": "string" + }, + "patronymicName": { + "description": "Patronymic names are important in some countries. Iceland does not have a concept of family name in the way that many other European countries do, for example. In Bulgaria and Russia, patronymic names are in every day usage, for example, the 'Sergeyevich' in 'Mikhail Sergeyevich Gorbachev'", + "title": "Patronymic Name", + "type": "string" + } + } + } + }, + "source": { + "description": "The source of the information that links the entity and the interested party, or that supports a null statement.", + "title": "Source", + "type": "object", + "propertyOrder": 89, + "properties": { + "url": { + "description": "If this information was fetched from an external URL, or a machine or human readable web page is available that provides addition information on how this statement was sourced, provide the URL.", + "title": "Source URL", "type": "string" }, - "postCode": { - "title": "Postcode", - "description": "The postal code for this address.", + "retrievedAt": { + "description": "If this statement was imported from some external system, include a timestamp indicating when this took place. The statement's own date should be set based on the source information. ", + "title": "Retrieved at", + "format": "datetime", "type": "string" }, - "country": { - "title": "Country", - "description": "The ISO 2-Digit county code for this address.", - "type": "string", - "minLength": 2, - "maxLength": 2 + "description": { + "description": "Where required, additional free-text information about the source of this statement can be provided here.", + "title": "Description", + "type": "string" + }, + "type": { + "description": "What type of source is this? Multiple tags can be combined. Values should come from the [source type](#sourcetype) codelist.", + "title": "Source type", + "type": "array", + "items": { + "enum": [ + "selfDeclaration", + "officialRegister", + "thirdParty", + "primaryResearch", + "verified" + ], + "openCodelist": false, + "type": "string", + "codelist": "sourceType.csv" + } + }, + "assertedBy": { + "description": "Who is making this statement? This may be the name of the person or organisation making a self-declaration (in which case, please make sure the name field matches the organisation or person name field), or the name or description of some other party. If this statment has been verified, this may also include the name of the organisation providing verification.", + "title": "Asserted by", + "type": "array", + "items": { + "description": "An individual, organisation or other responsible agent making, or supporting, a given statement or annotation.", + "title": "Agent", + "type": "object", + "properties": { + "uri": { + "description": "An optional URI to identify the agent.", + "title": "URI", + "format": "uri", + "type": "string" + }, + "name": { + "description": "The name of the agent", + "title": "Name", + "type": "string" + } + } + } + } + } + }, + "personType": { + "enum": [ + "anonymousPerson", + "unknownPerson", + "knownPerson" + ], + "openCodelist": false, + "title": "Person type", + "type": "string", + "codelist": "personType.csv", + "description": "Use the [personType codelist](#persontype). The ultimate beneficial owner of a legal entity is always a natural person. Where the beneficial owner has been identified, but information about them cannot be disclosed, use 'anonymousPerson'. Where the beneficial owner has not been clearly identified, use 'unknownPerson'. Where the beneficial owner has been identified use knownPerson.", + "propertyOrder": 4 + }, + "pepStatus": { + "description": "One or more descriptions of this person's Politically-Exposed Person (PEP) status.", + "title": "Politically Exposed Person Status", + "type": "array", + "propertyOrder": 80, + "items": { + "description": "A description of a politically-exposed person status.", + "title": "PEP Status", + "type": "object", + "properties": { + "reason": { + "description": "The reason for this person being declared a politically-exposed person.", + "title": "Reason", + "type": "string" + }, + "endDate": { + "description": "When did this PEP status end. Please provide as precise a date as possible in ISO 8601 format. When only the year or year and month is known, these can be given as YYYY or YYYY-MM.", + "title": "End date", + "type": "string", + "pattern": "^([\\+-]?\\d{4}(?!\\d{2}\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$" + }, + "startDate": { + "description": "When did this PEP status begin. Please provide as precise a date as possible in ISO 8601 format. When only the year or year and month is known, these can be given as YYYY or YYYY-MM.", + "title": "State date", + "type": "string", + "pattern": "^([\\+-]?\\d{4}(?!\\d{2}\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$" + }, + "jurisdiction": { + "description": "A jurisdiction MUST have a name. A jurisdiction SHOULD have an ISO ISO_3166-2 2-Digit country code, or ISO_3166-2 sub-division code.", + "title": "Jurisdiction", + "type": "object", + "properties": { + "code": { + "minLength": 2, + "description": "The ISO_3166-2 2-Digit country code, or ISO_3166-2 sub-division code of the jurisdiction", + "title": "Country code", + "maxLength": 6, + "type": "string" + }, + "name": { + "description": "The name of the jurisdiction", + "title": "Name", + "type": "string" + } + } + } + } + } + }, + "addresses": { + "description": "One or more addresses for this entity.", + "title": "Addresses", + "type": "array", + "propertyOrder": 60, + "items": { + "description": "A free text address string, providing as much address data as is relevant, suitable for processing using address parsing algorithms. For some uses (for example, Place of Birth) only a town and country are required.", + "title": "Address", + "type": "object", + "properties": { + "address": { + "description": "The address, with each line or component of the address separated by a line-break or comma. This field may also include the postal code. ", + "title": "Address", + "type": "string" + }, + "postCode": { + "description": "The postal code for this address.", + "title": "Postcode", + "type": "string" + }, + "type": { + "enum": [ + "placeOfBirth", + "home", + "residence", + "registered", + "service", + "alternative" + ], + "openCodelist": false, + "title": "Type", + "type": "string", + "codelist": "addressType.csv", + "description": "What type of address is this?" + }, + "country": { + "minLength": 2, + "description": "The ISO 2-Digit county code for this address.", + "title": "Country", + "maxLength": 2, + "type": "string" + } } } }, - "birthDate": { - "title": "Date of birth", - "description": "The date of birth for this individual. Please provide as precise a date as possible in ISO 8601 format. When only the year or year and month is known, these can be given as YYYY or YYYY-MM.", + "missingInfoReason": { + "description": "For PersonStatements with the type 'anonymousPerson' or 'unknownPerson' this field should contain an explanation of the reason that detailed information on the person is not provided. This may be a standard descriptive phrase from the source system, or a free-text justification.", + "title": "Missing information reason(s)", "type": "string", - "pattern": "^([\\+-]?\\d{4}(?!\\d{2}\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$", - "propertyOrder": 35 + "propertyOrder": 5 }, "deathDate": { - "title": "Death date", "description": "If this individual is no longer alive, provide their date of death. Please provide as precise a date as possible in ISO 8601 format. When only the year or year and month is known, these can be given as YYYY or YYYY-MM.", - "type": "string", + "title": "Death date", "pattern": "^([\\+-]?\\d{4}(?!\\d{2}\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$", - "propertyOrder": 36 + "propertyOrder": 36, + "type": "string" }, - "placeOfResidence": { - "title": "Address", + "statementDate": { + "description": "The date on which this statement was made.", + "title": "Statement date", + "format": "date", + "type": "string", + "propertyOrder": 3 + }, + "placeOfBirth": { "description": "A free text address string, providing as much address data as is relevant, suitable for processing using address parsing algorithms. For some uses (for example, Place of Birth) only a town and country are required.", + "title": "Place of birth", "type": "object", + "propertyOrder": 40, "properties": { + "address": { + "description": "The address, with each line or component of the address separated by a line-break or comma. This field may also include the postal code. ", + "title": "Address", + "type": "string" + }, + "postCode": { + "description": "The postal code for this address.", + "title": "Postcode", + "type": "string" + }, "type": { - "title": "Type", - "description": "What type of address is this? See the [addressType](#addresstype) codelist.", - "type": "string", "enum": [ "placeOfBirth", "home", @@ -643,269 +681,204 @@ "service", "alternative" ], + "openCodelist": false, + "title": "Type", + "type": "string", "codelist": "addressType.csv", - "openCodelist": false - }, - "address": { - "title": "Address", - "description": "The address, with each line or component of the address separated by a line-break or comma. This field may also include the postal code. ", - "type": "string" - }, - "postCode": { - "title": "Postcode", - "description": "The postal code for this address.", - "type": "string" + "description": "What type of address is this?" }, "country": { - "title": "Country", - "description": "The ISO 2-Digit county code for this address.", - "type": "string", "minLength": 2, - "maxLength": 2 + "description": "The ISO 2-Digit county code for this address.", + "title": "Country", + "maxLength": 2, + "type": "string" } } }, - "addresses": { - "title": "Addresses", - "description": "One or more addresses for this entity.", + "statementType": { + "enum": [ + "personStatement" + ], + "openCodelist": false, + "title": "Statement type", + "type": "string", + "codelist": "statementType.csv", + "description": "This should always be 'personStatement.", + "propertyOrder": 2 + }, + "statementID": { + "minLength": 32, + "maxLenth": 64, + "title": "Statement Identifier", + "type": "string", + "description": "A persistent globally unique identifier for this statement.", + "propertyOrder": 1 + }, + "replacesStatements": { + "description": "If this statement replaces a previous statement or statements, provide the identifier(s) for the previous statement(s) here. Consuming applications are advised to mark the identified statements as no longer active.", + "title": "Replaces statement(s)", "type": "array", "items": { - "title": "Address", - "description": "A free text address string, providing as much address data as is relevant, suitable for processing using address parsing algorithms. For some uses (for example, Place of Birth) only a town and country are required.", - "type": "object", - "properties": { - "type": { - "title": "Type", - "description": "What type of address is this? See the [addressType](#addresstype) codelist.", - "type": "string", - "enum": [ - "placeOfBirth", - "home", - "residence", - "registered", - "service", - "alternative" - ], - "codelist": "addressType.csv", - "openCodelist": false - }, - "address": { - "title": "Address", - "description": "The address, with each line or component of the address separated by a line-break or comma. This field may also include the postal code. ", - "type": "string" - }, - "postCode": { - "title": "Postcode", - "description": "The postal code for this address.", - "type": "string" - }, - "country": { - "title": "Country", - "description": "The ISO 2-Digit county code for this address.", - "type": "string", - "minLength": 2, - "maxLength": 2 - } - } - }, - "propertyOrder": 60 + "minLength": 32, + "description": "The identifier of a statement that is no longer active.", + "title": "Statement identifier", + "maxLength": 64, + "type": "string" + } }, - "pepStatus": { - "title": "Politically Exposed Person Status", - "description": "One or more descriptions of this person's Politically-Exposed Person (PEP) status.", + "identifiers": { + "description": "One or more official identifiers for this perrson. Where available, official registration numbers should be provided.", + "title": "Identifiers", "type": "array", + "propertyOrder": 20, "items": { - "title": "PEP Status", - "description": "A description of a politically-exposed person status.", + "description": "An identifier that has been assigned to this entity. The scheme or list from which the identifier is drawn should be declared.", + "title": "Identifier", "type": "object", + "anyOf": [ + { + "required": [ + "scheme" + ] + }, + { + "required": [ + "schemeName" + ] + }, + { + "required": [ + "scheme", + "schemeName" + ] + } + ], "properties": { - "reason": { - "title": "Reason", - "description": "The reason for this person being declared a politically-exposed person.", + "id": { + "description": "The identifier for this entity as provided in the declared scheme.", + "title": "ID", "type": "string" }, - "jurisdiction": { - "title": "Jurisdiction", - "description": "A jurisdiction MUST have a name. A jurisdiction SHOULD have an ISO ISO_3166-2 2-Digit country code, or ISO_3166-2 sub-division code.", - "type": "object", - "properties": { - "name": { - "title": "Name", - "description": "The name of the jurisdiction", - "type": "string" - }, - "code": { - "title": "Country code", - "description": "The ISO_3166-2 2-Digit country code, or ISO_3166-2 sub-division code of the jurisdiction", - "type": "string", - "maxLength": 6, - "minLength": 2 - } - } + "scheme": { + "description": "For entity statements, the scheme should be a entry from the [org-id.guide](http://www.org-id.guide) codelist. For person statements, recognised values include 'passport', 'internal' and 'id-card'.", + "title": "Scheme", + "type": "string" }, - "startDate": { - "title": "State date", - "description": "When did this PEP status begin. Please provide as precise a date as possible in ISO 8601 format. When only the year or year and month is known, these can be given as YYYY or YYYY-MM.", - "type": "string", - "pattern": "^([\\+-]?\\d{4}(?!\\d{2}\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$" + "schemeName": { + "Description": "The name of this scheme, where the org-id code is unknown or only an unvalidated string is provided.", + "title": "Scheme name", + "type": "string" }, - "endDate": { - "title": "End date", - "description": "When did this PEP status end. Please provide as precise a date as possible in ISO 8601 format. When only the year or year and month is known, these can be given as YYYY or YYYY-MM.", - "type": "string", - "pattern": "^([\\+-]?\\d{4}(?!\\d{2}\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$" + "uri": { + "description": "Where this identifier has a [canonical URI](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier) this may be included", + "title": "URI", + "format": "uri", + "type": "string" } } - }, - "propertyOrder": 80 + } }, - "source": { - "title": "Source", - "description": "The source object is used to explain where information in a statement originated from, and to link to supporting information.", + "birthDate": { + "description": "The date of birth for this individual. Please provide as precise a date as possible in ISO 8601 format. When only the year or year and month is known, these can be given as YYYY or YYYY-MM.", + "title": "Date of birth", + "pattern": "^([\\+-]?\\d{4}(?!\\d{2}\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$", + "propertyOrder": 35, + "type": "string" + }, + "placeOfResidence": { + "description": "A free text address string, providing as much address data as is relevant, suitable for processing using address parsing algorithms. For some uses (for example, Place of Birth) only a town and country are required.", + "title": "Place of residence", "type": "object", + "propertyOrder": 50, "properties": { - "type": { - "title": "Source type", - "description": "What type of source is this? Multiple tags can be combined. Values should come from the [source type](#sourcetype) codelist.", - "type": "array", - "items": { - "type": "string", - "enum": [ - "selfDeclaration", - "officialRegister", - "thirdParty", - "primaryResearch", - "verified" - ], - "codelist": "sourceType.csv", - "openCodelist": false - } - }, - "description": { - "title": "Description", - "description": "Where required, additional free-text information about the source of this statement can be provided here.", - "type": "string" - }, - "url": { - "title": "Source URL", - "description": "If this information was fetched from an external URL, or a machine or human readable web page is available that provides additional information on how this statement was sourced, provide the URL.", + "address": { + "description": "The address, with each line or component of the address separated by a line-break or comma. This field may also include the postal code. ", + "title": "Address", "type": "string" }, - "retrievedAt": { - "title": "Retrieved at", - "description": "If this statement was imported from some external system, include a timestamp indicating when this took place. The statement's own date should be set based on the source information. ", + "postCode": { + "description": "The postal code for this address.", + "title": "Postcode", + "type": "string" + }, + "type": { + "enum": [ + "placeOfBirth", + "home", + "residence", + "registered", + "service", + "alternative" + ], + "openCodelist": false, + "title": "Type", "type": "string", - "format": "date-time" + "codelist": "addressType.csv", + "description": "What type of address is this?" }, - "assertedBy": { - "title": "Asserted by", - "description": "Who is making this statement? This may be the name of the person or organisation making a self-declaration (in which case, please make sure the name field matches the organisation or person name field), or the name or description of some other party. If this statement has been verified, this may also include the name of the organisation providing verification.", - "type": "array", - "items": { - "type": "object", - "title": "Agent", - "description": "An individual, organisation or other responsible agent making, or supporting, a given statement or annotation.", - "properties": { - "name": { - "title": "Name", - "description": "The name of the agent", - "type": "string" - }, - "uri": { - "title": "URI", - "description": "An optional URI to identify the agent.", - "type": "string", - "format": "uri" - } - } - } + "country": { + "minLength": 2, + "description": "The ISO 2-Digit county code for this address.", + "title": "Country", + "maxLength": 2, + "type": "string" } } }, - "annotations": { - "title": "Annotations", - "description": "Annotations about this statement or parts of this statement", + "nationalities": { + "description": "An array of ISO 2-Digit country codes representing nationalities held by this individual.", + "title": "Nationality", "type": "array", + "propertyOrder": 30, "items": { - "title": "Annotation", - "description": "An annotation provides additional information about ownership or control data being provided. Annotations can be provided in free-text, and can apply to a whole statement, an object or a single field. Additional extended properties can be included on the annotation object to provide structured data where required.", + "description": "A country MUST have a name. A country SHOULD have an ISO 2-Digit county code.", + "title": "Country", "type": "object", "properties": { - "statementPointerTarget": { - "title": "Statement Fragment Pointer", - "description": "An [RFC6901 JSON Pointer](https://tools.ietf.org/html/rfc6901) describing the target fragment of the statement that this annotation applies to, starting from the root of the statement. A value of '/' indicates that the annotation applies to the whole statement.", + "code": { + "minLength": 2, + "description": "The ISO 2-digit code for the country.", + "title": "Country code", + "maxLength": 2, "type": "string" }, - "creationDate": { - "title": "Creation Date", - "description": "The date this annotation was created.", - "type": "string", - "format": "date" - }, - "createdBy": { - "title": "Created By", - "description": "The person, organisation or agent that created this annotation.", - "type": "object", - "properties": { - "name": { - "title": "Name", - "description": "The name of the person, organisation or agent that created this annotation.", - "type": "string" - }, - "uri": { - "title": "URI", - "description": "An optional URI to identify person, organisation or agent that created this annotation.", - "type": "string", - "format": "uri" - } - } - }, - "motivation": { - "title": "Motivation", - "description": "The motivation for this annotation, chosen from a codelist. See the [annotationMotivation](#annotationmotivation) codelist.", - "type": "string", - "enum": [ - "commenting", - "correcting", - "identifying", - "linking", - "transformation" - ], - "codelist": "annotationMotivation.csv", - "openCodelist": true - }, - "description": { - "title": "Description", - "description": "A free-text description to annotate this statement or field.", + "name": { + "description": "The name of the country", + "title": "Name", "type": "string" - }, - "transformedContent": { - "type": "string", - "title": "Transformed content", - "description": "A representation of the annotation target after the transformation in the description field has been applied. This field SHOULD only be used when the motivation is transformation." - }, - "url": { - "title": "URL", - "description": "A linked resource that annotates, provides context for or enhances this statement. The content of the resource, or the relationship to the statement, MAY be described in the description field.", - "type": "string", - "format": "uri" } - }, + } + } + }, + "annotations": { + "description": "Annotations about this statement or parts of this statement", + "title": "Annotations", + "type": "array", + "propertyOrder": 90, + "items": { + "description": "An annotation provides additional information about ownership or control data being provided. Annotations can be provided in free-text, and can apply to a whole statement, an object or a single field. Additional extended properties can be included on the annotation object to provide structured data where required.", + "title": "Annotation", + "type": "object", "anyOf": [ { + "required": [ + "statementPointerTarget", + "motivation" + ], "properties": { "motivation": { "enum": [ "linking" ] } - }, + } + }, + { "required": [ "statementPointerTarget", "motivation" - ] - }, - { + ], "properties": { "motivation": { "enum": [ @@ -916,107 +889,96 @@ "transformation" ] } - }, - "required": [ - "statementPointerTarget", - "motivation" - ] + } } - ] - }, - "propertyOrder": 90 - }, - "replacesStatements": { - "title": "Replaces statement(s)", - "description": "If this statement replaces a previous statement or statements, provide the identifier(s) for the previous statement(s) here. Consuming applications are advised to mark the identified statements as no longer active.", - "type": "array", - "items": { - "title": "Statement identifier", - "description": "The identifier of a statement that is no longer active.", - "type": "string", - "minLength": 32, - "maxLength": 64 + ], + "properties": { + "motivation": { + "enum": [ + "commenting", + "correcting", + "identifying", + "linking", + "transformation" + ], + "openCodelist": true, + "title": "Motivation", + "type": "string", + "codelist": "annotationMotivation.csv", + "description": "The motivation for this annotation, chosen from a codelist. See the [annotationMotivation](#annotationmotivation) codelist." + }, + "transformedContent": { + "description": "A representation of the annotation target after the transformation in the description field has been applied. This field SHOULD only be used when the motivation is transformation.", + "title": "Transformed content", + "type": "string" + }, + "statementPointerTarget": { + "description": "An [RFC6901 JSON Pointer](https://tools.ietf.org/html/rfc6901) describing the target fragment of the statement that this annotation applies to, starting from the root of the statement. A value of '/' indicates that the annotation applies to the whole statement.", + "title": "Statement Fragment Pointer", + "type": "string" + }, + "url": { + "description": "A linked resource that annotates, provides context for or enhances this statement. The content of the resource, or the relationship to the statement, MAY be described in the description field.", + "title": "URL", + "format": "uri", + "type": "string" + }, + "creationDate": { + "description": "The date this annotation was created.", + "title": "Creation Date", + "format": "date", + "type": "string" + }, + "createdBy": { + "description": "The person, organisation or agent that created this annotation.", + "title": "Created By", + "type": "object", + "properties": { + "uri": { + "description": "An optional URI to identify person, organisation or agent that created this annotation.", + "title": "URI", + "format": "uri", + "type": "string" + }, + "name": { + "description": "The name of the person, organisation or agent that created this annotation.", + "title": "Name", + "type": "string" + } + } + }, + "description": { + "description": "A free-text description to annotate this statment or field.", + "title": "Description", + "type": "string" + } + } } } - }, - "required": [ - "statementID", - "statementType" - ], - "dependencies": { - "missingPersonReason": [ - "missingPersonType" - ] } }, { "id": "ownership-or-control-statement.json", - "$schema": "http://json-schema.org/draft-04/schema#", - "version": "0.1", - "title": "Ownership or control Statement", - "description": "An ownership or control statement is made up of an entity, an interested party (a reference to an entity, natural person, arrangement or trust), details of the interest and provenance information for the statement.", - "type": "object", - "properties": { - "statementID": { - "title": "Statement Identifier", - "description": "A persistent globally unique identifier for this statement.", - "type": "string", - "minLength": 32, - "maxLenth": 64 - }, - "statementType": { - "title": "Statement type", - "description": "This should always be set to `ownershipOrControlStatement`.", - "type": "string", - "enum": [ - "ownershipOrControlStatement" - ] - }, - "statementDate": { - "title": "Statement date", - "description": "The date on which this statement was made.", - "type": "string", - "format": "date" - }, - "subject": { - "title": "Subject", - "description": "The subject of an ownership or control relationship.", - "type": "object", - "properties": { - "describedByEntityStatement": { - "title": "Described by entity statement", - "description": "Provide the identifier of the statement which describes the entity that the subject of an ownership or control interest.", - "type": "string" - } - }, - "required": [ - "describedByEntityStatement" - ] - }, - "interestedParty": { - "title": "Interested party", + "definitions": { + "InterestedParty": { "description": "The interested party has some level of ownership or control over the entity referenced in this ownership or control statement. This should be described with reference to either an entity statement or person statement, or, where the interested party is unknown, details of why. ", + "title": "Interested party", "type": "object", - "properties": { - "describedByEntityStatement": { - "title": "Described by entity statement", - "description": "A reference to a statement describing a registered entity, trust or arrangement that has an ownership or control interest in the subject of this statement. An entityStatement should be used when the direct interests to be described represents known control or ownership by anyone other than a natural person.", - "type": "string" - }, - "describedByPersonStatement": { - "title": "Described by person statement", - "description": "A reference to a statement describing a natural person who has an ownership or control interest in the subject of this statement.", + "properties": { + "describedByEntityStatement": { + "description": "A reference to a statement describing a registered entity, trust or arrangement that has an ownership or control interest in the subject of this statement. An entityStatement should be used when the direct interests to be described represents known control or ownership by anyone other than a natural person.", + "title": "Described by entity statement", "type": "string" }, "unspecified": { - "title": "Unspecified or unknown ownership and control", + "required": [ + "reason" + ], "description": "When confirmation has been provided that no interested party exists, where ownership and control information does not need to be provided, or where details of ownership and control are unknown, information explaining this should be given using the unspecified reason and description. Where there is a natural person with ownership or control, but their name or details are not known, or cannot be disclosed for some reason, unspecified should not be used, but instead a reference to a personStatement should be provided but identifying details of the person left blank.", + "title": "Unspecified or unknown ownership and control", "type": "object", "properties": { "reason": { - "title": "Reason", - "description": "The reason that an interested party cannot be specified. From the [unspecifiedReason codelist](#unspecifiedreason).", - "type": "string", "enum": [ "no-beneficial-owners", "subject-unable-to-confirm-or-identify-beneficial-owner", @@ -1025,139 +987,87 @@ "interested-party-exempt-from-disclosure", "unknown" ], + "openCodelist": false, + "title": "Reason", + "type": "string", "codelist": "unspecifiedReason.csv", - "openCodelist": false + "description": "The reason that an interested party cannot be specified. From the [unspecifiedReason codelist](#unspecifiedreason)." }, "description": { - "title": "Description", "description": "Any supporting information about the absence of a confirmed beneficial owner. This field may be used to provide set phrases from a source system, or for a free-text explanation.", + "title": "Description", "type": "string" } - }, - "required": [ - "reason" - ] + } + }, + "describedByPersonStatement": { + "description": "A reference to a statement describing a natural person who has an ownership or control interest in the subject of this statement.", + "title": "Described by person statement", + "type": "string" } } - }, - "interests": { - "title": "Interests", - "description": "A description of the interests held by the interestedParty covered by this statement in the entity covered by this statement.", - "type": "array", - "items": { - "title": "Interest", - "description": "A description of the interest held by an interestedParty in another entity.", - "type": "object", - "properties": { - "type": { - "title": "Type of interest", - "description": "A codelist value indicating the nature of the interest. See the [interestType codelist](#interesttype)", - "type": "string", - "enum": [ - "shareholding", - "voting-rights", - "appointment-of-board", - "influence-or-control", - "senior-managing-official", - "settlor-of-trust", - "trustee-of-trust", - "protector-of-trust", - "beneficiary-of-trust", - "other-influence-or-control-of-trust", - "rights-to-surplus-assets", - "rights-to-profit-or-income" - ], - "codelist": "interestType.csv", - "openCodelist": false - }, - "interestLevel": { - "title": "Interest level", - "description": "Is this interest held directly or indirectly?", - "type": "string", - "enum": [ - "direct", - "indirect", - "unknown" - ], - "codelist": "interestLevel.csv", - "openCodelist": false - }, - "beneficialOwnershipOrControl": { - "title": "Beneficial ownership or control", - "description": "Does this statement assert this as a beneficial ownership or control interest? A beneficial ownership or control interest is always between a natural person and some entity, and exists where the person ultimately benefits from, or has a degree of control over, the entity. There may be cases where a person has an interest in an entity, but where there are arrangements or other conditions that mean this interest does not constitute beneficial ownership or control.", - "type": "boolean" - }, - "details": { - "title": "Details", - "description": "This field may be used to provide the local name given to this kind of interest, or any further semi-structured or unstructured information to clarify the nature of the interest held.", - "type": "string" - }, - "share": { - "title": "Percentage share", - "description": "Where an exact percentage is available, this should be given, and maximum and minimum values set to the same as the exact percentage. Otherwise, maximum and minimum can be used to record the range into which the share of this kind of interest falls.", - "type": "object", - "properties": { - "exact": { - "title": "Exact share", - "description": "The exact share of this interest held (where available).", - "type": "number", - "maximum": 100, - "minimum": 0 - }, - "maximum": { - "title": "Maximum share", - "description": "The upper bound of the share of this interest known to be held.", - "type": "number", - "maximum": 100, - "minimum": 0 - }, - "minimum": { - "title": "Minimum share", - "description": "The lower bound of the share of this interest known to be held.", - "type": "number", - "maximum": 100, - "minimum": 0 - }, - "exclusiveMinimum": { - "title": "Exclusive minimum", - "description": "If exclusiveMinimum is true, then the share is at least greater than the minimum value given. E.g. if minimum is 25, the share is at least 25.1, and not simply 25.", - "type": "boolean", - "default": false - }, - "exclusiveMaximum": { - "title": "Exclusive maximum", - "description": "If exclusiveMaximum is true, then the share is at least less than the maximum value given. E.g. if maximum is 50, the share is less than 49.999, and not simply 50.", - "type": "boolean", - "default": true - } - } - }, - "startDate": { - "title": "State date", - "description": "When did this interest first occur. Please provide as precise a date as possible in ISO 8601 format. When only the year or year and month is known, these can be given as YYYY or YYYY-MM.", - "type": "string", - "pattern": "^([\\+-]?\\d{4}(?!\\d{2}\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$" - }, - "endDate": { - "title": "End date", - "description": "When did this interest cease. Please provide as precise a date as possible in ISO 8601 format. When only the year or year and month is known, these can be given as YYYY or YYYY-MM.", - "type": "string", - "pattern": "^([\\+-]?\\d{4}(?!\\d{2}\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$" - } + } + }, + "required": [ + "statementID", + "statementType", + "subject", + "interestedParty" + ], + "version": "0.2-beta", + "title": "Ownership or control Statement", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "An ownership or control statement is made up of an entity, an interested party (a reference to an entity, natural person, arrangement or trust), details of the interest and provenance information for the statement.", + "type": "object", + "properties": { + "subject": { + "required": [ + "describedByEntityStatement" + ], + "description": "The subject of an ownership or control relationship.", + "title": "Subject", + "type": "object", + "properties": { + "describedByEntityStatement": { + "description": "Provide the identifier of the statement which describes the entity that the subject of an ownership or control interest.", + "title": "Described by entity statement", + "type": "string" } } }, + "statementID": { + "minLength": 32, + "description": "A persistent globally unique identifier for this statement.", + "title": "Statement Identifier", + "type": "string", + "maxLenth": 64 + }, "source": { + "description": "The source of the information that links the entity and the interested party, or that supports a null statement.", "title": "Source", - "description": "The source object is used to explain where information in a statement originated from, and to link to supporting information.", "type": "object", "properties": { + "url": { + "description": "If this information was fetched from an external URL, or a machine or human readable web page is available that provides addition information on how this statement was sourced, provide the URL.", + "title": "Source URL", + "type": "string" + }, + "retrievedAt": { + "description": "If this statement was imported from some external system, include a timestamp indicating when this took place. The statement's own date should be set based on the source information. ", + "title": "Retrieved at", + "format": "datetime", + "type": "string" + }, + "description": { + "description": "Where required, additional free-text information about the source of this statement can be provided here.", + "title": "Description", + "type": "string" + }, "type": { - "title": "Source type", "description": "What type of source is this? Multiple tags can be combined. Values should come from the [source type](#sourcetype) codelist.", + "title": "Source type", "type": "array", "items": { - "type": "string", "enum": [ "selfDeclaration", "officialRegister", @@ -1165,198 +1075,174 @@ "primaryResearch", "verified" ], - "codelist": "sourceType.csv", - "openCodelist": false + "openCodelist": false, + "type": "string", + "codelist": "sourceType.csv" } }, - "description": { - "title": "Description", - "description": "Where required, additional free-text information about the source of this statement can be provided here.", - "type": "string" - }, - "url": { - "title": "Source URL", - "description": "If this information was fetched from an external URL, or a machine or human readable web page is available that provides additional information on how this statement was sourced, provide the URL.", - "type": "string" - }, - "retrievedAt": { - "title": "Retrieved at", - "description": "If this statement was imported from some external system, include a timestamp indicating when this took place. The statement's own date should be set based on the source information. ", - "type": "string", - "format": "date-time" - }, "assertedBy": { + "description": "Who is making this statement? This may be the name of the person or organisation making a self-declaration (in which case, please make sure the name field matches the organisation or person name field), or the name or description of some other party. If this statment has been verified, this may also include the name of the organisation providing verification.", "title": "Asserted by", - "description": "Who is making this statement? This may be the name of the person or organisation making a self-declaration (in which case, please make sure the name field matches the organisation or person name field), or the name or description of some other party. If this statement has been verified, this may also include the name of the organisation providing verification.", "type": "array", "items": { - "type": "object", - "title": "Agent", "description": "An individual, organisation or other responsible agent making, or supporting, a given statement or annotation.", + "title": "Agent", + "type": "object", "properties": { + "uri": { + "description": "An optional URI to identify the agent.", + "title": "URI", + "format": "uri", + "type": "string" + }, "name": { - "title": "Name", "description": "The name of the agent", + "title": "Name", "type": "string" - }, - "uri": { - "title": "URI", - "description": "An optional URI to identify the agent.", - "type": "string", - "format": "uri" } } } } } }, - "annotations": { - "title": "Annotations", - "description": "Annotations about this statement or parts of this statement", + "replacesStatements": { + "description": "If this statement replaces a previous statement or statements, provide the identifier(s) for the previous statement(s) here. Consuming applications are advised to mark the identified statements as no longer active.", + "title": "Replaces statement(s)", "type": "array", "items": { - "title": "Annotation", - "description": "An annotation provides additional information about ownership or control data being provided. Annotations can be provided in free-text, and can apply to a whole statement, an object or a single field. Additional extended properties can be included on the annotation object to provide structured data where required.", + "minLength": 32, + "description": "The identifier of a statement that is no longer active.", + "title": "Statement identifier", + "maxLength": 64, + "type": "string" + } + }, + "interests": { + "description": "A description of the interests held by the interestedParty covered by this statement in the entity covered by this statement.", + "title": "Interests", + "type": "array", + "items": { + "description": "A description of the interest held by an interestedParty in another entity.", + "title": "Interest", "type": "object", "properties": { - "statementPointerTarget": { - "title": "Statement Fragment Pointer", - "description": "An [RFC6901 JSON Pointer](https://tools.ietf.org/html/rfc6901) describing the target fragment of the statement that this annotation applies to, starting from the root of the statement. A value of '/' indicates that the annotation applies to the whole statement.", - "type": "string" + "type": { + "enum": [ + "shareholding", + "voting-rights", + "appointment-of-board", + "influence-or-control", + "senior-managing-official", + "settlor-of-trust", + "trustee-of-trust", + "protector-of-trust", + "beneficiary-of-trust", + "other-influence-or-control-of-trust", + "rights-to-surplus-assets", + "rights-to-profit-or-income" + ], + "openCodelist": false, + "title": "Type of interest", + "type": "string", + "codelist": "interestType.csv", + "description": "A codelist value indicating the nature of the interest. See the [interestType codelist](#interesttype)" }, - "creationDate": { - "title": "Creation Date", - "description": "The date this annotation was created.", + "interestLevel": { + "enum": [ + "direct", + "indirect", + "unknown" + ], + "openCodelist": false, + "title": "Interest level", "type": "string", - "format": "date" + "codelist": "interestLevel.csv", + "description": "Is this interest held directly or indirectly?" + }, + "details": { + "description": "This field may be used to provide the local name given to this kind of interest, or any further semi-structured or unstructured information to clarify the nature of the interest held.", + "title": "Details", + "type": "string" }, - "createdBy": { - "title": "Created By", - "description": "The person, organisation or agent that created this annotation.", + "share": { + "description": "Where an exact percentage is available, this should be given, and maximum and minimum values set to the same as the exact percentage. Otherwise, maximum and minimum can be used to record the range into which the share of this kind of interest falls.", + "title": "Percentage share", "type": "object", "properties": { - "name": { - "title": "Name", - "description": "The name of the person, organisation or agent that created this annotation.", - "type": "string" + "exclusiveMinimum": { + "description": "If exclusiveMinimum is true, then the share is at least greater than the minimum value given. E.g. if minimum is 25, the share is at least 25.1, and not simply 25.", + "title": "Exclusive minimum", + "type": "boolean", + "default": false }, - "uri": { - "title": "URI", - "description": "An optional URI to identify person, organisation or agent that created this annotation.", - "type": "string", - "format": "uri" + "exclusiveMaximum": { + "description": "If exclusiveMaximum is true, then the share is at least less than the maximum value given. E.g. if maximum is 50, the share is less than 49.999, and not simply 50.", + "title": "Exclusive maximum", + "type": "boolean", + "default": true + }, + "maximum": { + "description": "The upper bound of the share of this interest known to be held.", + "title": "Maximum share", + "minimum": 0, + "type": "number", + "maximum": 100 + }, + "minimum": { + "description": "The lower bound of the share of this interest known to be held.", + "title": "Minimum share", + "minimum": 0, + "type": "number", + "maximum": 100 + }, + "exact": { + "description": "The exact share of this interest held (where available).", + "title": "Exact share", + "minimum": 0, + "type": "number", + "maximum": 100 } } }, - "motivation": { - "title": "Motivation", - "description": "The motivation for this annotation, chosen from a codelist. See the [annotationMotivation](#annotationmotivation) codelist.", - "type": "string", - "enum": [ - "commenting", - "correcting", - "identifying", - "linking", - "transformation" - ], - "codelist": "annotationMotivation.csv", - "openCodelist": true - }, - "description": { - "title": "Description", - "description": "A free-text description to annotate this statement or field.", - "type": "string" - }, - "transformedContent": { + "endDate": { + "description": "When did this interest cease. Please provide as precise a date as possible in ISO 8601 format. When only the year or year and month is known, these can be given as YYYY or YYYY-MM.", + "title": "End date", "type": "string", - "title": "Transformed content", - "description": "A representation of the annotation target after the transformation in the description field has been applied. This field SHOULD only be used when the motivation is transformation." + "pattern": "^([\\+-]?\\d{4}(?!\\d{2}\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$" }, - "url": { - "title": "URL", - "description": "A linked resource that annotates, provides context for or enhances this statement. The content of the resource, or the relationship to the statement, MAY be described in the description field.", + "startDate": { + "description": "When did this interest first occur. Please provide as precise a date as possible in ISO 8601 format. When only the year or year and month is known, these can be given as YYYY or YYYY-MM.", + "title": "State date", "type": "string", - "format": "uri" - } - }, - "anyOf": [ - { - "properties": { - "motivation": { - "enum": [ - "linking" - ] - } - }, - "required": [ - "statementPointerTarget", - "motivation" - ] + "pattern": "^([\\+-]?\\d{4}(?!\\d{2}\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$" }, - { - "properties": { - "motivation": { - "enum": [ - "linking", - "identifying", - "commenting", - "correcting", - "transformation" - ] - } - }, - "required": [ - "statementPointerTarget", - "motivation" - ] + "beneficialOwnershipOrControl": { + "description": "Does this statement assert this as a beneficial ownership or control interest? A beneficial ownership or control interest is always between a natural person and some entity, and exists where the person ultimately benefits from, or has a degree of control over, the entity. There may be cases where a person has an interest in an entity, but where there are arrangements or other conditions that mean this interest does not constitute beneficial ownership or control.", + "title": "Beneficial ownership or control", + "type": "boolean" } - ] + } } }, - "replacesStatements": { - "title": "Replaces statement(s)", - "description": "If this statement replaces a previous statement or statements, provide the identifier(s) for the previous statement(s) here. Consuming applications are advised to mark the identified statements as no longer active.", - "type": "array", - "items": { - "title": "Statement identifier", - "description": "The identifier of a statement that is no longer active.", - "type": "string", - "minLength": 32, - "maxLength": 64 - } - } - }, - "required": [ - "statementID", - "statementType", - "subject", - "interestedParty" - ], - "definitions": { - "InterestedParty": { - "title": "Interested party", + "interestedParty": { "description": "The interested party has some level of ownership or control over the entity referenced in this ownership or control statement. This should be described with reference to either an entity statement or person statement, or, where the interested party is unknown, details of why. ", + "title": "Interested party", "type": "object", "properties": { "describedByEntityStatement": { - "title": "Described by entity statement", "description": "A reference to a statement describing a registered entity, trust or arrangement that has an ownership or control interest in the subject of this statement. An entityStatement should be used when the direct interests to be described represents known control or ownership by anyone other than a natural person.", - "type": "string" - }, - "describedByPersonStatement": { - "title": "Described by person statement", - "description": "A reference to a statement describing a natural person who has an ownership or control interest in the subject of this statement.", + "title": "Described by entity statement", "type": "string" }, "unspecified": { - "title": "Unspecified or unknown ownership and control", + "required": [ + "reason" + ], "description": "When confirmation has been provided that no interested party exists, where ownership and control information does not need to be provided, or where details of ownership and control are unknown, information explaining this should be given using the unspecified reason and description. Where there is a natural person with ownership or control, but their name or details are not known, or cannot be disclosed for some reason, unspecified should not be used, but instead a reference to a personStatement should be provided but identifying details of the person left blank.", + "title": "Unspecified or unknown ownership and control", "type": "object", "properties": { "reason": { - "title": "Reason", - "description": "The reason that an interested party cannot be specified. From the [unspecifiedReason codelist](#unspecifiedreason).", - "type": "string", "enum": [ "no-beneficial-owners", "subject-unable-to-confirm-or-identify-beneficial-owner", @@ -1365,20 +1251,142 @@ "interested-party-exempt-from-disclosure", "unknown" ], + "openCodelist": false, + "title": "Reason", + "type": "string", "codelist": "unspecifiedReason.csv", - "openCodelist": false + "description": "The reason that an interested party cannot be specified. From the [unspecifiedReason codelist](#unspecifiedreason)." }, "description": { - "title": "Description", "description": "Any supporting information about the absence of a confirmed beneficial owner. This field may be used to provide set phrases from a source system, or for a free-text explanation.", + "title": "Description", "type": "string" } + } + }, + "describedByPersonStatement": { + "description": "A reference to a statement describing a natural person who has an ownership or control interest in the subject of this statement.", + "title": "Described by person statement", + "type": "string" + } + } + }, + "statementDate": { + "description": "The date on which this statement was made.", + "title": "Statement date", + "format": "date", + "type": "string" + }, + "annotations": { + "description": "Annotations about this statement or parts of this statement", + "title": "Annotations", + "type": "array", + "items": { + "description": "An annotation provides additional information about ownership or control data being provided. Annotations can be provided in free-text, and can apply to a whole statement, an object or a single field. Additional extended properties can be included on the annotation object to provide structured data where required.", + "title": "Annotation", + "type": "object", + "anyOf": [ + { + "required": [ + "statementPointerTarget", + "motivation" + ], + "properties": { + "motivation": { + "enum": [ + "linking" + ] + } + } }, - "required": [ - "reason" - ] + { + "required": [ + "statementPointerTarget", + "motivation" + ], + "properties": { + "motivation": { + "enum": [ + "linking", + "identifying", + "commenting", + "correcting", + "transformation" + ] + } + } + } + ], + "properties": { + "motivation": { + "enum": [ + "commenting", + "correcting", + "identifying", + "linking", + "transformation" + ], + "openCodelist": true, + "title": "Motivation", + "type": "string", + "codelist": "annotationMotivation.csv", + "description": "The motivation for this annotation, chosen from a codelist. See the [annotationMotivation](#annotationmotivation) codelist." + }, + "transformedContent": { + "description": "A representation of the annotation target after the transformation in the description field has been applied. This field SHOULD only be used when the motivation is transformation.", + "title": "Transformed content", + "type": "string" + }, + "statementPointerTarget": { + "description": "An [RFC6901 JSON Pointer](https://tools.ietf.org/html/rfc6901) describing the target fragment of the statement that this annotation applies to, starting from the root of the statement. A value of '/' indicates that the annotation applies to the whole statement.", + "title": "Statement Fragment Pointer", + "type": "string" + }, + "url": { + "description": "A linked resource that annotates, provides context for or enhances this statement. The content of the resource, or the relationship to the statement, MAY be described in the description field.", + "title": "URL", + "format": "uri", + "type": "string" + }, + "creationDate": { + "description": "The date this annotation was created.", + "title": "Creation Date", + "format": "date", + "type": "string" + }, + "createdBy": { + "description": "The person, organisation or agent that created this annotation.", + "title": "Created By", + "type": "object", + "properties": { + "uri": { + "description": "An optional URI to identify person, organisation or agent that created this annotation.", + "title": "URI", + "format": "uri", + "type": "string" + }, + "name": { + "description": "The name of the person, organisation or agent that created this annotation.", + "title": "Name", + "type": "string" + } + } + }, + "description": { + "description": "A free-text description to annotate this statment or field.", + "title": "Description", + "type": "string" + } } } + }, + "statementType": { + "enum": [ + "ownershipOrControlStatement" + ], + "description": "This should always be set to `ownershipOrControlStatement`.", + "title": "Statement type", + "type": "string" } } } From e77dacaa1a190d321fbb1bef7d6796a70f563f8a Mon Sep 17 00:00:00 2001 From: Ben Webb Date: Fri, 22 Mar 2019 17:48:14 +0900 Subject: [PATCH 11/15] Ensure extra packages for jsonschema format validation are installed --- requirements.in | 2 +- requirements.txt | 2 ++ requirements_dev.in | 4 +--- requirements_dev.txt | 11 ++++++----- tests/test_api.py | 2 +- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/requirements.in b/requirements.in index c0f24fa..cf50894 100644 --- a/requirements.in +++ b/requirements.in @@ -1,3 +1,3 @@ -e git+https://github.com/OpenDataServices/flatten-tool.git@v0.5.0#egg=flattentool --e git+https://github.com/OpenDataServices/lib-cove.git@v0.3.1#egg=libcove +-e git+https://github.com/OpenDataServices/lib-cove.git@fe0ec370280e2986e6d47dff458a08313acceaef#egg=libcove -e . diff --git a/requirements.txt b/requirements.txt index 41240cc..7d0102a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -19,8 +19,10 @@ lxml==4.3.2 openpyxl==2.6.1 pytz==2018.9 requests==2.21.0 +rfc3987==1.3.8 schema==0.7.0 six==1.12.0 +strict-rfc3339==0.7 urllib3==1.24.1 webencodings==0.5.1 xmltodict==0.12.0 diff --git a/requirements_dev.in b/requirements_dev.in index 9adb621..fd4bf24 100644 --- a/requirements_dev.in +++ b/requirements_dev.in @@ -1,5 +1,3 @@ --e git+https://github.com/OpenDataServices/flatten-tool.git@v0.5.0#egg=flattentool --e git+https://github.com/OpenDataServices/lib-cove.git@v0.3.1#egg=libcove --e . +-r requirements.in pytest flake8 diff --git a/requirements_dev.txt b/requirements_dev.txt index 6720d91..f6f90b4 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -1,6 +1,4 @@ --e git+https://github.com/OpenDataServices/flatten-tool.git@4c13ef0b32a59e810919a3de09bc8f64ce8f9392#egg=flattentool --e git+https://github.com/OpenDataServices/lib-cove.git@fe0ec370280e2986e6d47dff458a08313acceaef#egg=libcove - +-r requirements.in pytest==4.3.1 flake8==3.7.7 ## The following requirements were added by pip freeze: @@ -15,25 +13,28 @@ contextlib2==0.5.5 Django==2.1.7 entrypoints==0.3 et-xmlfile==1.0.1 +-e git+https://github.com/OpenDataServices/flatten-tool.git@4c13ef0b32a59e810919a3de09bc8f64ce8f9392#egg=flattentool future==0.17.1 idna==2.8 jdcal==1.4 json-merge-patch==0.2 jsonref==0.2 jsonschema==2.6.0 +-e git+https://github.com/OpenDataServices/lib-cove.git@fe0ec370280e2986e6d47dff458a08313acceaef#egg=libcove lxml==4.3.2 mccabe==0.6.1 more-itertools==6.0.0 openpyxl==2.6.1 -pathlib2==2.3.3 pluggy==0.9.0 py==1.8.0 pycodestyle==2.5.0 pyflakes==2.1.1 pytz==2018.9 requests==2.21.0 +rfc3987==1.3.8 schema==0.7.0 six==1.12.0 +strict-rfc3339==0.7 urllib3==1.24.1 webencodings==0.5.1 -xmltodict==0.12.0 \ No newline at end of file +xmltodict==0.12.0 diff --git a/tests/test_api.py b/tests/test_api.py index ee4eb3d..470406f 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -629,7 +629,7 @@ def test_badfile_all_validation_errors(): assert results['validation_errors_count'] == 39 assert results['additional_fields_count'] == 1 - assert results['additional_checks_count'] == 9 + assert results['additional_checks_count'] == 11 assert results['file_type'] == 'json' for (i, (expected_error, expected_values)) in enumerate(BADFILE_RESULTS): From 8b2c0d665490bc2c6f2c4e11195488c8eac3e3d1 Mon Sep 17 00:00:00 2001 From: Ben Webb Date: Fri, 22 Mar 2019 19:08:02 +0900 Subject: [PATCH 12/15] Remove test of validation error message text This makes the tests suitable for this PR: https://github.com/OpenDataServices/lib-cove/pull/12 (instead of the larger https://github.com/OpenDataServices/lib-cove/pull/8) --- requirements.in | 2 +- requirements.txt | 2 +- requirements_dev.txt | 2 +- tests/test_api.py | 61 +++----------------------------------------- 4 files changed, 6 insertions(+), 61 deletions(-) diff --git a/requirements.in b/requirements.in index cf50894..194430b 100644 --- a/requirements.in +++ b/requirements.in @@ -1,3 +1,3 @@ -e git+https://github.com/OpenDataServices/flatten-tool.git@v0.5.0#egg=flattentool --e git+https://github.com/OpenDataServices/lib-cove.git@fe0ec370280e2986e6d47dff458a08313acceaef#egg=libcove +-e git+https://github.com/OpenDataServices/lib-cove.git@9fc8323052f2a4340a856d76f14fdfb8785b1aaa#egg=libcove -e . diff --git a/requirements.txt b/requirements.txt index 7d0102a..09137ec 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ -e git+https://github.com/OpenDataServices/flatten-tool.git@4c13ef0b32a59e810919a3de09bc8f64ce8f9392#egg=flattentool --e git+https://github.com/OpenDataServices/lib-cove.git@fe0ec370280e2986e6d47dff458a08313acceaef#egg=libcove +-e git+https://github.com/OpenDataServices/lib-cove.git@9fc8323052f2a4340a856d76f14fdfb8785b1aaa#egg=libcove ## The following requirements were added by pip freeze: bleach==3.1.0 cached-property==1.5.1 diff --git a/requirements_dev.txt b/requirements_dev.txt index f6f90b4..a4efc21 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -20,7 +20,7 @@ jdcal==1.4 json-merge-patch==0.2 jsonref==0.2 jsonschema==2.6.0 --e git+https://github.com/OpenDataServices/lib-cove.git@fe0ec370280e2986e6d47dff458a08313acceaef#egg=libcove +-e git+https://github.com/OpenDataServices/lib-cove.git@9fc8323052f2a4340a856d76f14fdfb8785b1aaa#egg=libcove lxml==4.3.2 mccabe==0.6.1 more-itertools==6.0.0 diff --git a/tests/test_api.py b/tests/test_api.py index 470406f..0d0d339 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -4,36 +4,6 @@ from libcovebods.api import bods_json_output -BADFILE_RESULTS = [ - ({'message': "'entityType' is missing but required. Check that the field is included and correctly spelled.", 'message_safe': 'entityType is missing but required. Check that the field is included and correctly spelled.', 'message_type': 'required', 'path_no_number': ''}, [{'path': '21'}]), # noqa - ({'message': "'exact' should be a number. Check that the value is not null, and doesn’t contain any characters other than 0-9 and dot ('.'). Number values should not be in quotes. ", 'message_safe': 'exact should be a number. Check that the value is not null, and doesn’t contain any characters other than 0-9 and dot (.). Number values should not be in quotes. ', 'message_type': 'number', 'path_no_number': 'interests/share/exact'}, [{'path': '16/interests/0/share/exact', 'value': 'not a number'}]), # noqa - ({'message': "'exclusiveMinimum' should be a JSON boolean, 'true' or 'false'.", 'message_safe': 'exclusiveMinimum should be a JSON boolean, true or false.', 'message_type': 'boolean', 'path_no_number': 'interests/share/exclusiveMinimum'}, [{'path': '19/interests/0/share/exclusiveMinimum', 'value': 'not a bool'}]), # noqa - ({'message': "'interestedParty' is missing but required. Check that the field is included and correctly spelled.", 'message_safe': 'interestedParty is missing but required. Check that the field is included and correctly spelled.', 'message_type': 'required', 'path_no_number': ''}, [{'path': '16'}, {'path': '17'}, {'path': '18'}, {'path': '19'}, {'path': '20'}]), # noqa - ({'message': "'interests' should be a JSON array. Check that value(s) appear within square brackets, [...]", 'message_safe': 'interests should be a JSON array. Check that value(s) appear within square brackets, [...]', 'message_type': 'array', 'path_no_number': 'interests'}, [{'path': '17/interests'}]), # noqa - ({'message': "'interests/0' should be a JSON object", 'message_safe': 'interests/0 should be a JSON object', 'message_type': 'object', 'path_no_number': 'interests'}, [{'path': '18/interests/0', 'value': 'not an object'}]), # noqa - ({'message': "'missingPersonType' is a dependency of 'missingPersonReason'", 'message_safe': ''missingPersonType' is a dependency of 'missingPersonReason'', 'message_type': 'dependencies', 'path_no_number': ''}, [{'path': '14'}]), # noqa - ({'message': "'motivation' contains an unrecognised value. Check the related codelist for allowed code values.", 'message_safe': 'motivation contains an unrecognised value. Check the related codelist for allowed code values.', 'message_type': 'enum', 'path_no_number': 'annotations/motivation'}, [{'path': '20/annotations/0/motivation', 'value': 'not on open list'}]), # noqa - ({'message': "'not a date' does not match '^([\\\\+-]?\\\\d{4}(?!\\\\d{2}\\x08))((-?)((0[1-9]|1[0-2])(\\\\3([12]\\\\d|0[1-9]|3[01]))?|W([0-4]\\\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\\\d|[12]\\\\d{2}|3([0-5]\\\\d|6[1-6])))([T\\\\s]((([01]\\\\d|2[0-3])((:?)[0-5]\\\\d)?|24\\\\:?00)([\\\\.,]\\\\d+(?!:))?)?(\\\\17[0-5]\\\\d([\\\\.,]\\\\d+)?)?([zZ]|([\\\\+-])([01]\\\\d|2[0-3]):?([0-5]\\\\d)?)?)?)?$'", 'message_safe': 'birthDate does not match the regex ^([\\+-]?\\d{4}(?!\\d{2}\x08))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$', 'message_type': 'pattern', 'path_no_number': 'birthDate'}, [{'path': '12/birthDate', 'value': 'not a date'}]), # noqa - ({'message': "'personType' contains an unrecognised value. Check the related codelist for allowed code values.", 'message_safe': 'personType contains an unrecognised value. Check the related codelist for allowed code values.', 'message_type': 'enum', 'path_no_number': 'personType'}, [{'path': '11/personType', 'value': 'bad person type'}]), # noqa - ({'message': "'replacesStatements' should be a JSON array. Check that value(s) appear within square brackets, [...]", 'message_safe': 'replacesStatements should be a JSON array. Check that value(s) appear within square brackets, [...]', 'message_type': 'array', 'path_no_number': 'replacesStatements'}, [{'path': '9/replacesStatements', 'value': 'not an array'}]), # noqa - ({'message': "'statementID' is missing but required. Check that the field is included and correctly spelled.", 'message_safe': 'statementID is missing but required. Check that the field is included and correctly spelled.', 'message_type': 'required', 'path_no_number': ''}, [{'path': '2'}, {'path': '7'}, {'path': '8'}, {'path': '9'}, {'path': '10'}]), # noqa - ({'message': "'statementID' should be a string. Check that the value is not null, and has quotes at the start and end. Escape any quotes in the value with '\\'", 'message_safe': 'statementID should be a string. Check that the value is not null, and has quotes at the start and end. Escape any quotes in the value with \\', 'message_type': 'string', 'path_no_number': 'statementID'}, [{'path': '4/statementID', 'value': 100}]), # noqa - ({'message': "'statementType' contains an unrecognised value. Check the related codelist for allowed code values.", 'message_safe': 'statementType contains an unrecognised value. Check the related codelist for allowed code values.', 'message_type': 'enum', 'path_no_number': 'statementType'}, [{'path': '1/statementType', 'value': 'bad statement type'}, {'path': '3/statementType'}]), # noqa - ({'message': "'statementType' is missing but required. Check that the field is included and correctly spelled.", 'message_safe': 'statementType is missing but required. Check that the field is included and correctly spelled.', 'message_type': 'required', 'path_no_number': ''}, [{'path': '0'}]), # noqa - ({'message': "'subject' is missing but required. Check that the field is included and correctly spelled.", 'message_safe': 'subject is missing but required. Check that the field is included and correctly spelled.', 'message_type': 'required', 'path_no_number': ''}, [{'path': '16'}, {'path': '17'}, {'path': '18'}, {'path': '19'}, {'path': '20'}]), # noqa - ({'message': "'too long long long long long long long long long long long long long long long long' is too long", 'message_safe': 'replacesStatements/0 is too long. It should not exceed 64 characters.', 'message_type': 'maxLength', 'path_no_number': 'replacesStatements'}, [{'path': '8/replacesStatements/0', 'value': 'too long long long long long long long long long long long long long long long long'}]), # noqa - ({'message': "'tooshort' is too short", 'message_safe': 'replacesStatements/0 is too short. It should be at least 32 characters.', 'message_type': 'minLength', 'path_no_number': 'replacesStatements'}, [{'path': '7/replacesStatements/0', 'value': 'tooshort'}]), # noqa - ({'message': "'tooshort' is too short", 'message_safe': 'statementID is too short. It should be at least 32 characters.', 'message_type': 'minLength', 'path_no_number': 'statementID'}, [{'path': '5/statementID', 'value': 'tooshort'}]), # noqa - ({'message': '-1 is less than the minimum of 0', 'message_safe': 'minimum is too small. The minimum allowed value is 0.', 'message_type': 'minimum', 'path_no_number': 'interests/share/minimum'}, [{'path': '16/interests/0/share/minimum', 'value': -1}]), # noqa - ({'message': '101 is greater than the maximum of 100', 'message_safe': 'maximum is too large. The maximum allowed value is 100.', 'message_type': 'maximum', 'path_no_number': 'interests/share/maximum'}, [{'path': '16/interests/0/share/maximum', 'value': 101}]), # noqa - ({'message': 'Date is not in the correct format. The correct format is YYYY-MM-DD.', 'message_safe': 'Date is not in the correct format. The correct format is YYYY-MM-DD.', 'message_type': 'date', 'path_no_number': 'statementDate'}, [{'path': '10/statementDate', 'value': 'not a date'}]), # noqa - ({'message': 'Date is not in the correct format. The correct format is YYYY-MM-DDThh:mm:ssZ.', 'message_safe': 'Date is not in the correct format. The correct format is YYYY-MM-DDT00:00:00Z.', 'message_type': 'date-time', 'path_no_number': 'source/retrievedAt'}, [{'path': '13/source/retrievedAt', 'value': 'not a date-time'}]), # noqa - ({'message': 'Invalid uri found', 'message_safe': 'Invalid uri found', 'message_type': 'uri', 'path_no_number': 'uri'}, [{'path': '21/uri', 'value': 'not a uri'}]), # noqa - ({'message': "{'motivation': 'not on open list'} is not valid under any of the given schemas", 'message_safe': '{'motivation': 'not on open list'} is not valid under any of the given schemas', 'message_type': 'anyOf', 'path_no_number': 'annotations'}, [{'path': '20/annotations/0'}]), # noqa - ({'message': '{} is not valid under any of the given schemas', 'message_safe': '{} is not valid under any of the given schemas', 'message_type': 'anyOf', 'path_no_number': 'identifiers'}, [{'path': '15/identifiers/0'}]), # noqa -] - - def unpack_validation_error(validation_error_result): validation_error, data = validation_error_result validation_error_data = json.loads(validation_error) @@ -294,12 +264,12 @@ def test_basic_statement_id_and_type_errors(): assert data[0]['path'] == '0' assert data[1]['path'] == '2' - validation_error_data, data = unpack_validation_error(results['validation_errors'][3]) + validation_error_data, data = unpack_validation_error(results['validation_errors'][2]) assert "'statementType' is missing but required" in validation_error_data['message'] assert data[0]['path'] == '3' - validation_error_data, data = unpack_validation_error(results['validation_errors'][2]) - assert "'statementType' contains an unrecognised value. Check the related codelist for allowed code values." in validation_error_data['message'] # noqa + validation_error_data, data = unpack_validation_error(results['validation_errors'][3]) + assert "Invalid code found in 'statementType'" in validation_error_data['message'] assert data[0]['path'] == '4/statementType' assert data[0]['value'] == 'test' @@ -618,31 +588,6 @@ def test_basic_bad_identifier_scheme(): assert results['additional_checks'][0]['entity_statement'] == '1dc0e987-5c57-4a1c-b3ad-61353b66a9b7' -def test_badfile_all_validation_errors(): - - cove_temp_folder = tempfile.mkdtemp(prefix='lib-cove-bods-tests-', dir=tempfile.gettempdir()) - json_filename = os.path.join(os.path.dirname( - os.path.realpath(__file__)), 'fixtures', 'api', 'badfile_all_validation_errors.json' - ) - - results = bods_json_output(cove_temp_folder, json_filename) - - assert results['validation_errors_count'] == 39 - assert results['additional_fields_count'] == 1 - assert results['additional_checks_count'] == 11 - assert results['file_type'] == 'json' - - for (i, (expected_error, expected_values)) in enumerate(BADFILE_RESULTS): - error, values = unpack_validation_error(results['validation_errors'][i]) - assert error['message'] == expected_error['message'] - assert error['message_safe'] == expected_error['message_safe'] - assert error['message_type'] == expected_error['message_type'] - assert error['path_no_number'] == expected_error['path_no_number'] - - for j, value in enumerate(expected_values): - assert value['path'] == expected_values[j]['path'] - - def test_basic_anonymous_person_1(): cove_temp_folder = tempfile.mkdtemp(prefix='lib-cove-bods-tests-', dir=tempfile.gettempdir()) From 38f52efec6fb219a3f2f32f3ae0b169c2153e98a Mon Sep 17 00:00:00 2001 From: Ben Webb Date: Tue, 26 Mar 2019 15:56:07 +0900 Subject: [PATCH 13/15] Update libcove requirements to latest release (v0.5.0) --- requirements.in | 2 +- requirements.txt | 2 +- requirements_dev.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements.in b/requirements.in index 194430b..1252aa7 100644 --- a/requirements.in +++ b/requirements.in @@ -1,3 +1,3 @@ -e git+https://github.com/OpenDataServices/flatten-tool.git@v0.5.0#egg=flattentool --e git+https://github.com/OpenDataServices/lib-cove.git@9fc8323052f2a4340a856d76f14fdfb8785b1aaa#egg=libcove +-e git+https://github.com/OpenDataServices/lib-cove.git@v0.5.0#egg=libcove -e . diff --git a/requirements.txt b/requirements.txt index 09137ec..df144bb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ -e git+https://github.com/OpenDataServices/flatten-tool.git@4c13ef0b32a59e810919a3de09bc8f64ce8f9392#egg=flattentool --e git+https://github.com/OpenDataServices/lib-cove.git@9fc8323052f2a4340a856d76f14fdfb8785b1aaa#egg=libcove +-e git+https://github.com/OpenDataServices/lib-cove.git@v0.5.0#egg=libcove ## The following requirements were added by pip freeze: bleach==3.1.0 cached-property==1.5.1 diff --git a/requirements_dev.txt b/requirements_dev.txt index a4efc21..876a61b 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -20,7 +20,7 @@ jdcal==1.4 json-merge-patch==0.2 jsonref==0.2 jsonschema==2.6.0 --e git+https://github.com/OpenDataServices/lib-cove.git@9fc8323052f2a4340a856d76f14fdfb8785b1aaa#egg=libcove +-e git+https://github.com/OpenDataServices/lib-cove.git@v0.5.0#egg=libcove lxml==4.3.2 mccabe==0.6.1 more-itertools==6.0.0 From d98fe0af750bbeb0be5e42b158755609af0bdb4a Mon Sep 17 00:00:00 2001 From: Ben Webb Date: Thu, 28 Mar 2019 12:18:57 +0900 Subject: [PATCH 14/15] Remove fixture file that isn't used in this branch https://github.com/openownership/lib-cove-bods/pull/8#issuecomment-477244204 --- .../api/badfile_all_validation_errors.json | 24 ------------------- 1 file changed, 24 deletions(-) delete mode 100644 tests/fixtures/api/badfile_all_validation_errors.json diff --git a/tests/fixtures/api/badfile_all_validation_errors.json b/tests/fixtures/api/badfile_all_validation_errors.json deleted file mode 100644 index cf294c0..0000000 --- a/tests/fixtures/api/badfile_all_validation_errors.json +++ /dev/null @@ -1,24 +0,0 @@ -[ -{}, -{"statementType": "bad statement type"}, -{"statementType": "personStatement"}, -{"statementType": {}}, -{"statementType": "personStatement", "statementID": 100}, -{"statementType": "personStatement", "statementID": "tooshort"}, -{"statementType": "personStatement", "statementID": "too long long long long long long long long long long long long long long long long"}, -{"statementType": "personStatement", "replacesStatements": ["tooshort"]}, -{"statementType": "personStatement", "replacesStatements": ["too long long long long long long long long long long long long long long long long"]}, -{"statementType": "personStatement", "replacesStatements": "not an array"}, -{"statementType": "personStatement", "statementDate": "not a date"}, -{"statementType": "personStatement", "statementID": "2f957a72-caaa-4363-bf45-1257f1b57db6", "personType": "bad person type"}, -{"statementType": "personStatement", "statementID": "2f957a72-caaa-4363-bf45-1257f1b57db6", "birthDate": "not a date"}, -{"statementType": "personStatement", "statementID": "2f957a72-caaa-4363-bf45-1257f1b57db6", "source":{"retrievedAt": "not a date-time"}}, -{"statementType": "personStatement", "statementID": "2f957a72-caaa-4363-bf45-1257f1b57db6", "missingPersonReason": "no missingPersonType"}, -{"statementType": "personStatement", "statementID": "2f957a72-caaa-4363-bf45-1257f1b57db6", "identifiers":[{}]}, -{"statementType": "ownershipOrControlStatement", "statementID": "772099ea-419f-418b-b6b8-98492e5516bb", "interests": [{"share": {"exact": "not a number", "minimum":-1, "maximum":101}}]}, -{"statementType": "ownershipOrControlStatement", "statementID": "772099ea-419f-418b-b6b8-98492e5516bb", "interests": {"share": {"exact": "not a number", "minimum":-1, "maximum":101}}}, -{"statementType": "ownershipOrControlStatement", "statementID": "772099ea-419f-418b-b6b8-98492e5516bb", "interests": ["not an object"]}, -{"statementType": "ownershipOrControlStatement", "statementID": "772099ea-419f-418b-b6b8-98492e5516bb", "interests": [{"share": {"exclusiveMinimum": "not a bool"}}]}, -{"statementType": "ownershipOrControlStatement", "statementID": "772099ea-419f-418b-b6b8-98492e5516bb", "annotations":[{"motivation": "not on open list"}]}, -{"statementType": "entityStatement", "statementID": "a484ec70-ef24-45ca-ae2a-52453ca2fe9b", "uri": "not a uri"} -] From 52d87b78d0f5443fcefa21b5d17ea36ed916fafb Mon Sep 17 00:00:00 2001 From: Ben Webb Date: Thu, 28 Mar 2019 12:25:57 +0900 Subject: [PATCH 15/15] Add CHANGELOG entry about lib-cove upgrade --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed8e0e8..cfe0f04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +### Changed + +- Use lib-cove version 0.5.0, which includes schema validation message changes + ## [0.3.0] - 2019-03-22 ### Added @@ -39,4 +43,4 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [0.1.0] - 2018-11-22 -First Release \ No newline at end of file +First Release