From 5a3d2a7fab0ae578d19af0905f0c5f0859f09955 Mon Sep 17 00:00:00 2001 From: Paul Horton Date: Thu, 14 Nov 2024 07:09:26 +0000 Subject: [PATCH 1/7] beginning of draft OpenApi Spec for TEA (consumer only considered at this time) Signed-off-by: Paul Horton --- spec | 405 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 405 insertions(+) create mode 100644 spec diff --git a/spec b/spec new file mode 100644 index 0000000..aae5e4d --- /dev/null +++ b/spec @@ -0,0 +1,405 @@ +{ + "$schema": "https://spec.openapis.org/oas/3.1/dialect/base", + "openapi": "3.1.1", + "info": { + "title": "Transparency Exchange API", + "summary": "Transparency Exchange API specification for consumers and publishers", + "description": "TBC", + "contact": { + "name": "TEA Working Group", + "email": "TBC", + "url": "https://github.com/CycloneDX/transparency-exchange-api" + }, + "license": { + "identifier": "Apache-2.0", + "url": "https://github.com/CycloneDX/transparency-exchange-api/blob/main/LICENSE" + }, + "version": "0.0.1" + }, + "servers": [ + { + "url": "http://localhost/tea/v1", + "description": "Local development" + } + ], + "paths": { + "/index/{product-identifier}": { + "get": { + "description": "Access the TEA Index for the supplied product identifier", + "operationId": "getTeaIndex", + "parameters": [ + { + "$ref": "#/components/parameters/product-identifier" + } + ], + "responses": { + "200": { + "description": "Requested TEA Index found and returned", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/tea-index" + } + } + } + }, + + "404": { + "$ref": "#/components/responses/404-object-by-id-not-found" + } + + }, + "tags": [ + "TEA Index" + ] + } + }, + "/leaf/{product-identifier}/{product-version}": { + "get": { + "description": "Get the TEA Leaf that describes the Version of a Product", + "operationId": "getTeaLeaf", + "parameters": [ + { + "$ref": "#/components/parameters/product-identifier" + }, + { + "$ref": "#/components/parameters/product-version" + } + ], + "responses": { + "200": { + "description": "Requested TEA Leaf found and returned", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/tea-leaf" + } + } + } + }, + "404": { + "$ref": "#/components/responses/404-object-by-id-not-found" + } + }, + "tags": [ + "TEA Leaf" + ] + } + }, + "/collection/{tea-collection-identifier}": { + "get": { + "description": "Get a TEA Collection by it's Identifier", + "operationId": "getTeaCollection", + "parameters": [ + { + "$ref": "#/components/parameters/tea-collection-identifier" + } + ], + "responses": { + "200": { + "description": "Requested TEA Collection found and returned", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/tea-collection" + } + } + } + }, + "404": { + "$ref": "#/components/responses/404-object-by-id-not-found" + } + }, + "tags": [ + "TEA Leaf" + ] + } + } + }, + "webhooks": {}, + "components": { + "schemas": { + "tea-index": { + "type": "object", + "properties": { + "uuid": { + "$ref": "#/components/schemas/type-uuid" + }, + "product-name": { + "type": "string" + }, + "product-version": { + "type": "string" + } + }, + "reqiured": [ + "uuid", + "product-name", + "product-version" + ] + }, + "tea-leaf": { + "type": "object", + "properties": { + "uuid": { + "$ref": "#/components/schemas/type-uuid" + }, + "product-name": { + "type": "string" + }, + "product-version": { + "type": "string" + }, + "release-date": { + "type": "string", + "format": "date-time" + }, + "prerelease": { + "type": "boolean" + }, + "tea-collection-uuid": { + "$ref": "#/components/schemas/type-uuid" + } + }, + "reqiured": [ + "uuid", + "product-name", + "product-version", + "release-date", + "prerelease", + "tea-collection-uuid" + ] + }, + "tea-collection": { + "type": "object", + "properties": { + "uuid": { + "$ref": "#/components/schemas/type-uuid" + }, + "product-name": { + "type": "string" + }, + "product-version": { + "type": "string" + }, + "release-date": { + "type": "string", + "format": "date-time" + }, + "author": { + "$ref": "#/components/schemas/type-author" + }, + "reason": { + "$ref": "#/components/schemas/type-collection-reason" + } + }, + "reqiured": [ + "uuid", + "product-name", + "product-version", + "release-date", + "author", + "reason" + ] + }, + "tea-collection-artifact": { + "type": "object", + "properties": { + "uuid": { + "$ref": "#/components/schemas/type-uuid" + }, + "name": { + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/type-tea-collection-artifact-type" + }, + "author": { + "$ref": "#/components/schemas/type-author" + } + }, + "reqiured": [ + "uuid", + "name", + "author" + ] + }, + "type-author": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "email": { + "type": "string", + "format": "email" + }, + "organization": { + "type": "string" + } + }, + "required": [ + "name", + "email", + "organization" + ] + }, + "type-collection-reason": { + "type": "string", + "description": "Event requiring this TEA Collection was published", + "enum": [ + "NEW_PRODUCT_RELEASE", + "BOM_UPDATED", + "ATTESTATION_ADDED", + "ATTESTATION_UPDATED" + ] + }, + "type-tea-collection-artifact-type": { + "type": "string", + "title": "Type", + "description": "Specifies the type of external reference.", + "enum": [ + "vcs", + "issue-tracker", + "website", + "advisories", + "bom", + "mailing-list", + "social", + "chat", + "documentation", + "support", + "source-distribution", + "distribution", + "distribution-intake", + "license", + "build-meta", + "build-system", + "release-notes", + "security-contact", + "model-card", + "log", + "configuration", + "evidence", + "formulation", + "attestation", + "threat-model", + "adversary-model", + "risk-assessment", + "vulnerability-assertion", + "exploitability-statement", + "pentest-report", + "static-analysis-report", + "dynamic-analysis-report", + "runtime-analysis-report", + "component-analysis-report", + "maturity-report", + "certification-report", + "codified-infrastructure", + "quality-metrics", + "poam", + "electronic-signature", + "digital-signature", + "rfc-9116", + "other" + ], + "meta:enum": { + "vcs": "Version Control System", + "issue-tracker": "Issue or defect tracking system, or an Application Lifecycle Management (ALM) system", + "website": "Website", + "advisories": "Security advisories", + "bom": "Bill of Materials (SBOM, OBOM, HBOM, SaaSBOM, etc)", + "mailing-list": "Mailing list or discussion group", + "social": "Social media account", + "chat": "Real-time chat platform", + "documentation": "Documentation, guides, or how-to instructions", + "support": "Community or commercial support", + "source-distribution": "The location where the source code distributable can be obtained. This is often an archive format such as zip or tgz. The source-distribution type complements use of the version control (vcs) type.", + "distribution": "Direct or repository download location", + "distribution-intake": "The location where a component was published to. This is often the same as \"distribution\" but may also include specialized publishing processes that act as an intermediary.", + "license": "The reference to the license file. If a license URL has been defined in the license node, it should also be defined as an external reference for completeness.", + "build-meta": "Build-system specific meta file (i.e. pom.xml, package.json, .nuspec, etc)", + "build-system": "Reference to an automated build system", + "release-notes": "Reference to release notes", + "security-contact": "Specifies a way to contact the maintainer, supplier, or provider in the event of a security incident. Common URIs include links to a disclosure procedure, a mailto (RFC-2368) that specifies an email address, a tel (RFC-3966) that specifies a phone number, or dns (RFC-4501) that specifies the records containing DNS Security TXT.", + "model-card": "A model card describes the intended uses of a machine learning model, potential limitations, biases, ethical considerations, training parameters, datasets used to train the model, performance metrics, and other relevant data useful for ML transparency.", + "log": "A record of events that occurred in a computer system or application, such as problems, errors, or information on current operations.", + "configuration": "Parameters or settings that may be used by other components or services.", + "evidence": "Information used to substantiate a claim.", + "formulation": "Describes how a component or service was manufactured or deployed.", + "attestation": "Human or machine-readable statements containing facts, evidence, or testimony.", + "threat-model": "An enumeration of identified weaknesses, threats, and countermeasures, dataflow diagram (DFD), attack tree, and other supporting documentation in human-readable or machine-readable format.", + "adversary-model": "The defined assumptions, goals, and capabilities of an adversary.", + "risk-assessment": "Identifies and analyzes the potential of future events that may negatively impact individuals, assets, and/or the environment. Risk assessments may also include judgments on the tolerability of each risk.", + "vulnerability-assertion": "A Vulnerability Disclosure Report (VDR) which asserts the known and previously unknown vulnerabilities that affect a component, service, or product including the analysis and findings describing the impact (or lack of impact) that the reported vulnerability has on a component, service, or product.", + "exploitability-statement": "A Vulnerability Exploitability eXchange (VEX) which asserts the known vulnerabilities that do not affect a product, product family, or organization, and optionally the ones that do. The VEX should include the analysis and findings describing the impact (or lack of impact) that the reported vulnerability has on the product, product family, or organization.", + "pentest-report": "Results from an authorized simulated cyberattack on a component or service, otherwise known as a penetration test.", + "static-analysis-report": "SARIF or proprietary machine or human-readable report for which static analysis has identified code quality, security, and other potential issues with the source code.", + "dynamic-analysis-report": "Dynamic analysis report that has identified issues such as vulnerabilities and misconfigurations.", + "runtime-analysis-report": "Report generated by analyzing the call stack of a running application.", + "component-analysis-report": "Report generated by Software Composition Analysis (SCA), container analysis, or other forms of component analysis.", + "maturity-report": "Report containing a formal assessment of an organization, business unit, or team against a maturity model.", + "certification-report": "Industry, regulatory, or other certification from an accredited (if applicable) certification body.", + "codified-infrastructure": "Code or configuration that defines and provisions virtualized infrastructure, commonly referred to as Infrastructure as Code (IaC).", + "quality-metrics": "Report or system in which quality metrics can be obtained.", + "poam": "Plans of Action and Milestones (POAM) complement an \"attestation\" external reference. POAM is defined by NIST as a \"document that identifies tasks needing to be accomplished. It details resources required to accomplish the elements of the plan, any milestones in meeting the tasks and scheduled completion dates for the milestones\".", + "electronic-signature": "An e-signature is commonly a scanned representation of a written signature or a stylized script of the person's name.", + "digital-signature": "A signature that leverages cryptography, typically public/private key pairs, which provides strong authenticity verification.", + "rfc-9116": "Document that complies with RFC-9116 (A File Format to Aid in Security Vulnerability Disclosure)", + "other": "Use this if no other types accurately describe the purpose of the external reference." + } + }, + "type-uuid": { + "type": "string", + "format": "uuid", + "required": true + } + }, + "responses": { + "404-object-by-id-not-found": { + "description": "Object requested by identifier not found", + "content": { + "application/json": {} + } + } + }, + "parameters": { + "product-identifier": { + "name": "product-identifier", + "description": "Product Identifier part of a TEI", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + "product-version": { + "name": "product-version", + "description": "Product Version string", + "in": "path", + "reqiured": true, + "schema": { + "type": "string" + } + }, + "tea-collection-identifier": { + "name": "tea-collection-identifier", + "description": "TEA Collection Identifier", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + } + }, + "security": [], + "tags": [ + "TEA Index", + "TEA Leaf" + ], + "externalDocs": { + "description": "Transparency Exchange API specification", + "url": "https://github.com/CycloneDX/transparency-exchange-api" + } +} \ No newline at end of file From 3a276ef832f6bde4decab7267f5ac7924199678c Mon Sep 17 00:00:00 2001 From: Paul Horton Date: Fri, 15 Nov 2024 07:30:17 +0000 Subject: [PATCH 2/7] fix: `GET` TEA Leaf by its ID Signed-off-by: Paul Horton --- spec => spec/openapi.json | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename spec => spec/openapi.json (100%) diff --git a/spec b/spec/openapi.json similarity index 100% rename from spec rename to spec/openapi.json From 78bddc3adc67828647d660dbbc4868086efbeeba Mon Sep 17 00:00:00 2001 From: Paul Horton Date: Fri, 15 Nov 2024 07:33:00 +0000 Subject: [PATCH 3/7] fix: `/index` --> `/product-index` Signed-off-by: Paul Horton --- spec/openapi.json | 77 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 68 insertions(+), 9 deletions(-) diff --git a/spec/openapi.json b/spec/openapi.json index aae5e4d..b0a33c9 100644 --- a/spec/openapi.json +++ b/spec/openapi.json @@ -23,7 +23,7 @@ } ], "paths": { - "/index/{product-identifier}": { + "/product-index/{product-identifier}": { "get": { "description": "Access the TEA Index for the supplied product identifier", "operationId": "getTeaIndex", @@ -38,7 +38,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/tea-index" + "$ref": "#/components/schemas/tea-product-index" } } } @@ -54,16 +54,13 @@ ] } }, - "/leaf/{product-identifier}/{product-version}": { + "/leaf/{tea-leaf-identifier}": { "get": { "description": "Get the TEA Leaf that describes the Version of a Product", "operationId": "getTeaLeaf", "parameters": [ { - "$ref": "#/components/parameters/product-identifier" - }, - { - "$ref": "#/components/parameters/product-version" + "$ref": "#/components/parameters/tea-leaf-identifier" } ], "responses": { @@ -119,7 +116,7 @@ "webhooks": {}, "components": { "schemas": { - "tea-index": { + "tea-product-index": { "type": "object", "properties": { "uuid": { @@ -191,6 +188,13 @@ }, "reason": { "$ref": "#/components/schemas/type-collection-reason" + }, + "artifacts": { + "type": "array", + "items": { + "$ref": "#/components/schemas/tea-collection-artifact" + }, + "minItems": 1 } }, "reqiured": [ @@ -216,12 +220,58 @@ }, "author": { "$ref": "#/components/schemas/type-author" + }, + "objects": { + "type": "array", + "items": { + "$ref": "#/components/schemas/tea-collection-artifact-object" + }, + "minItems": 1 } }, "reqiured": [ "uuid", "name", - "author" + "type", + "author", + "objects" + ] + }, + "tea-collection-artifact-object": { + "type": "object", + "properties": { + "uuid": { + "$ref": "#/components/schemas/type-uuid" + }, + "description": { + "type": "string" + }, + "mime_type": { + "type": "string" + }, + "artifact_url": { + "type": "string", + "format": "uri" + }, + "artifact_size_in_bytes": { + "type": "integer", + "format": "int64" + }, + "artifact_checksum": { + "type": "string" + }, + "signature_url": { + "type": "string", + "format": "uri" + } + }, + "reqiured": [ + "uuid", + "description", + "mime_type", + "artifact_url", + "artifact_size_in_bytes", + "artifact_checksum" ] }, "type-author": { @@ -390,6 +440,15 @@ "schema": { "type": "string" } + }, + "tea-leaf-identifier": { + "name": "tea-leaf-identifier", + "description": "TEA Leaf Identifier", + "in": "path", + "required": true, + "schema": { + "type": "string" + } } } }, From 2867a742f2cc5bba68fc48be57a24800ee897618 Mon Sep 17 00:00:00 2001 From: Paul Horton Date: Fri, 15 Nov 2024 07:40:39 +0000 Subject: [PATCH 4/7] fix: `GET /product-index/...` did not return an array Signed-off-by: Paul Horton --- spec/openapi.json | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/spec/openapi.json b/spec/openapi.json index b0a33c9..0ebd07b 100644 --- a/spec/openapi.json +++ b/spec/openapi.json @@ -38,7 +38,11 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/tea-product-index" + "type": "array", + "items:": { + "$ref": "#/components/schemas/tea-product-index" + }, + "minItems": 1 } } } @@ -50,6 +54,7 @@ }, "tags": [ + "Consumer", "TEA Index" ] } @@ -79,6 +84,7 @@ } }, "tags": [ + "Consumer", "TEA Leaf" ] } @@ -108,7 +114,8 @@ } }, "tags": [ - "TEA Leaf" + "Consumer", + "TEA Collection" ] } } @@ -454,6 +461,9 @@ }, "security": [], "tags": [ + "Consumer", + "Producer", + "TEA Collection", "TEA Index", "TEA Leaf" ], From 5630327d09a3b47beb8014e22da79b7aab2620a4 Mon Sep 17 00:00:00 2001 From: Paul Horton Date: Fri, 15 Nov 2024 07:52:28 +0000 Subject: [PATCH 5/7] added pagination to `/product-index` Signed-off-by: Paul Horton --- spec/openapi.json | 82 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 77 insertions(+), 5 deletions(-) diff --git a/spec/openapi.json b/spec/openapi.json index 0ebd07b..4482426 100644 --- a/spec/openapi.json +++ b/spec/openapi.json @@ -30,6 +30,12 @@ "parameters": [ { "$ref": "#/components/parameters/product-identifier" + }, + { + "$ref": "#/components/parameters/page-offset" + }, + { + "$ref": "#/components/parameters/page-size" } ], "responses": { @@ -38,11 +44,27 @@ "content": { "application/json": { "schema": { - "type": "array", - "items:": { - "$ref": "#/components/schemas/tea-product-index" - }, - "minItems": 1 + "allOf": [ + { + "$ref": "#/components/schemas/type-pagination-details" + }, + { + "type": "object", + "properties": { + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/tea-product-index" + }, + "minItems": 1 + } + }, + "required": [ + "results" + ] + } + ] + } } } @@ -311,6 +333,35 @@ "ATTESTATION_UPDATED" ] }, + "type-pagination-details": { + "type": "object", + "properties": { + "timestamp": { + "type": "string", + "format": "date-time" + }, + "page_start_index": { + "type": "number", + "format": "int64", + "default": 0 + }, + "page_size": { + "type": "number", + "format": "int64", + "default": 100 + }, + "total_results": { + "type": "number", + "format": "int64" + } + }, + "required": [ + "timestamp", + "page_start_index", + "page_size", + "total_results" + ] + }, "type-tea-collection-artifact-type": { "type": "string", "title": "Type", @@ -421,6 +472,27 @@ } }, "parameters": { + "page-offset": { + "name": "page-offset", + "description": "Pagination offset", + "in": "query", + "required": false, + "schema": { + "type": "number", + "format": "int64", + "default": 0 + } + }, + "page-size": { + "name": "page-size", + "description": "Pagination offset", + "in": "query", + "required": false, + "schema": { + "type": "number", + "format": "int64" + } + }, "product-identifier": { "name": "product-identifier", "description": "Product Identifier part of a TEI", From de44ad69b669208f13f286cb157a4191a9a34347 Mon Sep 17 00:00:00 2001 From: Paul Horton Date: Fri, 15 Nov 2024 07:53:45 +0000 Subject: [PATCH 6/7] define a default `page-size Signed-off-by: Paul Horton --- spec/openapi.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/openapi.json b/spec/openapi.json index 4482426..873fefa 100644 --- a/spec/openapi.json +++ b/spec/openapi.json @@ -490,7 +490,8 @@ "required": false, "schema": { "type": "number", - "format": "int64" + "format": "int64", + "default": 100 } }, "product-identifier": { From d7ca7ee9de17fbb0a1fe42364f5d0805671e3bcc Mon Sep 17 00:00:00 2001 From: Paul Horton Date: Fri, 15 Nov 2024 10:33:53 +0000 Subject: [PATCH 7/7] feat: GH workflow to build API clients from OpenAPI spec (thus validating the spec) Signed-off-by: Paul Horton --- .github/CODEOWNERS | 2 + .../workflows/test-generate-api-clients.yaml | 165 ++++++++++++++++++ .gitignore | 1 + spec/README.md | 13 ++ spec/generators/common.yaml | 6 + spec/generators/go.yaml | 7 + spec/generators/java-webclient.yaml | 27 +++ spec/generators/python.yaml | 8 + spec/generators/typescript.yaml | 7 + spec/generators/update-pom.sh | 15 ++ spec/openapi.json | 9 +- 11 files changed, 255 insertions(+), 5 deletions(-) create mode 100644 .github/CODEOWNERS create mode 100644 .github/workflows/test-generate-api-clients.yaml create mode 100644 spec/README.md create mode 100644 spec/generators/common.yaml create mode 100644 spec/generators/go.yaml create mode 100644 spec/generators/java-webclient.yaml create mode 100644 spec/generators/python.yaml create mode 100644 spec/generators/typescript.yaml create mode 100755 spec/generators/update-pom.sh diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..3460f17 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +* @oej +* @madpah \ No newline at end of file diff --git a/.github/workflows/test-generate-api-clients.yaml b/.github/workflows/test-generate-api-clients.yaml new file mode 100644 index 0000000..12afb9e --- /dev/null +++ b/.github/workflows/test-generate-api-clients.yaml @@ -0,0 +1,165 @@ +name: Build & Test Generated API Clients + +on: + push: + branches: ['main'] + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + OPEN_API_GENERATOR_VERSION: 'v7.9.0' + PYTHON_VERSION_DEFAULT: '3.12' + POETRY_VERSION: '1.8.1' + +jobs: + generate-library-code: + name: Generate Library Code ${{ matrix.language }} + runs-on: ubuntu-latest + strategy: + matrix: + language: ['go', 'java-webclient', 'python', 'typescript'] + steps: + - name: Checkout + # see https://github.com/actions/checkout + uses: actions/checkout@v4 + + - name: Create Output Directory + run: mkdir out/${{ matrix.language }} + + - name: Run OpenAPI Generator + uses: addnab/docker-run-action@v3 + with: + image: openapitools/openapi-generator-cli:${{ env.OPEN_API_GENERATOR_VERSION }} + options: -v ${{ github.workspace }}:/local + run: /usr/local/bin/docker-entrypoint.sh batch --clean /local/spec/generators/${{ matrix.language }}.yaml + + - name: Copy our scripts across too + run: cp spec/generators/*.sh ./out/${{ matrix.language }} + + - name: Save to Cache + uses: actions/cache/save@v4 + with: + path: out/${{ matrix.language }} + key: '${{ matrix.language }}-${{ github.run_id }}' + + validate-go: + name: Validate Go Library + runs-on: ubuntu-latest + needs: generate-library-code + + steps: + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: 1.22 + + - name: Get generated code from cache + uses: actions/cache/restore@v4 + with: + path: out/go + key: 'go-${{ github.run_id }}' + fail-on-cache-miss: true + + - name: Build Go API Client + run: go build -v ./ + working-directory: out/go + + - name: Install test dependencies & run generated tests + run: | + go get github.com/stretchr/testify/assert + go test -v ./test/ + working-directory: out/go + + validate-java-webclient: + name: Validate Java Webclient + runs-on: ubuntu-latest + needs: generate-library-code + + steps: + - name: Set up JDK 17 for x64 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + architecture: x64 + + - name: Set up Maven + uses: stCarolas/setup-maven@v5 + with: + maven-version: 3.9.4 + + - name: Get generated code from cache + uses: actions/cache/restore@v4 + with: + path: out/java-webclient + key: 'java-webclient-${{ github.run_id }}' + fail-on-cache-miss: true + + - name: Build & Test Java Webclient API Client + run: | + ./update-pom.sh pom.xml + mvn clean test + working-directory: out/java-webclient + + validate-python: + name: Validate Python Library + runs-on: ubuntu-latest + needs: generate-library-code + + steps: + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ${{ env.PYTHON_VERSION_DEFAULT }} + + - name: Install poetry + # see https://github.com/marketplace/actions/setup-poetry + uses: Gr1N/setup-poetry@v9 + with: + poetry-version: ${{ env.POETRY_VERSION }} + + - name: Get generated code from cache + uses: actions/cache/restore@v4 + with: + path: out/python + key: 'python-${{ github.run_id }}' + fail-on-cache-miss: true + + - name: Install dependencies + run: poetry install --no-root + working-directory: out/python + + - name: Ensure build successful + run: poetry build + working-directory: out/python + + - name: Run Tests + run: | + pip install -r test-requirements.txt + poetry run pytest + working-directory: out/python + + validate-typescript: + name: Validate Typescript Library + runs-on: ubuntu-latest + needs: generate-library-code + + steps: + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: latest + + - name: Get generated code from cache + uses: actions/cache/restore@v4 + with: + path: out/typescript + key: 'typescript-${{ github.run_id }}' + fail-on-cache-miss: true + + - name: Build Typescript API Client + run: npm i && npm run build + working-directory: out/typescript \ No newline at end of file diff --git a/.gitignore b/.gitignore index 9f11b75..16b0824 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .idea/ +out \ No newline at end of file diff --git a/spec/README.md b/spec/README.md new file mode 100644 index 0000000..2ccf73e --- /dev/null +++ b/spec/README.md @@ -0,0 +1,13 @@ +# TEA OpenAPI Spec + +The OpenAPI 3.1 specification for the Transparency Exchange API is available in [openapi.json](./openapi.json). + +- [Generating API Clients from OpenAPI Spec](#generating-api-clients-from-openapi-spec) + +## Generating API Clients from OpenAPI Spec + +We use the OpenAPI Generator with configuration per language/framework in the `generators` folder. An example is: + +``` +docker run --rm -v "$(PWD):/local" openapitools/openapi-generator-cli batch --clean /local/spec/generators/typescript.yaml +``` \ No newline at end of file diff --git a/spec/generators/common.yaml b/spec/generators/common.yaml new file mode 100644 index 0000000..b622fc3 --- /dev/null +++ b/spec/generators/common.yaml @@ -0,0 +1,6 @@ +inputSpec: /local/spec/openapi.json +gitUserId: CycloneDX +gitRepoId: transparency-exchange-api + +globalProperties: + skipFormModel: false \ No newline at end of file diff --git a/spec/generators/go.yaml b/spec/generators/go.yaml new file mode 100644 index 0000000..ead2d86 --- /dev/null +++ b/spec/generators/go.yaml @@ -0,0 +1,7 @@ +'!include': 'common.yaml' +outputDir: /local/out/go +generatorName: go +gitRepoId: transparency-exchange-api-go +additionalProperties: + goImportAlias: tea_client + packageName: tea_client \ No newline at end of file diff --git a/spec/generators/java-webclient.yaml b/spec/generators/java-webclient.yaml new file mode 100644 index 0000000..705cb35 --- /dev/null +++ b/spec/generators/java-webclient.yaml @@ -0,0 +1,27 @@ +'!include': 'common.yaml' +outputDir: /local/out/java-webclient +generatorName: java +additionalProperties: + library: webclient + artifactDescription: Transparency Exchange API Java Webclient + artifactId: tea-api-webclient + artifactUrl: https://github.com/CycloneDX/transparency-exchange-api + invokerPackage: org.cyclonedx.tea.webclient + apiPackage: org.cyclonedx.tea.webclient.api + modelPackage: org.cyclonedx.tea.webclient.model + developerEmail: community@sonatype.com + developerName: OWASP Foundation + developerOrganization: OWASP Foundation + developerOrganizationUrl: https://cyclonedx.org/ + groupId: org.cyclonedx.tea + licenseName: Apache-2.0 + licenseUrl: https://github.com/CycloneDX/transparency-exchange-api/blob/main/LICENSE + scmConnection: scm:git:git@github.com:CycloneDX/transparency-exchange-api.git + scmDeveloperConnection: scm:git:git@github.com:CycloneDX/transparency-exchange-api.git + scmUrl: https://github.com/CycloneDX/transparency-exchange-api + useJakartaEe: true + asyncNative: true + generateBuilders: true + parentGroupId: org.sonatype.buildsupport + parentArtifactId: public-parent + parentVersion: 50 \ No newline at end of file diff --git a/spec/generators/python.yaml b/spec/generators/python.yaml new file mode 100644 index 0000000..649b304 --- /dev/null +++ b/spec/generators/python.yaml @@ -0,0 +1,8 @@ +'!include': 'common.yaml' +outputDir: /local/out/python +generatorName: python +additionalProperties: + library: urllib3 + licenseInfo: "Apache-2.0" + packageName: "tea-api-client" + projectName: "Transparency Exchange API (TEA)" \ No newline at end of file diff --git a/spec/generators/typescript.yaml b/spec/generators/typescript.yaml new file mode 100644 index 0000000..01b8ef7 --- /dev/null +++ b/spec/generators/typescript.yaml @@ -0,0 +1,7 @@ +'!include': 'common.yaml' +outputDir: /local/out/typescript +generatorName: typescript-fetch +additionalProperties: + npmName: "@cyclonedx/tea-api-client" + supportsES6: true + withInterfaces: true \ No newline at end of file diff --git a/spec/generators/update-pom.sh b/spec/generators/update-pom.sh new file mode 100755 index 0000000..a10f2f5 --- /dev/null +++ b/spec/generators/update-pom.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# Check if the file path is provided +if [ -z "$1" ]; then + echo "Usage: $0 " + exit 1 +fi + +# File path +FILE=$1 + +# Use sed to remove the tag and its content +sed -i.bak '//,/<\/build>/d' "$FILE" + +echo "The tag has been removed from $FILE. A backup has been saved as $FILE.bak." \ No newline at end of file diff --git a/spec/openapi.json b/spec/openapi.json index 873fefa..97fa243 100644 --- a/spec/openapi.json +++ b/spec/openapi.json @@ -1,5 +1,5 @@ { - "$schema": "https://spec.openapis.org/oas/3.1/dialect/base", + "jsonSchemaDialect": "https://spec.openapis.org/oas/3.1/dialect/base", "openapi": "3.1.1", "info": { "title": "Transparency Exchange API", @@ -11,7 +11,7 @@ "url": "https://github.com/CycloneDX/transparency-exchange-api" }, "license": { - "identifier": "Apache-2.0", + "name": "Apache 2.0", "url": "https://github.com/CycloneDX/transparency-exchange-api/blob/main/LICENSE" }, "version": "0.0.1" @@ -459,8 +459,7 @@ }, "type-uuid": { "type": "string", - "format": "uuid", - "required": true + "format": "uuid" } }, "responses": { @@ -507,7 +506,7 @@ "name": "product-version", "description": "Product Version string", "in": "path", - "reqiured": true, + "required": true, "schema": { "type": "string" }