diff --git a/.openapi-generator/FILES b/.openapi-generator/FILES new file mode 100644 index 0000000..a0d7bca --- /dev/null +++ b/.openapi-generator/FILES @@ -0,0 +1,21 @@ +openapi_server/__init__.py +openapi_server/controllers/__init__.py +openapi_server/controllers/event_controller.py +openapi_server/controllers/file_controller.py +openapi_server/controllers/qc_result_controller.py +openapi_server/controllers/sample_controller.py +openapi_server/controllers/sample_status_controller.py +openapi_server/controllers/security_controller_.py +openapi_server/encoder.py +openapi_server/models/__init__.py +openapi_server/models/base_model_.py +openapi_server/models/error.py +openapi_server/models/event.py +openapi_server/models/file.py +openapi_server/models/qc_result.py +openapi_server/models/sample.py +openapi_server/models/status.py +openapi_server/openapi/openapi.yaml +openapi_server/typing_utils.py +openapi_server/util.py +setup.py diff --git a/.openapi-generator/VERSION b/.openapi-generator/VERSION index ecedc98..8044406 100644 --- a/.openapi-generator/VERSION +++ b/.openapi-generator/VERSION @@ -1 +1 @@ -4.3.1 \ No newline at end of file +5.2.1 \ No newline at end of file diff --git a/openapi_server/controllers/sample_controller.py b/openapi_server/controllers/sample_controller.py index cf99374..c4cdf00 100644 --- a/openapi_server/controllers/sample_controller.py +++ b/openapi_server/controllers/sample_controller.py @@ -121,4 +121,25 @@ def samples_id_patch(id, new_sample=None): # noqa: E501 except IntegrityError: return Error(409, 'Already existed'), 409 else: - return old_sample.to_model(), 200 \ No newline at end of file + return old_sample.to_model(), 200 + + +def samples_id_delete(id): # noqa: E501 + """samples_id_delete + + Delete a sample. # noqa: E501 + + :param id: + :type id: + + :rtype: Sample + """ + + inst = orm.Sample.query.get(id) + if not inst: + return Error(404, 'Not found'), 404 + + db.session.delete(inst) + db.session.commit() + + return '', 204 \ No newline at end of file diff --git a/openapi_server/models/__init__.py b/openapi_server/models/__init__.py index 8db31ac..b1ac899 100644 --- a/openapi_server/models/__init__.py +++ b/openapi_server/models/__init__.py @@ -5,7 +5,7 @@ # import models into model package from openapi_server.models.error import Error from openapi_server.models.event import Event +from openapi_server.models.file import File from openapi_server.models.qc_result import QcResult from openapi_server.models.sample import Sample from openapi_server.models.status import Status -from openapi_server.models.file import File \ No newline at end of file diff --git a/openapi_server/models/file.py b/openapi_server/models/file.py index e2e322e..b4898c3 100644 --- a/openapi_server/models/file.py +++ b/openapi_server/models/file.py @@ -10,9 +10,9 @@ class File(Model): - """NOTE: This class is COPIED FROM A GENERATED MODEL because OpenAPI Generator (https://openapi-generator.tech) couldn't generate the model (unknown reason). + """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - Do not edit the class manually ANY FURTHER. + Do not edit the class manually. """ def __init__(self, md5sum=None, filename=None, file_type=None): # noqa: E501 @@ -22,7 +22,7 @@ def __init__(self, md5sum=None, filename=None, file_type=None): # noqa: E501 :type md5sum: str :param filename: The filename of this File. # noqa: E501 :type filename: str - :param file_type: The file_type of this Event. # noqa: E501 + :param file_type: The file_type of this File. # noqa: E501 :type file_type: str """ self.openapi_types = { @@ -47,7 +47,7 @@ def from_dict(cls, dikt) -> 'File': :param dikt: A dict. :type: dict - :return: The File of this Event. # noqa: E501 + :return: The File of this File. # noqa: E501 :rtype: File """ return util.deserialize_model(dikt, cls) @@ -70,6 +70,8 @@ def md5sum(self, md5sum): :param md5sum: The md5sum of this File. :type md5sum: str """ + if md5sum is None: + raise ValueError("Invalid value for `md5sum`, must not be `None`") # noqa: E501 self._md5sum = md5sum @@ -91,6 +93,9 @@ def filename(self, filename): :param filename: The filename of this File. :type filename: str """ + if filename is None: + raise ValueError("Invalid value for `filename`, must not be `None`") # noqa: E501 + self._filename = filename @property diff --git a/openapi_server/openapi/openapi.yaml b/openapi_server/openapi/openapi.yaml index 8086411..9062b07 100644 --- a/openapi_server/openapi/openapi.yaml +++ b/openapi_server/openapi/openapi.yaml @@ -119,6 +119,35 @@ paths: - sample x-openapi-router-controller: openapi_server.controllers.sample_controller /samples/{id}: + delete: + description: Delete a sample. + operationId: samples_id_delete + parameters: + - explode: false + in: path + name: id + required: true + schema: + $ref: '#/components/schemas/SampleID' + style: simple + responses: + "204": + description: Deleted + "404": + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + description: Not found + default: + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + description: Unexpected error + tags: + - sample + x-openapi-router-controller: openapi_server.controllers.sample_controller get: description: Return a sample by its ID. operationId: samples_id_get @@ -818,6 +847,7 @@ components: SampleID: format: uuid pattern: ^[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}$ + title: id type: string Sample: example: @@ -826,13 +856,17 @@ components: experiment-id: experiment-id properties: experiment-id: + title: experiment-id type: string isolate-id: + title: isolate-id type: string id: format: uuid pattern: ^[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}$ + title: id type: string + title: Sample type: object File: example: @@ -841,18 +875,22 @@ components: md5sum: md5sum properties: md5sum: + title: md5sum type: string filename: + title: filename type: string file-type: enum: - fastq - vcf + title: file-type type: string required: - file-type - filename - md5sum + title: File type: object Event: example: @@ -865,6 +903,7 @@ components: command: command properties: id: + title: id type: integer name: enum: @@ -874,17 +913,23 @@ components: - prediction - bigsi-building - distance-calculation + title: name type: string software: + title: software type: string software-version: + title: software-version type: string start-time: + title: start-time type: number duration: format: int32 + title: duration type: integer command: + title: command type: string required: - command @@ -893,6 +938,7 @@ components: - software - software-version - start-time + title: Event type: object QcResult: example: @@ -902,19 +948,23 @@ components: properties: coverage: format: float + title: coverage type: number number_of_het_snps: format: int32 + title: number_of_het_snps type: integer decision: enum: - passed - failed + title: decision type: string required: - coverage - decision - number_of_het_snps + title: QcResult type: object Status: example: @@ -932,6 +982,7 @@ components: - started - complete - failed + title: de-contamination type: string qc: enum: @@ -939,6 +990,7 @@ components: - started - complete - failed + title: qc type: string variant-calling: enum: @@ -946,6 +998,7 @@ components: - started - complete - failed + title: variant-calling type: string prediction: enum: @@ -953,6 +1006,7 @@ components: - started - complete - failed + title: prediction type: string bigsi-building: enum: @@ -960,6 +1014,7 @@ components: - started - complete - failed + title: bigsi-building type: string distance-calculation: enum: @@ -967,6 +1022,7 @@ components: - started - complete - failed + title: distance-calculation type: string stage: enum: @@ -974,6 +1030,7 @@ components: - qc-failed - live - deprecated + title: stage type: string required: - bigsi-building @@ -982,15 +1039,19 @@ components: - prediction - qc - variant-calling + title: Status type: object Error: properties: code: format: int32 + title: code type: integer message: + title: message type: string required: - code - message + title: Error type: object diff --git a/openapi_server/test/conftest.py b/openapi_server/test/conftest.py index 1bf5ecd..c8c9b0f 100644 --- a/openapi_server/test/conftest.py +++ b/openapi_server/test/conftest.py @@ -1,6 +1,6 @@ import logging -from hypothesis import settings +from hypothesis import settings, HealthCheck from pytest import fixture from openapi_server.migrate import migrate @@ -202,11 +202,9 @@ def _(sample_id): @fixture -def delete_sample(): - def _(sample_id): - inst = Sample.query.get(sample_id) - db.session.delete(inst) - db.session.commit() +def delete_sample(make_request): + def _(sample_id, *args, **kwargs): + return make_request(f'/api/v1/samples/{sample_id}', 'DELETE', *args, **kwargs) return _ @@ -219,5 +217,5 @@ def _(model): return _ -settings.register_profile('e2e', deadline=None) +settings.register_profile('e2e', deadline=None, suppress_health_check=(HealthCheck.function_scoped_fixture,)) settings.load_profile('e2e') \ No newline at end of file diff --git a/openapi_server/test/test_invalid_sample_id.py b/openapi_server/test/test_invalid_sample_id.py index 911c745..69ac1e7 100644 --- a/openapi_server/test/test_invalid_sample_id.py +++ b/openapi_server/test/test_invalid_sample_id.py @@ -56,7 +56,7 @@ def _(sample_id, *args, **kwargs): return _ -@settings(suppress_health_check=(HealthCheck.filter_too_much,)) +@settings(suppress_health_check=(HealthCheck.filter_too_much, HealthCheck.function_scoped_fixture)) @given(sample_id=safe_strings(min_size=1)) def test_invalid_sample_id(sample_id, call_endpoint): def is_uuid(s): diff --git a/openapi_server/test/test_sample_controller.py b/openapi_server/test/test_sample_controller.py index 2342e0f..06cc071 100644 --- a/openapi_server/test/test_sample_controller.py +++ b/openapi_server/test/test_sample_controller.py @@ -208,4 +208,21 @@ def test_patching_samples(old_sample, new_sample, patch_sample, create_sample, g assert getattr(patched, prop) == getattr(new_sample, prop) retrieved = Sample.from_dict(get_sample(created.id, ensure=True).json) - assert retrieved == patched \ No newline at end of file + assert retrieved == patched + + +@given(sample_id=sample_ids()) +def test_deleting_non_existent_samples(sample_id, delete_sample): + response = delete_sample(sample_id) + assert response.status_code == 404 + + +@given(sample=samples()) +def test_deleting_samples(sample, create_sample, delete_sample): + with managed_db(): + response = create_sample(sample) + created = Sample.from_dict(response.json) + + response = delete_sample(created.id) + + assert response.status_code == 204 \ No newline at end of file diff --git a/openapi_server/util.py b/openapi_server/util.py index e1185a7..2a63f21 100644 --- a/openapi_server/util.py +++ b/openapi_server/util.py @@ -67,6 +67,9 @@ def deserialize_date(string): :return: date. :rtype: date """ + if string is None: + return None + try: from dateutil.parser import parse return parse(string).date() @@ -84,6 +87,9 @@ def deserialize_datetime(string): :return: datetime. :rtype: datetime """ + if string is None: + return None + try: from dateutil.parser import parse return parse(string) diff --git a/requirements.txt b/requirements.txt index 863071c..10b5f41 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,4 +10,8 @@ python_dateutil >= 2.6.0 setuptools >= 21.0.0 Flask-SQLAlchemy == 2.4.3 -psycopg2-binary == 2.8.6 \ No newline at end of file +psycopg2-binary == 2.8.6 + +openapi-spec-validator == 0.2.10 +click < 8.0 +Jinja2 < 3.0 \ No newline at end of file diff --git a/scripts/openapi-generator.jar b/scripts/openapi-generator.jar index 10a6bde..82fbcf6 100644 Binary files a/scripts/openapi-generator.jar and b/scripts/openapi-generator.jar differ diff --git a/swagger.yaml b/swagger.yaml index a3db7af..a7a8428 100644 --- a/swagger.yaml +++ b/swagger.yaml @@ -212,6 +212,31 @@ paths: application/json: schema: $ref: '#/components/schemas/Error' + delete: + tags: + - sample + description: Delete a sample. + parameters: + - name: id + in: path + required: true + schema: + $ref: '#/components/schemas/SampleID' + responses: + 204: + description: Deleted + 404: + description: Not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' /samples/{id}/files: get: tags: