Swagger defination, how to specify that a file is returned? - file-io

I want to download a file from the server, and I define the swagger file as follows:
swagger: '2.0'
################################################################################
# API Information
################################################################################
info:
version: v0
title: XXX REST API
host: api.xxx.io
basePath: /v0
schemes:
- http
- https
produces:
- application/json
################################################################################
# Security
################################################################################
################################################################################
# Parameters
################################################################################
parameters:
productId:
name: productId
in: path
description: The product identifier
type: string
required: true
################################################################################
# Paths
################################################################################
paths:
/products:
get:
description: Get the list of products
operationId: getProducts
responses:
200:
description: OK
schema:
type: array
items:
$ref: '#/definitions/Product'
/resources/{productId}:
parameters:
- $ref: '#/parameters/productId'
get:
description: Get resources of a product
operationId: getResourcesByProductId
produces:
- application/octet-stream
responses:
200:
description: OK
schema:
type: file
################################################################################
# Definitions
################################################################################
definitions:
Product:
type: object
required:
- id
properties:
id:
type: string
name:
type: string
category:
type: array
items:
type: string
description:
type: string
price:
type: number
thumbnailUri:
type: string
previewUris:
type: array
items:
type: string
resources:
type: array
items:
$ref: '#ResourceMeta'
And the api is as follows:
#javax.annotation.Generated(value = "class io.swagger.codegen.languages.SpringCodegen", date = "2016-10-24T17:56:03.446+08:00")
#Controller
public class ResourcesApiController implements ResourcesApi {
public ResponseEntity<File> getResourcesByProductId(
#ApiParam(value = "The product identifier",required=true ) #PathVariable("productId") String productId
) {
// do some magic!
return new ResponseEntity<File>(HttpStatus.OK);
}
}
My controller is as follows:
#Controller
public class ResourceController implements ResourcesApi {
private final Logger logger = Logger.getLogger(ResourceController.class);
// #RequestMapping(value="/resources/{productId}", method= RequestMethod.GET)
public ResponseEntity<File> getResourcesByProductId(#ApiParam(value = "The product identifier", required = true) #PathVariable("productId") String productId) {
String path = "resources" + File.separator + productId;
File file = new File(path);
FileSystemResource fileSystemResource = new FileSystemResource(file);
InputStreamResource inputStreamResource = null;
try {
inputStreamResource = new InputStreamResource(fileSystemResource.getInputStream());
} catch (IOException e) {
logger.error(e.toString());
}
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Disposition", String.format("attachment; filename=\"%s\"", file.getName()));
return ResponseEntity
.ok()
.headers(headers)
.contentLength(file.length())
.body(file);
}
}
However, when I run the application, it return a file but only contains the metadata of the file instead of its content. How can I make it return the file content? Thanks!

Use the InputStreamResource to return the file contents:
return new ResponseEntity(inputStreamResource, headers, HttpStatus.OK);

Related

Vert.x Web API Service codegen and Kotlin, issue with nested properties of referenced object

I'm trying to generate Web API Service from yaml
openapi: 3.0.1
info:
title: ""
description: ''
license:
name: Apache 2.0
url: http://www.apache.org/licenses/LICENSE-2.0.html
version: 1.0.0
servers:
- url: http://localhost:3001
tags:
- name: user
description: Operations with users
- name: stream
description: Operation with streams
paths:
/user/register:
post:
x-vertx-event-bus: user_manager.myapp
tags:
- user
summary: Create user
operationId: createUser
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/UserEnter'
required: true
responses:
'200':
description: successful operation
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'400':
description: Invalid phone number
content: {}
x-codegen-request-body-name: body
components:
schemas:
UserEnter:
type: object
properties:
phone:
type: integer
format: int32
password:
type: string
additionalProperties: false
required:
- phone
- password
UserService.kt:
#WebApiServiceGen
interface UserService {
#GenIgnore
companion object{
#JvmStatic
fun create(repository: Repository): UserServiceImpl {
return UserServiceImpl(repository)
}
}
fun createUser(userEnter: UserEnter, request: ServiceRequest, resultHandler: Handler<AsyncResult<ServiceResponse>>)
}
UserEnter.kt:
#DataObject(generateConverter = true, publicConverter = false)
class UserEnter {
var phone: Int = 0
var password: String
constructor(phone: Int, password: String){
this.phone = phone
this.password = password
}
constructor(json: JsonObject): this(
json.getInteger("phone", 0),
json.getString("password", ""),
)
fun toJson(): JsonObject {
return JsonObject.mapFrom(this)
}
}
I'm trying to post the data:
{
"phone": 23423423423,
"password": "enim ut"
}
But the server expects this type of data:
"userEnter": {
{
"phone": 23423423423,
"password": "enim ut"
}
}
The part of generated UserServiceVertxProxyHandler.java:
case "createUser": {
JsonObject contextSerialized = json.getJsonObject("context");
if (contextSerialized == null)
throw new IllegalStateException("Received action " + action + " without ServiceRequest \"context\"");
ServiceRequest context = new ServiceRequest(contextSerialized);
JsonObject params = context.getParams();
try {
service.createUser(
searchOptionalInJson(params, "userEnter").map(j -> (io.vertx.core.json.JsonObject)j).map(j -> new com.md.model.user.UserEnter(j)).orElse(null),
context,
res -> {
if (res.failed()) {
if (res.cause() instanceof ServiceException) {
msg.reply(res.cause());
} else {
msg.reply(new ServiceException(-1, res.cause().getMessage()));
}
} else {
msg.reply(res.result() == null ? null : res.result().toJson());
}
}
);
} catch (Exception e) {
HelperUtils.manageFailure(msg, e, includeDebugInfo);
}
break;
}
Gradle:
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
kotlin("jvm") version "1.6.21"
kotlin("kapt") version "1.7.0"
application
}
group = "com.md"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
google()
}
dependencies {
kapt("io.vertx:vertx-codegen:4.3.1:processor")
kapt("io.vertx:vertx-web-api-service:4.3.1")
implementation("io.vertx:vertx-core:4.3.1")
implementation("io.vertx:vertx-web:4.3.1")
implementation("io.vertx:vertx-web-validation:4.3.1")
implementation("io.vertx:vertx-web-openapi:4.3.1")
implementation("io.vertx:vertx-service-proxy:4.3.1")
implementation("io.vertx:vertx-web-api-service:4.3.1")
compileOnly("io.vertx:vertx-codegen:4.3.1")
implementation("io.vertx:vertx-mongo-client:4.3.1")
implementation("org.slf4j:jcl-over-slf4j:1.7.36")
implementation("ch.qos.logback:logback-classic:1.2.11")
testImplementation(kotlin("test"))
}
tasks.test {
useJUnitPlatform()
}
tasks.withType<KotlinCompile> {
kotlinOptions.jvmTarget = "1.8"
}
application {
mainClass.set("MainKt")
}
What I’m doing wrong? I’ve made all like in this example: https://github.com/vert-x3/vertx-examples/tree/4.x/web-api-service-example/src/main/java/io/vertx/examples/webapiservice
My YAML file is correct, I’ve generated the kotlin-client on https://editor.swagger.io and it sends:
{
"phone": 23423423423,
"password": "enim ut"
}

Modeling form-data API request using swagger 2.0

I have the following API request:
I have a problem of how to model the array part where there is for example skills[0][name]:
so OK, it is array type with items, but how do you model the [name] part in swagger? how would it look like? I've tried the following:
- in: formData
name: skills
type: array
items:
type: string
but how do you model the [id] part for example?
------------------------------EDIT----------------------------
As suggested I implemented the following:
parameters:
- $ref: '#/parameters/xAccessTokenHeader'
- in: formData
name: name
type: string
required: true
- in: formData
name: status
type: integer
- in: formData
name: level
type: integer
required: true
- in: formData
name: classification
type: integer
required: true
- in: formData
name: description
type: string
required: true
- in: formData
name: icon
type: string
required: true
- in: formData
name: syllabi
type: array
items:
type: integer
- in: formData
name: institutions
type: array
items:
type: integer
- in: formData
name: skills[0][name]
type: string
- in: formData
name: skills[0][id]
type: string
- in: formData
name: skills[0][selected]
type: boolean
- in: formData
name: skills[0][type]
type: string
- in: formData
name: skills[1][name]
type: string
- in: formData
name: skills[1][id]
type: string
- in: formData
name: skills[1][selected]
type: boolean
- in: formData
name: skills[1][type]
type: string
- in: formData
name: lessons
type: array
items:
type: string
- in: formData
name: status_mode
type: integer
responses:
"201":
description: "Created"
but when I sent the request I got the following error:
{"message":"The given data was invalid.","errors":{"syllabi":["The syllabi must be an array."],"lessons":["The lessons must be an array."]}}
and this is my java code that wrap code generation from swagger:
public void createNewModule(String moduleName, Status status, String desc, String icon) throws ApiException {
List<String> lessonLst = Arrays.asList("790f67a7-47de-4c86-aacb-b64ad10e8c76");
List<Integer>syllabiLst = Arrays.asList(0,1,2);
List<Integer>institutionsLst = Arrays.asList(0,1);
practiceArenaApi.createNewModule(token, moduleName, 4, 2, desc, icon, status.apiStatus, syllabiLst, institutionsLst,
"Web-scraping in Python", "1", true, "prerequisite",
"SMTP", "5", true, "achieved", lessonLst, 1);
}
indicating that the post request was illegal.
For the following form-data parameters:
In OpenAPI 2.0, form fields with [smth] in the name need to be defined as individual parameters:
parameters:
- in: formData
name: skills[0][name]
type: string
- in: formData
name: skills[0][id]
type: string
- in: formData
name: skills[0][selected]
type: boolean
- in: formData
name: skills[1][name]
type: string
# etc.
This means you can't really describe form data containing arbitrary-length arrays of objects, as you need to define all possible indexes (0, 1, 2, etc.) in advance as part of individual parameter names.
If you are designing a new API rather than documenting an existing one, consider using a JSON body instead of form data.

Enable diagnostic settings for Storage account using ARMTemplate

Storage account deployed from ARMTemplate is creating diagnostic settings as disabled.
How to enable diagnostics status using ARMTemplate or Powershell script?
Want to automate the process to deploy diagnostic settings.
Here is a solution using ARM templates in the newer Bicep format. In the example, it configures diagnostics settings for:
StorageAccount
Blob
File
Queue
Table
To reduce the template length, it configures only the StorageRead on the storage account services.
param name string
param location string = resourceGroup().location
param sku string
#description('Resource ID for the destination log analytics workspace.')
param logAnalyticsWorkspaceId string
resource storageAccount 'Microsoft.Storage/storageAccounts#2019-06-01' = {
name: name
location: location
kind: 'StorageV2'
sku: {
name: sku
}
properties: {
allowBlobPublicAccess: false
allowSharedKeyAccess: true
minimumTlsVersion: 'TLS1_2'
accessTier: 'Hot'
supportsHttpsTrafficOnly: true
networkAcls: {
defaultAction: 'Deny'
bypass: 'AzureServices'
}
}
}
resource diagnosticsStorage 'Microsoft.Insights/diagnosticSettings#2021-05-01-preview' = {
scope: storageAccount
name: 'diagnostics00'
properties: {
workspaceId: logAnalyticsWorkspaceId
metrics: [
{
category: 'Transaction'
enabled: true
}
]
}
}
resource blobService 'Microsoft.Storage/storageAccounts/blobServices#2021-06-01' = {
parent: storageAccount
name: 'default'
properties: {}
}
resource diagnosticsBlob 'Microsoft.Insights/diagnosticSettings#2021-05-01-preview' = {
scope: blobService
name: 'diagnostics00'
properties: {
workspaceId: logAnalyticsWorkspaceId
logs: [
{
category: 'StorageRead'
enabled: true
}
]
}
}
resource fileService 'Microsoft.Storage/storageAccounts/fileServices#2021-06-01' = {
parent: storageAccount
name: 'default'
properties: {}
}
resource diagnosticsFile 'Microsoft.Insights/diagnosticSettings#2021-05-01-preview' = {
scope: fileService
name: 'diagnostics00'
properties: {
workspaceId: logAnalyticsWorkspaceId
logs: [
{
category: 'StorageRead'
enabled: true
}
]
}
}
resource queueService 'Microsoft.Storage/storageAccounts/queueServices#2021-06-01' = {
parent: storageAccount
name: 'default'
properties: {}
}
resource diagnosticsQueue 'Microsoft.Insights/diagnosticSettings#2021-05-01-preview' = {
scope: queueService
name: 'diagnostics00'
properties: {
workspaceId: logAnalyticsWorkspaceId
logs: [
{
category: 'StorageRead'
enabled: true
}
]
}
}
resource tableService 'Microsoft.Storage/storageAccounts/tableServices#2021-06-01' = {
parent: storageAccount
name: 'default'
properties: {}
}
resource diagnosticsTable 'Microsoft.Insights/diagnosticSettings#2021-05-01-preview' = {
scope: tableService
name: 'diagnostics00'
properties: {
workspaceId: logAnalyticsWorkspaceId
logs: [
{
category: 'StorageRead'
enabled: true
}
]
}
}
Please follow the below URL to enable the Diagnostics Settings for Azure Storage Account using ARM Template:
https://learn.microsoft.com/en-us/azure/azure-monitor/essentials/resource-manager-diagnostic-settings#diagnostic-setting-for-azure-storage

Authentication with a second manager

I created a second manager for my second database.
In this base, i create a table which it contains my users.
The problem is the symfony don't load users from this database.
here is an extract of my config.yml :
Doctrine Configuration
doctrine:
dbal:
default_connection: default
connections:
default:
driver: pdo_mysql
host: "%database_host%"
port: "%database_port%"
dbname: "%database_name%"
user: "%database_user%"
password: "%database_password%"
charset: UTF8
seconddb:
driver: pdo_mysql
host: "xx.xx.xx.xx"
port: "3306"
dbname: "acme_test"
user: "acmegamestest"
password: "mypassword"
charset: UTF8
# if using pdo_sqlite as your database driver:
# 1. add the path in parameters.yml
# e.g. database_path: "%kernel.root_dir%/data/data.db3"
# 2. Uncomment database_path in parameters.yml.dist
# 3. Uncomment next line:
# path: "%database_path%"
orm:
default_entity_manager: default
auto_generate_proxy_classes: "%kernel.debug%"
#naming_strategy: doctrine.orm.naming_strategy.underscore
entity_managers:
default:
connection: default
mappings:
acmeAdminBundle: ~
acmeBlogBundle: ~
gedmo_translatable:
type: annotation
alias: GedmoTranslatable
prefix: Gedmo\Translatable\Entity
is_bundle: false
# make sure vendor library location is correct
dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity"
seconddb:
connection: seconddb
mappings:
acmeJoueurBundle: ~
and here is an extract of my security.yml :
encoders:
FOS\UserBundle\Model\UserInterface: sha512
Acme\JoueurBundle\Entity\Players:
algorithm: sha512
encode_as_base64: false
providers:
seconddb:
entity:
class: Acme\JoueurBundle\Entity\Players
property: username
manager_name: seconddb
fos_userbundle:
id: fos_user.user_provider.username
firewalls:
administration:
pattern: ^/admin
provider: fos_userbundle
context: administration
form_login:
#csrf_provider: security.csrf.token_manager
login_path : fos_user_security_login
check_path : fos_user_security_check
failure_path : null
default_target_path : /admin
logout:
path : fos_user_security_logout
target : /connexion
anonymous: true
frontend:
pattern: ^/
provider: acme_joueurbundle
context: frontend
form_login:
#csrf_provider: form.csrf_provider
login_path : acme_players_login
check_path : acme_players_check
failure_path : null
default_target_path : acme_players_userprofile
logout:
path : acme_players_logout
target : acme_players_login
anonymous: true
and my entity implements "AdvancedUserInterface, \Serializable"
with this function :
//////////////////////////liaison pour symfony////////////////////////////
public function getRoles()
{
return array('ROLE_PLAYERS');
}
public function getSalt(){
return $this->salt;
}
public function eraseCredentials(){
}
public function isAccountNonExpired()
{
return true;
}
public function isCredentialsNonExpired()
{
return true;
}
public function isAccountNonLocked()
{
return !$this->banned;
}
public function isEnabled()
{
return $this->active;
}
/** #see \Serializable::serialize() */
public function serialize()
{
return serialize(array(
$this->id,
$this->username,
$this->password,
$this->active
// see section on salt below
// $this->salt,
));
}
/** #see \Serializable::unserialize() */
public function unserialize($serialized)
{
list (
$this->id,
$this->username,
$this->password,
$this->active
// see section on salt below
// $this->salt
) = unserialize($serialized);
}
/////////////////////////////////////////////////////////////////////////////
However when i trying to connect, i get this errors in dump function :
and in the section Doctrine i get this :
Is what you know the solution?
Hope you can help me.
Thanks
EDIT
So, the error comes from this piece of code in the user authentication provider :
try {
$user = $this->retrieveUser($username, $token);
} catch (UsernameNotFoundException $e) {
if ($this->hideUserNotFoundExceptions) {
throw new BadCredentialsException('Bad credentials.', 0, $e);
}
$e->setUsername($username);
throw $e;
}
The method retrieveUser failed and it throws bad crendentials exception. This explains why there is no connection since it has not yet been called.
Before asking you for the code of your provider, let's look at your security.yml because you create a provider called seconddb and in your firewall frontend you try to call a provider acme_joueurbundle who propably doesn't exist.
So, your security.yml should look like this:
# app/config/security.yml
[...]
providers:
seconddb:
entity:
class: Acme\JoueurBundle\Entity\Players
property: username
manager_name: seconddb
fos_userbundle:
id: fos_user.user_provider.username
firewalls:
administration:
provider: fos_userbundle
[...]
frontend:
provider: seconddb
[...]

How to manage data template when serving an api using Node.js

I'm currently trying to create an api for my web app using Node.js. The api intended to return JSON data to the api user.
For example, i'm having following endpoint / object:
- Articles (collection of article)
- Article (single article, each article would be having tags)
- Tags
Each endpoint / object having they own json data format need to return, in example:
Articles: [{articleObject}, {articleObject}, {articleObject}]
Article
{
id: 12,
title: 'My awesome article *i said and i know it',
content: 'blah blah blah blah',
tags: [{tagObject}, {tagObject}, {tagObject}]
}
Each endpoint can call to other endpoint to get needed data, in example:
Article endpoint can calling the Tags endpoint to get the article tags collection, the Tags endpoint will be returning Array Object.
My questions:
How to manage the Object structures, so if a endpoint called from another endpoint it will return Array / Object, while it will be return JSON string when called from api user.
Should I create Object template for each endpoint. The process will be:
Endpoint will be calling Object Template;
The Object Template will fetching data from database;
Endpoint will return the Object Template when called from other endpoint;
Endpoint will call template engine to parse the Object Template to JSON string for api user. JSON string view will be provided for each endpoint.
What Template Engine can process below Object Template and treat them as variables, in PHP i'm using Twig that can receive PHP Object and treat them as variables. Here you can find how i do that using Twig
Object Template file content example:
var Tags = require('./tags');
module.exports = function(param){
/* do fetching data from database for specified parameters */
/* here */
var data = {};
return {
id: function() {
return data.id;
},
title: function() {
return data.title;
},
description: function() {
return data.description;
},
tags: function() {
return Tags(data.id);
}
}
}
Hope someone can show me the way for it.
I use express to do my apis...you can do something like this:
app.get('/api/article', authenticate, routes.api.article.get);
authenticate is just a middleware function that authenticates the user, typically this is grabbing the user from session and making sure they are logged in.
inside ./routes/api.js
var Article = require('../models/article.js');
exports.api = {}
exports.api.article = {}
exports.api.article.get = function(req, res){
//get article data here
Article.find({created_by: req.session.user._id}).populate('tags').populate('created_by').exec(function(err, list){
res.json(list);
});
};
This assume you're using mongoose and mongodb and have an Article schema similar to this, which includes a reference to a Tag schema:
var ArticleSchema = new Schema({
name : { type: String, required: true, trim: true }
, description: { type: String, trim: true }
, tags : [{ type: Schema.ObjectId, ref: 'Tag', index: true }]
, created_by : { type: Schema.ObjectId, ref: 'User', index: true }
, created_at : { type: Date }
, updated_at : { type: Date }
});