add sync API with swagger documentation at /api
This commit is contained in:
@@ -18,6 +18,9 @@ gem "turbo-rails"
|
|||||||
gem "stimulus-rails"
|
gem "stimulus-rails"
|
||||||
# Build JSON APIs with ease [https://github.com/rails/jbuilder]
|
# Build JSON APIs with ease [https://github.com/rails/jbuilder]
|
||||||
gem "jbuilder"
|
gem "jbuilder"
|
||||||
|
gem "grape"
|
||||||
|
gem "grape-swagger"
|
||||||
|
gem "rswag-ui"
|
||||||
gem "caxlsx"
|
gem "caxlsx"
|
||||||
gem "caxlsx_rails"
|
gem "caxlsx_rails"
|
||||||
|
|
||||||
|
|||||||
@@ -120,6 +120,26 @@ GEM
|
|||||||
docile (1.4.1)
|
docile (1.4.1)
|
||||||
dotenv (3.2.0)
|
dotenv (3.2.0)
|
||||||
drb (2.2.3)
|
drb (2.2.3)
|
||||||
|
dry-configurable (1.3.0)
|
||||||
|
dry-core (~> 1.1)
|
||||||
|
zeitwerk (~> 2.6)
|
||||||
|
dry-core (1.2.0)
|
||||||
|
concurrent-ruby (~> 1.0)
|
||||||
|
logger
|
||||||
|
zeitwerk (~> 2.6)
|
||||||
|
dry-inflector (1.3.1)
|
||||||
|
dry-logic (1.6.0)
|
||||||
|
bigdecimal
|
||||||
|
concurrent-ruby (~> 1.0)
|
||||||
|
dry-core (~> 1.1)
|
||||||
|
zeitwerk (~> 2.6)
|
||||||
|
dry-types (1.9.0)
|
||||||
|
bigdecimal (>= 3.0)
|
||||||
|
concurrent-ruby (~> 1.0)
|
||||||
|
dry-core (~> 1.0)
|
||||||
|
dry-inflector (~> 1.0)
|
||||||
|
dry-logic (~> 1.4)
|
||||||
|
zeitwerk (~> 2.6)
|
||||||
ed25519 (1.4.0)
|
ed25519 (1.4.0)
|
||||||
erb (6.0.1)
|
erb (6.0.1)
|
||||||
erubi (1.13.1)
|
erubi (1.13.1)
|
||||||
@@ -131,6 +151,15 @@ GEM
|
|||||||
raabro (~> 1.4)
|
raabro (~> 1.4)
|
||||||
globalid (1.3.0)
|
globalid (1.3.0)
|
||||||
activesupport (>= 6.1)
|
activesupport (>= 6.1)
|
||||||
|
grape (3.1.1)
|
||||||
|
activesupport (>= 7.1)
|
||||||
|
dry-configurable
|
||||||
|
dry-types (>= 1.1)
|
||||||
|
mustermann-grape (~> 1.1.0)
|
||||||
|
rack (>= 2)
|
||||||
|
zeitwerk
|
||||||
|
grape-swagger (2.1.3)
|
||||||
|
grape (>= 1.7, < 4.0)
|
||||||
htmlentities (4.4.2)
|
htmlentities (4.4.2)
|
||||||
i18n (1.14.8)
|
i18n (1.14.8)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
@@ -181,6 +210,10 @@ GEM
|
|||||||
minitest (6.0.1)
|
minitest (6.0.1)
|
||||||
prism (~> 1.5)
|
prism (~> 1.5)
|
||||||
msgpack (1.8.0)
|
msgpack (1.8.0)
|
||||||
|
mustermann (3.0.4)
|
||||||
|
ruby2_keywords (~> 0.0.1)
|
||||||
|
mustermann-grape (1.1.0)
|
||||||
|
mustermann (>= 1.0.0)
|
||||||
net-imap (0.6.2)
|
net-imap (0.6.2)
|
||||||
date
|
date
|
||||||
net-protocol
|
net-protocol
|
||||||
@@ -273,6 +306,9 @@ GEM
|
|||||||
logger (~> 1)
|
logger (~> 1)
|
||||||
nokogiri (~> 1)
|
nokogiri (~> 1)
|
||||||
rubyzip (>= 3.0.0, < 4.0.0)
|
rubyzip (>= 3.0.0, < 4.0.0)
|
||||||
|
rswag-ui (2.17.0)
|
||||||
|
actionpack (>= 5.2, < 8.2)
|
||||||
|
railties (>= 5.2, < 8.2)
|
||||||
rubocop (1.84.0)
|
rubocop (1.84.0)
|
||||||
json (~> 2.3)
|
json (~> 2.3)
|
||||||
language_server-protocol (~> 3.17.0.2)
|
language_server-protocol (~> 3.17.0.2)
|
||||||
@@ -305,6 +341,7 @@ GEM
|
|||||||
ruby-vips (2.3.0)
|
ruby-vips (2.3.0)
|
||||||
ffi (~> 1.12)
|
ffi (~> 1.12)
|
||||||
logger
|
logger
|
||||||
|
ruby2_keywords (0.0.5)
|
||||||
rubyzip (3.2.2)
|
rubyzip (3.2.2)
|
||||||
securerandom (0.4.1)
|
securerandom (0.4.1)
|
||||||
selenium-webdriver (4.40.0)
|
selenium-webdriver (4.40.0)
|
||||||
@@ -387,6 +424,8 @@ DEPENDENCIES
|
|||||||
caxlsx
|
caxlsx
|
||||||
caxlsx_rails
|
caxlsx_rails
|
||||||
debug
|
debug
|
||||||
|
grape
|
||||||
|
grape-swagger
|
||||||
image_processing (~> 1.2)
|
image_processing (~> 1.2)
|
||||||
importmap-rails
|
importmap-rails
|
||||||
jbuilder
|
jbuilder
|
||||||
@@ -395,6 +434,7 @@ DEPENDENCIES
|
|||||||
puma (>= 5.0)
|
puma (>= 5.0)
|
||||||
rails (~> 8.1.2)
|
rails (~> 8.1.2)
|
||||||
roo (~> 3.0)
|
roo (~> 3.0)
|
||||||
|
rswag-ui
|
||||||
rubocop-rails-omakase
|
rubocop-rails-omakase
|
||||||
selenium-webdriver
|
selenium-webdriver
|
||||||
simplecov
|
simplecov
|
||||||
@@ -445,6 +485,11 @@ CHECKSUMS
|
|||||||
docile (1.4.1) sha256=96159be799bfa73cdb721b840e9802126e4e03dfc26863db73647204c727f21e
|
docile (1.4.1) sha256=96159be799bfa73cdb721b840e9802126e4e03dfc26863db73647204c727f21e
|
||||||
dotenv (3.2.0) sha256=e375b83121ea7ca4ce20f214740076129ab8514cd81378161f11c03853fe619d
|
dotenv (3.2.0) sha256=e375b83121ea7ca4ce20f214740076129ab8514cd81378161f11c03853fe619d
|
||||||
drb (2.2.3) sha256=0b00d6fdb50995fe4a45dea13663493c841112e4068656854646f418fda13373
|
drb (2.2.3) sha256=0b00d6fdb50995fe4a45dea13663493c841112e4068656854646f418fda13373
|
||||||
|
dry-configurable (1.3.0) sha256=882d862858567fc1210d2549d4c090f34370fc1bb7c5c1933de3fe792e18afa8
|
||||||
|
dry-core (1.2.0) sha256=0cc5a7da88df397f153947eeeae42e876e999c1e30900f3c536fb173854e96a1
|
||||||
|
dry-inflector (1.3.1) sha256=7fb0c2bb04f67638f25c52e7ba39ab435d922a3a5c3cd196120f63accb682dcc
|
||||||
|
dry-logic (1.6.0) sha256=da6fedbc0f90fc41f9b0cc7e6f05f5d529d1efaef6c8dcc8e0733f685745cea2
|
||||||
|
dry-types (1.9.0) sha256=7b656fe0a78d2432500ae1f29fefd6762f5a032ca7000e4f36bc111453d45d4d
|
||||||
ed25519 (1.4.0) sha256=16e97f5198689a154247169f3453ef4cfd3f7a47481fde0ae33206cdfdcac506
|
ed25519 (1.4.0) sha256=16e97f5198689a154247169f3453ef4cfd3f7a47481fde0ae33206cdfdcac506
|
||||||
erb (6.0.1) sha256=28ecdd99c5472aebd5674d6061e3c6b0a45c049578b071e5a52c2a7f13c197e5
|
erb (6.0.1) sha256=28ecdd99c5472aebd5674d6061e3c6b0a45c049578b071e5a52c2a7f13c197e5
|
||||||
erubi (1.13.1) sha256=a082103b0885dbc5ecf1172fede897f9ebdb745a4b97a5e8dc63953db1ee4ad9
|
erubi (1.13.1) sha256=a082103b0885dbc5ecf1172fede897f9ebdb745a4b97a5e8dc63953db1ee4ad9
|
||||||
@@ -452,6 +497,8 @@ CHECKSUMS
|
|||||||
ffi (1.17.3-x86_64-linux-gnu) sha256=3746b01f677aae7b16dc1acb7cb3cc17b3e35bdae7676a3f568153fb0e2c887f
|
ffi (1.17.3-x86_64-linux-gnu) sha256=3746b01f677aae7b16dc1acb7cb3cc17b3e35bdae7676a3f568153fb0e2c887f
|
||||||
fugit (1.12.1) sha256=5898f478ede9b415f0804e42b8f3fd53f814bd85eebffceebdbc34e1107aaf68
|
fugit (1.12.1) sha256=5898f478ede9b415f0804e42b8f3fd53f814bd85eebffceebdbc34e1107aaf68
|
||||||
globalid (1.3.0) sha256=05c639ad6eb4594522a0b07983022f04aa7254626ab69445a0e493aa3786ff11
|
globalid (1.3.0) sha256=05c639ad6eb4594522a0b07983022f04aa7254626ab69445a0e493aa3786ff11
|
||||||
|
grape (3.1.1) sha256=774f16782d917a90e69de0499dfaab571e5ad967569ac066a2b0b918af12de69
|
||||||
|
grape-swagger (2.1.3) sha256=9ee955ada77c10ea2f2d2da5edcc4fc3cddcc40a6936f1b2558ee7b44e0f1153
|
||||||
htmlentities (4.4.2) sha256=bbafbdf69f2eca9262be4efef7e43e6a1de54c95eb600f26984f71d2fe96c5c3
|
htmlentities (4.4.2) sha256=bbafbdf69f2eca9262be4efef7e43e6a1de54c95eb600f26984f71d2fe96c5c3
|
||||||
i18n (1.14.8) sha256=285778639134865c5e0f6269e0b818256017e8cde89993fdfcbfb64d088824a5
|
i18n (1.14.8) sha256=285778639134865c5e0f6269e0b818256017e8cde89993fdfcbfb64d088824a5
|
||||||
image_processing (1.14.0) sha256=754cc169c9c262980889bec6bfd325ed1dafad34f85242b5a07b60af004742fb
|
image_processing (1.14.0) sha256=754cc169c9c262980889bec6bfd325ed1dafad34f85242b5a07b60af004742fb
|
||||||
@@ -472,6 +519,8 @@ CHECKSUMS
|
|||||||
mini_mime (1.1.5) sha256=8681b7e2e4215f2a159f9400b5816d85e9d8c6c6b491e96a12797e798f8bccef
|
mini_mime (1.1.5) sha256=8681b7e2e4215f2a159f9400b5816d85e9d8c6c6b491e96a12797e798f8bccef
|
||||||
minitest (6.0.1) sha256=7854c74f48e2e975969062833adc4013f249a4b212f5e7b9d5c040bf838d54bb
|
minitest (6.0.1) sha256=7854c74f48e2e975969062833adc4013f249a4b212f5e7b9d5c040bf838d54bb
|
||||||
msgpack (1.8.0) sha256=e64ce0212000d016809f5048b48eb3a65ffb169db22238fb4b72472fecb2d732
|
msgpack (1.8.0) sha256=e64ce0212000d016809f5048b48eb3a65ffb169db22238fb4b72472fecb2d732
|
||||||
|
mustermann (3.0.4) sha256=85fadcb6b3c6493a8b511b42426f904b7f27b282835502233dd154daab13aa22
|
||||||
|
mustermann-grape (1.1.0) sha256=8d258a986004c8f01ce4c023c0b037c168a9ed889cf5778068ad54398fa458c5
|
||||||
net-imap (0.6.2) sha256=08caacad486853c61676cca0c0c47df93db02abc4a8239a8b67eb0981428acc6
|
net-imap (0.6.2) sha256=08caacad486853c61676cca0c0c47df93db02abc4a8239a8b67eb0981428acc6
|
||||||
net-pop (0.1.2) sha256=848b4e982013c15b2f0382792268763b748cce91c9e91e36b0f27ed26420dff3
|
net-pop (0.1.2) sha256=848b4e982013c15b2f0382792268763b748cce91c9e91e36b0f27ed26420dff3
|
||||||
net-protocol (0.2.2) sha256=aa73e0cba6a125369de9837b8d8ef82a61849360eba0521900e2c3713aa162a8
|
net-protocol (0.2.2) sha256=aa73e0cba6a125369de9837b8d8ef82a61849360eba0521900e2c3713aa162a8
|
||||||
@@ -508,6 +557,7 @@ CHECKSUMS
|
|||||||
reline (0.6.3) sha256=1198b04973565b36ec0f11542ab3f5cfeeec34823f4e54cebde90968092b1835
|
reline (0.6.3) sha256=1198b04973565b36ec0f11542ab3f5cfeeec34823f4e54cebde90968092b1835
|
||||||
rexml (3.4.4) sha256=19e0a2c3425dfbf2d4fc1189747bdb2f849b6c5e74180401b15734bc97b5d142
|
rexml (3.4.4) sha256=19e0a2c3425dfbf2d4fc1189747bdb2f849b6c5e74180401b15734bc97b5d142
|
||||||
roo (3.0.0) sha256=6fdd7a9158d657c69768b4168754ff2110cc21fdc01a1bec1010820cb05c91b1
|
roo (3.0.0) sha256=6fdd7a9158d657c69768b4168754ff2110cc21fdc01a1bec1010820cb05c91b1
|
||||||
|
rswag-ui (2.17.0) sha256=5f707b9b5e8171ddf9f519f6e401e79e419bd1d07387508603e76124f2443212
|
||||||
rubocop (1.84.0) sha256=88dec310153bb685a879f5a7cdb601f6287b8f0ee675d9dc63a17c7204c4190a
|
rubocop (1.84.0) sha256=88dec310153bb685a879f5a7cdb601f6287b8f0ee675d9dc63a17c7204c4190a
|
||||||
rubocop-ast (1.49.0) sha256=49c3676d3123a0923d333e20c6c2dbaaae2d2287b475273fddee0c61da9f71fd
|
rubocop-ast (1.49.0) sha256=49c3676d3123a0923d333e20c6c2dbaaae2d2287b475273fddee0c61da9f71fd
|
||||||
rubocop-performance (1.26.1) sha256=cd19b936ff196df85829d264b522fd4f98b6c89ad271fa52744a8c11b8f71834
|
rubocop-performance (1.26.1) sha256=cd19b936ff196df85829d264b522fd4f98b6c89ad271fa52744a8c11b8f71834
|
||||||
@@ -515,6 +565,7 @@ CHECKSUMS
|
|||||||
rubocop-rails-omakase (1.1.0) sha256=2af73ac8ee5852de2919abbd2618af9c15c19b512c4cfc1f9a5d3b6ef009109d
|
rubocop-rails-omakase (1.1.0) sha256=2af73ac8ee5852de2919abbd2618af9c15c19b512c4cfc1f9a5d3b6ef009109d
|
||||||
ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33
|
ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33
|
||||||
ruby-vips (2.3.0) sha256=e685ec02c13969912debbd98019e50492e12989282da5f37d05f5471442f5374
|
ruby-vips (2.3.0) sha256=e685ec02c13969912debbd98019e50492e12989282da5f37d05f5471442f5374
|
||||||
|
ruby2_keywords (0.0.5) sha256=ffd13740c573b7301cf7a2e61fc857b2a8e3d3aff32545d6f8300d8bae10e3ef
|
||||||
rubyzip (3.2.2) sha256=c0ed99385f0625415c8f05bcae33fe649ed2952894a95ff8b08f26ca57ea5b3c
|
rubyzip (3.2.2) sha256=c0ed99385f0625415c8f05bcae33fe649ed2952894a95ff8b08f26ca57ea5b3c
|
||||||
securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1
|
securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1
|
||||||
selenium-webdriver (4.40.0) sha256=16ef7aa9853c1d4b9d52eac45aafa916e3934c5c83cb4facb03f250adfd15e5b
|
selenium-webdriver (4.40.0) sha256=16ef7aa9853c1d4b9d52eac45aafa916e3934c5c83cb4facb03f250adfd15e5b
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
"Sanasto Wiki" is a web-based dictionary application for simultaneous translators in the living Christianity. The application provides publicly accessible translations while restricting editing and commenting to invited contributors.
|
"Sanasto Wiki" is a web-based glossary for simultaneous translators in the living Christianity. The application provides publicly accessible translations while restricting editing and commenting to invited contributors.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -188,15 +188,25 @@ See 'public/Kristillisyyden sanasto ver 23.5.2013.xlsx'
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## API (Optional Future)
|
## API (Public Sync)
|
||||||
|
|
||||||
REST API for potential mobile app or integration:
|
Public JSON endpoint for syncing entries:
|
||||||
```
|
```
|
||||||
GET /api/entries
|
GET /api/entries
|
||||||
GET /api/entries/:id
|
GET /api/entries?since=2026-01-01T12:00:00Z
|
||||||
GET /api/entries/search?q=:query&lang=:code
|
```
|
||||||
POST /api/entries (authenticated)
|
|
||||||
PATCH /api/entries/:id (authenticated)
|
Responses include all language columns, category and `updated_at`. The optional `since`
|
||||||
|
parameter filters by `updated_at` (ISO8601).
|
||||||
|
|
||||||
|
Swagger docs:
|
||||||
|
```
|
||||||
|
GET /api/swagger
|
||||||
|
```
|
||||||
|
|
||||||
|
Swagger UI:
|
||||||
|
```
|
||||||
|
GET /api
|
||||||
```
|
```
|
||||||
|
|
||||||
## Deployment
|
## Deployment
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
require "grape"
|
||||||
|
require "grape-swagger"
|
||||||
|
require "ostruct"
|
||||||
|
|
||||||
|
class Api::Base < Grape::API
|
||||||
|
format :json
|
||||||
|
content_type :json, "application/json"
|
||||||
|
|
||||||
|
helpers do
|
||||||
|
def parse_since_param(raw_since)
|
||||||
|
return nil if raw_since.blank?
|
||||||
|
|
||||||
|
Time.iso8601(raw_since)
|
||||||
|
rescue ArgumentError
|
||||||
|
error!({ error: "Invalid since parameter. Use ISO8601 timestamp." }, 400)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
resource :entries do
|
||||||
|
desc "Return public entries in all languages",
|
||||||
|
attributes: OpenStruct.new(success: nil, produces: nil)
|
||||||
|
params do
|
||||||
|
optional :since,
|
||||||
|
type: String,
|
||||||
|
desc: "ISO8601 timestamp. Returns entries updated after this time."
|
||||||
|
end
|
||||||
|
get do
|
||||||
|
since_time = parse_since_param(params[:since])
|
||||||
|
|
||||||
|
entries_scope = Entry.active_entries
|
||||||
|
entries_scope = entries_scope.where("updated_at > ?", since_time) if since_time
|
||||||
|
|
||||||
|
entries_scope
|
||||||
|
.order(:updated_at, :id)
|
||||||
|
.select(
|
||||||
|
:id,
|
||||||
|
:category,
|
||||||
|
:fi,
|
||||||
|
:en,
|
||||||
|
:sv,
|
||||||
|
:no,
|
||||||
|
:ru,
|
||||||
|
:de,
|
||||||
|
:updated_at
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
add_swagger_documentation(
|
||||||
|
info: {
|
||||||
|
title: "Sanasto Wiki API",
|
||||||
|
description: "Public sync API for Sanasto Wiki glossary entries."
|
||||||
|
},
|
||||||
|
mount_path: "/swagger",
|
||||||
|
hide_documentation_path: true,
|
||||||
|
format: :json
|
||||||
|
)
|
||||||
|
end
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
Rswag::Ui.configure do |config|
|
||||||
|
config.swagger_endpoint "/api/swagger", "Sanasto Wiki API"
|
||||||
|
end
|
||||||
@@ -51,6 +51,9 @@ Rails.application.routes.draw do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
mount Api::Base => "/api"
|
||||||
|
mount Rswag::Ui::Engine => "/api"
|
||||||
|
|
||||||
resources :entries do
|
resources :entries do
|
||||||
resources :comments, only: [ :create ]
|
resources :comments, only: [ :create ]
|
||||||
collection do
|
collection do
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
class ApiEntriesTest < ActionDispatch::IntegrationTest
|
||||||
|
test "returns active entries with language fields" do
|
||||||
|
entry = entries(:one)
|
||||||
|
|
||||||
|
get "/api/entries"
|
||||||
|
|
||||||
|
assert_response :success
|
||||||
|
payload = JSON.parse(response.body)
|
||||||
|
entry_payload = payload.find { |item| item["id"] == entry.id }
|
||||||
|
|
||||||
|
assert_not_nil entry_payload
|
||||||
|
assert_equal entry.fi, entry_payload["fi"]
|
||||||
|
assert_equal entry.en, entry_payload["en"]
|
||||||
|
assert_equal entry.sv, entry_payload["sv"]
|
||||||
|
assert_equal entry.no, entry_payload["no"]
|
||||||
|
assert_equal entry.ru, entry_payload["ru"]
|
||||||
|
assert_equal entry.de, entry_payload["de"]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "filters by updated_at when since param is provided" do
|
||||||
|
older_entry = Entry.create!(
|
||||||
|
fi: "Older Entry",
|
||||||
|
category: :word,
|
||||||
|
status: :active
|
||||||
|
)
|
||||||
|
older_entry.update_column(:updated_at, 2.days.ago)
|
||||||
|
|
||||||
|
newer_entry = Entry.create!(
|
||||||
|
fi: "Newer Entry",
|
||||||
|
category: :word,
|
||||||
|
status: :active
|
||||||
|
)
|
||||||
|
newer_entry.update_column(:updated_at, 1.hour.ago)
|
||||||
|
|
||||||
|
get "/api/entries", params: { since: 1.day.ago.iso8601 }
|
||||||
|
|
||||||
|
assert_response :success
|
||||||
|
payload = JSON.parse(response.body)
|
||||||
|
returned_ids = payload.map { |item| item["id"] }
|
||||||
|
|
||||||
|
assert_includes returned_ids, newer_entry.id
|
||||||
|
assert_not_includes returned_ids, older_entry.id
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns bad request for invalid since param" do
|
||||||
|
get "/api/entries", params: { since: "not-a-time" }
|
||||||
|
|
||||||
|
assert_response :bad_request
|
||||||
|
payload = JSON.parse(response.body)
|
||||||
|
assert_equal "Invalid since parameter. Use ISO8601 timestamp.", payload["error"]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "exposes swagger docs" do
|
||||||
|
get "/api/swagger"
|
||||||
|
|
||||||
|
assert_response :success
|
||||||
|
payload = JSON.parse(response.body)
|
||||||
|
assert_equal "Sanasto Wiki API", payload.dig("info", "title")
|
||||||
|
assert_includes payload.keys, "paths"
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user