Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,40 @@ jobs:
DB_USER: user
DB_PASSWORD: password
run: bundle exec rake
OpenAPI:
runs-on: ubuntu-22.04
env:
ALCHEMY_BRANCH: main
steps:
- uses: actions/checkout@v4
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: "3.4"
bundler-cache: true
- name: Restore apt cache
id: apt-cache
uses: actions/cache@v4
with:
path: /home/runner/apt/cache
key: apt-sqlite-
- name: Install SQLite headers
run: |
sudo mkdir -p /home/runner/apt/cache
sudo apt-get update -qq
sudo apt-get install -qq --fix-missing libsqlite3-dev -o dir::cache::archives="/home/runner/apt/cache"
sudo chown -R runner /home/runner/apt/cache
- name: Regenerate OpenAPI spec
env:
RAILS_ENV: test
OPENAPI: "1"
run: bundle exec rake
- name: Check for OpenAPI spec drift
run: |
if ! git diff --ignore-all-space --exit-code docs/openapi.yml; then
echo "::error::docs/openapi.yml is out of date. Run 'OPENAPI=1 bundle exec rspec' locally and commit the result."
exit 1
fi
Vitest:
runs-on: ubuntu-24.04
env:
Expand Down
4 changes: 4 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,7 @@ gem "standard", "~> 1.25", require: false
gem "pry-byebug"

gem "propshaft", "~> 1.3"

gem "rspec-openapi", require: false

gem "puma", "~> 7.2"
49 changes: 48 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,56 @@ Alchemy::JsonApi.key_transform = :camel_lower

It defaults to `:underscore`.

## OpenAPI Documentation

The API is documented with an OpenAPI 3.0 spec at `docs/openapi.yml`. When the
engine is mounted, the spec is served as JSON at:

```
GET /jsonapi/openapi.json
```

Point Swagger UI, Redoc, or any OpenAPI client generator at this URL.

### Regenerating the spec

The spec is auto-generated from request specs using
[rspec-openapi](https://github.com/exoego/rspec-openapi). To update it after
changing endpoints or serializers:

```bash
OPENAPI=1 bundle exec rspec
```

This merges actual API responses into `docs/openapi.yml`. Hand-written component
schemas are preserved. Review the diff and commit the result.

> [!NOTE]
> CI will fail if `docs/openapi.yml` is out of date. Always regenerate and
> commit the spec when changing API endpoints or serializers.

## Contributing

Contribution directions go here.
1. Fork the repo and create your branch from `main`.
2. Install dependencies:
```bash
bundle install
```
3. Run the test suite:
```bash
bundle exec rake
```
4. If you changed any API endpoints or serializers, regenerate the OpenAPI spec:
```bash
OPENAPI=1 bundle exec rspec
```
Review the diff in `docs/openapi.yml` and include it in your commit. CI will
reject PRs where the spec is out of date.
5. Ensure linting passes:
```bash
bundle exec standardrb
```
6. Open a pull request.

## License

Expand Down
2 changes: 1 addition & 1 deletion alchemy-json_api.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Gem::Specification.new do |spec|
spec.description = "A JSONAPI compliant API for AlchemyCMS"
spec.license = "BSD-3-Clause"

spec.files = Dir["{app,config,db,lib}/**/*", "LICENSE", "Rakefile", "README.md"]
spec.files = Dir["{app,config,db,docs,lib}/**/*", "LICENSE", "Rakefile", "README.md"]

spec.add_dependency "alchemy_cms", [">= 8.2.0.a", "< 9"]
spec.add_dependency "jsonapi.rb", [">= 1.6.0", "< 2.2"]
Expand Down
30 changes: 30 additions & 0 deletions app/controllers/alchemy/json_api/openapi_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# frozen_string_literal: true

require "yaml"
require "json"

module Alchemy
module JsonApi
class OpenapiController < ::ApplicationController
def show
render json: spec
end

def docs
@spec_url = alchemy_json_api.openapi_path(format: :json)
render layout: false
end

private

def spec
@spec ||= JSON.generate(
YAML.safe_load_file(
Alchemy::JsonApi::Engine.root.join("docs", "openapi.yml"),
permitted_classes: [Date, Time]
)
)
end
end
end
end
22 changes: 22 additions & 0 deletions app/views/alchemy/json_api/openapi/docs.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Alchemy JSON:API Documentation</title>
<link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css">
<style>html { box-sizing: border-box; } *, *::before, *::after { box-sizing: inherit; }</style>
</head>
<body>
<div id="swagger-ui"></div>
<script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js"></script>
<script>
SwaggerUIBundle({
url: <%== @spec_url.to_json %>,
dom_id: '#swagger-ui',
deepLinking: true,
presets: [SwaggerUIBundle.presets.apis, SwaggerUIBundle.SwaggerUIStandalonePreset],
layout: "BaseLayout"
});
</script>
</body>
</html>
3 changes: 3 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# frozen_string_literal: true

Alchemy::JsonApi::Engine.routes.draw do
get "openapi", to: "openapi#show", defaults: {format: :json}, as: :openapi
get "docs", to: "openapi#docs", constraints: ->(_r) { Rails.env.development? }

resources :pages, only: [:index]
get "pages/*path" => "pages#show", :as => :page
resources :layout_pages, only: [:index]
Expand Down
9 changes: 9 additions & 0 deletions docs/bruno/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Secrets
.env*

# Dependencies
node_modules

# OS files
.DS_Store
Thumbs.db
10 changes: 10 additions & 0 deletions docs/bruno/opencollection.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
opencollection: 1.0.0

info:
name: Alchemy JSON:API
bundled: false
extensions:
bruno:
ignore:
- node_modules
- .git
Loading
Loading