Skip to content

Commit

Permalink
docs: update architecture description and examples (#550)
Browse files Browse the repository at this point in the history

Signed-off-by: Jan Kowalleck <jan.kowalleck@gmail.com>
  • Loading branch information
jkowalleck authored Feb 11, 2024
1 parent 63cff7e commit a19fd28
Show file tree
Hide file tree
Showing 8 changed files with 288 additions and 29 deletions.
1 change: 0 additions & 1 deletion .isort.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ skip_glob =
.git/*,.tox/*,.venv/*,venv/*,.venv*/*,venv*/*,
_OLD/*,_TEST/*,
docs/*
examples/*
combine_as_imports = true
default_section = THIRDPARTY
ensure_newline_before_comments = true
Expand Down
3 changes: 1 addition & 2 deletions docs/architecture.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Architecture

This library broadly is separated into three key functional areas:

1. **Model**: Internal models used to unify data from different parsers
1. **Model**: Internal models used to unify data.

**Note:** As of version 4.0.0 of this library we support deserialization from JSON and XML as well as
serialization to JSON and XML.
Expand All @@ -30,7 +30,6 @@ When wishing to generate a BOM, the process is as follows:
1. Generate a Model by either:
1. Programmatically using this library
2. By deserializing from an existing CycloneDX BOM document
3. From a :py:mod:`cyclonedx.parser`
2. Output the Model using an :py:mod:`cyclonedx.output` instance that reflects the schema version and format you require

.. toctree::
Expand Down
14 changes: 11 additions & 3 deletions docs/examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,17 @@
Examples
========

Complex
-------
Complex Serialize
-----------------

.. literalinclude:: ../examples/complex.py
.. literalinclude:: ../examples/complex_serialize.py
:language: python
:linenos:


Complex Deserialize
-------------------

.. literalinclude:: ../examples/complex_deserialize.py
:language: python
:linenos:
20 changes: 3 additions & 17 deletions docs/modelling.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@
Modelling
=========

You can create a BOM Model from either a :py:mod:`cyclonedx.parser` instance or manually using the methods available
directly on the :py:mod:`cyclonedx.model.bom.Bom` class.
You can create a BOM Model from either manually using the methods available
directly on the :py:class:`cyclonedx.model.bom.Bom` class,
or deserialize a JSON/XML via :py:meth:`cyclonedx.model.bom.Bom.from_json`/:py:meth:`cyclonedx.model.bom.Bom.from_xml`

Vulnerabilities are supported by the Model as of version 0.3.0.

Expand Down Expand Up @@ -76,21 +77,6 @@ you use a library such as `defusedxml` instead of the native `xml.etree.ElementT
deserialized_bom = cast(Bom, Bom.from_xml(data=ElementTree.fromstring(input_xml.read())))
Example BOM using a Parser
--------------------------

**Note:** Concreate parser implementations were moved out of this library and into `CycloneDX Python`_ as of version
``1.0.0``.

.. code-block:: python
from cyclonedx.model.bom import Bom
from cyclonedx_py.parser.environment import EnvironmentParser
parser = EnvironmentParser()
bom = Bom.from_parser(parser=parser)
.. _CycloneDX Python: https://github.com/CycloneDX/cyclonedx-python
Expand Down
3 changes: 2 additions & 1 deletion examples/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Examples

* [Build & Serialize](build_and_serialize.py)
* [Build & Serialize](complex_serialize.py)
* [Deserialize](complex_deserialize.py)

----

Expand Down
267 changes: 267 additions & 0 deletions examples/complex_deserialize.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
# This file is part of CycloneDX Python Lib
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
# Copyright (c) OWASP Foundation. All Rights Reserved.

import sys
from json import loads as json_loads
from typing import TYPE_CHECKING

from defusedxml import ElementTree as SafeElementTree # type:ignore[import-untyped]

from cyclonedx.exception import MissingOptionalDependencyException
from cyclonedx.model.bom import Bom
from cyclonedx.schema import OutputFormat, SchemaVersion
from cyclonedx.validation import make_schemabased_validator
from cyclonedx.validation.json import JsonStrictValidator

if TYPE_CHECKING:
from cyclonedx.validation.xml import XmlValidator

# region JSON

json_data = """{
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"serialNumber": "urn:uuid:88fabcfa-7529-4ba2-8256-29bec0c03900",
"version": 1,
"metadata": {
"timestamp": "2024-02-10T21:38:53.313120+00:00",
"tools": [
{
"vendor": "CycloneDX",
"name": "cyclonedx-python-lib",
"version": "6.4.1",
"externalReferences": [
{
"type": "build-system",
"url": "https://github.com/CycloneDX/cyclonedx-python-lib/actions"
},
{
"type": "distribution",
"url": "https://pypi.org/project/cyclonedx-python-lib/"
},
{
"type": "documentation",
"url": "https://cyclonedx-python-library.readthedocs.io/"
},
{
"type": "issue-tracker",
"url": "https://github.com/CycloneDX/cyclonedx-python-lib/issues"
},
{
"type": "license",
"url": "https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/LICENSE"
},
{
"type": "release-notes",
"url": "https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/CHANGELOG.md"
},
{
"type": "vcs",
"url": "https://github.com/CycloneDX/cyclonedx-python-lib"
},
{
"type": "website",
"url": "https://github.com/CycloneDX/cyclonedx-python-lib/#readme"
}
]
}
],
"component": {
"bom-ref": "myApp",
"name": "myApp",
"type": "application",
"licenses": [
{
"license": {
"id": "MIT"
}
}
]
}
},
"components": [
{
"bom-ref": "myComponent@1.33.7-beta.1",
"type": "library",
"group": "acme",
"name": "some-component",
"version": "1.33.7-beta.1",
"purl": "pkg:generic/acme/some-component@1.33.7-beta.1",
"licenses": [
{
"license": {
"name": "(c) 2021 Acme inc."
}
}
],
"supplier": {
"name": "Acme Inc",
"url": [
"https://www.acme.org"
]
}
},
{
"bom-ref": "some-lib",
"type": "library",
"name": "some-library",
"licenses": [
{
"expression": "GPL-3.0-only WITH Classpath-exception-2.0"
}
]
}
],
"dependencies": [
{
"ref": "some-lib"
},
{
"dependsOn": [
"myComponent@1.33.7-beta.1"
],
"ref": "myApp"
},
{
"dependsOn": [
"some-lib"
],
"ref": "myComponent@1.33.7-beta.1"
}
]
}"""
my_json_validator = JsonStrictValidator(SchemaVersion.V1_5)
try:
validation_errors = my_json_validator.validate_str(json_data)
if validation_errors:
print('JSON invalid', 'ValidationError:', repr(validation_errors), sep='\n', file=sys.stderr)
sys.exit(2)
print('JSON valid')
except MissingOptionalDependencyException as error:
print('JSON-validation was skipped due to', error)
bom_from_json = Bom.from_json( # type: ignore[attr-defined]
json_loads(json_data))
print('bom_from_json', repr(bom_from_json))

# endregion JSON

print('', '=' * 30, '', sep='\n')

# endregion XML

xml_data = """<?xml version="1.0" ?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.5"
serialNumber="urn:uuid:88fabcfa-7529-4ba2-8256-29bec0c03900"
version="1"
>
<metadata>
<timestamp>2024-02-10T21:38:53.313120+00:00</timestamp>
<tools>
<tool>
<vendor>CycloneDX</vendor>
<name>cyclonedx-python-lib</name>
<version>6.4.1</version>
<externalReferences>
<reference type="build-system">
<url>https://github.com/CycloneDX/cyclonedx-python-lib/actions</url>
</reference>
<reference type="distribution">
<url>https://pypi.org/project/cyclonedx-python-lib/</url>
</reference>
<reference type="documentation">
<url>https://cyclonedx-python-library.readthedocs.io/</url>
</reference>
<reference type="issue-tracker">
<url>https://github.com/CycloneDX/cyclonedx-python-lib/issues</url>
</reference>
<reference type="license">
<url>https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/LICENSE</url>
</reference>
<reference type="release-notes">
<url>https://github.com/CycloneDX/cyclonedx-python-lib/blob/main/CHANGELOG.md</url>
</reference>
<reference type="vcs">
<url>https://github.com/CycloneDX/cyclonedx-python-lib</url>
</reference>
<reference type="website">
<url>https://github.com/CycloneDX/cyclonedx-python-lib/#readme</url>
</reference>
</externalReferences>
</tool>
</tools>
<component type="application" bom-ref="myApp">
<name>myApp</name>
<licenses>
<license>
<id>MIT</id>
</license>
</licenses>
</component>
</metadata>
<components>
<component type="library" bom-ref="myComponent@1.33.7-beta.1">
<supplier>
<name>Acme Inc</name>
<url>https://www.acme.org</url>
</supplier>
<group>acme</group>
<name>some-component</name>
<version>1.33.7-beta.1</version>
<licenses>
<license>
<name>(c) 2021 Acme inc.</name>
</license>
</licenses>
<purl>pkg:generic/acme/some-component@1.33.7-beta.1</purl>
</component>
<component type="library" bom-ref="some-lib">
<name>some-library</name>
<licenses>
<expression>GPL-3.0-only WITH Classpath-exception-2.0</expression>
</licenses>
</component>
</components>
<dependencies>
<dependency ref="some-lib"/>
<dependency ref="myApp">
<dependency ref="myComponent@1.33.7-beta.1"/>
</dependency>
<dependency ref="myComponent@1.33.7-beta.1">
<dependency ref="some-lib"/>
</dependency>
</dependencies>
</bom>"""
my_xml_validator: 'XmlValidator' = make_schemabased_validator(OutputFormat.XML, SchemaVersion.V1_5)
try:
validation_errors = my_xml_validator.validate_str(xml_data)
if validation_errors:
print('XML invalid', 'ValidationError:', repr(validation_errors), sep='\n', file=sys.stderr)
sys.exit(2)
print('XML valid')
except MissingOptionalDependencyException as error:
print('XML-validation was skipped due to', error)
bom_from_xml = Bom.from_xml( # type: ignore[attr-defined]
SafeElementTree.fromstring(xml_data))
print('bom_from_xml', repr(bom_from_xml))

# endregion XML

print('', '=' * 30, '', sep='\n')

print('assert bom_from_json equals bom_from_xml')
assert bom_from_json == bom_from_xml, 'expected to have equal BOMs from JSON and XML'
7 changes: 3 additions & 4 deletions examples/complex.py → examples/complex_serialize.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
# Copyright (c) OWASP Foundation. All Rights Reserved.

import sys
from typing import TYPE_CHECKING

from packageurl import PackageURL

Expand All @@ -26,11 +27,9 @@
from cyclonedx.model.component import Component, ComponentType
from cyclonedx.output import make_outputter
from cyclonedx.output.json import JsonV1Dot5
from cyclonedx.schema import SchemaVersion, OutputFormat
from cyclonedx.validation.json import JsonStrictValidator
from cyclonedx.schema import OutputFormat, SchemaVersion
from cyclonedx.validation import make_schemabased_validator

from typing import TYPE_CHECKING
from cyclonedx.validation.json import JsonStrictValidator

if TYPE_CHECKING:
from cyclonedx.output.json import Json as JsonOutputter
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ include = [
{ path="README.md", format =["sdist","wheel"] },
{ path="LICENSE", format=["sdist","wheel"] },
{ path="NOTICE", format=["sdist","wheel"] },
{ path="tests", format=["sdist"] },
{ path="CHANGELOG.md", format=["sdist"] },
{ path="docs", format=["sdist"] },
{ path="examples", format=["sdist"] },
{ path="tests", format=["sdist"] },
]
exclude = [
# exclude dotfiles and dotfolders
Expand Down

0 comments on commit a19fd28

Please sign in to comment.