Skip to content

Commit

Permalink
Calculate additional codelist values for schema using anyOf or oneOf,…
Browse files Browse the repository at this point in the history
… like OCDS record packages

#125
open-contracting/lib-cove-ocds#106
  • Loading branch information
jpmckinney authored and odscjames committed Nov 23, 2023
1 parent b2c6851 commit 079a67c
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 10 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Restore jsonschema's type validator, as its performance has improved in recent Python versions https://github.com/OpenDataServices/lib-cove/pull/127
- Allow `SchemaJsonMixin` classes to define a `validator` method, that accepts lib-cove's JSON Schema draft 4 validator class and its format checker, and returns a validator instance. https://github.com/OpenDataServices/lib-cove/pull/128

### Fixed

- Calculate additional codelist values for schema using `anyOf` or `oneOf`, like OCDS record packages https://github.com/open-contracting/lib-cove-ocds/issues/106

## [0.31.0] - 2023-07-06

### Changed
Expand Down
31 changes: 21 additions & 10 deletions libcove/lib/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -456,17 +456,28 @@ def get_schema_codelist_paths(
if "codelist" in value and path not in codelist_paths:
codelist_paths[path] = (value["codelist"], value.get("openCodelist", False))

if value.get("type") == "object":
get_schema_codelist_paths(None, value, path, codelist_paths)
elif value.get("type") == "array" and isinstance(value.get("items"), dict):
if value.get("items").get("type") == "string":
if "codelist" in value["items"] and path not in codelist_paths:
codelist_paths[path] = (
value["items"]["codelist"],
value["items"].get("openCodelist", False),
descendants = []
if "oneOf" in value and isinstance(value["oneOf"], list):
descendants = value["oneOf"]
elif "anyOf" in value and isinstance(value["anyOf"], list):
descendants = value["anyOf"]
else:
descendants = [value]

for value in descendants:
if value.get("type") == "object":
get_schema_codelist_paths(None, value, path, codelist_paths)
elif value.get("type") == "array" and isinstance(value.get("items"), dict):
if value.get("items").get("type") == "string":
if "codelist" in value["items"] and path not in codelist_paths:
codelist_paths[path] = (
value["items"]["codelist"],
value["items"].get("openCodelist", False),
)
elif value.get("items").get("properties"):
get_schema_codelist_paths(
None, value["items"], path, codelist_paths
)
elif value.get("items").get("properties"):
get_schema_codelist_paths(None, value["items"], path, codelist_paths)

return codelist_paths

Expand Down
34 changes: 34 additions & 0 deletions tests/lib/fixtures/common/schema_with_oneof_codelists.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"data": {
"oneOf": [
{
"type": "object",
"properties": {
"entityType": {
"openCodelist": false,
"title": "Type",
"type": "string",
"codelist": "currency.csv"
}
}
},
{
"type": "object",
"properties": {
"entityType": {
"enum": [
"otherValue"
],
"type": "string"
}
}
}
]
}
},
"type": "array",
"version": "0.1"
}
32 changes: 32 additions & 0 deletions tests/lib/test_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -1347,3 +1347,35 @@ def test_get_additional_codelist_values():
"extension_codelist": False,
},
}


def test_get_additional_codelist_values_anyOf():
json_data = {"data": {"entityType": "additional"}}

schema_obj = SchemaJsonMixin()
schema_obj.schema_host = os.path.join(
os.path.dirname(os.path.realpath(__file__)), "fixtures", "common/"
)
schema_obj.release_pkg_schema_name = "schema_with_oneof_codelists.json"
schema_obj.pkg_schema_url = os.path.join(
schema_obj.schema_host, schema_obj.release_pkg_schema_name
)
# Ideally we wouldn't rely on getting codelists from an external source.
# (Makes for faster tests with less dependencies)
# But load_codelist always uses requests lib, so we have to.
schema_obj.codelists = "https://raw.githubusercontent.com/open-contracting/standard/1.1/schema/codelists/"

additional_codelist_values = get_additional_codelist_values(schema_obj, json_data)

assert additional_codelist_values == {
"data/entityType": {
"path": "data",
"field": "entityType",
"codelist": "currency.csv",
"codelist_url": "https://raw.githubusercontent.com/open-contracting/standard/1.1/schema/codelists/currency.csv",
"codelist_amend_urls": [],
"isopen": False,
"values": ["additional"],
"extension_codelist": False,
}
}

0 comments on commit 079a67c

Please sign in to comment.