Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remote file format validator extension #257

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions remote_file_format_validator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
## Remote file format validator

Fetch the target value url and determine it's file format.

It has two functions:

1. Detect file type with pre-defined types.
2. Determine file type strings contatins specific keyword.

### Installation

This extension needs `python-magic` to work

- pypi: [http://pypi.python.org/pypi/python-magic](http://pypi.python.org/pypi/python-magic)
- github: [https://github.com/ahupp/python-magic](https://github.com/ahupp/python-magic)

Use this Command to install

```
$pip install python-magic
```

On Mac OSX need `libmagic`

```
brew install libmagic
```

### Usage

Use `remote_file_format` with `test` or `contains` keyword to validate the JSON format.

**Example:** Given this JSON:

```
[
{
"name": "track1",
"audio_url": "http://path/to/mp3.mp3"
}
]
```

We can do a test case for like this

```
- test:
- name: "audio-list"
- url: "/my-audio-list"
- method: "GET"
- validators:
- compare: {jsonpath_mini: "0.name", comparator: "type", expected: "string"}
- compare: {jsonpath_mini: "0.audio_url", comparator: "type", expected: "string"}
- remote_file_format: {jsonpath_mini: '0.audio_url', test: 'is_mp3'}
- remote_file_format: {jsonpath_mini: '0.audio_url', contains: 'Audio'}
```

Finaily, don't forget to add `--import_extensions` arguments when excute the test, enjoy!

```
$ resttest.py https://my.domain/ test.yaml --import_extensions 'remote_file_format_validator'
```
153 changes: 153 additions & 0 deletions remote_file_format_validator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import pyresttest.validators as validators
from pyresttest.binding import Context
import sys

# Python 3 compatibility
if sys.version_info[0] > 2:
from past.builtins import basestring

if sys.version_info[0] > 2:
from io import BytesIO
else:
from StringIO import StringIO
from pyresttest.six import text_type
from pyresttest.six import binary_type
import pycurl
import certifi
import magic

class RemoteFileFormatValidator(validators.AbstractValidator):
""" Does extract and test from request body """
name = 'RemoteFileFormatValidator'
extractor = None
test_fn = None
test_name = None
config = None
contains_str = None

def get_readable_config(self, context=None):
""" Get a human-readable config string """
return "Extractor: " + self.extractor.get_readable_config(context=context)

@staticmethod
def parse(config):
output = RemoteFileFormatValidator()
config = validators.parsing.lowercase_keys(validators.parsing.flatten_dictionaries(config))
output.config = config
extractor = validators._get_extractor(config)
output.extractor = extractor

if 'test' not in config: # contains if unspecified
test_name = 'contains'
else:
test_name = config['test']

output.test_name = test_name
test_fn = VALIDATOR_TESTS[test_name]
output.test_fn = test_fn

if test_name == 'contains':
try:
output.contains_str = config['contains']
except KeyError:
raise ValueError(
"No string value found when using contains test.")

return output

def validate(self, body=None, headers=None, context=None):
try:
extracted = self.extractor.extract(
body=body, headers=headers, context=context)
except Exception as e:
trace = validators.traceback.format_exc()
return validators.Failure(message="Exception thrown while running extraction from body", details=trace, validator=self, failure_type=validators.FAILURE_EXTRACTOR_EXCEPTION)

if self.test_name == 'contains':
tested = self.test_fn(extracted, self.contains_str)
else:
tested = self.test_fn(extracted)
if tested:
return True
else:
failure = validators.Failure(details=self.get_readable_config(
context=context), validator=self, failure_type=validators.FAILURE_VALIDATOR_FAILED)
failure.message = "Extract and test validator failed on test: {0}({1})".format(
self.test_name, extracted)
# TODO can we do better with details?
return failure

def getFileFormat(url):
if sys.version_info[0] > 2:
buffer = BytesIO()
else:
buffer = StringIO()
c = pycurl.Curl()
c.setopt(c.URL, url)
c.setopt(pycurl.CAINFO, certifi.where())
c.setopt(c.RANGE, "0-200")
c.setopt(c.WRITEDATA, buffer)
c.perform()
c.close()
return magic.from_buffer(buffer.getvalue()).encode('utf-8')

def test_is_mp4(url):
fileFormat = getFileFormat(url)
return ("MP4" in fileFormat)

def test_is_webm(url):
fileFormat = getFileFormat(url)
return ("WebM" in fileFormat)

def test_is_ogg(url):
fileFormat = getFileFormat(url)
return ("Ogg" in fileFormat)

def test_is_3gp(url):
fileFormat = getFileFormat(url)
return ("3GPP" in fileFormat)

def test_is_wma(url):
fileFormat = getFileFormat(url)
return ("Microsoft ASF" in fileFormat)

def test_is_mp3(url):
fileFormat = getFileFormat(url)
return ("MPEG" in fileFormat) and ("layer III" in fileFormat)

def test_is_flv(url):
fileFormat = getFileFormat(url)
return ("Flash Video" in fileFormat)

def test_is_jpg(url):
fileFormat = getFileFormat(url)
return ("JPEG" in fileFormat)

def test_is_png(url):
fileFormat = getFileFormat(url)
return ("PNG" in fileFormat)

def test_is_gif(url):
fileFormat = getFileFormat(url)
return ("PNG" in fileFormat)

def test_contains(url, input):
fileFormat = getFileFormat(url)
return (input in fileFormat)

# This is where the magic happens, each one of these is a registry of
# validators/extractors/generators to use
VALIDATORS = {'remote_file_format': RemoteFileFormatValidator.parse}
VALIDATOR_TESTS = {
'contains': lambda x, y: test_contains(x, y),
'is_mp3': test_is_mp3,
'is_mp4': test_is_mp4,
'is_wma': test_is_wma,
'is_3gp': test_is_3gp,
'is_webm': test_is_webm,
'is_flv': test_is_flv,
'is_ogg': test_is_ogg,
'is_jpg': test_is_jpg,
'is_png': test_is_png,
'is_gif': test_is_gif
}