ad-on-is / adonis-autoswagger Goto Github PK
View Code? Open in Web Editor NEWAuto-Generate swagger docs for AdonisJS
License: MIT License
Auto-Generate swagger docs for AdonisJS
License: MIT License
I'm setting up a new adonis project and after installing lucid and auth I have installed autoswagger. When I try to expand the schema for the users model I receive 4 errors
Resolver error at components.schemas.User.properties.id.$ref
Could not resolve reference: Could not resolve pointer: /components/schemas/number; does not exist in document
Resolver error at components.schemas.User.properties.remember_me_token.$ref
Could not resolve reference: Could not resolve pointer: /components/schemas/string; does not exist in document
Resolver error at components.schemas.User.properties.created_at.$ref
Could not resolve reference: Could not resolve pointer: /components/schemas/DateTime; does not exist in document
Resolver error at components.schemas.User.properties.updated_at.$ref
Could not resolve reference: Could not resolve pointer: /components/schemas/DateTime; does not exist in document
My model looks like this
import { DateTime } from 'luxon';
import Hash from '@ioc:Adonis/Core/Hash';
import { column, beforeSave, BaseModel } from '@ioc:Adonis/Lucid/Orm';
export default class User extends BaseModel {
@column({ isPrimary: true })
// @example()
public id: number;
@column()
// @example([email protected])
public email: string;
@column({ serializeAs: null })
// @example(hunter2)
public password: string;
@column()
// @example(Some token)
public rememberMeToken?: string;
@column.dateTime({ autoCreate: true })
// @example(2022-04-04T13:00:00.000Z)
public createdAt: DateTime;
@column.dateTime({ autoCreate: true, autoUpdate: true })
// @example(2022-04-04T13:00:00.000Z)
public updatedAt: DateTime;
@beforeSave()
public static async hashPassword(user: User) {
if (user.$dirty.password) {
user.password = await Hash.make(user.password);
}
}
}
Would be good to be able to describe a file upload in some way mapping onto https://swagger.io/docs/specification/2-0/file-upload/
Hey there,
When i try to add documentation from controllers, it works as expected, until the controller has any decorators
This works
export default class AuthController {
constructor(
private readonly authService: BasicAuthService,
private eadonly twoFactorAuthService: TwoFactorAuthService
) {}
/**
* @register
* @responseBody 202 - <User>
*/
public async register({ request, response }: HttpContextContract) {
const registrationData = await request.validate(RegisterValidator);
const user = await this.authService.register(registrationData);
Event.emit('registered', {
version: 'v1',
method: 'internal',
user,
});
//Event.fire(new Registered({}))
const profileUrl = ''; //Route.makeUrl("v1.users.show", [user.username]);
response.header('Location', profileUrl).created({
message: 'Verification email sent!',
data: {
token: await user.createToken(),
user
}
});
}
But this not
@inject()
export default class AuthController {
constructor(
private readonly authService: BasicAuthService,
private eadonly twoFactorAuthService: TwoFactorAuthService
) {}
/**
* @register
* @responseBody 202 - <User>
*/
public async register({ request, response }: HttpContextContract) {
const registrationData = await request.validate(RegisterValidator);
const user = await this.authService.register(registrationData);
Event.emit('registered', {
version: 'v1',
method: 'internal',
user,
});
//Event.fire(new Registered({}))
const profileUrl = ''; //Route.makeUrl("v1.users.show", [user.username]);
response.header('Location', profileUrl).created({
message: 'Verification email sent!',
data: {
token: await user.createToken(),
user
}
});
}
How to solve the issue??
Hello, I'd like to use comment to describe my swagger. I get stucked on @param markup that lacks an example.
In my case, the resulting swagger.yml all @param Query as required and of type string for example. I'd like to alter this so that "* required" annotation disappears and type be integer on some params.
In windows, __dirname return C:\\Projects\\adonis-rest\\start
. So path.replace
not work here
this.options.path = this.options.path.replace("/start", "") + "/app";
Error: ENOENT: no such file or directory, scandir 'C:\Projects\adonis-rest\start/app/Models'
It should be including instead inlcuding
adonis-autoswagger/src/autoswagger.ts
Line 554 in 2487769
Thanks for your work. I'm using here and it's waaaay more productive than using Swagger from scratch!
I don't have this folder here and autoswagger return the following error:
err: {
"type": "Error",
"message": "ENOENT: no such file or directory, scandir '/app/app/Interfaces'",
"stack":
Error: ENOENT: no such file or directory, scandir '/app/app/Interfaces'
at Object.readdirSync (node:fs:1390:3)
at AutoSwagger.<anonymous> (/app/node_modules/adonis-autoswagger/dist/autoswagger.js:1158:34)
at Generator.next (<anonymous>)
at /app/node_modules/adonis-autoswagger/dist/autoswagger.js:8:71
at new Promise (<anonymous>)
at __awaiter (/app/node_modules/adonis-autoswagger/dist/autoswagger.js:4:12)
at AutoSwagger.getFiles (/app/node_modules/adonis-autoswagger/dist/autoswagger.js:1155:16)
at AutoSwagger.<anonymous> (/app/node_modules/adonis-autoswagger/dist/autoswagger.js:883:38)
at Generator.next (<anonymous>)
at /app/node_modules/adonis-autoswagger/dist/autoswagger.js:8:71
"errno": -2,
"syscall": "scandir",
"code": "ENOENT",
"path": "/app/app/Interfaces",
"status": 500
}```
I had to temporary create this folder to keep using it.
I have the following routes
router
.group(() => {
router.get('/', [CompetitionsController, 'index'])
router.get('/:id/active-round', [CompetitionsController, 'getActiveRound'])
router.get('/:id/round/:roundId', [CompetitionsController, 'getRound'])
})
.prefix('competition')
the route params are taken correctly, but the @ notations are ignored: summary, responseBody - prolly the other ones are ignored also
Hello mate !
This morning trying to use your lib.
When i put on my routes.ts AutoSwagger.docs & AutoSwagger.ui. I have AutoSwagger.docs is not a function & AutoSwagger.swagger is not a function
Reproduce steps :
model.$columnsDefinitions
does not provide property types, except for datetime properties.
Enum management in models generates an error.
Here's my model:
export default class Model extends BaseModel {
static selfAssignPrimaryKey = true
@column({ isPrimary: true })
// @example('XXXX')
declare label: string
@column()
// @example('XXXX')
declare otherLabel: string
@column()
// @example("Lorem ipsum dolor sit amet")
declare desc: string | null
@column()
// @enum('XX', 'YYY', 'ZZZZZ', 'AAAA', 'BBBB', 'CCCC', 'DDDD'
// @example('XX')
declare dirType: EDirType
}
Here's the enum:
export enum EDirType {
'XX' = 'XX',
'YYY' = 'YYY',
'ZZZZZ' = 'ZZZZZ',
'AAAA' = 'AAAA',
'BBBB' = 'BBBB',
'CCCC' = 'CCCC',
'DDDD' = 'DDDD',
}
Here's the generated diagram:
Model:
type: "object"
properties:
label:
type: "string"
example: "XXX"
otherLabel:
type: "string"
example: "XXXX'"
desc:
type: "string"
example: "\"Lorem ipsum dolor sit amet\""
dir_type:
$ref: "#/components/schemas/EDirType"
example: "'XX'"
description: "Model"
...
e_dir_type:
type: "object"
properties: {}
description: "Model"
My swagger.ts configuration:
import path from 'node:path'
import url from 'node:url'
export default {
path: path.dirname(url.fileURLToPath(import.meta.url)) + '/../',
title: 'api',
version: '0.0.0',
tagIndex: 2,
snakeCase: true,
ignore: ['/api/swagger', '/api/docs', '/api/v1/directions', '/api/v1/directions/:id'],
preferredPutPatch: 'PUT',
common: {
parameters: {},
headers: {},
},
persistAuthorization: true,
showFullPath: false,
}
Open API doesn't understand: "#/components/schemas/EDirType" it returns unresolved reference
.
How can I do this without replacing the enum with a string?
I'm getting an error because I'm using Japa's functionality:
export const plugins: Config['plugins'] = [
assert({
openApi: {
schemas: [app.makePath('swagger.yml')],
},
}),
Sincerely sorry for the offuscation of the code, hope this issue remains relevant
The current implementation of handling arrays of standard datatypes in the codebase is not working as expected. When attempting to use arrays of standard datatypes, the application encounters errors or produces unexpected results.
type: 'array',
items: { '$ref': '#/components/schemas/string[]', example: 'string' }
it should be
type: 'array'
items:
type: 'string'
I think, it needs to alter directory name from Models to models, and Interfaces to interfaces and change or add more types
Would it be possible to ignore several routes with just one *?
Like this:
export default {
path: path.dirname(url.fileURLToPath(import.meta.url)) + '/../',
title: 'XXX',
version: '0.0.0',
tagIndex: 2,
snakeCase: true,
ignore: ['/api/swagger', '/api/docs', '/api/v1/*'],
preferredPutPatch: 'PUT',
common: {
parameters: {},
headers: {},
},
persistAuthorization: true,
showFullPath: false,
}
Instead of:
export default {
path: path.dirname(url.fileURLToPath(import.meta.url)) + '/../',
title: 'XXX',
version: '0.0.0',
tagIndex: 2,
snakeCase: true,
ignore: ['/api/swagger', '/api/docs', '/api/v1/directions', '/api/v1/directions/:id'],
preferredPutPatch: 'PUT',
common: {
parameters: {},
headers: {},
},
persistAuthorization: true,
showFullPath: false,
}
?
Ex:
Produces:
{
"ids": {
"0": 2,
"1": 3,
"2": 7
},
"description": "xulambs"
}
To solve I did, in autoswagger.js:
jsonToRef(json) {
let out = {};
for (let [k, v] of Object.entries(json)) {
if (typeof v === "object") {
// v = this.jsonToRef(v);
if (!Array.isArray(v) || !v.reduce((p, c) => p && c !== Object(c), true)) v = this.jsonToRef(v);
Using the example of Controller in official documentation of AdonisJS, i tried to generate the swagger docs.
The controller has one method:
app/Controllers/Http/PostsController.ts
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
export default class PostsController {
public async index(_ctx: HttpContextContract) {
return [
{
id: 1,
title: 'Hello world',
},
{
id: 2,
title: 'Hello universe',
},
]
}
}
And the routes file has this content
import Route from '@ioc:Adonis/Core/Route'
import AutoSwagger from 'adonis-autoswagger'
// returns swagger in YAML
Route.get('/swagger', async () => {
return AutoSwagger.docs(Route.toJSON(), {
path: __dirname,
title: 'Foo',
version: '1.0.0',
tagIndex: 2,
ignore: ['/swagger', '/docs'],
common: {
parameters: {},
headers: {}, // OpenAPI confomr headers that are commonly used
},
snakeCase: true,
})
})
// Renders Swagger-UI and passes YAML-output of /swagger
Route.get('/docs', async () => {
return AutoSwagger.ui('/swagger')
})
Route.get('/', async () => {
return { hello: 'world' }
})
// Route.get('/hc', 'PostsController.index')
// Route.get('/post', 'PostsController.index')
// Route.get('posts', 'PostsController.index')
Route.resource('posts', 'PostsController')
package.json
{
"name": "hello-world",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "node ace serve --watch",
"build": "node ace build --production",
"start": "node server.js",
"lint": "eslint . --ext=.ts",
"format": "prettier --write ."
},
"devDependencies": {
"@adonisjs/assembler": "^5.4.2",
"adonis-preset-ts": "^2.1.0",
"eslint": "^8.11.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-adonis": "^2.1.0",
"eslint-plugin-prettier": "^4.0.0",
"pino-pretty": "^7.5.4",
"prettier": "^2.6.0",
"typescript": "~4.5",
"youch": "^3.1.1",
"youch-terminal": "^2.1.3"
},
"dependencies": {
"@adonisjs/core": "^5.5.3",
"@adonisjs/repl": "^3.1.9",
"adonis-autoswagger": "^1.7.5",
"proxy-addr": "^2.0.7",
"reflect-metadata": "^0.1.13",
"source-map-support": "^0.5.21"
}
}
Running the project, the endpoints are OK, returning expected responses from requests.
But when I request the swagger file, using the route /docs or /swagger, I get an error in log and a 500 response:
guionardo @ guio-note ~/dev/lab/adonis/hello-world
└─ $ ▶ node ace serve --watch
[ info ] building project...
[ info ] starting http server...
[1647946652482] INFO (hello-world/602887 on guio-note): started server on 0.0.0.0:3333
[ info ] watching file system for changes
╭─────────────────────────────────────────────────╮
│ │
│ Server address: http://127.0.0.1:3333 │
│ Watching filesystem for changes: YES │
│ │
╰─────────────────────────────────────────────────╯
[1647946664164] ERROR (hello-world/602887 on guio-note): Cannot read property 'toLowerCase' of undefined
err: {
"type": "TypeError",
"message": "Cannot read property 'toLowerCase' of undefined",
"stack":
TypeError: Cannot read property 'toLowerCase' of undefined
at /home/guionardo/dev/lab/adonis/hello-world/node_modules/adonis-autoswagger/dist/autoswagger.js:285:74
at Array.forEach (<anonymous>)
at AutoSwagger.<anonymous> (/home/guionardo/dev/lab/adonis/hello-world/node_modules/adonis-autoswagger/dist/autoswagger.js:228:35)
at Generator.next (<anonymous>)
at fulfilled (/home/guionardo/dev/lab/adonis/hello-world/node_modules/adonis-autoswagger/dist/autoswagger.js:5:58)
"status": 500
}
First, thx for this package
import { BaseCommand } from '@adonisjs/core/build/standalone'
import AutoSwagger from 'adonis-autoswagger'
import swagger from '../config/swagger'
export default class DocsGenerate extends BaseCommand {
public static commandName = 'docs:generate'
public static description = ""
public static settings = {
loadApp: true,
stayAlive: false,
}
public async run() {
const Router = await this.application.container.use('Adonis/Core/Route')
Router.commit()
await AutoSwagger.writeFile(await Router.toJSON(), swagger)
}
}
I tried to refactor this example to take the Route and transform it into toJson, but apparently this.application.container.use is deprecated, so I tried changing it to this.app but it doesn't have the .use, does anyone know what the call is like now in the new version ?
Hi.
is can hide/ignore some routes via
ignore: [
"/swagger/json",
"/swagger/docs",
"/uploads/*",
"/admin/*",
it possibile to hide also Models? I did not want to list models for an ignored path like '/admin/'
Bye, René
Patch for bug
tags[0] = tags[0] ?? '';
err: {
"type": "TypeError",
"message": "Cannot read properties of undefined (reading 'toLowerCase')",
"stack":
TypeError: Cannot read properties of undefined (reading 'toLowerCase')
at E:\app-cuoma\AdonisJS\adonis-swagger\node_modules\adonis-autoswagger\dist\autoswagger.js: 287: 74
at Array.forEach (<anonymous>)
at AutoSwagger.<anonymous> (E:\app-cuoma\AdonisJS\adonis-swagger\node_modules\adonis-autoswagger\dist\autoswagger.js: 230: 35)
at Generator.next (<anonymous>)
at fulfilled (E:\app-cuoma\AdonisJS\adonis-swagger\node_modules\adonis-autoswagger\dist\autoswagger.js: 5: 58)
"status": 500
}
I tried manually adding 'security: BearerAuth', but it didn't work, although I tried different spellings...
middleware connection:
...
Route
.group(() => {
Route.delete('/delete/:id', 'ApartmentsController.delete')
Route.post('/create', 'ApartmentsController.create')
Route.post('/update', 'ApartmentsController.update')
})
.prefix('apartments')
})
.prefix('/api/v1').middleware('apiAuth')
adonis-autoswagger:
...
Route.get('/docs', async () => {
return AutoSwagger.ui('/swagger')
})
})
.prefix('/api/v1')Route.get('/swagger', async () => {
return await AutoSwagger.docs(Route.toJSON(), {
path: __dirname,
title: 'Rento',
version: '1.0.0',
tagIndex: 3,
ignore: ['/swagger', '/docs', '/', '/health', '/uploads/*'],
common: {
parameters: {},
headers: {},
},
snakeCase: false
})
})
I just installed and copied the code to routes file. I get this error
TS7016: Could not find a declaration file for module adonis-autoswagger.
/Users/ionutcapraru/work/github/ep2-api/node_modules/adonis-autoswagger/dist/index.jsimplicitly has an any type.
Try npm i --save-dev @types/adonis-autoswagger if it exists or add a new declaration (.d.ts) file containing declare module 'adonis-autoswagger';
Here on line 247 (with the most recent commit at the time of writing being de43f67) I noticed that you're ignoring routes with the PATCH method. Could you explain why you chose to do this?
In my particular case I have the following which is not picked up by swagger-autogen
Route.patch('/:id', 'SomeController.update')
/**
* @update
* @paramPath id - SomeId
* @requestBody { "data": "some data" } // Expects model specification
*/
public async update({ request, response, params }: HttpContextContract) {
...some update code
}
If I add a line to the if statement at line 247 and log the route it does come out, so it seems to me that it being ignored was intentional. Is there a reason for this?
Hello,
I am currently working on a project using AdonisJS and I am using the adonis-autoswagger
package to generate Swagger documentation for my API. I have noticed that while adonis-autoswagger
provides a lot of flexibility and control over the generated documentation, it does not currently support generating Swagger request bodies directly from validator classes.
This limitation has led to some redundancy in our codebase, as we have to manually define the structure of the request body in both the validator class and the Swagger documentation. This not only increases the maintenance effort but also makes the code harder to understand and manage. This will keep our docs sync the code base
I believe that adding support for generating Swagger request bodies directly from validator classes would greatly enhance the usability and efficiency of adonis-autoswagger
. It would allow us to define the structure of the request body in one place (the validator class), and then automatically generate the corresponding Swagger documentation.
Here's a rough idea of how this could work:
class LoginValidator {
// generate the body using this property
public schema = schema.create({
email: schema.string(),
password: schema.string()
});
}
/**
* @api {post} /login login a new user
* @requestBody <LoginValidator> // it will be equal to { email: string, password: string }
*/
Route.post('/login', async ({ request }) => {
const user = await request.validate(LoginValidator)
// ... rest of my code
})
In this example, the LoginValidator
class defines the structure of the request body. We could then use this information to automatically generate the corresponding Swagger documentation for the /login
endpoint.
I would appreciate if you could consider this feature request and let me know if there are any plans to implement it in the future. Alternatively, I would be happy to contribute to the development of this feature if you need help.
Thank you for considering this request.
Best regards,
Hasan Ashab
Adonis models file names are now snake_case
for example: models/reward_type.ts
will result in yaml schema name of reward_type
The issue is that when this model is referenced inside another model, it's with the name of its class that is pascal case RewardType
rewardType:
$ref: "#/components/schemas/RewardType"
Hello.
I'm using this package and now I tried to deploy it to production.
I had missed that I had to do a li'l bit of extra stuff to get the docs to work properly in prod though.
So when I tried to follow the guide for production I see that the DocsGenerate.ts.example tries to import a ../config/swagger
What should that file contain? I can't see an example of that it in this repo.
Hi!
Thank you for your project!
But we have one notice, in module you did hardcode with snake_kase field in model and query params.
adonis-autoswagger/src/autoswagger.ts
Line 1118 in ba8b763
Thank you!
Is it possible to describe data types in schemas using keywords? After reading the guide I haven't been able to think of a way to do it.
I've also tried directly modifying the generated swagger.yml file and adding some keywords:
Brand:
type: "object"
properties:
id:
type: "number"
example: 164
name:
type: "string"
example: "John Doe"
minLength: 1
maxLength: 100
created_by:
type: "number"
example: 638
But even then they won't show up the in schema section on the docs:
Right now, it only works, when the folder is located in the root directory of an adonisJs project, but not when it's published to npm
import AutoSwagger from '../adonis-autoswagger' // (local) works
import AutoSwagger from 'adonis-autoswagger' // (npm) does not work
I would like to have the ability to specify a custom location for my controller files. Currently, adonis-autoswagger scans the app/Controllers/Http
directory by default, which may not always align with my preferred project structure.
that's also applicable to other components (interfaces, models...)
Proposed Solution:
To address this issue, I propose introducing a configuration option in adonis-autoswagger that allows users to specify a custom directory for their controllers. This option should be set in the adonis-autoswagger configuration file. Additionally, if no custom location is specified, adonis-autoswagger should fall back to checking the adonisrc.json
file for a controllers
property.
Example Configuration:
module.exports = {
// Other configurations...
controllersPath: 'path/to/my/controllers',
}
Or in adonisrc.json
:
With this configuration, adonis-autoswagger should scan the specified directory for controller files and generate Swagger documentation accordingly.
Benefits:
This feature would allow developers to organize their project structure according to their preferences, without having to modify the source code of adonis-autoswagger. It would also make it easier to integrate adonis-autoswagger into existing projects with non-standard controller locations.
Please let me know if you need any further information or clarification. I look forward to hearing from you soon.
Best regards,
Hasan Ashab
It would be very helpful to have a @usePagination annotation for responseBody that conforms to how lucid returns paginated results
{
"meta": {
"total": 50,
"perPage": 5,
"currentPage": 1,
"lastPage": 10,
"firstPage": 1,
"firstPageUrl": "/?page=1",
"lastPageUrl": "/?page=10",
"nextPageUrl": "/?page=2",
"previousPageUrl": null
},
"data": []
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.