Skip to content

Commit

Permalink
Json-schema-validation (#14)
Browse files Browse the repository at this point in the history
* feat(json-schema-validation): validate manifest.json

* feat(json-schema-validation): add dedicated commit/pr action

* chore: fix file path for schema

* chore: specify draft2020 for schema validation
  • Loading branch information
frankkilcommins authored Jun 25, 2024
1 parent c8b8d3e commit eb228c5
Show file tree
Hide file tree
Showing 4 changed files with 256 additions and 7 deletions.
131 changes: 131 additions & 0 deletions .github/workflows/docs-as-code-pr-commit-validation.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
name: Docs as Code PR and Commit Validation

on:
push:
branches:
- '*'
pull_request:
branches:
- '*'
workflow_dispatch:
inputs:
log_level:
description: 'Log level: 1=DEBUG, 2=INFO, 3=WARNING, 4=ERROR'
required: false
default: '2' # Set the default log level to INFO
skip_api_linting:
description: 'Skip the API linting job'
required: false
default: 'false'

env:
SWAGGERHUB_API_KEY: ${{ secrets.SWAGGERHUB_API_KEY }}
LOG_LEVEL: ${{ github.event.inputs.log_level }}

jobs:
spell-check:
runs-on: ubuntu-latest
environment: Production

steps:
- name: Checkout repository
uses: actions/checkout@v2

- name: Install Node.js
uses: actions/setup-node@v2
with:
node-version: '18'

- name: Install cspell
run: npm install -g cspell

- name: Run cspell
run: cspell --config ./.cspell.json "./products/**/*.md"

validate-manifests:
runs-on: ubuntu-latest
environment: Production
needs: spell-check

steps:
- name: Checkout repository
uses: actions/checkout@v2

- name: Install Node.js
uses: actions/setup-node@v2
with:
node-version: '18'

- name: Install AJV CLI
run: npm install -g ajv-cli

- name: Validate manifests
run: |
# source the utility script
. ./scripts/utilities.sh
for product in ./products/*; do
if [[ -d "$product" ]]; then
product_name=${product#./products/}
manifest="./products/$product_name/manifest.json"
if [[ -f "$manifest" ]]; then
# Further actions...
log_message $INFO "Validating manifest in product: $product_name"
log_message $DEBUG "Validating manifest: $manifest"
ajv validate -s ./schemas/manifest.schema.json -d "$manifest" --spec=draft2020
fi
fi
done
lint-api:
runs-on: ubuntu-latest
if: github.event.inputs.skip_api_linting != 'true'
environment: Production
needs: validate-manifests

steps:
- name: Checkout repository
uses: actions/checkout@v2

- name: Install SwaggerHub CLI
run: npm install -g swaggerhub-cli

- name: Iterate over product folders and validate APIs
shell: bash
run: |
# source the utility script
. ./scripts/utilities.sh
for product in ./products/*; do
log_message $DEBUG "Product: $product"
if [[ -d "$product" ]]; then
log_message $DEBUG "Product is a directory"
product_name=${product#./products/}
log_message $DEBUG "Product name: $product_name"
manifest="./products/$product_name/manifest.json"
log_message $DEBUG "Manifest: $manifest"
if [[ -f "$manifest" ]]; then
log_message $DEBUG "Manifest is a file"
validateAPIs=$(jq -r '.productMetadata.validateAPIs' "$manifest")
if [[ "$validateAPIs" == "true" ]]; then
log_message $INFO "Validating APIs for product: $product_name"
contentMetadata=$(jq -c '.contentMetadata[] | select(.type | ascii_downcase == "apiurl")' "$manifest")
echo "$contentMetadata" | jq -c '.' | while IFS= read -r contentMetadataItem; do
slug=$(echo "$contentMetadataItem" | jq -r '.slug')
log_message $INFO "Validating API: $slug"
swaggerhub api:validate "${SWAGGERHUB_ORG_NAME}/$slug" --fail-on-critical
done
else
log_message $WARNING "API validation is not enabled for product: $product_name"
fi
else
log_message $ERROR "Manifest is not a file"
fi
else
log_message $ERROR "Product is not a directory"
fi
done
env:
SWAGGERHUB_API_KEY: ${{ secrets.SWAGGERHUB_API_KEY }}
SWAGGERHUB_ORG_NAME: ${{ vars.SWAGGERHUB_ORG_NAME }}

36 changes: 36 additions & 0 deletions .github/workflows/docs-as-code.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,46 @@ jobs:
- name: Run cspell
run: cspell --config ./.cspell.json "./products/**/*.md"

validate-manifests:
runs-on: ubuntu-latest
environment: Production
needs: spell-check

steps:
- name: Checkout repository
uses: actions/checkout@v2

- name: Install Node.js
uses: actions/setup-node@v2
with:
node-version: '14'

- name: Install AJV CLI
run: npm install -g ajv-cli

- name: Validate manifests
run: |
# source the utility script
. ./scripts/utilities.sh
for product in ./products/*; do
if [[ -d "$product" ]]; then
product_name=${product#./products/}
manifest="./products/$product_name/manifest.json"
if [[ -f "$manifest" ]]; then
# Further actions...
log_message $INFO "Validating manifest in product: $product_name"
log_message $DEBUG "Validating manifest: $manifest"
ajv validate -s ./schemas/manifest-schema.json -d "$manifest" --spec=draft2020
fi
fi
done
lint-api:
runs-on: ubuntu-latest
if: github.event.inputs.skip_api_linting != 'true'
environment: Production
needs: validate-manifests

steps:
- name: Checkout repository
Expand Down
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ The following product structure must be adhered to allow for the automation to p
- product One _folder_ - contains all data relevant to "Product One"
- *.md _files_ - contains the markdown documents to be published within "Product One". The file name is used as the table-of-contents entry name.
- images _folder_ - a folder to house the product logo and _embedded_ sub-folder
- *.png / *.jpeg - a root level image to be used as the product logo (needs to be reference from the `manifest.json`)
- `*.png / *.jpeg` - a root level image to be used as the product logo (needs to be reference from the `manifest.json`)
- embedded _folder_ - a folder to storing all images to be embedded within the product markdown pages. See [Image Embedding Conventions](#image-embedding-convention) for more info on how to reference.
- manifest.json - stores product metadata (like description, slug, logo url, visibility, etc.) and content metadata (like table of contents order, page nesting, etc.)
- product Two ...
- product N ...
- product Two _folder_ ...
- product N _folder_ ...

### Image Embedding Convention

Expand Down Expand Up @@ -63,7 +63,7 @@ Check out the products operation at [`/products`](https://frankkilcommins.portal

### Table of Contents Conventions

The table of contents is completely driven by the `manifest.json` file contained within each Product folder.
The table of contents is completely driven by the `manifest.json` file contained within each Product folder. The specified manifest file MUST validate against [mainfest.schema.json](./schemas/manifest.schema.json).

A sample manifest is as follows:

Expand Down Expand Up @@ -152,7 +152,7 @@ The `contentMetadata` defines the following properties:

## GitHub Action

This repo comes with a simple boilerplate action that can be trigger manually or upon merge into the `main` branch.
This repo comes with a simple boilerplate action that can be triggered manually or upon merge into the `main` branch.

The action requires the following **repository secrets** to be configured:
- `SWAGGERHUB-API-KEY` - an API key associated to a user with the appropriate permission to be able to publish Portal content. See [Portal User Management](https://support.smartbear.com/swaggerhub-portal/docs/en/user-management.html) for more info.
Expand All @@ -166,5 +166,6 @@ The action requires the following **repository environment variables** to be con

The action performs the following jobs:
1. `spell-check`: Performs spell checking on all of the markdown files under the _products_ folder (**note** to add a list of known good custom words update the ./custom-words.txt file)
2. `lint-api`: Performs API standardization checks against each API referenced by a product manifest.json file. There is the ability to skip API validation for a specific API product via the productMetadata in the manifest.json.
3. `publish`: Publishes all of configured products into the referenced SwaggerHub Portal instance.
2. `validate-manifests`: Performs a JSON Schema validation check against the defined product manifest.json files to ensure they are correctly specified.
3. `lint-api`: Performs API standardization checks against each API referenced by a product manifest.json file. There is the ability to skip API validation for a specific API product via the productMetadata in the manifest.json.
4. `publish`: Publishes all of configured products into the referenced SwaggerHub Portal instance.
81 changes: 81 additions & 0 deletions schemas/manifest.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"productMetadata": {
"type": "object",
"properties": {
"description": {
"type": "string"
},
"slug": {
"type": "string"
},
"public": {
"type": "boolean"
},
"hidden": {
"type": "boolean"
},
"logo": {
"type": "string"
},
"logoDark": {
"type": "string"
},
"autoPublish": {
"type": "boolean"
},
"validateAPIs": {
"type": "boolean"
}
},
"required": [
"description",
"slug",
"public",
"hidden",
"logo",
"autoPublish",
"validateAPIs"
]
},
"contentMetadata": {
"type": "array",
"items": {
"type": "object",
"properties": {
"order": {
"type": "integer"
},
"parent": {
"type": "string"
},
"name": {
"type": "string"
},
"slug": {
"type": "string"
},
"type": {
"type": "string"
},
"contentUrl": {
"type": "string"
}
},
"required": [
"order",
"name",
"slug",
"type",
"contentUrl"
]
}
}
},
"required": [
"productMetadata",
"contentMetadata"
]
}

0 comments on commit eb228c5

Please sign in to comment.