NestJS / Swagger - Not Getting Complete Descriptions - swagger

I'm having issues getting Swagger to work correctly. I can get an example of the request body, but not the response or the swagger-json. The response shows as {} and the swagger-json is:
{
statusCode: 404,
message: "Cannot GET /swagger-json",
error: "Not Found",
}
My nest-cli.json file is:
{
"collection": "#nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true,
"plugins": ["#nestjs/swagger/plugin"]
}
}
And in my main.ts I have:
import { DocumentBuilder, SwaggerModule } from '#nestjs/swagger';
...
const options = new DocumentBuilder()
.setTitle('ILuvCoffee')
.setDescription('Coffee Application')
.setVersion('1.0')
.build();
const document = SwaggerModule.createDocument(app, options);
SwaggerModule.setup('api', app, document);
I'm doing the Nest.js course, and I have followed along pretty faithfully so far. When I hit this snag, I double-checked my code was correct, even copy/pasting. I also double-checked that my DTO files follow *.dto.ts and my Entity files *.entity.ts. But I still can't get Swagger to show anything more than the request. Thoughts? Who's seeing what I'm not?
Here is my repo for it if you'd like to take a deeper peek: https://github.com/jstrother/iluvcoffee
Thanks!

Looks like you did not specifify the actual response types in your controller, instead you're using <any>, e.g:
#Get(':id')
findOne(#Param('id') id: string): Promise<any> {
console.log(id);
return this.coffeesService.findOne(id);
}
Try changing those anys to the actual types
#Get(':id')
findOne(#Param('id') id: string): Promise<Coffee> {
console.log(id);
return this.coffeesService.findOne(id);
}
Note that you can also use the ApiResponse decorator to explicitely define responses -> check the official example for more details.

Related

NestJS req.body from POST method is empty/undefined when awaiting ArrayBuffer

I am trying to send file from Client (Angular) to the NestJS same way like it is working now with Java-springboot API.
I am using POST method in NestJS but, unfortunatelly I am not able to got any data from the body :
here is the code :
#Post('/uploadExportFile')
uploadAttachment(#Req() req: Request, #Body() attachment: ArrayBuffer): any {
console.log(attachment);
return {};
}
content-type is set in header on Client side, I am not sure if I need to set content-types there ? Content type depends on file mimetype it should be (application/pdf/png/jpeg)..not multiform or what I need to do to achieve that attachment object will not return empty {} .
req.body is undefined
What I need to do with that file is to again change it back to Base64 (in angular it is in Base64) but Java API consumes only byte[] so I need to keep that like it is on FE.
any suggestions what is wrong in this "simple" code?
** EDIT **
====↓ EDIT ↓====
Solution: request.body is undefined is:
NestJS use as default body jsonBody, so in that case you have to override for specific routes that you want to use raw-body, and if raw-body is used insted of jsonBody, then the body from request is not undefined and it contain ArrayBuffer.
What you need to do is something like this;
Create rawBody middleware raw-body.middleware.ts
import { Injectable, NestMiddleware } from '#nestjs/common';
import { Request, Response } from 'express';
import * as bodyParser from 'body-parser';
#Injectable()
export class RawBodyMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: () => any) {
bodyParser.raw({type: '*/*'})(req, res, next);
}
}
app.module.ts
export class AppModule implements NestModule {
public configure(consumer: MiddlewareConsumer): void {
consumer
.apply(RawBodyMiddleware)
.forRoutes({
path: '/uploadExportFile',
method: RequestMethod.POST,
})
.apply(JsonBodyMiddleware)
.forRoutes('*');
}
}
and you need to disable bodyparser in main.ts
const app = await NestFactory.create(AppModule, { bodyParser: false })
in new version of NestJS is introduced new option raw-body but I have no possibility to test that https://docs.nestjs.com/faq/raw-body#raw-body
frist thing send the content-type application/x-www-form-urlencoded
and sure you have add UseInterceptors Like FileInterceptor
you can import FileInterceptor
if you need to get buffer try use file.buffer
import {FileInterceptor} from "#nestjs/platform-express";
#Post('upload')
#UseInterceptors(FileInterceptor('file'))
async upload(#Req() request: RequestWithUser, #UploadedFile() file) {
if (!file) {
throw new HttpException('File is required', HttpStatus.BAD_REQUEST);
}
// you have file
return await this.storageService.upload(file, request.user);
}

Cannot create batch work items using batch operations in TFS

I am getting below errors while trying to create workitems with batch creation method
Error 1
"Message":"No MediaTypeFormatter is available to read an object of type 'JsonBatchHttpRequest' from content with media type 'application/json-patch+json'."
Error 2
{"count":1,"value":{"Message":"One or more errors occurred."}}
I have referred to this documentation https://www.visualstudio.com/en-us/docs/integrate/api/wit/batch from Microsoft . and my question on stackoverflaw Create Large Amount of Work Items in TFS Using Javascript REST API
I have tried to send data as below methods
"json: x"
"body: x:"
"body:JSON.stringify(x)"
"json:[body:x]"
I have tried both "application/json-patch+json" and "application/json"(recommended as MIcrosoft documentation) as Content-Types
I have tired both Post (recommended as MIcrosoft documentation) and Patch methods
There is no references available for this error hence I have sucked at this point.What could be possibly wrong here please help..
public batchOperation( ):q.Promise<boolean>{
let deferred = q.defer<boolean>();
try {
var batchCreateUrl = this.collectionURL+"/_apis/wit/$batch?api-version=1.0";
var x= {
method:"PATCH",
uri:"/VSTS_TFS_Test/_apis/wit/workItems/$Bug?api-version=1.0",
headers:{
"Content-Type":"application/json-patch+json"
},
body:[
{ "op":"add",
"path": "/fields/System.Tags",
"value":"tg;tg1;tg2"
},
{
"op": "add",
"path": "/fields/System.Title",
"value": "Some Title Text "
},
{
"op": "add",
"path": "/fields/System.Description",
"value":"this is description"
}
]
}
var options = {
url: batchCreateUrl,
username: this.username,
password: this.password,
domain: this.domain,
method: 'PATCH',
headers: {
'Content-Type': 'application/json-patch+json'
},
body: x
};
httpntlm.patch(options, function(err,res) {
if(err) {
return deferred.reject(false);}
else{
console.log("Patch Complete");
console.log(res.body);
deferred.resolve(true);
}
});
} catch (error) {
console.log("Failed to Perform Batch Operation ")
deferred.reject(false);
}
return deferred.promise;
}
You need to use "application/json" as Content-Types and the post method just like the tutorial of Microsoft documentation described.
Since you are using httpntlm, you can include the following options:
json: if you want to send json directly (content-type is set to
application/json)
files: an object of files to upload (content-type is set to
multipart/form-data; boundary=xxx)
body: custom body content you want to send. If used, previous
options will be ignored and your custom body will be sent.
(content-type will not be set)
Source Link
If you are using body, your previous options will be ignored(content-type will lose), this may cause the issue. Give a try with directly using json.

In dart httpRequest getstring always error

I established a RESTful server, and I can get a simple string with my Chrome or IE using this URL: "http://localhost:8080/WebService/RESTful/getString"
But when using Dart, it always returns a error message:
"[object XMLHttpRequestProgressEvent]"
From the onError() callback method,
I'm sure that server returns a string with "text/plain" MIME type in Java.
Here is the code:
import 'dart:html';
void main() {
HtmlElement btn = document.body.querySelector("#btn");
btn.onClick.listen(onClick);
}
void onClick(Event v) {
var url = "http://localhost:8080/WebService/RESTful/getString";
HttpRequest.getString(url).then((str) {
window.alert(str.toString());
}, onError: (e) {
window.alert(e);
});
}
Who can help me ?
If you try to fetch resources from another server than the one your page was loaded from, this server needs to return CORS headers otherwise your browser refuses to fetch form this other server.
It depends on your server how this can be configured or added.
See for example
- How do you add CORS headers in Redstone interceptor?
- CORS with Dart, how do I get it to work?

How can one split API documentation in multiple files using Swagger 2.0

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

What am I doing wrong in this QBO v3 Reports API query?

When I use the following query, I get a good response (with only the first 5 days of May, so apparently the default is not 'This Fiscal Year-to-date' as the documentation suggests, but I digress):
https://quickbooks.api.intuit.com/v3/company/0123456789/reports/CustomerSales
When I add parameters, I get an oauth exception. For example:
https://quickbooks.api.intuit.com/v3/company/0123456789/reports/CustomerSales?start_date='2013-01-01'&end_date='2014-05-06'
Gives me this:
{
"Fault": {
"type": "AUTHENTICATION",
"Error": [
{
"Message": "message=Exception authenticating OAuth; errorCode=003200; statusCode=401",
"code": "3200"
}
]
},
"requestId": "[redacted]",
"time": "[redacted]"
}
This gives me the same result:
https://quickbooks.api.intuit.com/v3/company/0123456789/reports/CustomerSales?date_macro='This Fiscal Year'
So does this:
https://quickbooks.api.intuit.com/v3/company/148305798/reports/CustomerSales?accounting_method='Accrual'
I figure I'm missing something small. I'm not changing any of the headers or any of the other request details...just the url.
I tried without the single quotes around the dates and other params too.
What am I breaking?
Are you including the data to the right of the ? in the URL in the "base" string and are you sorting it with the other parameters?
I've tried this report using java devkit.
It worked fine for me. PFB details.
Request URI - https://quickbooks.api.intuit.com/v3/company/1092175540/reports/CustomerSales?accounting_method=Accrual&start_date=2014-01-01&requestid=61234ddb7e14ce2a5fe4e2f0318b31c&minorversion=1&
My test company file is empty.. That's why got the following JSON response.
{
"Header":{
"Time":"2014-05-06T20:42:08.783-07:00",
"ReportName":"CustomerSales",
"ReportBasis":"Accrual",
"StartPeriod":"2014-05-01",
"EndPeriod":"2014-05-06",
"SummarizeColumnsBy":"Total",
"Currency":"USD"
},
"Columns":{
"Column":[
{
"ColTitle":"",
"ColType":"Customer"
}
]
},
"Rows":{
"Row":[
{
"ColData":[
{
"value":"TOTAL"
}
],
"group":"GrandTotal"
}
]
}
}
JAVA code
void testCustomerSalesReport(Context context) {
Config.setProperty(Config.SERIALIZATION_RESPONSE_FORMAT, "json");
ReportService service = new ReportService(context);
service.setStart_date("2014-01-01");
service.setAccounting_method("Accrual");
Report report = null;
try {
report = service.executeReport(ReportName.CUSTOMERSALES.toString());
} catch (FMSException e) {
e.printStackTrace();
}
}
API Doc Ref - https://developer.intuit.com/docs/0025_quickbooksapi/0050_data_services/reports/customersales
Hope it will be useful.
Thanks

Resources