From 14399c504a98a3d81d90d917dd4afa0841605515 Mon Sep 17 00:00:00 2001 From: Philippe Ombredanne Date: Thu, 25 May 2017 23:04:49 +0200 Subject: [PATCH] Add documentation to Package models #617 Signed-off-by: Philippe Ombredanne --- .../doc/AssertedLicense-json-schema.json | 57 + .../sch2js/doc/Dependency-json-schema.json | 41 + .../sch2js/doc/Package-json-schema.json | 1056 +++++++++++++++++ etc/scripts/sch2js/doc/Party-json-schema.json | 60 + .../doc/RelatedPackage-json-schema.json | 52 + .../sch2js/doc/Repository-json-schema.json | 86 ++ etc/scripts/sch2js/sch2js.ABOUT | 5 + etc/scripts/sch2js/sch2js.LICENSE | 21 + etc/scripts/sch2js/sch2js.py | 194 +++ src/packagedcode/models.py | 343 ++++-- 10 files changed, 1844 insertions(+), 71 deletions(-) create mode 100644 etc/scripts/sch2js/doc/AssertedLicense-json-schema.json create mode 100644 etc/scripts/sch2js/doc/Dependency-json-schema.json create mode 100644 etc/scripts/sch2js/doc/Package-json-schema.json create mode 100644 etc/scripts/sch2js/doc/Party-json-schema.json create mode 100644 etc/scripts/sch2js/doc/RelatedPackage-json-schema.json create mode 100644 etc/scripts/sch2js/doc/Repository-json-schema.json create mode 100644 etc/scripts/sch2js/sch2js.ABOUT create mode 100644 etc/scripts/sch2js/sch2js.LICENSE create mode 100644 etc/scripts/sch2js/sch2js.py diff --git a/etc/scripts/sch2js/doc/AssertedLicense-json-schema.json b/etc/scripts/sch2js/doc/AssertedLicense-json-schema.json new file mode 100644 index 00000000000..eb29eaa9e06 --- /dev/null +++ b/etc/scripts/sch2js/doc/AssertedLicense-json-schema.json @@ -0,0 +1,57 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "", + "type": "object", + "title": "asserted license", + "description": "Represents the licensing as asserted in a package metadata.", + "properties": { + "license": { + "oneOf": [ + { + "type": "string", + "title": "license", + "description": "license as asserted. This can be a text, a name or anything." + }, + { + "type": "null" + } + ] + }, + "url": { + "oneOf": [ + { + "type": "string", + "title": "url", + "description": "URL to a web page for this license." + }, + { + "type": "null" + } + ] + }, + "text": { + "oneOf": [ + { + "type": "string", + "title": "license text", + "description": "license text as asserted." + }, + { + "type": "null" + } + ] + }, + "notice": { + "oneOf": [ + { + "type": "string", + "title": "notice", + "description": "a license notice for this package" + }, + { + "type": "null" + } + ] + } + } +} \ No newline at end of file diff --git a/etc/scripts/sch2js/doc/Dependency-json-schema.json b/etc/scripts/sch2js/doc/Dependency-json-schema.json new file mode 100644 index 00000000000..7fc6983fbd5 --- /dev/null +++ b/etc/scripts/sch2js/doc/Dependency-json-schema.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "", + "type": "object", + "title": "dependency", + "description": "A dependency points to a Package via a package name and a version constraint (such as \">= 3.4\"). The version is the effective version that has been picked and resolved.", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "title": "name", + "description": "Name of the package for this dependency." + }, + "version": { + "oneOf": [ + { + "type": "string", + "title": "version", + "description": "Version of this dependent package: The effective or concrete resolved and used version." + }, + { + "type": "null" + } + ] + }, + "version_constraint": { + "oneOf": [ + { + "type": "string", + "title": "version", + "description": "The version constraints (aka. possible versions) for this dependent package: The meaning of this constraings is package type-specific. " + }, + { + "type": "null" + } + ] + } + } +} \ No newline at end of file diff --git a/etc/scripts/sch2js/doc/Package-json-schema.json b/etc/scripts/sch2js/doc/Package-json-schema.json new file mode 100644 index 00000000000..0488777c073 --- /dev/null +++ b/etc/scripts/sch2js/doc/Package-json-schema.json @@ -0,0 +1,1056 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "", + "type": "object", + "title": "package", + "description": "A package object.", + "required": [ + "type", + "name" + ], + "properties": { + "type": { + "type": "string", + "title": "package type", + "description": "Descriptive name of the type of package: RubyGem, Python Wheel, Java Jar, Debian package, etc." + }, + "name": { + "type": "string", + "title": "package name", + "description": "Name of the package." + }, + "version": { + "oneOf": [ + { + "type": "string", + "title": "package version", + "description": "Version of the package. Package types may implement specific handling for versions but this is always serialized as a string." + }, + { + "type": "null" + } + ] + }, + "primary_language": { + "oneOf": [ + { + "type": "string", + "title": "Primary programming language", + "description": "Primary programming language of the package, such as Java, C, C++. Derived from the package type: i.e. RubyGems are primarily ruby, etc." + }, + { + "type": "null" + } + ] + }, + "packaging": { + "oneOf": [ + { + "type": "string", + "title": "Packaging", + "description": "How a package is packaged. One of: archive, directory, file", + "enum": [ + "archive", + "directory", + "file" + ] + }, + { + "type": "null" + } + ] + }, + "summary": { + "oneOf": [ + { + "type": "string", + "title": "Summary", + "description": "Summary for this package i.e. a short description" + }, + { + "type": "null" + } + ] + }, + "description": { + "oneOf": [ + { + "type": "string", + "title": "Description", + "description": "Description for this package i.e. a long description, often several pages of text" + }, + { + "type": "null" + } + ] + }, + "payload_type": { + "oneOf": [ + { + "type": "string", + "title": "Payload type", + "description": "The type of payload for this package. One of: source, binary, doc", + "enum": [ + "source", + "binary", + "doc" + ] + }, + { + "type": "null" + } + ] + }, + "size": { + "oneOf": [ + { + "type": "integer", + "title": "size", + "description": "size of the package download in bytes" + }, + { + "type": "null" + } + ] + }, + "release_date": { + "oneOf": [ + { + "type": "string", + "title": "release date", + "description": "Release date of the package" + }, + { + "type": "null" + } + ] + }, + "authors": { + "oneOf": [ + { + "type": "array", + "title": "authors", + "description": "A list of party objects. Note: this model schema will change soon.", + "items": { + "type": "object", + "title": "party", + "description": "A party is a person, project or organization related to a package.", + "properties": { + "type": { + "oneOf": [ + { + "type": "string", + "title": "party type", + "description": "the type of this party: One of: person, project, organization", + "enum": [ + "person", + "project", + "organization" + ] + }, + { + "type": "null" + } + ] + }, + "name": { + "oneOf": [ + { + "type": "string", + "title": "url", + "description": "URL to a primary web page for this party." + }, + { + "type": "null" + } + ] + }, + "url": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "email": { + "oneOf": [ + { + "type": "string", + "title": "email", + "description": "Email for this party." + }, + { + "type": "null" + } + ] + } + } + } + }, + { + "type": "null" + } + ] + }, + "maintainers": { + "oneOf": [ + { + "type": "array", + "title": "maintainers", + "description": "A list of party objects. Note: this model schema will change soon.", + "items": { + "type": "object", + "title": "party", + "description": "A party is a person, project or organization related to a package.", + "properties": { + "type": { + "oneOf": [ + { + "type": "string", + "title": "party type", + "description": "the type of this party: One of: person, project, organization", + "enum": [ + "person", + "project", + "organization" + ] + }, + { + "type": "null" + } + ] + }, + "name": { + "oneOf": [ + { + "type": "string", + "title": "url", + "description": "URL to a primary web page for this party." + }, + { + "type": "null" + } + ] + }, + "url": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "email": { + "oneOf": [ + { + "type": "string", + "title": "email", + "description": "Email for this party." + }, + { + "type": "null" + } + ] + } + } + } + }, + { + "type": "null" + } + ] + }, + "contributors": { + "oneOf": [ + { + "type": "array", + "title": "contributors", + "description": "A list of party objects. Note: this model schema will change soon.", + "items": { + "type": "object", + "title": "party", + "description": "A party is a person, project or organization related to a package.", + "properties": { + "type": { + "oneOf": [ + { + "type": "string", + "title": "party type", + "description": "the type of this party: One of: person, project, organization", + "enum": [ + "person", + "project", + "organization" + ] + }, + { + "type": "null" + } + ] + }, + "name": { + "oneOf": [ + { + "type": "string", + "title": "url", + "description": "URL to a primary web page for this party." + }, + { + "type": "null" + } + ] + }, + "url": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "email": { + "oneOf": [ + { + "type": "string", + "title": "email", + "description": "Email for this party." + }, + { + "type": "null" + } + ] + } + } + } + }, + { + "type": "null" + } + ] + }, + "owners": { + "oneOf": [ + { + "type": "array", + "title": "owners", + "description": "A list of party objects. Note: this model schema will change soon.", + "items": { + "type": "object", + "title": "party", + "description": "A party is a person, project or organization related to a package.", + "properties": { + "type": { + "oneOf": [ + { + "type": "string", + "title": "party type", + "description": "the type of this party: One of: person, project, organization", + "enum": [ + "person", + "project", + "organization" + ] + }, + { + "type": "null" + } + ] + }, + "name": { + "oneOf": [ + { + "type": "string", + "title": "url", + "description": "URL to a primary web page for this party." + }, + { + "type": "null" + } + ] + }, + "url": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "email": { + "oneOf": [ + { + "type": "string", + "title": "email", + "description": "Email for this party." + }, + { + "type": "null" + } + ] + } + } + } + }, + { + "type": "null" + } + ] + }, + "packagers": { + "oneOf": [ + { + "type": "array", + "title": "owners", + "description": "A list of party objects. Note: this model schema will change soon.", + "items": { + "type": "object", + "title": "party", + "description": "A party is a person, project or organization related to a package.", + "properties": { + "type": { + "oneOf": [ + { + "type": "string", + "title": "party type", + "description": "the type of this party: One of: person, project, organization", + "enum": [ + "person", + "project", + "organization" + ] + }, + { + "type": "null" + } + ] + }, + "name": { + "oneOf": [ + { + "type": "string", + "title": "url", + "description": "URL to a primary web page for this party." + }, + { + "type": "null" + } + ] + }, + "url": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "email": { + "oneOf": [ + { + "type": "string", + "title": "email", + "description": "Email for this party." + }, + { + "type": "null" + } + ] + } + } + } + }, + { + "type": "null" + } + ] + }, + "distributors": { + "oneOf": [ + { + "type": "array", + "title": "distributors", + "description": "A list of party objects. Note: this model schema will change soon.", + "items": { + "type": "object", + "title": "party", + "description": "A party is a person, project or organization related to a package.", + "properties": { + "type": { + "oneOf": [ + { + "type": "string", + "title": "party type", + "description": "the type of this party: One of: person, project, organization", + "enum": [ + "person", + "project", + "organization" + ] + }, + { + "type": "null" + } + ] + }, + "name": { + "oneOf": [ + { + "type": "string", + "title": "url", + "description": "URL to a primary web page for this party." + }, + { + "type": "null" + } + ] + }, + "url": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "email": { + "oneOf": [ + { + "type": "string", + "title": "email", + "description": "Email for this party." + }, + { + "type": "null" + } + ] + } + } + } + }, + { + "type": "null" + } + ] + }, + "vendors": { + "oneOf": [ + { + "type": "array", + "title": "vendors", + "description": "A list of party objects. Note: this model schema will change soon.", + "items": { + "type": "object", + "title": "party", + "description": "A party is a person, project or organization related to a package.", + "properties": { + "type": { + "oneOf": [ + { + "type": "string", + "title": "party type", + "description": "the type of this party: One of: person, project, organization", + "enum": [ + "person", + "project", + "organization" + ] + }, + { + "type": "null" + } + ] + }, + "name": { + "oneOf": [ + { + "type": "string", + "title": "url", + "description": "URL to a primary web page for this party." + }, + { + "type": "null" + } + ] + }, + "url": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "email": { + "oneOf": [ + { + "type": "string", + "title": "email", + "description": "Email for this party." + }, + { + "type": "null" + } + ] + } + } + } + }, + { + "type": "null" + } + ] + }, + "keywords": { + "oneOf": [ + { + "type": "array", + "title": "keywords", + "description": "A list of keywords or tags.", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "keywords_doc_url": { + "oneOf": [ + { + "type": "string", + "title": "keywords documentation URL", + "description": "URL to a reference documentation for keywords or tags (such as a Pypi or SF.net Trove map)" + }, + { + "type": "null" + } + ] + }, + "metafile_locations": { + "oneOf": [ + { + "type": "array", + "title": "metafile locations", + "description": "A list of metafile locations for this package (such as a package.json, a setup.py). Relative to the package root directory or archive root", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "metafile_urls": { + "oneOf": [ + { + "type": "array", + "title": "metafile URLs", + "description": "A list of metafile remote URLs for this package (such as a package.json, a setup.py)", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "homepage_url": { + "oneOf": [ + { + "type": "string", + "title": "homepage URL", + "description": "URL to the homepage for this package" + }, + { + "type": "null" + } + ] + }, + "notes": { + "oneOf": [ + { + "type": "string", + "title": "Notes", + "description": "Notes, free text about this package" + }, + { + "type": "null" + } + ] + }, + "download_urls": { + "oneOf": [ + { + "type": "array", + "title": "Download URLs", + "description": "A list of direct download URLs, possibly in SPDX VCS url form. The first item is considered to be the primary download URL", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "download_sha1": { + "oneOf": [ + { + "type": "string", + "title": "Download SHA1", + "description": "Shecksum for the download" + }, + { + "type": "null" + } + ] + }, + "download_sha256": { + "oneOf": [ + { + "type": "string", + "title": "Download SHA256", + "description": "Shecksum for the download" + }, + { + "type": "null" + } + ] + }, + "download_md5": { + "oneOf": [ + { + "type": "string", + "title": "Download MD5", + "description": "Shecksum for the download" + }, + { + "type": "null" + } + ] + }, + "bug_tracking_url": { + "oneOf": [ + { + "type": "string", + "title": "bug tracking URL", + "description": "URL to the issue or bug tracker for this package" + }, + { + "type": "null" + } + ] + }, + "support_contacts": { + "oneOf": [ + { + "type": "array", + "title": "Support contacts", + "description": "A list of strings (such as email, urls, etc) for support contacts", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "code_view_url": { + "oneOf": [ + { + "type": "string", + "title": "code view URL", + "description": "a URL where the code can be browsed online" + }, + { + "type": "null" + } + ] + }, + "vcs_tool": { + "oneOf": [ + { + "type": "string", + "title": "Version control system tool", + "description": "The type of VCS tool for this package. One of: git, svn, hg, bzr, cvs", + "enum": [ + "git", + "svn", + "hg", + "bzr", + "cvs" + ] + }, + { + "type": "null" + } + ] + }, + "vcs_repository": { + "oneOf": [ + { + "type": "string", + "title": "VCS Repository URL", + "description": "a URL to the VCS repository in the SPDX form of:git+https://github.com/nexb/scancode-toolkit.git" + }, + { + "type": "null" + } + ] + }, + "vcs_revision": { + "oneOf": [ + { + "type": "string", + "title": "VCS revision", + "description": "a revision, commit, branch or tag reference, etc. (can also be included in the URL)" + }, + { + "type": "null" + } + ] + }, + "copyright_top_level": { + "oneOf": [ + { + "type": "string", + "title": "Top level Copyright", + "description": "a top level copyright often asserted in package metadata" + }, + { + "type": "null" + } + ] + }, + "copyrights": { + "oneOf": [ + { + "type": "array", + "title": "Copyrights", + "description": "A list of effective copyrights as detected and eventually summarized", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "asserted_licenses": { + "oneOf": [ + { + "type": "array", + "title": "asserted licenses", + "description": "A list of asserted license objects representing the asserted licensing information for this package", + "items": { + "type": "object", + "title": "asserted license", + "description": "Represents the licensing as asserted in a package metadata.", + "properties": { + "license": { + "oneOf": [ + { + "type": "string", + "title": "license", + "description": "license as asserted. This can be a text, a name or anything." + }, + { + "type": "null" + } + ] + }, + "url": { + "oneOf": [ + { + "type": "string", + "title": "url", + "description": "URL to a web page for this license." + }, + { + "type": "null" + } + ] + }, + "text": { + "oneOf": [ + { + "type": "string", + "title": "license text", + "description": "license text as asserted." + }, + { + "type": "null" + } + ] + }, + "notice": { + "oneOf": [ + { + "type": "string", + "title": "notice", + "description": "a license notice for this package" + }, + { + "type": "null" + } + ] + } + } + } + }, + { + "type": "null" + } + ] + }, + "legal_file_locations": { + "oneOf": [ + { + "type": "array", + "title": "legal file locations", + "description": "A list of paths to legal files (such as COPYING, NOTICE, LICENSE, README, etc.). Paths are relative to the root of the package", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "license_expression": { + "oneOf": [ + { + "type": "string", + "title": "license expression", + "description": "license expression: either resolved or detected license expression" + }, + { + "type": "null" + } + ] + }, + "license_texts": { + "oneOf": [ + { + "type": "array", + "title": "notice texts", + "description": "A list of notice texts for this package.", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "notice_texts": { + "oneOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "dependencies": { + "oneOf": [ + { + "type": "string", + "title": "dependencies", + "description": "An object mapping a dependency group to a list of dependency objects for this package. Note: the schema for this will change soon.The possible values for dependency grousp are:runtime, development, optional, test, build, continuous integration, bundled" + }, + { + "type": "null" + } + ] + }, + "related_packages": { + "oneOf": [ + { + "type": "array", + "title": "related packages", + "description": "A list of related_package objects for this package. For instance the SRPM source of a binary RPM.", + "items": { + "type": "object", + "title": "related package", + "description": "A generic related package.", + "required": [ + "type", + "name" + ], + "properties": { + "type": { + "type": "string", + "title": "type", + "description": "Descriptive name of the type of package: RubyGem, Python Wheel, Java Jar, Debian package, etc." + }, + "name": { + "type": "string", + "title": "name", + "description": "Name of the package." + }, + "version": { + "oneOf": [ + { + "type": "string", + "title": "version", + "description": "Version of the package" + }, + { + "type": "null" + } + ] + }, + "payload_type": { + "oneOf": [ + { + "type": "string", + "title": "Payload type", + "description": "The type of payload for this package. One of: source, binary, doc", + "enum": [ + "source", + "binary", + "doc" + ] + }, + { + "type": "null" + } + ] + } + } + } + }, + { + "type": "null" + } + ] + } + } +} \ No newline at end of file diff --git a/etc/scripts/sch2js/doc/Party-json-schema.json b/etc/scripts/sch2js/doc/Party-json-schema.json new file mode 100644 index 00000000000..c654ba08faa --- /dev/null +++ b/etc/scripts/sch2js/doc/Party-json-schema.json @@ -0,0 +1,60 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "", + "type": "object", + "title": "party", + "description": "A party is a person, project or organization related to a package.", + "properties": { + "type": { + "oneOf": [ + { + "type": "string", + "title": "party type", + "description": "the type of this party: One of: person, project, organization", + "enum": [ + "person", + "project", + "organization" + ] + }, + { + "type": "null" + } + ] + }, + "name": { + "oneOf": [ + { + "type": "string", + "title": "url", + "description": "URL to a primary web page for this party." + }, + { + "type": "null" + } + ] + }, + "url": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "email": { + "oneOf": [ + { + "type": "string", + "title": "email", + "description": "Email for this party." + }, + { + "type": "null" + } + ] + } + } +} \ No newline at end of file diff --git a/etc/scripts/sch2js/doc/RelatedPackage-json-schema.json b/etc/scripts/sch2js/doc/RelatedPackage-json-schema.json new file mode 100644 index 00000000000..a09ed5765d8 --- /dev/null +++ b/etc/scripts/sch2js/doc/RelatedPackage-json-schema.json @@ -0,0 +1,52 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "", + "type": "object", + "title": "related package", + "description": "A generic related package.", + "required": [ + "type", + "name" + ], + "properties": { + "type": { + "type": "string", + "title": "type", + "description": "Descriptive name of the type of package: RubyGem, Python Wheel, Java Jar, Debian package, etc." + }, + "name": { + "type": "string", + "title": "name", + "description": "Name of the package." + }, + "version": { + "oneOf": [ + { + "type": "string", + "title": "version", + "description": "Version of the package" + }, + { + "type": "null" + } + ] + }, + "payload_type": { + "oneOf": [ + { + "type": "string", + "title": "Payload type", + "description": "The type of payload for this package. One of: source, binary, doc", + "enum": [ + "source", + "binary", + "doc" + ] + }, + { + "type": "null" + } + ] + } + } +} \ No newline at end of file diff --git a/etc/scripts/sch2js/doc/Repository-json-schema.json b/etc/scripts/sch2js/doc/Repository-json-schema.json new file mode 100644 index 00000000000..aa33919bcba --- /dev/null +++ b/etc/scripts/sch2js/doc/Repository-json-schema.json @@ -0,0 +1,86 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "", + "type": "object", + "title": "package repository", + "description": "Represents a package repository.", + "properties": { + "type": { + "oneOf": [ + { + "type": "string", + "title": "package repository type", + "description": "The type of package repository for this repository. One of: Bower, CPAN, Debian, Rubygems, Godoc, IVY, Maven, NPM, Packagist, Nuget, Pypi, YUM", + "enum": [ + "Bower", + "CPAN", + "Debian", + "Rubygems", + "Godoc", + "IVY", + "Maven", + "NPM", + "Packagist", + "Nuget", + "Pypi", + "YUM" + ] + }, + { + "type": "null" + } + ] + }, + "url": { + "oneOf": [ + { + "type": "string", + "title": "url", + "description": "URL to this repository." + }, + { + "type": "null" + } + ] + }, + "public": { + "oneOf": [ + { + "type": "boolean", + "title": "public repository", + "description": "A flag set to true if this is a public repository." + }, + { + "type": "null" + } + ] + }, + "mirror_urls": { + "oneOf": [ + { + "type": "array", + "title": "repository mirror urls", + "description": "A list of URLs for mirrors of this repository.", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "nickname": { + "oneOf": [ + { + "type": "string", + "title": "repository nickname", + "description": "nickname used for well known \"named\" public repos such as: Maven Central, Pypi, RubyGems, npmjs.org or their mirrors" + }, + { + "type": "null" + } + ] + } + } +} \ No newline at end of file diff --git a/etc/scripts/sch2js/sch2js.ABOUT b/etc/scripts/sch2js/sch2js.ABOUT new file mode 100644 index 00000000000..862c9a42e87 --- /dev/null +++ b/etc/scripts/sch2js/sch2js.ABOUT @@ -0,0 +1,5 @@ +about_resource: sch2js.py +name: jsonschematics +download_url: https://raw.githubusercontent.com/boblannon-picwell/jsonschematics/ac24246459c51ad730d008a4e97c2f16a115dd2c/jsonschematics/__init__.py +license_file: sch2js.LICENSE +license: mit \ No newline at end of file diff --git a/etc/scripts/sch2js/sch2js.LICENSE b/etc/scripts/sch2js/sch2js.LICENSE new file mode 100644 index 00000000000..9760844a693 --- /dev/null +++ b/etc/scripts/sch2js/sch2js.LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Alex Kessinger + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/etc/scripts/sch2js/sch2js.py b/etc/scripts/sch2js/sch2js.py new file mode 100644 index 00000000000..e99527a0b2e --- /dev/null +++ b/etc/scripts/sch2js/sch2js.py @@ -0,0 +1,194 @@ +# The MIT License (MIT) +# +# Copyright (c) 2014 Alex Kessinger +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +# based on download_url: https://raw.githubusercontent.com/boblannon-picwell/jsonschematics/ac24246459c51ad730d008a4e97c2f16a115dd2c/jsonschematics/__init__.py +# and modified + +""" +Convert a schematics model to a JSON schema. + +To use: + +from packagedcode.models import * +import json +from sch2js import to_jsonschema +models = [Package, Repository, AssertedLicense, Party, Dependency, RelatedPackage] +for model in models: + jsc = to_jsonschema(model) + with open('doc/' + model.__name__ + '-json-schema.json', 'w') as o: + json.dump(jsc, o, indent=2) +""" + +from collections import OrderedDict + +from six import iteritems + +from schematics.types.base import BaseType +from schematics.types.compound import ListType +from schematics.types.compound import ModelType + + +__version__ = '1.0.1.patch' + + +SCHEMATIC_TYPE_TO_JSON_TYPE = { + 'NumberType': 'number', + 'IntType': 'integer', + 'LongType': 'integer', + 'FloatType': 'number', + 'DecimalType': 'number', + 'BooleanType': 'boolean', + 'BaseType': 'object' +} + +# Schema Serialization + +# Parameters for serialization to JSONSchema +schema_kwargs_to_schematics = { + 'maxLength': 'max_length', + 'minLength': 'min_length', + 'pattern': 'regex', + 'minimum': 'min_value', + 'maximum': 'max_value', + 'enum': 'choices' +} + + +def jsonschema_for_single_field(field_instance): + """ + Return a mapping for the schema of a single field. + """ + field_schema = OrderedDict() + + field_schema['type'] = SCHEMATIC_TYPE_TO_JSON_TYPE.get( + field_instance.__class__.__name__, 'string') + + if hasattr(field_instance, 'metadata'): + field_schema['title'] = field_instance.metadata.get('label', '') + field_schema['description'] = field_instance.metadata.get('description', '') + + for js_key, schematic_key in iteritems(schema_kwargs_to_schematics): + value = getattr(field_instance, schematic_key, None) + if value is not None: + field_schema[js_key] = value + + return field_schema + + +def jsonschema_for_fields(model): + """ + Return a mapping for the schema of a collection of fields. + """ + properties = OrderedDict() + required = [] + for field_name, field_instance in iteritems(model.fields): + serialized_name = getattr(field_instance, 'serialized_name', None) or field_name + + if isinstance(field_instance, ModelType): + node = jsonschema_for_model(field_instance.model_class) + + elif isinstance(field_instance, ListType): + try: + node = jsonschema_for_model(field_instance.model_class, 'array') + if hasattr(field_instance, 'metadata'): + _node = OrderedDict() + _node['type'] = node.pop('type') + _node['title'] = field_instance.metadata.get('label', '') + _node['description'] = field_instance.metadata.get('description', '') + _node.update(node) + node = _node + except AttributeError: + field_schema = jsonschema_for_single_field(field_instance.field) + node = OrderedDict() + node['type'] = 'array' + if hasattr(field_instance, 'metadata'): + node['title'] = field_instance.metadata.get('label', '') + node['description'] = field_instance.metadata.get('description', '') + node['items'] = field_schema + + # Convert field as single model + elif isinstance(field_instance, BaseType): + node = jsonschema_for_single_field(field_instance) + + if getattr(field_instance, 'required', False): + required.append(serialized_name) + properties[serialized_name] = node + else: + properties[serialized_name] = { + 'oneOf': [ + node, + {'type': 'null'}, + ] + } + + return properties, required + + +def jsonschema_for_model(model, _type='object'): + """ + Return a mapping for the schema of a model field. + """ + + properties, required = jsonschema_for_fields(model) + + if hasattr(model, 'metadata'): + schema_title = model.metadata.get('label', '') + schema_description = model.metadata.get('description', '') + else: + schema_title = '' + schema_description = '' + + schema = OrderedDict([ + ('type', 'object'), + ('title', schema_title), + ('description', schema_description), + ]) + + if required: + schema['required'] = required + + if hasattr(model, '_schema_order'): + ordered_properties = [(i, properties.pop(i)) for i in model._schema_order] + schema['properties'] = OrderedDict(ordered_properties) + else: + schema['properties'] = properties + + if _type == 'array': + schema = OrderedDict([ + ('type', 'array'), + ('items', schema), + ]) + + return schema + + +def to_jsonschema(model, **kwargs): + """ + Return a a JSON schema mapping for the `model` class. + """ + schema_id = kwargs.pop('schema_id', '') + jsonschema = OrderedDict([ + ('$schema', 'http://json-schema.org/draft-04/schema#'), + ('id', schema_id) + ]) + jsonschema.update(jsonschema_for_model(model)) + return jsonschema diff --git a/src/packagedcode/models.py b/src/packagedcode/models.py index b16f43b64da..a52d8d7667c 100644 --- a/src/packagedcode/models.py +++ b/src/packagedcode/models.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2016 nexB Inc. and others. All rights reserved. +# Copyright (c) 2017 nexB Inc. and others. All rights reserved. # http://nexb.com and https://github.com/nexB/scancode-toolkit/ # The ScanCode software is licensed under the Apache License version 2.0. # Data generated with ScanCode require an acknowledgment. @@ -24,6 +24,7 @@ from __future__ import absolute_import from __future__ import print_function +from __future__ import unicode_literals from collections import namedtuple import string @@ -229,6 +230,10 @@ class VersionType(BaseType): instance when storing a tuple, namedtuple or list for each each version parts or parsing the version in parts or exposiing parts. """ + metadata = dict( + label='version', + description='The version of the package as a string. ' + 'Package types may implement specialized handling for versions, but this seralizes as a string') def __init__(self, separator=None, **kwargs): if not separator: @@ -336,17 +341,36 @@ def to_dict(self, **kwargs): class Repository(BaseModel): - """ - A package repository. - """ + metadata = dict( + label='package repository', + description='Represents a package repository.') + type = StringType(choices=REPO_TYPES) + type.metadata = dict( + label='package repository type', + description='The type of package repository for this repository. ' + 'One of: ' + ', '.join(REPO_TYPES)) + url = URIType() - # True if this is a public repository + url.metadata = dict( + label='url', + description='URL to this repository.') + public = BooleanType(default=False) + public.metadata = dict( + label='public repository', + description='A flag set to true if this is a public repository.') + mirror_urls = ListType(URIType) - # optional: nickname used for well known "named" public repos such as: - # Maven Central, Pypi, RubyGems, npmjs.org or their mirrors + mirror_urls.metadata = dict( + label='repository mirror urls', + description='A list of URLs for mirrors of this repository.') + nickname = StringType() + nickname.metadata = dict( + label='repository nickname', + description='nickname used for well known "named" public repos such as: ' + 'Maven Central, Pypi, RubyGems, npmjs.org or their mirrors') class Options: fields_order = 'type', 'url', 'public', 'mirror_urls', 'name' @@ -365,13 +389,29 @@ def packages(self): class AssertedLicense(BaseModel): - """ - License as asserted in a package metadata. - """ + metadata = dict( + label='asserted license', + description='Represents the licensing as asserted in a package metadata.') + license = StringType() + license.metadata = dict( + label='license', + description='license as asserted. This can be a text, a name or anything.') + url = URIType() + url.metadata = dict( + label='url', + description='URL to a web page for this license.') + text = StringType() + text.metadata = dict( + label='license text', + description='license text as asserted.') + notice = StringType() + notice.metadata = dict( + label='notice', + description='a license notice for this package') class Options: fields_order = 'license', 'url', 'text', 'notice' @@ -386,13 +426,29 @@ class Options: class Party(BaseModel): - """ - A party is a person, project or organization. - """ + metadata = dict( + label='party', + description='A party is a person, project or organization related to a package.') + type = StringType(choices=PARTY_TYPES) + type.metadata = dict( + label='party type', + description='the type of this party: One of: ' + ', '.join(PARTY_TYPES)) + name = StringType() + name.metadata = dict( + label='name', + description='Name of this party.') + url = URLType() + name.metadata = dict( + label='url', + description='URL to a primary web page for this party.') + email = EmailType() + email.metadata = dict( + label='email', + description='Email for this party.') class Options: fields_order = 'type', 'name', 'email', 'url' @@ -410,22 +466,31 @@ class Options: DEPENDENCY_GROUPS = (dep_runtime, dep_dev, dep_optional, dep_test, dep_build, dep_ci, dep_bundled,) -# FIXME: this is broken ... Debs and RPMs use file deps as an indirection and not directly package deps. +# FIXME: this is broken ... OSGi uses "Java packages" deps as an indirection and not directly package deps. class Dependency(BaseModel): - """ - A dependency points to a Package via a package name and a version constraint - (such as ">= 3.4"). The version is the effective version that has been - picked and resolved. - """ - # package name for this dependency + metadata = dict( + label='dependency', + description='A dependency points to a Package via a package name and a version constraint ' + '(such as ">= 3.4"). The version is the effective version that has been ' + 'picked and resolved.') + name = StringType(required=True) + name.metadata = dict( + label='name', + description='Name of the package for this dependency.') - # The effective or concrete resolved and used version version = VersionType() + version.metadata = dict( + label='version', + description='Version of this dependent package: ' + 'The effective or concrete resolved and used version.') - # The version constraints (aka. possible versions) for this dep. - # The meaning is package type-specific version_constraint = StringType() + version_constraint.metadata = dict( + label='version', + description='The version constraints (aka. possible versions) ' + 'for this dependent package: The meaning of this constraings is ' + 'package type-specific. ') class Options: fields_order = 'type', 'name', 'version', 'version_constraint' @@ -461,18 +526,30 @@ def resolve(self): class RelatedPackage(BaseModel): - """ - A generic related package. - """ - # Descriptive name of the type of package: - # RubyGem, Python Wheel, Maven Jar, etc. - type = StringType(choices=PARTY_TYPES) + metadata = dict( + label='related package', + description='A generic related package.') + + type = StringType(required=True) + type.metadata = dict( + label='type', + description='Descriptive name of the type of package: ' + 'RubyGem, Python Wheel, Java Jar, Debian package, etc.') name = StringType(required=True) + name.metadata = dict( + label='name', + description='Name of the package.') + version = VersionType() + version.metadata = dict( + label='version', + description='Version of the package') - # the type of payload in this package. one of PAYLOADS or none payload_type = StringType(choices=PAYLOADS) + payload_type.metadata = dict( + label='Payload type', + description='The type of payload for this package. One of: ' + ', '.join(PAYLOADS)) class Options: fields_order = 'type', 'name', 'version', 'payload_type' @@ -480,10 +557,15 @@ class Options: class Package(BaseModel): """ - A package base class. Override for specific package behaviour. The way a + A package object. + Override for specific package behaviour. The way a package is created and serialized should be uniform across all Package types. """ + metadata = dict( + label='package', + description='A package object.') + ############################### # real class-level attributes ############################### @@ -491,7 +573,6 @@ class Package(BaseModel): filetypes = tuple() mimetypes = tuple() extensions = tuple() - # list of known metafiles for a package type, to recognize a package metafiles = [] @@ -503,116 +584,236 @@ class Package(BaseModel): ############################### # from here on, these are actual instance attributes, using descriptors - # Descriptive name of the type of package: - # RubyGem, Python Wheel, Maven Jar, etc. type = StringType(required=True) + type.metadata = dict( + label='package type', + description='Descriptive name of the type of package: ' + 'RubyGem, Python Wheel, Java Jar, Debian package, etc.') name = StringType(required=True) + name.metadata = dict( + label='package name', + description='Name of the package.') + version = VersionType() + version.metadata = dict( + label='package version', + description='Version of the package. ' + 'Package types may implement specific handling for versions but this is always serialized as a string.') - # primary programming language for a package type - # i.e. RubyGems are primarily ruby, etc primary_language = StringType() + primary_language.metadata = dict( + label='Primary programming language', + description='Primary programming language of the package, such as Java, C, C++. ' + 'Derived from the package type: i.e. RubyGems are primarily ruby, etc.') - # type of packaging of this Package packaging = StringType(choices=PACKAGINGS) + packaging.metadata = dict( + label='Packaging', + description='How a package is packaged. One of: ' + ', '.join(PACKAGINGS)) # TODO: add os and arches!! - # this is a "short" description. summary = StringType() - # this is a "long" description, often several pages of text. + summary.metadata = dict( + label='Summary', + description='Summary for this package i.e. a short description') + description = StringType() - # the type of payload in this package. one of PAYLOADS or none + description.metadata = dict( + label='Description', + description='Description for this package ' + 'i.e. a long description, often several pages of text') + payload_type = StringType(choices=PAYLOADS) - # size of the package in bytes, use LongType instead of IntType is because + payload_type.metadata = dict( + label='Payload type', + description='The type of payload for this package. One of: ' + ', '.join(PAYLOADS)) + + # we useLongType instead of IntType is because # IntType 2147483647 is the max size which means we cannot store # more than 2GB files size = LongType() - # release date of the package + size.metadata = dict( + label='size', + description='size of the package download in bytes') + release_date = DateTimeType() + release_date.metadata = dict( + label='release date', + description='Release date of the package') - # list of Parties: authors, packager, maintainers, contributors, distributor, vendor, etc # FIXME: this would be simpler as a list where each Party has also a type authors = ListType(ModelType(Party)) + authors.metadata = dict( + label='authors', + description='A list of party objects. Note: this model schema will change soon.') + maintainers = ListType(ModelType(Party)) + maintainers.metadata = dict( + label='maintainers', + description='A list of party objects. Note: this model schema will change soon.') + contributors = ListType(ModelType(Party)) + contributors.metadata = dict( + label='contributors', + description='A list of party objects. Note: this model schema will change soon.') + owners = ListType(ModelType(Party)) + owners.metadata = dict( + label='owners', + description='A list of party objects. Note: this model schema will change soon.') + packagers = ListType(ModelType(Party)) + packagers.metadata = dict( + label='owners', + description='A list of party objects. Note: this model schema will change soon.') + distributors = ListType(ModelType(Party)) + distributors.metadata = dict( + label='distributors', + description='A list of party objects. Note: this model schema will change soon.') + vendors = ListType(ModelType(Party)) + vendors.metadata = dict( + label='vendors', + description='A list of party objects. Note: this model schema will change soon.') - # keywords or tags keywords = ListType(StringType()) + keywords.metadata = dict( + label='keywords', + description='A list of keywords or tags.') - # url to a reference documentation for keywords or tags (such as a Pypi or SF.net Trove map) # FIXME: this is a Package-class attribute keywords_doc_url = URLType() + keywords_doc_url.metadata = dict( + label='keywords documentation URL', + description='URL to a reference documentation for keywords or ' + 'tags (such as a Pypi or SF.net Trove map)') - # Paths to actual metadata files for this package, if any. - # Relative to the package root directory or archive root. metafile_locations = ListType(StringType()) + metafile_locations.metadata = dict( + label='metafile locations', + description='A list of metafile locations for this package ' + '(such as a package.json, a setup.py). ' + 'Relative to the package root directory or archive root') - # URLs to remote metadata files for this package if available metafile_urls = ListType(URIType()) + metafile_urls.metadata = dict( + label='metafile URLs', + description='A list of metafile remote URLs for this package ' + '(such as a package.json, a setup.py)') homepage_url = URIType() + homepage_url.metadata = dict( + label='homepage URL', + description='URL to the homepage for this package') + notes = StringType() + notes.metadata = dict( + label='Notes', + description='Notes, free text about this package') - # one or more direct download urls, possibly in SPDX vcs url form - # the first one is considered to be the primary download_urls = ListType(URIType()) + download_urls.metadata = dict( + label='Download URLs', + description='A list of direct download URLs, possibly in SPDX VCS url form. ' + 'The first item is considered to be the primary download URL') - # checksums for the download download_sha1 = SHA1Type() + download_sha1.metadata = dict(label='Download SHA1', description='Shecksum for the download') download_sha256 = SHA256Type() + download_sha256.metadata = dict(label='Download SHA256', description='Shecksum for the download') download_md5 = MD5Type() + download_md5.metadata = dict(label='Download MD5', description='Shecksum for the download') - # issue or bug tracker bug_tracking_url = URLType() + bug_tracking_url.metadata = dict( + label='bug tracking URL', + description='URL to the issue or bug tracker for this package') - # strings (such as email, urls, etc) support_contacts = ListType(StringType()) + support_contacts.metadata = dict( + label='Support contacts', + description='A list of strings (such as email, urls, etc) for support contacts') - # a URL where the code can be browsed online code_view_url = URIType() + code_view_url.metadata = dict( + label='code view URL', + description='a URL where the code can be browsed online') + + VCS_CHOICES = ['git', 'svn', 'hg', 'bzr', 'cvs'] + vcs_tool = StringType(choices=VCS_CHOICES) + vcs_tool.metadata = dict( + label='Version control system tool', + description='The type of VCS tool for this package. One of: ' + ', '.join(VCS_CHOICES)) - # one of git, svn, hg, etc - vcs_tool = StringType(choices=['git', 'svn', 'hg', 'bzr', 'cvs']) - # a URL in the SPDX form of: - # git+https://github.com/nexb/scancode-toolkit.git vcs_repository = URIType() - # a revision, branch, tag reference, etc (can also be included in the URL + vcs_repository.metadata = dict( + label='VCS Repository URL', + description='a URL to the VCS repository in the SPDX form of:' + 'git+https://github.com/nexb/scancode-toolkit.git') + vcs_revision = StringType() + vcs_revision.metadata = dict( + label='VCS revision', + description='a revision, commit, branch or tag reference, etc. ' + '(can also be included in the URL)') - # a top level copyright often asserted in metadata copyright_top_level = StringType() - # effective copyrights as detected and eventually summarized + copyright_top_level.metadata = dict( + label='Top level Copyright', + description='a top level copyright often asserted in package metadata') + copyrights = ListType(StringType()) + copyrights.metadata = dict( + label='Copyrights', + description='A list of effective copyrights as detected and eventually summarized') - # as asserted licensing information - # a list of AssertLicense objects asserted_licenses = ListType(ModelType(AssertedLicense)) + asserted_licenses.metadata = dict( + label='asserted licenses', + description='A list of asserted license objects representing ' + 'the asserted licensing information for this package') - # List of paths legal files (such as COPYING, NOTICE, LICENSE, README, etc.) - # Paths are relative to the root of the package legal_file_locations = ListType(StringType()) + legal_file_locations.metadata = dict( + label='legal file locations', + description='A list of paths to legal files ' + '(such as COPYING, NOTICE, LICENSE, README, etc.). ' + 'Paths are relative to the root of the package') - # Resolved or detected license expressions license_expression = StringType() - # list of license texts + license_expression.metadata = dict( + label='license expression', + description='license expression: either resolved or detected license expression') + license_texts = ListType(StringType()) + license_texts.metadata = dict( + label='license texts', + description='A list of license texts for this package.') - # list of notices texts notice_texts = ListType(StringType()) + license_texts.metadata = dict( + label='notice texts', + description='A list of notice texts for this package.') # Map a DEPENDENCY_GROUPS group name to a list of Dependency - # FIXME: we should instead just have a plain list where each dep contain a groups list. + # FIXME: we should instead just have a plain list where each dep contain a group. dependencies = DictType(ListType(ModelType(Dependency)), default={}) + dependencies.metadata = dict( + label='dependencies', + description='An object mapping a dependency group to a ' + 'list of dependency objects for this package. ' + 'Note: the schema for this will change soon.' + 'The possible values for dependency grousp are:' + ', '.join(DEPENDENCY_GROUPS) + ) - # List of related packages and the corresponding payload. - # For instance the SRPM of an RPM related_packages = ListType(ModelType(RelatedPackage)) + related_packages.metadata = dict( + label='related packages', + description='A list of related_package objects for this package. ' + 'For instance the SRPM source of a binary RPM.') class Options: # this defines the important serialization order