At node.js, I just started working with swagger, and I want to integrate the parameter-validation schemas I created using 'joi' - with swagger.
There's https://www.npmjs.com/package/joi-to-swagger but I don't understand how to use the generated object in swagger...
This is how I use swagger:
swaggerAutogen(outputFile, endpointsFiles, doc);
This is how I use joi-to-swagger:
const { swagger, components } = j2s(postVersionInfoValidator);
How do I integrate the swagger-output.json created in the first step, with the swagger object created in the 2nd step?
Thanks
You can do something like this
Generate the Swagger schema using joi-to-swagger
import j2s from 'joi-to-swagger';
const { swagger: swaggerA } = j2s(joiSchemaA);
const { swagger: swaggerB } = j2s(joiSchemaB);
const { swagger: swaggerC } = j2s(joiSchemaC);
Add the generated schemas into the swagger-autogen options. Note the usage of #definitions instead of definition. This is to allow us to use the Swagger schema object generated from joi-to-swagger as is.
import swaggerAutogen from 'swagger-autogen';
const outputFile = './swagger-output.json';
const apiFiles = ['./app.js'];
const doc = {
info: {
title: 'Your API',
...
},
'#definitions': {
PayloadA: swaggerA,
PayloadB: swaggerB,
PayloadC: swaggerC,
},
...
};
swaggerAutogen({ openapi: '3.0.0' })(outputFile, apiFiles, doc);
Add the Swagger comments into your endpoints
app.post('/endpoint-a', (res, req) => {
// #swagger.summary = 'Perform stuff'
/* #swagger.requestBody = {
required: true,
schema: { $ref: "#/definitions/PayloadA" },
}
*/
res.send('A');
});
Related
I need to configure Playwright to use different POM fixtures for different project settings.
All examples I find configure the POM while extending the base test. This works, but this way Playwright would use the same POM fixture for all projects.
import { test as base } from '#playwright/test';
type TestOptions = {
productDetailPom: ProductDetailPom
};
export const test = base.extend<TestOptions>({
productDetailPom: async ({ browser }, use) => {
await use(await ProductDetailPom.create(browser, 'url'));
},
});
What I need are different POMs for each configured project. Is there a way to create a POM instance with the browser or page fixture for each project in the config?
// playwright.config.ts
const config: PlaywrightTestConfig<TestOptions> = {
...
projects: [
{
name: 'proj1',
use: {
productDetailPom: new ProductDetailPom1(browser, 'url1') // POM instance 1
}
},
{
name: 'proj2',
use: {
productDetailPom: new ProductDetailPom2(browser, 'url2') // POM instance 2
}
}
],
};
I found no way to construct POMs in the config, but a workaround. Here is what I came up with. Please tell me if you have a better solution.
In short: The POM instances are created not in the config, but later. For instantiation I use a factory, which can be the same for all projects. Only the data fed into the factory differ per project.
In the config I add the data I need for construction: the POM class itself and data that will get passed to the constructor. Those data can be different in each project.
// playwright.config.ts
const config: PlaywrightTestConfig<TestOptions> = {
...
projects: [
{
name: 'proj1',
use: {
productDetailPomConstr: [ProductDetailPom1, 'url1']
},
},
{
name: 'proj2',
use: {
productDetailPomConstr: [ProductDetailPom2, 'url2']
}
}
]
};
For creating the instance of the POM I use a factory, which is the same for all projects and because of this can be added by extending the base test. This is where I get the browser instance from (you could also get all other fixtures like page).
import { test as base } from '#playwright/test';
type TestOptions = {
pomFactory: PomFactory,
productDetailPomConstr: [typeof ProductDetailPom, Record<string, any>]
};
export const test = base.extend<TestOptions>({
pomFactory: async ({ browser }, use) => {
const pomFactory = new PomFactory(browser);
await use(pomFactory);
},
productDetailPomConstr: [null, { option: true }],
});
In the test I can get both fixtures, factory and PomConstruction data, and instantiate my POM with them.
test('someTest', async ({pomFactory, productDetailPomConstr}) => {
const pdPom = await pomFactory.create<ProductDetailPom>(productDetailPomConstr);
});
Maybe this helps someone.
I have configured Swagger within my Feather.js app and it automatically generates docs for all endpoints on each service. Now, some endpoints on some service I want to omit from being generated as docs, because I simply disallow these endpoints or have some hidden logic behind them, that does not allow external calls.
F.e. I have the following setup for the endpoints of my /users/me service:
before: {
all: [authenticate('jwt')],
find: [
/*
* We don't use an ID when calling `/users/me` like `/users/me/<id>`, and therefore Feathers understands the
* incoming request as a `find` method instead of `get`, therefore we simply redirect it internally.
*/
async context => {
context.result = await context.service.get(context.params.user.id); // eslint-disable-line
return context;
}
],
get: [
iff(isProvider('external'), disallow()),
includeGender()
],
create: [disallow()],
update: [setAuthenticatedUserId()],
patch: [setAuthenticatedUserId()],
remove: [setAuthenticatedUserId()]
}
As you can see from the logic setup, I want to have the following docs generated:
I've followed these docs regarding feathers-swagger. I use the schemasGenerator(service, model, modelName, schemas) to generate docs for each service. Understandably this will generate the same schema of docs for each service. I tried adding custom stuff, as per the github module explanations, by either adding the docs object:
service.docs = {
...service.docs,
operations: {
find: false,
create: false
}
};
or adding a global operations: { find: false, create: false } object on the Swagger config.
The first option doesn't have an effect, and the second option applies it to all endpoints, which doesn't help me.
You must use the 'ignore' option to exclude the end-points that you want to. You may either specify the 'tags' array or the 'paths' array.
app.configure(swagger({
docsPath: '/api/docs',
uiIndex: true,
specs: {
info: {
title: 'API Docs',
description: 'Rest APIs',
version: '1.0.0',
},
schemes: ['http', 'https'],
},
ignore: {
paths: [
'users'
]
}
}));
You can also ignore end-points from service level.
usersService.docs = {
description: 'A service to manage users',
definitions: {
users: m2s(options.Model),
'users_list': {
type: 'array',
items: { $ref: '#/definitions/users' }
}
},
securities: ['find', 'get', 'update', 'patch', 'remove'],
operations: {'create': false}
};
Get complete documentation for feathers-swagger here
I'm trying to generate multiple swagger.json documents and using Customize the Action Selection Process to determine which action goes to which swagger document.
Is it possible to have different DocInclusionPredicate for different swagger document?
For example, I have
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = "My API - V1", Version = "v1" });
c.SwaggerDoc("v2", new Info { Title = "My API - V2", Version = "v2" });
})
How do I specify one DocInclusionPredicate for v1 and another DocInclusionPredicate for v2 document?
c.DocInclusionPredicate((docName, apiDesc) =>
{
if (docName == "v1")
{ /* actions for v1 */ }
else if (docName == "v2")
{ /* actions for v2 */ }
});
and then return true if the predicate belongs to the specific version or false if it shall be filetered out in the correct if-statement
I am new to swagger. Currently, I am using swagger ui version v2.1.4. My API consists query parameters. Into that one parameter accepts JSON body. I want to show this parameter into textarea. Currently, it showing in the input tag . also, I want to display Parameter content type below that textarea. How I do that please help me?
In the current swagger 2.0 specification, you can not use complex values as query parameters. They can be primitives or arrays of primitive values. You can find out more from the specification directly:
https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#parameter-object
As this has been a commonly requested feature, it will be supported in the next version of the specification, but the feature will not be backported to the swagger 2.0 tooling.
add format in describing parameters
eg:
parameters:
- name: key
type: string
in: query
format: textarea
add custom plugin when init swagger-ui
// Custom plugin that adds extra stuff
const TextAreaPlugin = function() {
return {
wrapComponents: {
// add text above InfoContainer - same effect as above title
JsonSchema_string: (Original, runtime) => (props) => {
var React = runtime.React,
schema = props.schema || {},
errors = props.errors || {},
format = schema.format || "",
isDisabled = props.disabled;
errors = errors.toJS ? errors.toJS() : []
function handleOnChange(e) {
const inputValue = e.target.value;
props.onChange(inputValue)
}
if (format == "textarea") {
return React.createElement("div", null,
React.createElement("textarea", {
title: errors.length ? errors : "",
className: errors.length ? "invalid" : "",
value: props.value,
disabled: isDisabled,
onChange: handleOnChange
})
)
}
return React.createElement(Original, props);
},
}
}
};
// Begin Swagger UI call region
var ui = SwaggerUIBundle({
url: "./swagger.json",
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
TextAreaPlugin,
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout"
});
Objects as query parameters are now supported in OpenAPI 3.0. See this Q&A for an example:
Use object type query param in Swagger documentation
According to Swagger 2.0 specs, it might be possible to do this. I am referencing PathObject using $ref which points to another file. We used to be able to do this nicely using Swagger 1.2. But Swagger-UI does not seem to be able to read the referred PathObject in another file.
Is this part of spec too new and is not yet supported? Is there a way to split each "path"'s documentation into another file?
{
"swagger": "2.0",
"basePath": "/rest/json",
"schemes": [
"http",
"https"
],
"info": {
"title": "REST APIs",
"description": "desc",
"version": "1.0"
},
"paths": {
"/time": {
"$ref": "anotherfile.json"
}
}
}
To support multiple files, your libraries have to support dereferencing the $ref field. But I would not recommend to deliver the swagger file with unresolved references. Our swagger defintion has around 30-40 files. Delivering them via HTTP/1.1 could slow down any reading application.
Since we are building javascript libs, too, we already had a nodejs based build system using gulp. For the node package manager (npm) you can find some libraries supporting dereferencing to build one big swagger file.
Our base file looks like this (shortened):
swagger: '2.0'
info:
version: 2.0.0
title: App
description: Example
basePath: /api/2
paths:
$ref: "routes.json"
definitions:
example:
$ref: "schema/example.json"
The routes.json is generated from our routing file. For this we use a gulp target implementing swagger-jsdoc like this:
var gulp = require('gulp');
var fs = require('fs');
var gutil = require('gulp-util');
var swaggerJSDoc = require('swagger-jsdoc');
gulp.task('routes-swagger', [], function (done) {
var options = {
swaggerDefinition: {
info: {
title: 'Routes only, do not use, only for reference',
version: '1.0.0',
},
},
apis: ['./routing.php'], // Path to the API docs
};
var swaggerSpec = swaggerJSDoc(options);
fs.writeFile('public/doc/routes.json', JSON.stringify(swaggerSpec.paths, null, "\t"), function (error) {
if (error) {
gutil.log(gutil.colors.red(error));
} else {
gutil.log(gutil.colors.green("Succesfully generated routes include."));
done();
}
});
});
And for generating the swagger file, we use a build task implementing SwaggerParser like this:
var gulp = require('gulp');
var bootprint = require('bootprint');
var bootprintSwagger = require('bootprint-swagger');
var SwaggerParser = require('swagger-parser');
var gutil = require('gulp-util');
var fs = require('fs');
gulp.task('swagger', ['routes-swagger'], function () {
SwaggerParser.bundle('public/doc/swagger.yaml', {
"cache": {
"fs": false
}
})
.then(function(api) {
fs.writeFile('public/doc/swagger.json', JSON.stringify(api, null, "\t"), function (error) {
if (error) {
gutil.log(gutil.colors.red(error));
} else {
gutil.log("Bundled API %s, Version: %s", gutil.colors.magenta(api.info.title), api.info.version);
}
});
})
.catch(function(err) {
gutil.log(gutil.colors.red.bold(err));
});
});
With this implementation we can maintain a rather large swagger specification and we are not restricted to special programming language or framework implementation, since we define the paths in the comments to the real routing definitions. (Note: The gulp tasks are split in multiple files too.)
While it would theoretically be possible to do that in the future, the solution is still not fully baked into the supporting tools so for now I'd highly recommend keeping it in one file.
If you're looking for a way to manage and navigate the Swagger definition, I'd recommend using the YAML format of the spec, where you can add comments and that may ease up navigation and splitting of a large definition.
You can also use JSON Refs library to resolve such multi-file Swagger spec.
I've written about it in this blog post
There is also this GitHub repo to demonstrate how all of this work.
My solution to this problem is using this package below to solve the reference issue
https://www.npmjs.com/package/json-schema-ref-parser
Here is the code snippet when generating the swagger UI using that library. I was using Express.js for my node server.
import express from 'express';
import * as path from 'path';
import refParser from '#apidevtools/json-schema-ref-parser';
import swaggerUi from 'swagger-ui-express';
const port = 3100;
const app = express();
app.get('/', async (req, res) => {
res.redirect('/api-docs')
});
app.use(
'/api-docs',
async function (req: express.Request, res: express.Response, next: express.NextFunction) {
const schemaFilePath = path.join(__dirname, 'schema', 'openapi.yml');
try {
// Resolve $ref in schema
const swaggerDocument = await refParser.dereference(schemaFilePath);
(req as any).swaggerDoc = swaggerDocument;
next();
} catch (err) {
console.error(err);
next(err);
}
},
swaggerUi.serve,
swaggerUi.setup()
);
app.listen(port, () => console.log(`Local web server listening on port ${port}!`));
Take a look at my Github repository to see how it works