From 72553a2d8b5fa094a5edd5e6ec15b5125a052016 Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Wed, 16 Feb 2022 00:55:04 +0100 Subject: assume and use "$schema" properties in item definitions --- common/entities.js | 11 ----------- default_settings.json | 2 ++ html/install.js | 37 +++++++++++++++++++++---------------- html/item_preview.html | 4 ++++ html/item_preview.js | 12 ++++++++++++ html/repo_query.html | 8 +++++++- html/repo_query.js | 28 ++++++++++++---------------- test/unit/test_install.py | 14 +++++++------- test/unit/test_repo_query.py | 30 +++++++++++++++--------------- test/unit/utils.py | 10 ++++++++++ test/world_wide_library.py | 13 ++++++------- 11 files changed, 96 insertions(+), 73 deletions(-) diff --git a/common/entities.js b/common/entities.js index 96de5cb..74cad20 100644 --- a/common/entities.js +++ b/common/entities.js @@ -100,17 +100,6 @@ function get_newest_version(versioned_item) } #EXPORT get_newest_version AS get_newest -/* - * Returns true if the argument is a nonempty array of numbers without trailing - * zeros. - */ -function is_valid_version(version) { - return Array.isArray(version) && version.length > 0 && - version.every(n => typeof n === "number") && - version[version.length - 1] !== 0; -} -#EXPORT is_valid_version - /* * item is a definition of a resource or mapping. Yield all file references * (objects with `file` and `sha256` properties) this definition has. diff --git a/default_settings.json b/default_settings.json index a187c46..647b0a7 100644 --- a/default_settings.json +++ b/default_settings.json @@ -24,6 +24,7 @@ "resource": { "haketilo-demo-script": { "2022.1.28": { + "$schema": "https://hydrilla.koszko.org/schemas/api_resource_description-1.schema.json", "source_name": "haketilo-default-settings", "source_copyright": [{ "file": "CC0-1.0.txt", @@ -47,6 +48,7 @@ "mapping": { "haketilo-demo-message": { "2022.1.28": { + "$schema": "https://hydrilla.koszko.org/schemas/api_mapping_description-1.schema.json", "source_name": "haketilo-default-settings", "source_copyright": [{ "file": "CC0-1.0.txt", diff --git a/html/install.js b/html/install.js index 2a72662..7b3f3fe 100644 --- a/html/install.js +++ b/html/install.js @@ -47,8 +47,7 @@ #FROM common/browser.js IMPORT browser #FROM html/DOM_helpers.js IMPORT clone_template, Showable -#FROM common/entities.js IMPORT item_id_string, version_string, get_files, \ - is_valid_version +#FROM common/entities.js IMPORT item_id_string, version_string, get_files #FROM common/misc.js IMPORT sha256_async AS compute_sha256 const coll = new Intl.Collator(); @@ -114,6 +113,19 @@ async function init_work() { new Promise((...cbs) => [work.resolve_cb, work.reject_cb] = cbs)]; } +function _make_url_reg(item_type) { + return new RegExp( + `^https://hydrilla\\.koszko\\.org/schemas/api_${item_type}_description-1\\.([1-9][0-9]*\\.)*schema\\.json$` + ); +} + +const _regexes = {}; + +function item_schema_url_regex(item_type) { + _regexes[item_type] = _regexes[item_type] || _make_url_reg(item_type); + return _regexes[item_type]; +} + function InstallView(tab_id, on_view_show, on_view_hide) { Showable.call(this, on_view_show, on_view_hide); @@ -191,26 +203,19 @@ function InstallView(tab_id, on_view_show, on_view_hide) { "Repository's response is not valid JSON :("); } - if (!is_valid_version(response.json.api_schema_version)) { - var bad_api_ver = ""; - } else if (response.json.api_schema_version > [1]) { - var bad_api_ver = - ` (${version_string(response.json.api_schema_version)})`; - } else { - var bad_api_ver = false; - } + const captype = item_type[0].toUpperCase() + item_type.substring(1); + const reg = item_schema_url_regex(item_type); - if (bad_api_ver !== false) { - const captype = item_type[0].toUpperCase() + item_type.substring(1); - const msg = `${captype} ${item_id_string(id, ver)} was served using unsupported Hydrilla API version${bad_api_ver}. You might need to update Haketilo.`; + if (!response.json["$schema"]) { + const msg = `${captype} ${item_id_string(id, ver)} was served using a nonconforming response format.`; + return work.err(null, msg); + } else if (!reg.test(response.json["$schema"])) { + const msg = `${captype} ${item_id_string(id, ver)} was served using unsupported Hydrilla API version. You might need to update Haketilo.`; return work.err(null, msg); } /* TODO: JSON schema validation should be added here. */ - delete response.json.api_schema_version; - delete response.json.api_schema_revision; - const scripts = item_type === "resource" && response.json.scripts; const files = response.json.source_copyright.concat(scripts || []); diff --git a/html/item_preview.html b/html/item_preview.html index 34ce6e1..356ee62 100644 --- a/html/item_preview.html +++ b/html/item_preview.html @@ -46,6 +46,8 @@

resource preview

+ + ... ... @@ -68,6 +70,8 @@

mapping preview

+ + ... ... diff --git a/html/item_preview.js b/html/item_preview.js index 1da9492..b67455a 100644 --- a/html/item_preview.js +++ b/html/item_preview.js @@ -73,6 +73,12 @@ function resource_preview(resource, preview_object, link_cb=make_file_link) { if (preview_object === undefined) preview_object = clone_template("resource_preview"); + preview_object.conforms_to.innerHTML = ""; + const schema_link = document.createElement("a"); + schema_link.href = resource.$schema; + schema_link.innerText = resource.$schema; + preview_object.conforms_to.append(schema_link); + preview_object.identifier.innerText = resource.identifier; preview_object.long_name.innerText = resource.long_name; preview_object.uuid.innerText = resource.uuid; @@ -104,6 +110,12 @@ function mapping_preview(mapping, preview_object, link_cb=make_file_link) { if (preview_object === undefined) preview_object = clone_template("mapping_preview"); + preview_object.conforms_to.innerHTML = ""; + const schema_link = document.createElement("a"); + schema_link.href = mapping.$schema; + schema_link.innerText = mapping.$schema; + preview_object.conforms_to.append(schema_link); + preview_object.identifier.innerText = mapping.identifier; preview_object.long_name.innerText = mapping.long_name; preview_object.uuid.innerText = mapping.uuid; diff --git a/html/repo_query.html b/html/repo_query.html index b9c9269..67158cc 100644 --- a/html/repo_query.html +++ b/html/repo_query.html @@ -61,6 +61,10 @@ background-color: #f0f0f0; } + .repo_query_info_div { + margin: 0.5em; + } + .repo_query_result_li { margin: 0; padding: 0.2em; @@ -134,7 +138,9 @@
- Querying repository... +
+ Querying repository... +
    diff --git a/html/repo_query.js b/html/repo_query.js index a4b8890..d2f0e9b 100644 --- a/html/repo_query.js +++ b/html/repo_query.js @@ -45,8 +45,7 @@ #FROM common/browser.js IMPORT browser #FROM html/DOM_helpers.js IMPORT clone_template, Showable -#FROM common/entities.js IMPORT item_id_string, version_string, \ - is_valid_version +#FROM common/entities.js IMPORT item_id_string, version_string #FROM html/install.js IMPORT InstallView const coll = new Intl.Collator(); @@ -69,6 +68,10 @@ function ResultEntry(repo_entry, mapping_ref) { this.install_but.addEventListener("click", cb); } +const query_schema_url_regex = new RegExp( + "^https://hydrilla\\.koszko\\.org/schemas/api_query_result-1\\.([1-9][0-9]*\\.)*schema\\.json$" +); + function RepoEntry(query_view, repo_url) { Object.assign(this, clone_template("repo_query_single_repo")); Object.assign(this, {query_view, repo_url}); @@ -91,17 +94,10 @@ function RepoEntry(query_view, repo_url) { if ("error_json" in response) throw "Repository's response is not valid JSON :("; - if (!is_valid_version(response.json.api_schema_version)) { - var bad_api_ver = ""; - } else if (response.json.api_schema_version > [1]) { - var bad_api_ver = - ` (${version_string(response.json.api_schema_version)})`; - } else { - var bad_api_ver = false; - } - - if (bad_api_ver !== false) - throw `Results were served using unsupported Hydrilla API version${bad_api_ver}. You might need to update Haketilo.`; + if (!response.json["$schema"]) + throw "Results were served using a nonconforming response format."; + if (!query_schema_url_regex.test(response.json["$schema"])) + throw "Results were served using unsupported Hydrilla API version. You might need to update Haketilo."; /* TODO: here we should perform JSON schema validation! */ @@ -114,7 +110,7 @@ function RepoEntry(query_view, repo_url) { try { var results = await query_results(); } catch(e) { - this.info_span.innerText = e; + this.info_div.innerText = e; return; } @@ -122,12 +118,12 @@ function RepoEntry(query_view, repo_url) { if (this.result_entries.length > 0) { this.results_list.classList.remove("hide"); - this.info_span.remove(); + this.info_div.remove(); const to_append = this.result_entries.map(re => re.main_li); this.results_list.append(...to_append); } else { - this.info_span.innerText = "No results :("; + this.info_div.innerText = "No results :("; } } diff --git a/test/unit/test_install.py b/test/unit/test_install.py index cef2aa6..f4bc483 100644 --- a/test/unit/test_install.py +++ b/test/unit/test_install.py @@ -197,7 +197,7 @@ def test_install_normal_usage(driver, execute_in_page, complex_variant): 'HTTP_code_item', 'invalid_JSON', 'newer_API_version', - 'invalid_API_version', + 'invalid_response_format', 'indexeddb_error_item', 'installing', 'indexeddb_error_file_uses', @@ -285,7 +285,7 @@ def test_install_dialogs(driver, execute_in_page, message): const response = { ok: true, status: 200, - json: {api_schema_version: [99, 99]} + json: {$schema: "https://hydrilla.koszko.org/schemas/api_mapping_description-2.1.schema.json"} }; browser.tabs.sendMessage = () => Promise.resolve(response); install_view.show(...arguments); @@ -293,15 +293,15 @@ def test_install_dialogs(driver, execute_in_page, message): 'https://hydril.la/', 'mapping', 'somemapping', [2, 1]) assert_dlg(['conf_buts'], - 'Mapping somemapping-2.1 was served using unsupported Hydrilla API version (99.99). You might need to update Haketilo.') - elif message == 'invalid_API_version': + 'Mapping somemapping-2.1 was served using unsupported Hydrilla API version. You might need to update Haketilo.') + elif message == 'invalid_response_format': execute_in_page( ''' const response = { ok: true, status: 200, - /* API version here is not an array as it should be. */ - json: {api_schema_version: 123} + /* $schema is not a string as it should be. */ + json: {$schema: null} }; browser.tabs.sendMessage = () => Promise.resolve(response); install_view.show(...arguments); @@ -309,7 +309,7 @@ def test_install_dialogs(driver, execute_in_page, message): 'https://hydril.la/', 'resource', 'someresource') assert_dlg(['conf_buts'], - 'Resource someresource was served using unsupported Hydrilla API version. You might need to update Haketilo.') + 'Resource someresource was served using a nonconforming response format.') elif message == 'indexeddb_error_item': execute_in_page( ''' diff --git a/test/unit/test_repo_query.py b/test/unit/test_repo_query.py index 77c5e75..c8c4875 100644 --- a/test/unit/test_repo_query.py +++ b/test/unit/test_repo_query.py @@ -137,7 +137,7 @@ def test_repo_query_normal_usage(driver, execute_in_page): 'HTTP_code', 'invalid_JSON', 'newer_API_version', - 'invalid_API_version', + 'invalid_response_format', 'querying_repo', 'no_results' ]) @@ -183,7 +183,7 @@ def test_repo_query_messages(driver, execute_in_page, message): ) show_and_wait_for_repo_entry() - elem = execute_in_page('returnval(view.repo_entries[0].info_span);') + elem = execute_in_page('returnval(view.repo_entries[0].info_div);') done = has_msg('Failure to communicate with repository :(', elem) WebDriverWait(driver, 10).until(done) elif message == 'HTTP_code': @@ -195,7 +195,7 @@ def test_repo_query_messages(driver, execute_in_page, message): ''') show_and_wait_for_repo_entry() - elem = execute_in_page('returnval(view.repo_entries[0].info_span);') + elem = execute_in_page('returnval(view.repo_entries[0].info_div);') done = has_msg('Repository sent HTTP code 405 :(', elem) WebDriverWait(driver, 10).until(done) elif message == 'invalid_JSON': @@ -207,7 +207,7 @@ def test_repo_query_messages(driver, execute_in_page, message): ''') show_and_wait_for_repo_entry() - elem = execute_in_page('returnval(view.repo_entries[0].info_span);') + elem = execute_in_page('returnval(view.repo_entries[0].info_div);') done = has_msg("Repository's response is not valid JSON :(", elem) WebDriverWait(driver, 10).until(done) elif message == 'newer_API_version': @@ -217,30 +217,31 @@ def test_repo_query_messages(driver, execute_in_page, message): const response = { ok: true, status: 200, - json: {api_schema_version: [1234]} + json: {$schema: "https://hydrilla.koszko.org/schemas/api_query_result-3.2.1.schema.json"} }; browser.tabs.sendMessage = () => Promise.resolve(response); ''') show_and_wait_for_repo_entry() - elem = execute_in_page('returnval(view.repo_entries[0].info_span);') - msg = 'Results were served using unsupported Hydrilla API version (1234). You might need to update Haketilo.' + elem = execute_in_page('returnval(view.repo_entries[0].info_div);') + msg = 'Results were served using unsupported Hydrilla API version. You might need to update Haketilo.' WebDriverWait(driver, 10).until(has_msg(msg, elem)) - elif message == 'invalid_API_version': + elif message == 'invalid_response_format': setup_view(execute_in_page, repo_urls) execute_in_page( ''' const response = { ok: true, status: 200, - json: {api_schema_version: null} + /* $schema is not a string as it should be. */ + json: {$schema: null} }; browser.tabs.sendMessage = () => Promise.resolve(response); ''') show_and_wait_for_repo_entry() - elem = execute_in_page('returnval(view.repo_entries[0].info_span);') - msg = 'Results were served using unsupported Hydrilla API version. You might need to update Haketilo.' + elem = execute_in_page('returnval(view.repo_entries[0].info_div);') + msg = 'Results were served using a nonconforming response format.' WebDriverWait(driver, 10).until(has_msg(msg, elem)) elif message == 'querying_repo': setup_view(execute_in_page, repo_urls) @@ -249,7 +250,7 @@ def test_repo_query_messages(driver, execute_in_page, message): ) show_and_wait_for_repo_entry() - elem = execute_in_page('returnval(view.repo_entries[0].info_span);') + elem = execute_in_page('returnval(view.repo_entries[0].info_div);') assert has_msg('Querying repository...', elem)(0) elif message == 'no_results': setup_view(execute_in_page, repo_urls) @@ -259,8 +260,7 @@ def test_repo_query_messages(driver, execute_in_page, message): ok: true, status: 200, json: { - api_schema_version: [1], - api_schema_revision: 1, + $schema: "https://hydrilla.koszko.org/schemas/api_query_result-1.schema.json", mappings: [] } }; @@ -268,7 +268,7 @@ def test_repo_query_messages(driver, execute_in_page, message): ''') show_and_wait_for_repo_entry() - elem = execute_in_page('returnval(view.repo_entries[0].info_span);') + elem = execute_in_page('returnval(view.repo_entries[0].info_div);') WebDriverWait(driver, 10).until(has_msg('No results :(', elem)) else: raise Exception('made a typo in test function params?') diff --git a/test/unit/utils.py b/test/unit/utils.py index 6ff04ed..b27a209 100644 --- a/test/unit/utils.py +++ b/test/unit/utils.py @@ -79,6 +79,11 @@ def make_sample_mapping(with_files=True): Haketilo's IndexedDB. """ return { + '$schema': 'https://hydrilla.koszko.org/schemas/api_mapping_description-1.schema.json', + 'generated_by': { + 'name': 'human', + 'version': 'sapiens-0.8.14' + }, 'source_name': 'example-org-fixes-new', 'source_copyright': [ sample_file_ref('report.spdx'), @@ -106,6 +111,11 @@ def make_sample_resource(with_files=True): Haketilo's IndexedDB. """ return { + '$schema': 'https://hydrilla.koszko.org/schemas/api_resource_description-1.schema.json', + 'generated_by': { + 'name': 'human', + 'version': 'sapiens-0.8.14' + }, 'source_name': 'hello', 'source_copyright': [ sample_file_ref('report.spdx'), diff --git a/test/world_wide_library.py b/test/world_wide_library.py index 56af35f..1e802ab 100644 --- a/test/world_wide_library.py +++ b/test/world_wide_library.py @@ -150,8 +150,6 @@ sample_queries = {} for srt in sample_resource_templates: resource = make_sample_resource() - resource['api_schema_version'] = [1] - resource['api_schema_revision'] = 1 resource['identifier'] = f'resource_{srt["id_suffix"]}' resource['long_name'] = resource['identifier'].upper() resource['uuid'] = str(uuid4()) @@ -166,8 +164,6 @@ for srt in sample_resource_templates: resource_versions[1][-1] += 1 mapping = make_sample_mapping() - mapping['api_schema_version'] = [1] - mapping['api_schema_revision'] = 1 mapping['identifier'] = f'mapping_{srt["id_suffix"]}' mapping['long_name'] = mapping['identifier'].upper() mapping['uuid'] = str(uuid4()) @@ -208,9 +204,12 @@ for srt in sample_resource_templates: def serve_query(command, get_params, post_params): response = { - 'api_schema_version': [1], - 'api_schema_revision': 1, - 'mappings': sample_queries[get_params['url'][0]] + '$schema': 'https://hydrilla.koszko.org/schemas/api_query_result-1.schema.json', + 'generated_by': { + 'name': 'human', + 'version': 'sapiens-0.8.15' + }, + 'mappings': sample_queries[get_params['url'][0]] } return (200, {}, json.dumps(response)) -- cgit v1.2.3