Skip to content

Commit

Permalink
feat(combine-service): added options for validation of COMBINE archives
Browse files Browse the repository at this point in the history
  • Loading branch information
jonrkarr committed Oct 6, 2021
1 parent 18990f0 commit 42febbe
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 6 deletions.
2 changes: 1 addition & 1 deletion apps/combine-service/src/handlers/combine/get_manifest.py
Expand Up @@ -69,7 +69,7 @@ def handler(body, file=None):

manifest_filename = os.path.join(temp_dirname, 'manifest.xml')
reader = CombineArchiveReader()
contents = reader.read_manifest(manifest_filename)
contents = reader.read_manifest(manifest_filename, archive_filename)
if reader.errors:
raise BadRequestException(
title='COMBINE/OMEX archive does not contain a valid manifest.\n {}'.format(
Expand Down
30 changes: 25 additions & 5 deletions apps/combine-service/src/handlers/combine/validate.py
Expand Up @@ -3,27 +3,46 @@
from biosimulators_utils.combine.data_model import CombineArchiveContentFormat
from biosimulators_utils.combine.io import CombineArchiveReader
from biosimulators_utils.combine.validation import validate
from biosimulators_utils.config import Config
from biosimulators_utils.omex_meta.data_model import OmexMetaSchema
import os
import requests
import requests.exceptions


def handler(body, file=None):
def handler(body, file=None,
validateOmexManifest=True,
validateSedml=True,
validateSedmlModels=True,
validateOmexMetadata=True,
validateImages=True):
''' Validate a COMBINE/OMEX archive
Args:
body (:obj:`dict`): dictionary with keys
* ``url`` whose value
has schema ``#/components/schemas/Url`` with the
URL for a COMBINE/OMEX archive
* ``url`` whose value has schema ``#/components/schemas/Url`` with the URL for a COMBINE/OMEX archive
validateOmexManifest (:obj:`bool`, optional): Whether to validate the OMEX manifest file in the archive
validateSedml (:obj:`bool`, optional): Whether to validate the SED-ML files in the archive
validateSedmlModels (:obj:`bool`, optional): Whether to validate the sources of the models in the SED-ML files in the archive
validateOmexMetadata (:obj:`bool`, optional): Whether to validate the OMEX metdata files in the archive according to
`BioSimulators' conventions <https://biosimulators.org/conventions/metadata>`_
validateImages (:obj:`bool`, optional): Whether to validate the images (BMP, GIF, JPEG, PNG, TIFF WEBP) files in the archive
file (:obj:`werkzeug.datastructures.FileStorage`): COMBINE/OMEX archive file
Returns:
``#/components/schemas/ValidationReport``: information about the validity or
lack thereof of a COMBINE/OMEX archive
'''
config = Config(
VALIDATE_OMEX_MANIFESTS=validateOmexManifest,
VALIDATE_SEDML=validateSedml,
VALIDATE_SEDML_MODELS=validateSedmlModels,
VALIDATE_OMEX_METADATA=validateOmexMetadata,
VALIDATE_IMAGES=validateImages,
)

archive_file = file
archive_url = body.get('url', None)
if archive_url and archive_file:
Expand Down Expand Up @@ -62,7 +81,7 @@ def handler(body, file=None):
errors = []
warnings = []
try:
archive = reader.run(archive_filename, archive_dirname)
archive = reader.run(archive_filename, archive_dirname, config=config)
except Exception as exception:
errors = [['The file could not be parsed as a COMBINE/OMEX archive.', [[str(exception)]]]]

Expand All @@ -71,6 +90,7 @@ def handler(body, file=None):
archive, archive_dirname,
formats_to_validate=list(CombineArchiveContentFormat.__members__.values()),
metadata_schema=OmexMetaSchema.biosimulations,
config=config,
)

return make_validation_report(errors, warnings, filenames=[archive_filename])
58 changes: 58 additions & 0 deletions apps/combine-service/src/spec/spec.yml
Expand Up @@ -155,6 +155,64 @@ paths:
required: true
tags:
- COMBINE
parameters:
- examples:
"True":
value: "true"
name: validateOmexManifest
description: |-
Whether to validate the OMEX manifest file in the archive.
Default: `true`.
schema:
type: boolean
in: query
required: false
- examples:
"True":
value: "true"
name: validateSedml
description: |-
Whether to validate the SED-ML files in the archive.
Default: `true`.
schema:
type: boolean
in: query
- examples:
"True":
value: "true"
name: validateSedmlModels
description: |-
Whether to validate the source (e.g., CellML, SBML file) of each model of each SED-ML file in the archive.
Default: `true`.
schema:
type: boolean
in: query
- examples:
"True":
value: "true"
name: validateOmexMetadata
description: |-
Whether to validate the OMEX Metadata files in the archive according to [BioSimulators' conventions](https://biosimulators.org/conventions/metadata).
Default: `true`.
schema:
type: boolean
in: query
required: false
- examples:
"True":
value: "true"
name: validateImages
description: |-
Whether to validate the image (BMP, GIF, PNG, JPEG, TIFF, WEBP) files in the archive.
Default: `true`.
schema:
type: boolean
in: query
responses:
"200":
content:
Expand Down
63 changes: 63 additions & 0 deletions apps/combine-service/tests/test_app.py
Expand Up @@ -1410,6 +1410,69 @@ def test_validate_is_not_an_archive(self):
self.assertEqual(validation_report['status'], "invalid")
self.assertIn('is not a valid COMBINE/OMEX archive', json.dumps(validation_report['errors']))

def test_validate_omex_manifest_option(self):
# no manifest
archive_filename = os.path.join(
self.FIXTURES_DIR, 'no-manifest.omex')

fid = open(archive_filename, 'rb')
data = MultiDict([
('file', fid),
])
endpoint = '/combine/validate'
with app.app.app.test_client() as client:
response = client.post(endpoint, data=data, content_type="multipart/form-data")
fid.close()
self.assertEqual(response.status_code, 200, response.json)
validation_report = response.json

self.assertEqual(validation_report['status'], "invalid")
self.assertIn('is not a valid COMBINE/OMEX archive', json.dumps(validation_report['errors']))

fid = open(archive_filename, 'rb')
data = MultiDict([
('file', fid),
])
endpoint = '/combine/validate?validateOmexManifest=false'
with app.app.app.test_client() as client:
response = client.post(endpoint, data=data, content_type="multipart/form-data")
fid.close()
self.assertEqual(response.status_code, 200, response.json)
validation_report = response.json

self.assertEqual(validation_report['status'], "warnings")

# bad manifest
archive_filename = os.path.join(
self.FIXTURES_DIR, 'bad-manifest.omex')

fid = open(archive_filename, 'rb')
data = MultiDict([
('file', fid),
])
endpoint = '/combine/validate'
with app.app.app.test_client() as client:
response = client.post(endpoint, data=data, content_type="multipart/form-data")
fid.close()
self.assertEqual(response.status_code, 200, response.json)
validation_report = response.json

self.assertEqual(validation_report['status'], "invalid")
self.assertIn('is not a valid COMBINE/OMEX archive', json.dumps(validation_report['errors']))

fid = open(archive_filename, 'rb')
data = MultiDict([
('file', fid),
])
endpoint = '/combine/validate?validateOmexManifest=false'
with app.app.app.test_client() as client:
response = client.post(endpoint, data=data, content_type="multipart/form-data")
fid.close()
self.assertEqual(response.status_code, 200, response.json)
validation_report = response.json

self.assertEqual(validation_report['status'], "warnings")

def test_get_similar_algorithms(self):
endpoint = '/kisao/get-similar-algorithms?algorithms=KISAO_0000088'
with app.app.app.test_client() as client:
Expand Down

0 comments on commit 42febbe

Please sign in to comment.