How to enable swagger UI on micronaut? - kotlin

I'm following the micronaut doc for openapi at https://micronaut-projects.github.io/micronaut-openapi/latest/guide/
and the UI is not being generated, every time when I try to access /swagger, /swagger-ui I got this error:
{
"message": "Not Found",
"_links": {
"self": {
"href": "/swagger",
"templated": false
}
},
"_embedded": {
"errors": [
{
"message": "Page Not Found"
}
]
}
}
The dependencies are installed:
implementation("io.swagger.core.v3:swagger-annotations")
annotationProcessor("io.micronaut.openapi:micronaut-openapi:4.5.2")
The router on application.yml is defined:
micronaut:
application:
name: myapp
router:
static-resources:
default:
enabled: true
swagger:
enabled: true
paths: classpath:META-INF/swagger
mapping: /swagger/**
and the file openapi.properties on the root folder is created:
swagger-ui.enabled=true
micronaut.openapi.views.spec=apidoc.enabled=true,swagger-ui.enabled=true,swagger-ui.theme=flattop
micronaut.openapi.expand.api.version=v0.1
micronaut.openapi.expand.openapi.description=myapp
according to the docs a .yml file should be generated at;
For Kotlin build/tmp/kapt3/classes/main/META-INF/swagger/myapp-0.1.yml
On my controllers I have the Operation and ApiResponses annotation as well, but when I run the application the file yml is not being generated, how can I properly enable swagger on my micronaut project?
Code sample: https://github.com/rafa-acioly/micronaut

You defined only swagger in your application.yml. You should add also add a section for swagger-ui with a similar definition:
micronaut:
application:
name: openapitest
router:
static-resources:
swagger:
paths: classpath:META-INF/swagger
mapping: /swagger/**
swagger-ui:
paths: classpath:META-INF/swagger/views/swagger-ui
mapping: /swagger-ui/**
Also, I used the default openapi.properties file:
swagger-ui.enabled=true
redoc.enabled=false
rapidoc.enabled=false
rapidoc.bg-color=#14191f
rapidoc.text-color=#aec2e0
rapidoc.sort-endpoints-by=method
And did you try to clean and build your project with gradlew?
I create a sample repository on GitHub and you can check it.
=========UPDATE========
Ok, I've found what is incorrect! You are using annotation-processing :
annotationProcessor("io.micronaut.openapi:micronaut-openapi:4.5.2")
But if you are using Kotlin, you should use kapt - Kotlin Annotation Processing Tool. Here is a few articles - link1, link2.
So you should change to:
kapt("io.micronaut.openapi:micronaut-openapi:4.5.2")

Related

Issues creating Admin module controller on Prestashop

The controller for URI "/modules/mm_custom/deleter" is not callable. Class "Mm_Custom\Controller\ConfigureController" does not exist.
[InvalidArgumentException 0]
That's what I get after 3 days trying to set an AdminController on my Prestashop module.
Here is the controller "configureController.php":
<?php
// modules/mm_custom/src/controller/configureController.php
namespace Mm_Custom\Controller;
use Symfony\Component\HttpFoundation\Response;
use PrestaShopBundle\Controller\Admin\FrameworkBundleAdminController;
class ConfigureController extends FrameworkBundleAdminController
{
public function deleteAction()
{
return new Response('Hello');
}
}
Here is the routes.yml :
# modules/mm_custom/config/routes.yml
delete:
path: mm_custom/deleter
methods: [GET]
defaults:
_controller: 'Mm_Custom\Controller\ConfigureController::deleteAction'
Here is the composer.json at the module folder root:
{
"name": "sixten/mm_custom",
"description": "A tool developped to allow your customers to easily and efficiently custom your product.",
"autoload": {
"psr-4": {
"Mm_Custom\\Controller\\": "src/Controller/"
}
},
"config": {
"prepend-autoloader": false
},
"type": "prestashop-module"
}
If the problem is not from a mistake in the code himself, it might comes from the fact that I couldn't run the composer dumpautoload console command in the module's folder directly because the website is online on a ftp server.
I put the composer.json somewhere else on my computer, ran the command and copy/paste the composer.json and the vendor file I get in the ftp's module folder. Maybe it's not the good way to do it ?
Thanks for reading me !

"error": "Could not connect to websocket endpoint ws://localhost:8000/subscriptions. Please check if the endpoint url is correct." for graphql spqr

I have my data stored in mongodb and I'm trying to implemet subscription using graphql spqr and project reactive.
I'm getting respones for when I'm trying to test mutations and query, but I keep on getting the following error message for subsctiptions:
{ "error": "Could not connect to websocket endpoint
ws://localhost:8080/subscriptions. Please check if the endpoint url is
correct." }
I've done some reserch on this issue and came accross this, including "spring-boot-starter-websocket" in my dependencies didn't solve this, anf I'm not using firefox as my browser for the playground gui.
I followed this example on how to implement subscriptions using graphql spqr, and when I trying to clone the project the subscription actualy worked just fine.
Can't fine any difference on my implementation from the project above...
Here is my build.gradle file:
configurations {
compile.exclude module: 'spring-boot-starter-tomcat'
compile.exclude group: 'org.apache.tomcat'
compileOnly {
extendsFrom annotationProcessor
}
}
dependencies {
// https://mvnrepository.com/artifact/de.flapdoodle.embed/de.flapdoodle.embed.mongo
testImplementation group: 'de.flapdoodle.embed', name: 'de.flapdoodle.embed.mongo', version: '3.0.0'
// https://mvnrepository.com/artifact/io.leangen.graphql/graphql-spqr-spring-boot-starter
implementation group: 'io.leangen.graphql', name: 'graphql-spqr-spring-boot-starter', version: '0.0.6'
implementation group: 'io.leangen.graphql', name: 'spqr', version: io_leangen_graphql_spqr
// https://mvnrepository.com/artifact/com.graphql-java-kickstart/playground-spring-boot-starter
implementation group: 'com.graphql-java-kickstart', name: 'playground-spring-boot-starter', version: '11.1.0'
// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-websocket
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-websocket', version: '2.5.4'
// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-mongodb-reactive
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-data-mongodb-reactive', version: '2.5.4'
// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-webflux
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-webflux', version: '2.5.4'
compileOnly group: 'org.projectlombok', name: 'lombok', version: org_projectlombok_lombok_version
annotationProcessor group: 'org.projectlombok', name: 'lombok', version: org_projectlombok_lombok_version
}
I'm using
compile.exclude module: 'spring-boot-starter-tomcat'
compile.exclude group: 'org.apache.tomcat'
To start netty instead of tomcat
And here is my subsctiption implementation:
#Autowired
private ProductRepository productRepository;
private final ConcurrentMultiMap<Double, FluxSink<ProductDto>> subscribers = new ConcurrentMultiMap<>();
#GraphQLQuery
public Flux<ProductDto> getProducts(){
return productRepository.findAll().map(AppUtils::ProductEntityToDto);
}
#GraphQLSubscription
public Publisher<ProductDto> taskStatusChanged(double price) {
return Flux.create(subscriber -> subscribers.add(price, subscriber.onDispose(() -> subscribers.remove(price, subscriber))), FluxSink.OverflowStrategy.LATEST);
}
Is there something I'm missing in my code? Or a dependancy? Or any other configuration? Any help would be very appreciated...
By default it seems GraphQl SPQR mapped the WebSocket subscription handling endpoint to /graphql, not the general /subscriptions in other GraphQL framework.
See the following info in console when the application is starting.
2021-10-09 12:50:02.653 DEBUG 14632 --- [ Test worker] o.s.w.s.s.s.WebSocketHandlerMapping : Patterns [/graphql] in 'webSocketHandlerMapping'
It can be changed by customizing graphql.spqr.ws.endpoint property in your application.properties.
But it seems its GraphQL WebSocket subscription implementation is very confused, it does not follow the Appllo WebSocket subscription protocol or the new graphql-ws spec.

How to set provider.apiGateway.shouldStartNameWithService in a serverless.ts file?

I am kicking off a new project with a new Serverless TypeScript monorepo! Used the aws-nodejs-typescript template, which gave a serverless.ts config file. After some weeks, I am now getting the nice warning below from Serverless on command line:
Serverless: Deprecation warning: Starting with next major version, API Gateway naming will be changed from “{stage}-{service}” to “{service}-{stage}”.
Set “provider.apiGateway.shouldStartNameWithService” to “true” to adapt to the new behavior now.
More Info: https://www.serverless.com/framework/docs/deprecations/#AWS_API_GATEWAY_NAME_STARTING_WITH_SERVICE
Ok! Looks great, and I like the new naming. And since it’s a new project, better to apply the new naming now, before we release anything. However, it looks like the TypeScript definitions are rather strict, and do not seem to allow for the new variable yet:
Loading failed with: TSError: ⨯ Unable to compile TypeScript:
serverless.ts(44,7): error TS2322: Type ‘{ minimumCompressionSize: number; shouldStartNameWithService: true; }’ is not assignable to type ‘ApiGateway’.
Object literal may only specify known properties, and ‘shouldStartNameWithService’ does not exist in type ‘ApiGateway’.
awsProvider.d.ts(51, 9): The expected type comes from property ‘apiGateway’ which is declared here on type ‘Provider’
Is there a way to set the new property without reverting everything to YAML, which would be somewhat painful at this point?
Update 1
Many thanks to #NexGen for the pointer! Here is a minimal serverless.ts (emphasis on the TS!) showing the solution.
import type { Serverless, ApiGateway } from 'serverless/aws';
const serverlessConfiguration: Serverless = {
service: {
name: 'foo',
},
frameworkVersion: '2',
custom: {
webpack: {
webpackConfig: './webpack.config.js',
packager: 'yarn',
includeModules: true,
},
alerts: {
stages: ['stage', 'prod'],
definitions: {
functionErrors: { treatMissingData: 'notBreaching' },
},
alarms: ['functionErrors'],
},
},
package: {
individually: true,
},
plugins: [
'serverless-webpack',
'serverless-jest-plugin',
'serverless-plugin-aws-alerts',
],
provider: {
name: 'aws',
runtime: 'nodejs12.x',
region: 'us-west-2',
stage: "${opt:stage, 'dev'}",
apiGateway: {
minimumCompressionSize: 1024,
shouldStartNameWithService: true,
} as ApiGateway,
environment: {
AWS_NODEJS_CONNECTION_REUSE_ENABLED: '1',
},
},
};
module.exports = serverlessConfiguration;
It is really simple to apply this change, all what you need to do is to add this to your serverless.yml file.
provider:
apiGateway:
shouldStartNameWithService: true
provider: {
name: 'aws',
runtime: 'nodejs12.x',
apiGateway: {
shouldStartNameWithService: true
} as ApiGateway,
stage: 'dev'
}

splitting swagger definition across many files

Question: how can I split swagger definition across files? What are the possibilities in that area? The question details are described below:
example of what I want - in RAML
I do have experience in RAML and what I do is, for example:
/settings:
description: |
This resource defines application & components configuration
get:
is: [ includingCustomHeaders ]
description: |
Fetch entire configuration
responses:
200:
body:
example: !include samples/settings.json
schema: !include schemas/settings.json
The last two lines are important here - theones with !include <filepath> - in RAML I can split my entire contract into many files that just get included dynamically by the RAML parser (and RAML parser is used by all tools that base on RAML).
My benefit from this is that:
I get my contract more clear and easier to maintain, because schemas are not inline
but that's really important: I can reuse the schema files within other tools to do validation, mock generation, stubs, generate tests, etc. In other words, this way I can reuse schema files within both contract (RAML, this case) and other tools (non-RAML, non-swagger, just JSONschema-based ones).
back to Swagger
As far as I read, swagger supports $ref keyword which allows to load external files. But is that files fetched through HTTP/AJAX or can they just be local files?
And is that supported by the whole specification or is it just some tools that support it and some that don't?
What I found here is that the input for swagger has to be one file. And this is extremely inconvenient for big projects:
because of size
and because I can't reuse the schema if I want to use something non-swagger
Or, in other words, can I achieve the same with swagger, that I can with RAML - in terms of splitting files?
The specification allows for references in multiple locations but not everywhere. These references are resolved depending on where the specification is being hosted--and what you're trying to do.
For something like rendering a dynamic user interface, then yes you do need to eventually load the entire definition into "a single object" which may be composed from many files. If performing a code generation, the definitions may be loaded directly from the file system. But ultimately there are swagger parsers doing the resolution, which is much more fine grained and controllable in Swagger than other definition formats.
In your case, you would use a JSON pointer to the schema reference:
responses:
200:
description: the response
schema:
via local reference
$ref: '#/definitions/myModel'
via absolute reference:
$ref: 'http://path/to/your/resource'
via relative reference, which would be 'relative to where this doc is loaded'
$ref: 'resource.json#/myModel
via inline definition
type: object
properties:
id:
type: string
When I split OpenAPI V3 files using references, I try to avoid the sock drawer anti-pattern and instead use functional groupings for the YAML files.
I also make it so that each YAML file itself is a valid OpenAPI V3 spec.
I start out with the openapi.yaml file.
openapi: 3.0.3
info:
title: MyAPI
description: |
This is the public API for my stuff.
version: "3"
tags:
# NOTE: the name is needed as the info block uses `title` rather than name
- name: Authentication
$ref: 'authn.yaml#/info'
paths:
# NOTE: here are the references to the other OpenAPI files
# from the path. Note because OpenAPI requires paths to
# start with `/` and that is already used as a separator
# replace the `/` with `%2F` for the path reference.
'/authn/start':
$ref: 'authn.yaml#/paths/%2Fstart'
Then in the functional group:
openapi: 3.0.3
info:
title: Authentication
description: |
This is the authentication module.
version: "3"
paths:
# NOTE: don't include the `/authn` prefix here that top level grouping is
# in the `openapi.yaml` file.
'/start':
get:
responses:
"200":
description: OK
By doing this separation you can independently test each file or the whole API as a group.
There may be points where you repeat yourself, but by doing this you limit the chance of breaking changes to other API endpoints when using a "common" library.
However, you should still have a common definition library for some things such as:
errors
security
There is a limitation on this approach and that's the "Discriminators" (it may be a ReDoc issue though, but if you had types that have discriminators outside of the openapi.yaml ReDoc fails to render correctly.
See this answer for details on how to split your Swagger documentation across many files. This is done using JSON, but the same concept can apply to RAML.
EDIT: Adding content of link here
The basic structure of your Swagger JSON should look something like this:
{
"swagger": "2.0",
"info": {
"title": "",
"version": "version number here"
},
"basePath": "/",
"host": "host goes here",
"schemes": [
"http"
],
"produces": [
"application/json"
],
"paths": {},
"definitions": {}
}
The paths and definitions are where you need to insert the paths that your API supports and the model definitions describing your response objects. You can populate these objects dynamically. One way of doing this could be to have a separate file for each entity's paths and models.
Let's say one of the objects in your API is a "car".
Path:
{
"paths": {
"/cars": {
"get": {
"tags": [
"Car"
],
"summary": "Get all cars",
"description": "Returns all of the cars.",
"responses": {
"200": {
"description": "An array of cars",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/car"
}
}
},
"404": {
"description": "error fetching cars",
"schema": {
"$ref": "#/definitions/error"
}
}
}
}
}
}
Model:
{
"car": {
"properties": {
"_id": {
"type": "string",
"description": "car unique identifier"
},
"make": {
"type": "string",
"description": "Make of the car"
},
"model":{
"type": "string",
"description": "Model of the car."
}
}
}
}
You could then put each of these in their own files. When you start your server, you could grab these two JSON objects, and append them to the appropriate object in your base swagger object (either paths or definitions) and serve that base object as your Swagger JSON object.
You could also further optimize this by only doing the appending once when the server is started (since the API documentation will not change while the server is running). Then, when when the "serve Swagger docs" endpoint is hit, you can just return the cached Swagger JSON object that you created when the server was started.
The "serve Swagger docs" endpoint can be intercepted by catching a request to /api-docs like below:
app.get('/api-docs', function(req, res) {
// return the created Swagger JSON object here
});
You can use $ref but not have good flexibility, I suggest you process YAML with an external tool like 'Yamlinc' that mix multiple files into one using '$include' tag.
read more: https://github.com/javanile/yamlinc

Cannot run unit tests on modules with dependencies on dojo 1.x using the Intern

We are just starting out with getting some unit tests running in the Intern on our dojo-based project.
What happens is that when the intern tries to load the module under test's dependencies we get the following error:
/<path/to/dev/folder>/app/node_modules/intern/node_modules/dojo/dojo.js:406
match = mid.match(/^(.+?)\!(.*)$/);
^
TypeError: Cannot read property 'match' of null at getModule (/<path/to/dev/folder>/app/node_modules/intern/node_modules/dojo/dojo.js:406:15) at mix.amd.vendor (/<path/to/dev/folder>/app/node_modules/intern/node_modules/dojo/dojo.js:832:17) at /<path/to/dev/folder>/app/src/simplebuilding/model/ModelError.js:10:1
at exports.runInThisContext (vm.js:74:17)
at Object.vm.runInThisContext (/<path/to/dev/folder>/app/node_modules/intern/node_modules/istanbul/lib/hook.js:163:16)
at /<path/to/dev/folder>/app/node_modules/intern/node_modules/dojo/dojo.js:762:8
at fs.js:334:14
at FSReqWrap.oncomplete (fs.js:95:15)
Here is my config file - I started by copying the example one, and adding the map section to the loader.
define({
proxyPort: 9000,
proxyUrl: 'http://localhost:9000/',
capabilities: {
'selenium-version': '2.41.0'
},
{ browserName: 'chrome', version: '40', platform: [ 'OS X' ] }
],
maxConcurrency: 3,
tunnel: 'NullTunnel',
loader: {
// Packages that should be registered with the loader in each testing environment
packages: [
{ name: 'dojo', location: 'src/dojo' },
{ name: 'dojox', location: 'src/dojox' },
{ name: 'dijit', location: 'src/dijit' },
{ name: 'app', location: 'src/app' },
{ name: 'tests', location: 'tests' }
],
map: {
'*': {
'dojo' : 'dojo'
},
app : {
'dojo' : 'dojo'
},
intern : {
'dojo' : 'node_modules/intern/node_modules/dojo'
},
'tests' : {
'dojo' : 'dojo'
}
}
},
suites: [ 'tests/model/modelerror' ],
functionalSuites: [ /* 'myPackage/tests/functional' */ ],
excludeInstrumentation: /^(?:tests|test\-explore|node_modules)\//
});
The file under test has dependencies on dojo/_base/declare, dojo/_base/lang, and dojo/Stateful, and that is about it.
I created a dummy class to test where there were no dojo dependencies and it runs fine.
I've tried switching the loader to be the local dojo 1.10.3 version we have in our project, and that throws entirely different errors about not being able to find the intern (even if I give it a package definition in the config). Those errors look like this:
{ [Error: ENOENT, no such file or directory '/<path/to/dev/folder>/app/node_modules/.bin/main.js']
errno: -2,
code: 'ENOENT',
path: '/<path/to/dev/folder>/app/node_modules/.bin/main.js',
syscall: 'open' }
Our project structure is pretty straight-forward:
root
|--src
|--dojo (dijit/dojox/dgrid/etc)
|--app
|--tests
|--intern.js (config file)
I've tried several variations besides changing the loader, like trying to make sure the base-path is correct. I've tried running it in Node 0.10.36, and 0.12.2. But every time I debug this with node-inspector when it gets to load the module for my file under test and the mid is null, and jumping back up the stack trace it looks fine, but something is lost in the vm.runInThisContext() call, and the mid disappears by the time getModule() is called.
Any help is appreciated - Thanks!
So I figured this out - we had modules we were loading inside of our project that used an old style of the define() function. We had moved from the old define('my.module.namespace', ['deps'], function(deps){ ... }); to replacing the dot namespace for the module in the first argument with null. We were doing this as a transitionary phase to removing that argument completely, but hadn't ever finished that transition. This was causing the dojo2 loader to think the "id" of the module was null, and that was causing the loader to not find a Module ID.
This was a completely silly mistake on our part, and this will help us modernize to the updated signature for future-dojo-readiness.