Project

General

Profile

« Previous | Next » 

Revision 72553a2d

Added by koszko over 1 year ago

assume and use "$schema" properties in item definitions

View differences:

common/entities.js
100 100
}
101 101
#EXPORT get_newest_version AS get_newest
102 102

  
103
/*
104
 * Returns true if the argument is a nonempty array of numbers without trailing
105
 * zeros.
106
 */
107
function is_valid_version(version) {
108
    return Array.isArray(version) && version.length > 0 &&
109
	version.every(n => typeof n === "number") &&
110
	version[version.length - 1] !== 0;
111
}
112
#EXPORT is_valid_version
113

  
114 103
/*
115 104
 * item is a definition of a resource or mapping. Yield all file references
116 105
 * (objects with `file` and `sha256` properties) this definition has.
default_settings.json
24 24
    "resource": {
25 25
	"haketilo-demo-script": {
26 26
	    "2022.1.28": {
27
		"$schema": "https://hydrilla.koszko.org/schemas/api_resource_description-1.schema.json",
27 28
		"source_name": "haketilo-default-settings",
28 29
		"source_copyright": [{
29 30
		    "file": "CC0-1.0.txt",
......
47 48
    "mapping": {
48 49
	"haketilo-demo-message": {
49 50
	    "2022.1.28": {
51
		"$schema": "https://hydrilla.koszko.org/schemas/api_mapping_description-1.schema.json",
50 52
		"source_name": "haketilo-default-settings",
51 53
		"source_copyright": [{
52 54
		    "file": "CC0-1.0.txt",
html/install.js
47 47

  
48 48
#FROM common/browser.js   IMPORT browser
49 49
#FROM html/DOM_helpers.js IMPORT clone_template, Showable
50
#FROM common/entities.js  IMPORT item_id_string, version_string, get_files, \
51
                                 is_valid_version
50
#FROM common/entities.js  IMPORT item_id_string, version_string, get_files
52 51
#FROM common/misc.js      IMPORT sha256_async AS compute_sha256
53 52

  
54 53
const coll = new Intl.Collator();
......
114 113
	    new Promise((...cbs) => [work.resolve_cb, work.reject_cb] = cbs)];
115 114
}
116 115

  
116
function _make_url_reg(item_type) {
117
    return new RegExp(
118
	`^https://hydrilla\\.koszko\\.org/schemas/api_${item_type}_description-1\\.([1-9][0-9]*\\.)*schema\\.json$`
119
    );
120
}
121

  
122
const _regexes = {};
123

  
124
function item_schema_url_regex(item_type) {
125
    _regexes[item_type] = _regexes[item_type] || _make_url_reg(item_type);
126
    return _regexes[item_type];
127
}
128

  
117 129
function InstallView(tab_id, on_view_show, on_view_hide) {
118 130
    Showable.call(this, on_view_show, on_view_hide);
119 131

  
......
191 203
			    "Repository's response is not valid JSON :(");
192 204
	}
193 205

  
194
	if (!is_valid_version(response.json.api_schema_version)) {
195
	    var bad_api_ver = "";
196
	} else if (response.json.api_schema_version > [1]) {
197
	    var bad_api_ver =
198
		` (${version_string(response.json.api_schema_version)})`;
199
	} else {
200
	    var bad_api_ver = false;
201
	}
206
	const captype = item_type[0].toUpperCase() + item_type.substring(1);
207
	const reg = item_schema_url_regex(item_type);
202 208

  
203
	if (bad_api_ver !== false) {
204
	    const captype = item_type[0].toUpperCase() + item_type.substring(1);
205
	    const msg = `${captype} ${item_id_string(id, ver)} was served using unsupported Hydrilla API version${bad_api_ver}. You might need to update Haketilo.`;
209
	if (!response.json["$schema"]) {
210
	    const msg = `${captype} ${item_id_string(id, ver)} was served using a nonconforming response format.`;
211
	    return work.err(null, msg);
212
	} else if (!reg.test(response.json["$schema"])) {
213
	    const msg = `${captype} ${item_id_string(id, ver)} was served using unsupported Hydrilla API version. You might need to update Haketilo.`;
206 214
	    return work.err(null, msg);
207 215
	}
208 216

  
209 217
	/* TODO: JSON schema validation should be added here. */
210 218

  
211
	delete response.json.api_schema_version;
212
	delete response.json.api_schema_revision;
213

  
214 219
	const scripts = item_type === "resource" && response.json.scripts;
215 220
	const files = response.json.source_copyright.concat(scripts || []);
216 221

  
html/item_preview.html
46 46
  <div id="resource_preview" data-template="main_div"
47 47
       class="grid_2 grid_form item_preview_main_div">
48 48
    <h3 class="grid_col_both">resource preview</h3>
49
    <label class="grid_col_1">conforms to:</label>
50
    <span data-template="conforms_to" class="grid_col_2">...</span>
49 51
    <label class="grid_col_1">identifier:</label>
50 52
    <span data-template="identifier" class="grid_col_2">...</span>
51 53
    <label class="grid_col_1">long name:</label>
......
68 70
  <div id="mapping_preview" data-template="main_div"
69 71
       class="grid_2 grid_form">
70 72
    <h3 class="grid_col_both">mapping preview</h3>
73
    <label class="grid_col_1">conforms to:</label>
74
    <span data-template="conforms_to" class="grid_col_2">...</span>
71 75
    <label class="grid_col_1">identifier:</label>
72 76
    <span data-template="identifier" class="grid_col_2">...</span>
73 77
    <label class="grid_col_1">long name:</label>
html/item_preview.js
73 73
    if (preview_object === undefined)
74 74
	preview_object = clone_template("resource_preview");
75 75

  
76
    preview_object.conforms_to.innerHTML = "";
77
    const schema_link     = document.createElement("a");
78
    schema_link.href      = resource.$schema;
79
    schema_link.innerText = resource.$schema;
80
    preview_object.conforms_to.append(schema_link);
81

  
76 82
    preview_object.identifier.innerText  = resource.identifier;
77 83
    preview_object.long_name.innerText   = resource.long_name;
78 84
    preview_object.uuid.innerText        = resource.uuid;
......
104 110
    if (preview_object === undefined)
105 111
	preview_object = clone_template("mapping_preview");
106 112

  
113
    preview_object.conforms_to.innerHTML = "";
114
    const schema_link     = document.createElement("a");
115
    schema_link.href      = mapping.$schema;
116
    schema_link.innerText = mapping.$schema;
117
    preview_object.conforms_to.append(schema_link);
118

  
107 119
    preview_object.identifier.innerText  = mapping.identifier;
108 120
    preview_object.long_name.innerText   = mapping.long_name;
109 121
    preview_object.uuid.innerText        = mapping.uuid;
html/repo_query.html
61 61
      background-color: #f0f0f0;
62 62
  }
63 63

  
64
  .repo_query_info_div {
65
      margin: 0.5em;
66
  }
67

  
64 68
  .repo_query_result_li {
65 69
      margin: 0;
66 70
      padding: 0.2em;
......
134 138
      </span>
135 139
    </div>
136 140
    <div data-template="list_container" class="hide repo_query_results_list">
137
      <span data-template="info_span">Querying repository...</span>
141
      <div data-template="info_div" class="repo_query_info_div">
142
	Querying repository...
143
      </div>
138 144
      <ul data-template="results_list" class="hide"></ul>
139 145
    </div>
140 146
  </li>
html/repo_query.js
45 45

  
46 46
#FROM common/browser.js   IMPORT browser
47 47
#FROM html/DOM_helpers.js IMPORT clone_template, Showable
48
#FROM common/entities.js  IMPORT item_id_string, version_string, \
49
                                 is_valid_version
48
#FROM common/entities.js  IMPORT item_id_string, version_string
50 49
#FROM html/install.js     IMPORT InstallView
51 50

  
52 51
const coll = new Intl.Collator();
......
69 68
    this.install_but.addEventListener("click", cb);
70 69
}
71 70

  
71
const query_schema_url_regex = new RegExp(
72
    "^https://hydrilla\\.koszko\\.org/schemas/api_query_result-1\\.([1-9][0-9]*\\.)*schema\\.json$"
73
);
74

  
72 75
function RepoEntry(query_view, repo_url) {
73 76
    Object.assign(this, clone_template("repo_query_single_repo"));
74 77
    Object.assign(this, {query_view, repo_url});
......
91 94
	if ("error_json" in response)
92 95
	    throw "Repository's response is not valid JSON :(";
93 96

  
94
	if (!is_valid_version(response.json.api_schema_version)) {
95
	    var bad_api_ver = "";
96
	} else if (response.json.api_schema_version > [1]) {
97
	    var bad_api_ver =
98
		` (${version_string(response.json.api_schema_version)})`;
99
	} else {
100
	    var bad_api_ver = false;
101
	}
102

  
103
	if (bad_api_ver !== false)
104
	    throw `Results were served using unsupported Hydrilla API version${bad_api_ver}. You might need to update Haketilo.`;
97
	if (!response.json["$schema"])
98
	    throw "Results were served using a nonconforming response format.";
99
	if (!query_schema_url_regex.test(response.json["$schema"]))
100
	    throw "Results were served using unsupported Hydrilla API version. You might need to update Haketilo.";
105 101

  
106 102
	/* TODO: here we should perform JSON schema validation! */
107 103

  
......
114 110
	try {
115 111
	    var results = await query_results();
116 112
	} catch(e) {
117
	    this.info_span.innerText = e;
113
	    this.info_div.innerText = e;
118 114
	    return;
119 115
	}
120 116

  
......
122 118

  
123 119
	if (this.result_entries.length > 0) {
124 120
	    this.results_list.classList.remove("hide");
125
	    this.info_span.remove();
121
	    this.info_div.remove();
126 122

  
127 123
	    const to_append = this.result_entries.map(re => re.main_li);
128 124
	    this.results_list.append(...to_append);
129 125
	} else {
130
	    this.info_span.innerText = "No results :(";
126
	    this.info_div.innerText = "No results :(";
131 127
	}
132 128
    }
133 129

  
test/unit/test_install.py
197 197
    'HTTP_code_item',
198 198
    'invalid_JSON',
199 199
    'newer_API_version',
200
    'invalid_API_version',
200
    'invalid_response_format',
201 201
    'indexeddb_error_item',
202 202
    'installing',
203 203
    'indexeddb_error_file_uses',
......
285 285
            const response = {
286 286
                ok: true,
287 287
                status: 200,
288
                json: {api_schema_version: [99, 99]}
288
                json: {$schema: "https://hydrilla.koszko.org/schemas/api_mapping_description-2.1.schema.json"}
289 289
            };
290 290
            browser.tabs.sendMessage = () => Promise.resolve(response);
291 291
            install_view.show(...arguments);
......
293 293
            'https://hydril.la/', 'mapping', 'somemapping', [2, 1])
294 294

  
295 295
        assert_dlg(['conf_buts'],
296
                   'Mapping somemapping-2.1 was served using unsupported Hydrilla API version (99.99). You might need to update Haketilo.')
297
    elif message == 'invalid_API_version':
296
                   'Mapping somemapping-2.1 was served using unsupported Hydrilla API version. You might need to update Haketilo.')
297
    elif message == 'invalid_response_format':
298 298
        execute_in_page(
299 299
            '''
300 300
            const response = {
301 301
                ok: true,
302 302
                status: 200,
303
                /* API version here is not an array as it should be. */
304
                json: {api_schema_version: 123}
303
                /* $schema is not a string as it should be. */
304
                json: {$schema: null}
305 305
            };
306 306
            browser.tabs.sendMessage = () => Promise.resolve(response);
307 307
            install_view.show(...arguments);
......
309 309
            'https://hydril.la/', 'resource', 'someresource')
310 310

  
311 311
        assert_dlg(['conf_buts'],
312
                   'Resource someresource was served using unsupported Hydrilla API version. You might need to update Haketilo.')
312
                   'Resource someresource was served using a nonconforming response format.')
313 313
    elif message == 'indexeddb_error_item':
314 314
        execute_in_page(
315 315
            '''
test/unit/test_repo_query.py
137 137
    'HTTP_code',
138 138
    'invalid_JSON',
139 139
    'newer_API_version',
140
    'invalid_API_version',
140
    'invalid_response_format',
141 141
    'querying_repo',
142 142
    'no_results'
143 143
])
......
183 183
        )
184 184
        show_and_wait_for_repo_entry()
185 185

  
186
        elem = execute_in_page('returnval(view.repo_entries[0].info_span);')
186
        elem = execute_in_page('returnval(view.repo_entries[0].info_div);')
187 187
        done = has_msg('Failure to communicate with repository :(', elem)
188 188
        WebDriverWait(driver, 10).until(done)
189 189
    elif message == 'HTTP_code':
......
195 195
            ''')
196 196
        show_and_wait_for_repo_entry()
197 197

  
198
        elem = execute_in_page('returnval(view.repo_entries[0].info_span);')
198
        elem = execute_in_page('returnval(view.repo_entries[0].info_div);')
199 199
        done = has_msg('Repository sent HTTP code 405 :(', elem)
200 200
        WebDriverWait(driver, 10).until(done)
201 201
    elif message == 'invalid_JSON':
......
207 207
            ''')
208 208
        show_and_wait_for_repo_entry()
209 209

  
210
        elem = execute_in_page('returnval(view.repo_entries[0].info_span);')
210
        elem = execute_in_page('returnval(view.repo_entries[0].info_div);')
211 211
        done = has_msg("Repository's response is not valid JSON :(", elem)
212 212
        WebDriverWait(driver, 10).until(done)
213 213
    elif message == 'newer_API_version':
......
217 217
            const response = {
218 218
                ok: true,
219 219
                status: 200,
220
                json: {api_schema_version: [1234]}
220
                json: {$schema: "https://hydrilla.koszko.org/schemas/api_query_result-3.2.1.schema.json"}
221 221
            };
222 222
            browser.tabs.sendMessage = () => Promise.resolve(response);
223 223
            ''')
224 224
        show_and_wait_for_repo_entry()
225 225

  
226
        elem = execute_in_page('returnval(view.repo_entries[0].info_span);')
227
        msg = 'Results were served using unsupported Hydrilla API version (1234). You might need to update Haketilo.'
226
        elem = execute_in_page('returnval(view.repo_entries[0].info_div);')
227
        msg = 'Results were served using unsupported Hydrilla API version. You might need to update Haketilo.'
228 228
        WebDriverWait(driver, 10).until(has_msg(msg, elem))
229
    elif message == 'invalid_API_version':
229
    elif message == 'invalid_response_format':
230 230
        setup_view(execute_in_page, repo_urls)
231 231
        execute_in_page(
232 232
            '''
233 233
            const response = {
234 234
                ok: true,
235 235
                status: 200,
236
                json: {api_schema_version: null}
236
                /* $schema is not a string as it should be. */
237
                json: {$schema: null}
237 238
            };
238 239
            browser.tabs.sendMessage = () => Promise.resolve(response);
239 240
            ''')
240 241
        show_and_wait_for_repo_entry()
241 242

  
242
        elem = execute_in_page('returnval(view.repo_entries[0].info_span);')
243
        msg = 'Results were served using unsupported Hydrilla API version. You might need to update Haketilo.'
243
        elem = execute_in_page('returnval(view.repo_entries[0].info_div);')
244
        msg = 'Results were served using a nonconforming response format.'
244 245
        WebDriverWait(driver, 10).until(has_msg(msg, elem))
245 246
    elif message == 'querying_repo':
246 247
        setup_view(execute_in_page, repo_urls)
......
249 250
        )
250 251
        show_and_wait_for_repo_entry()
251 252

  
252
        elem = execute_in_page('returnval(view.repo_entries[0].info_span);')
253
        elem = execute_in_page('returnval(view.repo_entries[0].info_div);')
253 254
        assert has_msg('Querying repository...', elem)(0)
254 255
    elif message == 'no_results':
255 256
        setup_view(execute_in_page, repo_urls)
......
259 260
                ok: true,
260 261
                status: 200,
261 262
                json: {
262
                    api_schema_version: [1],
263
                    api_schema_revision: 1,
263
                    $schema: "https://hydrilla.koszko.org/schemas/api_query_result-1.schema.json",
264 264
                    mappings: []
265 265
                }
266 266
            };
......
268 268
            ''')
269 269
        show_and_wait_for_repo_entry()
270 270

  
271
        elem = execute_in_page('returnval(view.repo_entries[0].info_span);')
271
        elem = execute_in_page('returnval(view.repo_entries[0].info_div);')
272 272
        WebDriverWait(driver, 10).until(has_msg('No results :(', elem))
273 273
    else:
274 274
        raise Exception('made a typo in test function params?')
test/unit/utils.py
79 79
    Haketilo's IndexedDB.
80 80
    """
81 81
    return {
82
        '$schema': 'https://hydrilla.koszko.org/schemas/api_mapping_description-1.schema.json',
83
        'generated_by': {
84
            'name': 'human',
85
            'version': 'sapiens-0.8.14'
86
        },
82 87
        'source_name': 'example-org-fixes-new',
83 88
        'source_copyright': [
84 89
            sample_file_ref('report.spdx'),
......
106 111
    Haketilo's IndexedDB.
107 112
    """
108 113
    return {
114
        '$schema': 'https://hydrilla.koszko.org/schemas/api_resource_description-1.schema.json',
115
        'generated_by': {
116
            'name': 'human',
117
            'version': 'sapiens-0.8.14'
118
        },
109 119
        'source_name': 'hello',
110 120
        'source_copyright': [
111 121
            sample_file_ref('report.spdx'),
test/world_wide_library.py
150 150

  
151 151
for srt in sample_resource_templates:
152 152
    resource = make_sample_resource()
153
    resource['api_schema_version']  = [1]
154
    resource['api_schema_revision'] = 1
155 153
    resource['identifier']          = f'resource_{srt["id_suffix"]}'
156 154
    resource['long_name']           = resource['identifier'].upper()
157 155
    resource['uuid']                = str(uuid4())
......
166 164
    resource_versions[1][-1] += 1
167 165

  
168 166
    mapping = make_sample_mapping()
169
    mapping['api_schema_version']  = [1]
170
    mapping['api_schema_revision'] = 1
171 167
    mapping['identifier']          = f'mapping_{srt["id_suffix"]}'
172 168
    mapping['long_name']           = mapping['identifier'].upper()
173 169
    mapping['uuid']                = str(uuid4())
......
208 204

  
209 205
def serve_query(command, get_params, post_params):
210 206
    response = {
211
        'api_schema_version':  [1],
212
        'api_schema_revision': 1,
213
        'mappings':            sample_queries[get_params['url'][0]]
207
        '$schema': 'https://hydrilla.koszko.org/schemas/api_query_result-1.schema.json',
208
        'generated_by': {
209
            'name': 'human',
210
            'version': 'sapiens-0.8.15'
211
        },
212
        'mappings': sample_queries[get_params['url'][0]]
214 213
    }
215 214

  
216 215
    return (200, {}, json.dumps(response))

Also available in: Unified diff