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

PBL-40767 Add JSON validation for project info files #45

Open
wants to merge 4 commits 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
2 changes: 1 addition & 1 deletion pebble_tool/commands/sdk/project/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def __call__(self, args):
event_map = {
InvalidProjectException: "sdk_run_without_project",
InvalidJSONException: "sdk_json_error",
OutdatedProjectException: "sdk_json_error",
OutdatedProjectException: "sdk_outdated_project",
}
if type(e) in event_map:
post_event(event_map[type(e)])
Expand Down
114 changes: 64 additions & 50 deletions pebble_tool/sdk/project.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from __future__ import print_function

__author__ = 'katharine'

import json
from jsonschema import Draft4Validator, RefResolver, ValidationError
import os
import os.path
import uuid
Expand All @@ -9,7 +12,7 @@

from pebble_tool.exceptions import (InvalidProjectException, InvalidJSONException, OutdatedProjectException,
PebbleProjectException)
from pebble_tool.sdk import sdk_version
from pebble_tool.sdk import sdk_version, sdk_path
from . import pebble_platforms


Expand Down Expand Up @@ -59,35 +62,17 @@ def _parse_project(self):

@staticmethod
def check_project_directory(project_dir):
try:
NpmProject.check_project_directory(project_dir)
except PebbleProjectException:
AppinfoProject.check_project_directory(project_dir)
_validate_project_json(project_dir)
if not os.path.isdir(os.path.join(project_dir, 'src')):
raise InvalidProjectException("This is not a project directory.")
if not os.path.exists(os.path.join(project_dir, 'wscript')):
raise OutdatedProjectException("This project is missing a wscript file and cannot be handled by the SDK.")


class AppinfoProject(PebbleProject):
def __new__(cls, *args, **kwargs):
return object.__new__(cls, *args, **kwargs)

@staticmethod
def check_project_directory(project_dir):
"""Check to see if the current directory matches what is created by PblProjectCreator.run.

Raises an InvalidProjectException or an OutdatedProjectException if everything isn't quite right.
"""

if not os.path.isdir(os.path.join(project_dir, 'src')):
raise InvalidProjectException("This is not a project directory.")

try:
with open(os.path.join(project_dir, "appinfo.json"), "r") as f:
try:
json.load(f)
except ValueError as e:
raise InvalidJSONException("Could not parse appinfo.json: %s" % e)
except IOError:
raise InvalidProjectException("Couldn't open project info.")

@staticmethod
def should_process(project_dir):
return os.path.exists(os.path.join(project_dir, 'appinfo.json'))
Expand Down Expand Up @@ -120,36 +105,14 @@ class NpmProject(PebbleProject):
def __new__(cls, *args, **kwargs):
return object.__new__(cls, *args, **kwargs)

@staticmethod
def check_project_directory(project_dir):
"""Check to see if the current directory matches what is created by PblProjectCreator.run.

Raises an InvalidProjectException or an OutdatedProjectException if everything isn't quite right.
"""

try:
with open(os.path.join(project_dir, "package.json"), "r") as f:
try:
app_info = json.load(f)
except ValueError as e:
raise InvalidJSONException("Could not parse package.json: %s" % ex)
if 'pebble' not in app_info:
raise InvalidProjectException("package.json doesn't have a 'pebble' key.")
except IOError:
if not os.path.isdir(os.path.join(project_dir, 'src')):
raise InvalidProjectException("This is not a project directory.")
raise InvalidProjectException("Couldn't open project info.")

@staticmethod
def should_process(project_dir):
_validate_project_json(project_dir)
package_json_path = os.path.join(project_dir, 'package.json')
if os.path.exists(package_json_path):
with open(package_json_path) as f:
try:
if 'pebble' in json.load(f):
return True
except ValueError:
return False
with open(os.path.join(project_dir, 'package.json')) as f:
if 'pebble' in json.load(f):
return True
return False

def _parse_project(self):
Expand Down Expand Up @@ -187,6 +150,57 @@ def _parse_project(self):
self.long_name = self.short_name


def _validate_project_json(project_dir):
package_json_path = os.path.join(project_dir, 'package.json')
appinfo_json_path = os.path.join(project_dir, 'appinfo.json')

try:
with open(package_json_path, 'r') as f:
try:
info = json.load(f)
except ValueError as e:
raise InvalidJSONException("package.json file does not contain valid JSON:\n{}".format(e))
else:
if 'pebble' not in info:
raise IOError
except IOError:
try:
with open(appinfo_json_path, 'r') as f:
try:
info = json.load(f)
except ValueError as e:
raise InvalidJSONException("appinfo.json file does not contain valid JSON:\n{}".format(e))
except IOError:
raise InvalidProjectException("Unable to find package.json file.")
else:
_validate_with_schema(info, "appinfo.json")
else:
_validate_with_schema(info, "package.json")


def _validate_with_schema(json_info, filetype):
try:
_get_json_schema_validator(filetype).validate(json_info)
except ValidationError as e:
error_message = "Unable to validate {} due to one of the following reasons:\n".format(filetype)
for error in [e] + e.context:
if error.absolute_path:
absolute_path = '.'.join([str(path) for path in list(error.absolute_path)])
error_message += "- {}: {}\n".format(absolute_path, error.message)
else:
error_message += "- {}\n".format(error.message)
raise InvalidJSONException(error_message)


def _get_json_schema_validator(json_filetype):
schema_path = os.path.join(sdk_path(), 'pebble', 'common', 'tools', 'schemas', json_filetype)
if os.path.exists(schema_path):
resolver = RefResolver('file://' + os.path.dirname(schema_path) + '/', os.path.basename(schema_path))
with open(schema_path, "r") as f:
return Draft4Validator(json.load(f), resolver=resolver)
return Draft4Validator({})


def check_current_directory():
return PebbleProject.check_project_directory(os.getcwd())

Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ websocket-client==0.32.0
wheel==0.24.0
colorama==0.3.3
packaging==16.7
jsonschema==2.5.1