From 58066f1948b1fa7233a27625e0cbfa59b8413a1b Mon Sep 17 00:00:00 2001 From: Richard Bowman Date: Fri, 29 Nov 2024 13:03:36 +0000 Subject: [PATCH] Add a first unit test This checks that the server can start and perform an autofocus. Autofocus in turn will test the camera and stage dependencies. --- tests/images/capture_v280.jpg | Bin 906 -> 0 bytes tests/test_api_utilities.py | 19 - tests/test_app.py | 43 -- tests/test_camera_framestream.py | 74 --- tests/test_capture_manager.py | 150 ----- tests/test_capture_object.py | 232 ------- tests/test_dummy_server.py | 22 + tests/test_extension_scan.py | 57 -- tests/test_json.py | 59 -- tests/test_utilities.py | 31 - tests/w3c_td_schema.json | 1035 ------------------------------ 11 files changed, 22 insertions(+), 1700 deletions(-) delete mode 100644 tests/images/capture_v280.jpg delete mode 100644 tests/test_api_utilities.py delete mode 100644 tests/test_app.py delete mode 100644 tests/test_camera_framestream.py delete mode 100644 tests/test_capture_manager.py delete mode 100644 tests/test_capture_object.py create mode 100644 tests/test_dummy_server.py delete mode 100644 tests/test_extension_scan.py delete mode 100644 tests/test_json.py delete mode 100644 tests/test_utilities.py delete mode 100644 tests/w3c_td_schema.json diff --git a/tests/images/capture_v280.jpg b/tests/images/capture_v280.jpg deleted file mode 100644 index a2d4d1a188962c8bb37a2ee0e3efe71f8fdaa25e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 906 zcmex=GzJD=Uj{7(1_llW#`a7G76t|eMg|53DFzT=oYcm^&cML%oPmKs zqgp95H!(d`$x5MGDKkaMNIzCa$}%z<=9)SNh9)K^^6Hkh7DhID#wJD}Ll`+ZIeEBw zBzSoxj1*-QjYtOn4=@OFFo-aSFf%GKFbOg;3o`yc!XVGUz{tu72B0VcVMZoq7FITP z4o)ua|3?_M3NSD+GBY!=Ftf6ovIz$!vMUve7&T5@$f4}C z@t|nX#SbdRNkvVZTw>x9l2WQ_>Kd9_CZ=ZQ7M51dF0O9w9-dyoA)#U65s^{JDXD4c z8JStdC8cHM6_r)ZEv;?s9i3g1CQq3GGAU*RJ2VdF$b$$4{OPfBE|D`;VW$K>lK6U=!0ld z(M2vX6_bamA3 0: - assert f.frames[i].time > f.frames[i - 1].time - - # Test FrameStream.last - assert f.last.size == sizes[-1] + 2 - - -def test_new_frame_event(): - d = b"openflexure" - - def target(framestream): - time.sleep(0.01) - framestream.write(d) - - with FrameStream() as f: - # Start a thread to write some data - thread = threading.Thread(target=target, args=(f,)) - thread.daemon = True - thread.start() - # Make sure the stream is empty before we wait for a frame - assert not f.getvalue() - # Wait for a frame to be written - frame = f.getframe() - assert frame == d - # Make sure getframe() cleared the event - assert not f.new_frame.events[threading.get_ident()][0].is_set() diff --git a/tests/test_capture_manager.py b/tests/test_capture_manager.py deleted file mode 100644 index 16fb768b..00000000 --- a/tests/test_capture_manager.py +++ /dev/null @@ -1,150 +0,0 @@ -import os - -import pytest -from freezegun import freeze_time - -from openflexure_microscope.captures.capture_manager import ( - BASE_CAPTURE_PATH, - TEMP_CAPTURE_PATH, - CaptureManager, -) - - -def test_new_manager(): - m = CaptureManager() - assert BASE_CAPTURE_PATH - assert TEMP_CAPTURE_PATH - assert m.paths["default"] == BASE_CAPTURE_PATH - assert m.paths["temp"] == TEMP_CAPTURE_PATH - - -def test_update_settings(): - m = CaptureManager() - assert m.paths["default"] == BASE_CAPTURE_PATH - assert m.paths["temp"] == TEMP_CAPTURE_PATH - - new_settings = { - "paths": {"default": "BASE_CAPTURE_PATH", "temp": "TEMP_CAPTURE_PATH"} - } - m.update_settings(new_settings) - assert m.paths["default"] == "BASE_CAPTURE_PATH" - assert m.paths["temp"] == "TEMP_CAPTURE_PATH" - assert m.read_settings() == new_settings - - -def test__new_output(): - m = CaptureManager() - - out = m._new_output(True, "filename", "subfolder", "fmt") - assert out.file == os.path.join(TEMP_CAPTURE_PATH, "subfolder", "filename.fmt") - - -def test__new_output_generate_filename(): - m = CaptureManager() - - out = m._new_output(True, None, "subfolder", "fmt") - # Assert a filename was actually generated - assert out.name != ".fmt" - - -def test_new_image(): - m = CaptureManager() - out = m.new_image() - capture_key = str(out.id) - assert capture_key in m.images.keys() - assert m.images[capture_key] is out - - -def test_new_video(): - m = CaptureManager() - out = m.new_video() - capture_key = str(out.id) - assert capture_key in m.videos.keys() - assert m.videos[capture_key] is out - - -def test_image_from_id_str(): - m = CaptureManager() - out = m.new_image() - capture_key = str(out.id) - - assert m.image_from_id(capture_key) is out - - -def test_image_from_id_int(): - m = CaptureManager() - out = m.new_image() - capture_key = int(out.id) - - assert m.image_from_id(capture_key) is out - - -def test_image_from_id_uuid(): - m = CaptureManager() - out = m.new_image() - capture_key = out.id - - assert m.image_from_id(capture_key) is out - - -def test_image_from_id_invalid(): - m = CaptureManager() - out = m.new_image() - capture_key = object() - - with pytest.raises(TypeError): - m.image_from_id(capture_key) is out - - -def test_video_from_id_str(): - m = CaptureManager() - out = m.new_video() - capture_key = str(out.id) - - assert m.video_from_id(capture_key) is out - - -def test_video_from_id_int(): - m = CaptureManager() - out = m.new_video() - capture_key = int(out.id) - - assert m.video_from_id(capture_key) is out - - -def test_video_from_id_uuid(): - m = CaptureManager() - out = m.new_video() - capture_key = out.id - - assert m.video_from_id(capture_key) is out - - -def test_video_from_id_invalid(): - m = CaptureManager() - out = m.new_video() - capture_key = object() - - with pytest.raises(TypeError): - m.video_from_id(capture_key) is out - - -@freeze_time("2020-11-27 12:00:01") -def test_generate_numbered_basename(): - m = CaptureManager() - for _ in range(10): - m.new_image() - generated_filenames = [obj.basename for obj in m.images.values()] - # Assert enough names were generated - assert len(generated_filenames) == 10 - # Assert all names are unique - len(generated_filenames) > len(set(generated_filenames)) - # Assert all filenames are in the expected form - for fname in generated_filenames: - assert fname.startswith("2020-11-27_12-00-01") - - -def test_context_manager(): - with CaptureManager() as m: - m.new_image() - m.new_video() diff --git a/tests/test_capture_object.py b/tests/test_capture_object.py deleted file mode 100644 index 12bcb99d..00000000 --- a/tests/test_capture_object.py +++ /dev/null @@ -1,232 +0,0 @@ -import datetime -import io -import os -import uuid -from collections import OrderedDict - -import piexif -from PIL import Image, ImageChops - -from openflexure_microscope.captures import THUMBNAIL_SIZE, CaptureObject -from openflexure_microscope.captures.capture import ( - build_captures_from_exif, - capture_from_path, -) - -IN_DIR = "tests/images/" -OUT_DIR = "tests/images/out/" - - -def generate_small_image(): - # Create a dummy image to serve in the stream - image = Image.new("RGB", (20, 20), color=(0, 0, 0)) - - return image - - -def generate_small_thumbnail(): - # Create a dummy image to serve in the stream - image = Image.new("RGB", (20, 20), color=(0, 0, 0)) - image.thumbnail(THUMBNAIL_SIZE) - - return image - - -def generate_small_capture(filename: str): - out = os.path.join(OUT_DIR, filename) - obj = CaptureObject(out) - generate_small_image().save(obj, format="JPEG") - - return obj - - -def check_valid_capture(obj: CaptureObject): - assert isinstance(obj.time, datetime.datetime) - assert isinstance(obj.id, uuid.UUID) - - assert isinstance(obj.annotations, dict) - assert isinstance(obj.tags, list) - - assert obj.file - assert obj.format - assert obj.basename - assert obj.filefolder - assert obj.name - - -def check_valid_openflexure_metadata(d: dict): - assert "image" in d - assert d["image"].get("id") - assert d["image"].get("name") - assert d["image"].get("time") - assert d["image"].get("format") - - assert isinstance(d["image"].get("tags"), list) - assert isinstance(d["image"].get("annotations"), dict) - - -def test_new_capture_object(): - obj = generate_small_capture("capture0.jpg") - check_valid_capture(obj) - - -def test_initial_metadata(): - obj = generate_small_capture("capture1.jpg") - - data = obj.read_full_metadata() - check_valid_openflexure_metadata(data) - - -def test_tags(): - obj = generate_small_capture("capture2.jpg") - - obj.put_tags(["foo", "bar"]) - assert obj.read_full_metadata()["image"]["tags"] == ["foo", "bar"] - - obj.delete_tag("foo") - assert obj.read_full_metadata()["image"]["tags"] == ["bar"] - - # Delete nonexistent tag, should change nothing - obj.delete_tag("foo") - assert obj.read_full_metadata()["image"]["tags"] == ["bar"] - - -def test_annotations(): - obj = generate_small_capture("capture3.jpg") - - obj.put_annotations({"foo": "zoo", "bar": "zar"}) - assert obj.read_full_metadata()["image"]["annotations"] == { - "foo": "zoo", - "bar": "zar", - } - - obj.delete_annotation("foo") - assert obj.read_full_metadata()["image"]["annotations"] == {"bar": "zar"} - - # Delete nonexistent annotation, should change nothing - obj.delete_annotation("foo") - assert obj.read_full_metadata()["image"]["annotations"] == {"bar": "zar"} - - -def test_put_metadata(): - obj = generate_small_capture("capture4.jpg") - - obj.put_metadata({"foo": {"bar": "zar"}}) - assert obj.read_full_metadata()["foo"] == {"bar": "zar"} - - -def test_put_and_save(): - obj = generate_small_capture("capture_all.jpg") - obj.put_and_save( - tags=["foo", "bar"], - annotations={"foo": "zoo", "bar": "zar"}, - metadata={"foo": {"bar": "zar"}}, - ) - - full_metadata = obj.read_full_metadata() - assert full_metadata["image"]["tags"] == ["foo", "bar"] - assert full_metadata["image"]["annotations"] == {"foo": "zoo", "bar": "zar"} - assert full_metadata["foo"] == {"bar": "zar"} - - -def test_capture_from_path_this_version(): - """Tests reloading a capture object from a file created in the working server version - """ - - def _check_metadata(data: dict): - assert data["image"]["tags"] == ["foo", "bar"] - assert data["image"]["annotations"] == {"foo": "zoo", "bar": "zar"} - assert data["foo"] == {"bar": "zar"} - - # Create the capture file - obj_in = generate_small_capture("capture_reload.jpg") - obj_in.put_and_save( - tags=["foo", "bar"], - annotations={"foo": "zoo", "bar": "zar"}, - metadata={"foo": {"bar": "zar"}}, - ) - - full_metadata_in = obj_in.read_full_metadata() - check_valid_openflexure_metadata(full_metadata_in) - _check_metadata(full_metadata_in) - - # Reload the capture file - obj = capture_from_path(os.path.join(OUT_DIR, "capture_reload.jpg")) - check_valid_capture(obj) - full_metadata = obj.read_full_metadata() - check_valid_openflexure_metadata(full_metadata) - _check_metadata(full_metadata) - - -def test_capture_from_path_v280(): - """Tests reloading a capture object from a file created in server v2.8.0 - """ - obj = capture_from_path(os.path.join(IN_DIR, "capture_v280.jpg")) - check_valid_capture(obj) - full_metadata = obj.read_full_metadata() - check_valid_openflexure_metadata(full_metadata) - assert full_metadata["image"]["tags"] == ["foo", "bar"] - assert full_metadata["image"]["annotations"] == {"foo": "zoo", "bar": "zar"} - assert full_metadata["foo"] == {"bar": "zar"} - - -def test_build_captures_from_exif(): - # Load the whole tests captures directory - objs = build_captures_from_exif(IN_DIR) - # Make sure we have a valid, populated OrderedDict - assert isinstance(objs, OrderedDict) - assert len(objs) > 0 - # Check each reloaded capture - for obj in objs.values(): - check_valid_capture(obj) - - -def test_data(): - # Get a reference PIL image - ref_data = generate_small_image() - # Generate a capture with the same image data as our reference - obj = generate_small_capture("capture5.jpg") - # Pull the capture data out - obj_data = Image.open(obj.data) - # Compare the images - diff = ImageChops.difference(obj_data, ref_data) - assert not diff.getbbox() - - -def test_binary(): - # Get a reference PIL image - ref_data = generate_small_image() - # Generate a capture with the same image data as our reference - obj = generate_small_capture("capture6.jpg") - # Pull the capture data out - obj_data = Image.open(io.BytesIO(obj.binary)) - # Compare the images - diff = ImageChops.difference(obj_data, ref_data) - assert not diff.getbbox() - - -def test_thumbnail(): - # Get a reference PIL image - ref_thumb = generate_small_thumbnail() - # Generate a capture with the same image data as our reference - obj = generate_small_capture("capture7.jpg") - # Pull the generated thumbnail out - obj_thumb = Image.open(obj.thumbnail) - # Compare the images - diff = ImageChops.difference(obj_thumb, ref_thumb) - assert not diff.getbbox() - - # Assert thumbnail was saved to capture file EXIF data - exif_dict = piexif.load(obj.file) - thumbnail = exif_dict.pop("thumbnail") - assert thumbnail - # Compare the images - diff = ImageChops.difference(Image.open(io.BytesIO(thumbnail)), ref_thumb) - - -def test_delete(): - # Generate a capture with the same image data as our reference - obj = generate_small_capture("capture_del.jpg") - assert os.path.isfile(obj.file) - obj.delete() - assert not os.path.isfile(obj.file) diff --git a/tests/test_dummy_server.py b/tests/test_dummy_server.py new file mode 100644 index 00000000..db7fd89e --- /dev/null +++ b/tests/test_dummy_server.py @@ -0,0 +1,22 @@ +import tempfile + +from fastapi import Depends, FastAPI +from fastapi.testclient import TestClient + +from labthings_fastapi.client import ThingClient + +from openflexure_microscope_server.server import ThingServer +from openflexure_microscope_server.things.camera.simulation import SimulatedCamera +from openflexure_microscope_server.things.stage.dummy import DummyStage +from openflexure_microscope_server.things.autofocus import AutofocusThing + +temp_folder = tempfile.TemporaryDirectory() +server = ThingServer(temp_folder.name) +server.add_thing(SimulatedCamera(), "/camera/") +server.add_thing(DummyStage(), "/stage/") +server.add_thing(AutofocusThing(), "/autofocus/") + +def test_autofocus(): + with TestClient(server.app) as client: + autofocus = ThingClient.from_url("/autofocus/", client) + _ = autofocus.fast_autofocus() diff --git a/tests/test_extension_scan.py b/tests/test_extension_scan.py deleted file mode 100644 index 924e7ea5..00000000 --- a/tests/test_extension_scan.py +++ /dev/null @@ -1,57 +0,0 @@ -from openflexure_microscope.api.default_extensions import scan - - -def test_construct_grid_raster(): - grid = scan.construct_grid((0, 0), (100, 100), (3, 3), style="raster") - assert grid == [ - [(0, 0), (0, 100), (0, 200)], - [(100, 0), (100, 100), (100, 200)], - [(200, 0), (200, 100), (200, 200)], - ] - - -def test_construct_grid_snake(): - grid = scan.construct_grid((0, 0), (100, 100), (3, 3), style="snake") - assert grid == [ - [(0, 0), (0, 100), (0, 200)], - [(100, 200), (100, 100), (100, 0)], - [(200, 0), (200, 100), (200, 200)], - ] - - -def test_construct_grid_spiral(): - grid = scan.construct_grid((0, 0), (100, 100), (3, 0), style="spiral") - assert grid == [ - [(0, 0)], - [ - (0, 100), - (100, 100), - (100, 0), - (100, -100), - (0, -100), - (-100, -100), - (-100, 0), - (-100, 100), - ], - [ - (-100, 200), - (0, 200), - (100, 200), - (200, 200), - (200, 100), - (200, 0), - (200, -100), - (200, -200), - (100, -200), - (0, -200), - (-100, -200), - (-200, -200), - (-200, -100), - (-200, 0), - (-200, 100), - (-200, 200), - ], - ] - - # Ensure second n_steps value makes no difference - assert grid == scan.construct_grid((0, 0), (100, 100), (3, 3), style="spiral") diff --git a/tests/test_json.py b/tests/test_json.py deleted file mode 100644 index 0bd53f9f..00000000 --- a/tests/test_json.py +++ /dev/null @@ -1,59 +0,0 @@ -import json -import uuid -from fractions import Fraction - -import numpy as np -import pytest - -from openflexure_microscope.json import JSONEncoder - - -@pytest.mark.parametrize( - "test_input,expected", - [ - (uuid.uuid1(), 1), - (uuid.uuid3(uuid.NAMESPACE_DNS, "openflexure.org"), 3), - (uuid.uuid4(), 4), - (uuid.uuid5(uuid.NAMESPACE_DNS, "openflexure.org"), 5), - ], -) -def test_encode_uuid(test_input, expected): - encoded = json.dumps(test_input, cls=JSONEncoder) - decoded = json.loads(encoded) - assert uuid.UUID(decoded).version == expected - - -@pytest.mark.parametrize( - "test_input", - [ - Fraction(1, 2), - Fraction(1, 3), - Fraction(-27, 4), - Fraction(27, -4), - Fraction(10), - Fraction(18, 63), - Fraction(1, 1000000), - ], -) -def test_encode_fraction(test_input): - encoded = json.dumps(test_input, cls=JSONEncoder) - decoded = json.loads(encoded) - assert Fraction(decoded).limit_denominator() == test_input - - -@pytest.mark.parametrize( - "test_input", [np.random.randint(100, dtype=np.int64) for _ in range(10)] -) -def test_encode_np_int(test_input): - encoded = json.dumps(test_input, cls=JSONEncoder) - decoded = json.loads(encoded) - assert np.int64(decoded) == test_input - - -@pytest.mark.parametrize( - "test_input", [np.float64(np.random.random()) for _ in range(10)] -) -def test_encode_np_float(test_input): - encoded = json.dumps(test_input, cls=JSONEncoder) - decoded = json.loads(encoded) - assert np.float64(decoded) == test_input diff --git a/tests/test_utilities.py b/tests/test_utilities.py deleted file mode 100644 index d2cf4f69..00000000 --- a/tests/test_utilities.py +++ /dev/null @@ -1,31 +0,0 @@ -import numpy as np - -from openflexure_microscope import utilities - - -def test_serialise_array_b64(): - shape_in = (3, 2) - arr_in = np.random.randint(100, size=shape_in, dtype=np.int32) - - b64_string, dtype, shape = utilities.serialise_array_b64(arr_in) - assert b64_string - assert dtype == "int32" - assert shape == shape_in - - arr_out = utilities.deserialise_array_b64(b64_string, dtype, shape) - assert np.array_equal(arr_out, arr_in) - - -def test_ndarray_to_json(): - shape_in = (3, 2) - arr_in = np.random.randint(100, size=shape_in, dtype=np.int32) - - json_out = utilities.ndarray_to_json(arr_in) - - arr_out = utilities.json_to_ndarray(json_out) - assert np.array_equal(arr_out, arr_in) - - -def test_axes_to_array(): - dict_in = {"x": 1, "y": 2, "z": 3} - assert utilities.axes_to_array(dict_in) == [1, 2, 3] diff --git a/tests/w3c_td_schema.json b/tests/w3c_td_schema.json deleted file mode 100644 index e15f1bbf..00000000 --- a/tests/w3c_td_schema.json +++ /dev/null @@ -1,1035 +0,0 @@ -{ - "title": "WoT TD Schema - 16 October 2019", - "description": "JSON Schema for validating TD instances against the TD model. TD instances can be with or without terms that have default values", - "$schema ": "http://json-schema.org/draft-07/schema#", - "definitions": { - "anyUri": { - "type": "string", - "format": "iri-reference" - }, - "description": { - "type": "string" - }, - "descriptions": { - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "title": { - "type": "string" - }, - "titles": { - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "security": { - "oneOf": [{ - "type": "array", - "items": { - "type": "string" - } - }, - { - "type": "string" - } - ] - }, - "scopes": { - "oneOf": [{ - "type": "array", - "items": { - "type": "string" - } - }, - { - "type": "string" - } - ] - }, - "subprotocol": { - "type": "string", - "enum": [ - "longpoll", - "websub", - "sse" - ] - }, - "thing-context-w3c-uri": { - "type": "string", - "enum": [ - "https://www.w3.org/2019/wot/td/v1" - ] - }, - "thing-context": { - "oneOf": [{ - "type": "array", - "prefixItems": [{ - "$ref": "#/definitions/thing-context-w3c-uri" - }], - "items": { - "anyOf": [{ - "$ref": "#/definitions/anyUri" - }, - { - "type": "object" - } - ] - } - }, - { - "$ref": "#/definitions/thing-context-w3c-uri" - } - ] - }, - "type_declaration": { - "oneOf": [{ - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] - }, - "dataSchema": { - "type": "object", - "properties": { - "@type": { - "$ref": "#/definitions/type_declaration" - }, - "description": { - "$ref": "#/definitions/description" - }, - "title": { - "$ref": "#/definitions/title" - }, - "descriptions": { - "$ref": "#/definitions/descriptions" - }, - "titles": { - "$ref": "#/definitions/titles" - }, - "writeOnly": { - "type": "boolean" - }, - "readOnly": { - "type": "boolean" - }, - "oneOf": { - "type": "array", - "items": { - "$ref": "#/definitions/dataSchema" - } - }, - "unit": { - "type": "string" - }, - "enum": { - "type": "array", - "minItems": 1, - "uniqueItems": true - }, - "format": { - "type": "string" - }, - "const": {}, - "type": { - "type": "string", - "enum": [ - "boolean", - "integer", - "number", - "string", - "object", - "array", - "null" - ] - }, - "items": { - "oneOf": [{ - "$ref": "#/definitions/dataSchema" - }, - { - "type": "array", - "items": { - "$ref": "#/definitions/dataSchema" - } - } - ] - }, - "maxItems": { - "type": "integer", - "minimum": 0 - }, - "minItems": { - "type": "integer", - "minimum": 0 - }, - "minimum": { - "type": "number" - }, - "maximum": { - "type": "number" - }, - "properties": { - "additionalProperties": { - "$ref": "#/definitions/dataSchema" - } - }, - "required": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "form_element_property": { - "type": "object", - "properties": { - "op": { - "oneOf": [{ - "type": "string", - "enum": [ - "readproperty", - "writeproperty", - "observeproperty", - "unobserveproperty" - ] - }, - { - "type": "array", - "items": { - "type": "string", - "enum": [ - "readproperty", - "writeproperty", - "observeproperty", - "unobserveproperty" - ] - } - } - ] - }, - "href": { - "$ref": "#/definitions/anyUri" - }, - "contentType": { - "type": "string" - }, - "contentCoding": { - "type": "string" - }, - "subprotocol": { - "$ref": "#/definitions/subprotocol" - }, - "security": { - "$ref": "#/definitions/security" - }, - "scopes": { - "$ref": "#/definitions/scopes" - }, - "response": { - "type": "object", - "properties": { - "contentType": { - "type": "string" - } - } - } - }, - "required": [ - "href" - ], - "additionalProperties": true - }, - "form_element_action": { - "type": "object", - "properties": { - "op": { - "oneOf": [{ - "type": "string", - "enum": [ - "invokeaction" - ] - }, - { - "type": "array", - "items": { - "type": "string", - "enum": [ - "invokeaction" - ] - } - } - ] - }, - "href": { - "$ref": "#/definitions/anyUri" - }, - "contentType": { - "type": "string" - }, - "contentCoding": { - "type": "string" - }, - "subprotocol": { - "$ref": "#/definitions/subprotocol" - }, - "security": { - "$ref": "#/definitions/security" - }, - "scopes": { - "$ref": "#/definitions/scopes" - }, - "response": { - "type": "object", - "properties": { - "contentType": { - "type": "string" - } - } - } - }, - "required": [ - "href" - ], - "additionalProperties": true - }, - "form_element_event": { - "type": "object", - "properties": { - "op": { - "oneOf": [{ - "type": "string", - "enum": [ - "subscribeevent", - "unsubscribeevent" - ] - }, - { - "type": "array", - "items": { - "type": "string", - "enum": [ - "subscribeevent", - "unsubscribeevent" - ] - } - } - ] - }, - "href": { - "$ref": "#/definitions/anyUri" - }, - "contentType": { - "type": "string" - }, - "contentCoding": { - "type": "string" - }, - "subprotocol": { - "$ref": "#/definitions/subprotocol" - }, - "security": { - "$ref": "#/definitions/security" - }, - "scopes": { - "$ref": "#/definitions/scopes" - }, - "response": { - "type": "object", - "properties": { - "contentType": { - "type": "string" - } - } - } - }, - "required": [ - "href" - ], - "additionalProperties": true - }, - "form_element_root": { - "type": "object", - "properties": { - "op": { - "oneOf": [{ - "type": "string", - "enum": [ - "readallproperties", - "writeallproperties", - "readmultipleproperties", - "writemultipleproperties" - ] - }, - { - "type": "array", - "items": { - "type": "string", - "enum": [ - "readallproperties", - "writeallproperties", - "readmultipleproperties", - "writemultipleproperties" - ] - } - } - ] - }, - "href": { - "$ref": "#/definitions/anyUri" - }, - "contentType": { - "type": "string" - }, - "contentCoding": { - "type": "string" - }, - "subprotocol": { - "$ref": "#/definitions/subprotocol" - }, - "security": { - "$ref": "#/definitions/security" - }, - "scopes": { - "$ref": "#/definitions/scopes" - }, - "response": { - "type": "object", - "properties": { - "contentType": { - "type": "string" - } - } - } - }, - "required": [ - "href" - ], - "additionalProperties": true - }, - "property_element": { - "type": "object", - "properties": { - "@type": { - "$ref": "#/definitions/type_declaration" - }, - "description": { - "$ref": "#/definitions/description" - }, - "descriptions": { - "$ref": "#/definitions/descriptions" - }, - "title": { - "$ref": "#/definitions/title" - }, - "titles": { - "$ref": "#/definitions/titles" - }, - "forms": { - "type": "array", - "minItems": 1, - "items": { - "$ref": "#/definitions/form_element_property" - } - }, - "uriVariables": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/dataSchema" - } - }, - "observable": { - "type": "boolean" - }, - "writeOnly": { - "type": "boolean" - }, - "readOnly": { - "type": "boolean" - }, - "oneOf": { - "type": "array", - "items": { - "$ref": "#/definitions/dataSchema" - } - }, - "unit": { - "type": "string" - }, - "enum": { - "type": "array", - "minItems": 1, - "uniqueItems": true - }, - "format": { - "type": "string" - }, - "const": {}, - "type": { - "type": "string", - "enum": [ - "boolean", - "integer", - "number", - "string", - "object", - "array", - "null" - ] - }, - "items": { - "oneOf": [{ - "$ref": "#/definitions/dataSchema" - }, - { - "type": "array", - "items": { - "$ref": "#/definitions/dataSchema" - } - } - ] - }, - "maxItems": { - "type": "integer", - "minimum": 0 - }, - "minItems": { - "type": "integer", - "minimum": 0 - }, - "minimum": { - "type": "number" - }, - "maximum": { - "type": "number" - }, - "properties": { - "additionalProperties": { - "$ref": "#/definitions/dataSchema" - } - }, - "required": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": [ - "forms" - ], - "additionalProperties": true - }, - "action_element": { - "type": "object", - "properties": { - "@type": { - "$ref": "#/definitions/type_declaration" - }, - "description": { - "$ref": "#/definitions/description" - }, - "descriptions": { - "$ref": "#/definitions/descriptions" - }, - "title": { - "$ref": "#/definitions/title" - }, - "titles": { - "$ref": "#/definitions/titles" - }, - "forms": { - "type": "array", - "minItems": 1, - "items": { - "$ref": "#/definitions/form_element_action" - } - }, - "uriVariables": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/dataSchema" - } - }, - "input": { - "$ref": "#/definitions/dataSchema" - }, - "output": { - "$ref": "#/definitions/dataSchema" - }, - "safe": { - "type": "boolean" - }, - "idempotent": { - "type": "boolean" - } - }, - "required": [ - "forms" - ], - "additionalProperties": true - }, - "event_element": { - "type": "object", - "properties": { - "@type": { - "$ref": "#/definitions/type_declaration" - }, - "description": { - "$ref": "#/definitions/description" - }, - "descriptions": { - "$ref": "#/definitions/descriptions" - }, - "title": { - "$ref": "#/definitions/title" - }, - "titles": { - "$ref": "#/definitions/titles" - }, - "forms": { - "type": "array", - "minItems": 1, - "items": { - "$ref": "#/definitions/form_element_event" - } - }, - "uriVariables": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/dataSchema" - } - }, - "subscription": { - "$ref": "#/definitions/dataSchema" - }, - "data": { - "$ref": "#/definitions/dataSchema" - }, - "cancellation": { - "$ref": "#/definitions/dataSchema" - } - }, - "required": [ - "forms" - ], - "additionalProperties": true - }, - "link_element": { - "type": "object", - "properties": { - "href": { - "$ref": "#/definitions/anyUri" - }, - "type": { - "type": "string" - }, - "rel": { - "type": "string" - }, - "anchor": { - "$ref": "#/definitions/anyUri" - } - }, - "required": [ - "href" - ], - "additionalProperties": true - }, - "securityScheme": { - "oneOf": [{ - "type": "object", - "properties": { - "@type": { - "$ref": "#/definitions/type_declaration" - }, - "description": { - "$ref": "#/definitions/description" - }, - "descriptions": { - "$ref": "#/definitions/descriptions" - }, - "proxy": { - "$ref": "#/definitions/anyUri" - }, - "scheme": { - "type": "string", - "enum": [ - "nosec" - ] - } - }, - "required": [ - "scheme" - ] - }, - { - "type": "object", - "properties": { - "@type": { - "$ref": "#/definitions/type_declaration" - }, - "description": { - "$ref": "#/definitions/description" - }, - "descriptions": { - "$ref": "#/definitions/descriptions" - }, - "proxy": { - "$ref": "#/definitions/anyUri" - }, - "scheme": { - "type": "string", - "enum": [ - "basic" - ] - }, - "in": { - "type": "string", - "enum": [ - "header", - "query", - "body", - "cookie" - ] - }, - "name": { - "type": "string" - } - }, - "required": [ - "scheme" - ] - }, - { - "type": "object", - "properties": { - "@type": { - "$ref": "#/definitions/type_declaration" - }, - "description": { - "$ref": "#/definitions/description" - }, - "descriptions": { - "$ref": "#/definitions/descriptions" - }, - "proxy": { - "$ref": "#/definitions/anyUri" - }, - "scheme": { - "type": "string", - "enum": [ - "digest" - ] - }, - "qop": { - "type": "string", - "enum": [ - "auth", - "auth-int" - ] - }, - "in": { - "type": "string", - "enum": [ - "header", - "query", - "body", - "cookie" - ] - }, - "name": { - "type": "string" - } - }, - "required": [ - "scheme" - ] - }, - { - "type": "object", - "properties": { - "@type": { - "$ref": "#/definitions/type_declaration" - }, - "description": { - "$ref": "#/definitions/description" - }, - "descriptions": { - "$ref": "#/definitions/descriptions" - }, - "proxy": { - "$ref": "#/definitions/anyUri" - }, - "scheme": { - "type": "string", - "enum": [ - "apikey" - ] - }, - "in": { - "type": "string", - "enum": [ - "header", - "query", - "body", - "cookie" - ] - }, - "name": { - "type": "string" - } - }, - "required": [ - "scheme" - ] - }, - { - "type": "object", - "properties": { - "@type": { - "$ref": "#/definitions/type_declaration" - }, - "description": { - "$ref": "#/definitions/description" - }, - "descriptions": { - "$ref": "#/definitions/descriptions" - }, - "proxy": { - "$ref": "#/definitions/anyUri" - }, - "scheme": { - "type": "string", - "enum": [ - "bearer" - ] - }, - "authorization": { - "$ref": "#/definitions/anyUri" - }, - "alg": { - "type": "string" - }, - "format": { - "type": "string" - }, - "in": { - "type": "string", - "enum": [ - "header", - "query", - "body", - "cookie" - ] - }, - "name": { - "type": "string" - } - }, - "required": [ - "scheme" - ] - }, - { - "type": "object", - "properties": { - "@type": { - "$ref": "#/definitions/type_declaration" - }, - "description": { - "$ref": "#/definitions/description" - }, - "descriptions": { - "$ref": "#/definitions/descriptions" - }, - "proxy": { - "$ref": "#/definitions/anyUri" - }, - "scheme": { - "type": "string", - "enum": [ - "psk" - ] - }, - "identity": { - "type": "string" - } - }, - "required": [ - "scheme" - ] - }, - { - "type": "object", - "properties": { - "@type": { - "$ref": "#/definitions/type_declaration" - }, - "description": { - "$ref": "#/definitions/description" - }, - "descriptions": { - "$ref": "#/definitions/descriptions" - }, - "proxy": { - "$ref": "#/definitions/anyUri" - }, - "scheme": { - "type": "string", - "enum": [ - "oauth2" - ] - }, - "authorization": { - "$ref": "#/definitions/anyUri" - }, - "token": { - "$ref": "#/definitions/anyUri" - }, - "refresh": { - "$ref": "#/definitions/anyUri" - }, - "scopes": { - "oneOf": [{ - "type": "array", - "items": { - "type": "string" - } - }, - { - "type": "string" - } - ] - }, - "flow": { - "type": "string", - "enum": [ - "code" - ] - } - }, - "required": [ - "scheme" - ] - } - ] - } - }, - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uri" - }, - "title": { - "$ref": "#/definitions/title" - }, - "titles": { - "$ref": "#/definitions/titles" - }, - "properties": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/property_element" - } - }, - "actions": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/action_element" - } - }, - "events": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/event_element" - } - }, - "description": { - "$ref": "#/definitions/description" - }, - "descriptions": { - "$ref": "#/definitions/descriptions" - }, - "version": { - "type": "object", - "properties": { - "instance": { - "type": "string" - } - }, - "required": [ - "instance" - ] - }, - "links": { - "type": "array", - "items": { - "$ref": "#/definitions/link_element" - } - }, - "forms": { - "type": "array", - "minItems": 1, - "items": { - "$ref": "#/definitions/form_element_root" - } - }, - "base": { - "$ref": "#/definitions/anyUri" - }, - "securityDefinitions": { - "type": "object", - "minProperties": 1, - "additionalProperties": { - "$ref": "#/definitions/securityScheme" - } - }, - "support": { - "$ref": "#/definitions/anyUri" - }, - "created": { - "type": "string", - "format": "date-time" - }, - "modified": { - "type": "string", - "format": "date-time" - }, - "security": { - "oneOf": [{ - "type": "string" - }, - { - "type": "array", - "minItems": 1, - "items": { - "type": "string" - } - } - ] - }, - "@type": { - "$ref": "#/definitions/type_declaration" - }, - "@context": { - "$ref": "#/definitions/thing-context" - } - }, - "required": [ - "title", - "security", - "securityDefinitions", - "@context" - ], - "additionalProperties": true -} \ No newline at end of file