Skip to content
This repository has been archived by the owner on Jul 16, 2020. It is now read-only.

Commit

Permalink
Merge pull request #31 from flywheel-io/optional-config
Browse files Browse the repository at this point in the history
Add optional input values and test suite
  • Loading branch information
kofalt authored Apr 26, 2018
2 parents 6acf4d9 + a5d8ce4 commit 4cc366e
Show file tree
Hide file tree
Showing 19 changed files with 306 additions and 37 deletions.
36 changes: 18 additions & 18 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
version: 2

jobs:
build:
working_directory: "~/gears"
build:
working_directory: "~/gears"

docker:
- image: python:2-alpine3.7
docker:
- image: python:2-alpine3.7

steps:
- checkout
steps:
- checkout

- restore_cache:
key: gears-1-{{ checksum "setup.py"}}
- restore_cache:
key: gears-1-{{ checksum "setup.py"}}

- run:
name: Install Dependencies
command: pip install -e .
- run:
name: Install Dependencies
command: pip install -e .

- save_cache:
key: gears-1-{{ checksum "setup.py"}}
paths:
- "~/.cache/pip"
- save_cache:
key: gears-1-{{ checksum "setup.py"}}
paths:
- "~/.cache/pip"

- run:
name: Validate
command: python validate.py
- run:
name: Validate
command: examples/test.py
29 changes: 29 additions & 0 deletions examples/invalid-default-optional/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "example-gear",
"label": "Example Gear",
"description": "A gear that performs a task.",
"version": "1.0",
"author": "Flywheel",
"license": "Apache-2.0",
"url": "http://example.com",
"source": "http://example.com/code",
"config": {
"speed": {
"type": "integer",
"minimum": 0,
"maximum": 3
},
"special": {
"type": "integer",
"optional": true,
"default": 1
}
},
"inputs": {
"dicom": {
"base": "file",
"type": { "enum": [ "dicom" ] }

}
}
}
3 changes: 3 additions & 0 deletions examples/invalid-default-optional/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Default-optional example

This gear specifies a `default` and `optional` key on the same input, which is not allowed.
3 changes: 3 additions & 0 deletions examples/invalid-empty/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{

}
3 changes: 3 additions & 0 deletions examples/invalid-empty/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Empty example

An empty object.
5 changes: 5 additions & 0 deletions examples/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Test cases for the gear spec

This suite is intended for testing an implementation of the gear spec. Each example folder must be prefixed with `valid` or `invalid`, and contain a readme, a manifest, and possibly an invocation.

To run the suite against the reference implementation, run `test.py` in this folder.
77 changes: 77 additions & 0 deletions examples/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#!/usr/bin/env python

from os import sys, path, listdir
import sys
import jsonschema

# This file is not a package; python gets cranky
sys.path.append(path.dirname(path.dirname(path.abspath(__file__))))

import gears

def header(name):
sys.stdout.write('Testing ' + name + '...')

spacing = 80 - 8 - 3 -3 - len(name)
if spacing > 0:
sys.stdout.write(' ' * spacing)

sys.stdout.flush()

def test(name, folder):
header(name)

manifest = path.join(folder, 'manifest.json')
invocation = path.join(folder, 'invocation.json')

if path.isfile(manifest):
x = gears.load_json_from_file(manifest)
gears.validate_manifest(x)
else:
raise Exception("Test has no manifest: " + name)

if path.isfile(invocation):
y = gears.load_json_from_file(invocation)
gears.validate_invocation(x, y)

def test_wrap(name, folder, shouldpass):
e = None

try:
test(name, folder)
if shouldpass:
print '[X]'
return

except jsonschema.exceptions.ValidationError as ex:

if shouldpass:
print '[ ]'
raise ex
else:
print '[X]'
return

print '[ ]'
raise Exception('Test should fail but did not: ' + name)

def test_folders():
base = path.dirname(path.abspath(__file__))

for f in listdir(base):
file = path.join(base, f)
name = f.replace('invalid-', '').replace('valid-', '')

if path.isdir(file):
if f.startswith('valid-'):
test_wrap(name, file, True)
elif f.startswith('invalid-'):
test_wrap(name, file, False)
else:
raise Exception('Folder is improperly named: ' + f)

def main():
test_folders()

if __name__ == "__main__":
main()
83 changes: 83 additions & 0 deletions examples/valid-advanced/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
{
"name": "flywheel-example-gear",
"label": "Flywheel Example Gear",
"description": "Sample gear to demonstrate a simple use case of outputting the name of each input file.",
"author": "Flywheel <[email protected]>",
"maintainer": "Ryan Sanford <[email protected]>",
"url": "https://flywheel.io/",
"source": "https://github.com/flywheel-apps/example-gear",
"license": "MIT",
"flywheel": "0",
"version": "0.0.4",

"config": {
"boolean": {
"default": true,
"type": "boolean",
"description": "Any boolean."
},
"integer": {
"default": 7,
"type": "integer",
"description": "Any integer."
},
"number": {
"default": 3.5,
"type": "number",
"description": "Any number."
},
"string": {
"default": "Example",
"type": "string",
"description": "Any string."
},
"phone": {
"default": "555-5555",
"pattern": "^[0-9]{3}-[0-9]{4}$",
"type": "string",
"description": "Any local phone number, no country or area code."
},
"string2": {
"default": "Example 2",
"maxLength": 15,
"minLength": 2,
"type": "string",
"description": "Any string from 2 to 15 characters long."
},
"multiple": {
"default": 20,
"exclusiveMaximum": true,
"maximum": 100,
"multipleOf": 10,
"type": "number",
"description": "Any two-digit multiple of ten."
}
},

"inputs": {
"dicom": {
"base": "file",
"type": {
"enum": [
"dicom"
]
},
"description": "Any dicom file."
},
"file": {
"base": "file",
"description": "Any file."
},
"text": {
"base": "file",
"optional": true,
"name": {
"pattern": "^.*.txt$"
},
"size": {
"maximum": 100000
},
"description": "Any test file that is 10 KB in size or less."
}
}
}
3 changes: 3 additions & 0 deletions examples/valid-advanced/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Advanced example

This is a copy of the flywheel example gear, which demonstrates several features of the gear spec.
28 changes: 28 additions & 0 deletions examples/valid-default/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "example-gear",
"label": "Example Gear",
"description": "A gear that performs a task.",
"version": "1.0",
"author": "Flywheel",
"license": "Apache-2.0",
"url": "http://example.com",
"source": "http://example.com/code",
"config": {
"speed": {
"type": "integer",
"minimum": 0,
"maximum": 3
},
"special": {
"type": "integer",
"default": 1
}
},
"inputs": {
"dicom": {
"base": "file",
"type": { "enum": [ "dicom" ] }

}
}
}
3 changes: 3 additions & 0 deletions examples/valid-default/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Default example

This gear has a default config value.
28 changes: 28 additions & 0 deletions examples/valid-optional/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "example-gear",
"label": "Example Gear",
"description": "A gear that performs a task.",
"version": "1.0",
"author": "Flywheel",
"license": "Apache-2.0",
"url": "http://example.com",
"source": "http://example.com/code",
"config": {
"speed": {
"type": "integer",
"minimum": 0,
"maximum": 3
},
"special": {
"type": "integer",
"optional": true
}
},
"inputs": {
"dicom": {
"base": "file",
"type": { "enum": [ "dicom" ] }

}
}
}
3 changes: 3 additions & 0 deletions examples/valid-optional/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Optional example

This gear has an optional input.
File renamed without changes.
File renamed without changes.
3 changes: 3 additions & 0 deletions examples/valid-simple/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Simple example

This is almost the simplest manifest possible, with one config value and one input.
15 changes: 14 additions & 1 deletion spec/manifest.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@
],
"description": "A schema directive."
},
"config-directive": {
"allOf": [
{"$ref": "http://json-schema.org/draft-04/schema"},
{ "not": {"required": ["default", "optional"]} }
],
"description": "A config schema directive."
},
"uriOrEmpty": {
"type": "string",
"maxLength": 1000,
Expand Down Expand Up @@ -42,7 +49,7 @@
"config": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/directive"
"$ref": "#/definitions/config-directive"
},
"description": "Schema snippets describing the options this gear consumes. Not currently processed."
},
Expand Down Expand Up @@ -102,6 +109,12 @@
"maxLength": 100,
"description": "The human-friendly name of this gear."
},
"capabilities": {
"type": "array",
"items": {
"type": "string"
}
},
"license": {
"type": "string",
"enum": [
Expand Down
4 changes: 3 additions & 1 deletion spec/readme.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Flywheel Gear Spec (v0.1.5)
# Flywheel Gear Spec (v0.1.6)

This document describes the structure of a Flywheel Gear.

Expand Down Expand Up @@ -150,6 +150,8 @@ Like the inputs, you can add JSON schema constraints as desired. Please specify

The example has named one config option, called "speed", which must be an integer between zero and three, and another called "coordinates", which must be a set of three floats.

In some cases, a configuration option may not have a safe default, and it only makes sense to sometimes omit it entirely. If that is the case, specify `"optional": true` on that config key.

### Contextual values

Context inputs are values that are generally provided by the environment, rather than the human or process running the gear. These are generally values that are incidentally required rather than directly a part of the algorithm - for example, a license key.
Expand Down
17 changes: 0 additions & 17 deletions validate.py

This file was deleted.

0 comments on commit 4cc366e

Please sign in to comment.