Swagger UI & Vercel: Unexpected token < in JSON at position 1 - swagger

Code
Full codebase & folder structure can be seen in GitHub
Here is the Swagger related route (had to make it a standalone server)
// api/v1.ts
import express = require("express");
import swaggerJSDoc = require("swagger-jsdoc");
import swaggerUi = require("swagger-ui-express");
import packageJSON = require("../package.json");
import path = require("path");
const app = express();
app.use(express.json());
app.use(express.static(path.resolve(__dirname, "../", "public")));
const swaggerSpec = swaggerJSDoc({
swaggerDefinition: some_spec,
apis: ["api/*"]
});
const cssOpts = some_css_override;
app.use("/api/v1", swaggerUi.serve, swaggerUi.setup(swaggerSpec, cssOpts));
module.exports = app;
Problem
When I run vercel dev (locally- localhost:3000/api/v1), I see documentation as expected:
However when I push my code to a branch which triggers a vercel build, I see the following:
Checking the console, I see:
DevTools failed to load source map: Could not parse content for https://colormaster-1unjfn63b-lbragile.vercel.app/api/v1/swagger-ui-bundle.js.map: Unexpected token < in JSON at position 1
DevTools failed to load source map: Could not parse content for https://colormaster-1unjfn63b-lbragile.vercel.app/api/v1/swagger-ui-standalone-preset.js.map: Unexpected token < in JSON at position 1
Even though they respond with 200
I understand that this has something to do with JSON.parse() of HTML content, but not sure how to fix this. Any ideas?

I am facing the exact same problem of you, trying without success to deploy Swagger to Vercel with Express.
I did one step more, and now I'm seen an error in my console:
Refused to apply style from
'https://myurlishere.vercel.app/api-docs/swagger-ui.css' because its
MIME type ('text/html') is not a supported stylesheet MIME type, and
strict MIME checking is enabled.
What I did was, adding a file routes.ts
import { Router } from 'express';
import LanguageController from './controller/LanguageController';
import WordController from './controller/WordController';
const routes = Router();
routes.get("/word", WordController.find);
routes.get("/word/:wordName/language/:languageId", WordController.findByWordAndLanguage);
routes.post("/word", WordController.create);
routes.get("/language", LanguageController.find);
export default routes;
And my server.ts looks like that:
import mongoose from 'mongoose';
import routes from './routes';
const express = require("express");
if (process.env.NODE_ENV !== 'production') {
require('dotenv').config();
}
const app = express();
const cors = require('cors');
mongoose.connect(process.env.MONGODB_URI || "", {
dbName: "WordsThatIKnowMongoDB"
})
.then(() => console.debug("Database connected!"))
.catch(err => { console.debug(err) });
app.use(express.json());
app.use(express.static("/api-docs"));
app.use(cors());
app.use(routes);
const swaggerUi = require('swagger-ui-express');
const swaggerDocument = require('./swagger.json');
routes.use('/api-docs', swaggerUi.serve);
routes.get('/api-docs', swaggerUi.setup(swaggerDocument));
app.listen(5000, () => {
console.debug("Running on port 5000.");
});
// Export the Express API
module.exports = app;
You will see in the file above that I changed app. to routes. like this:
routes.use('/api-docs', swaggerUi.serve);
routes.get('/api-docs', swaggerUi.setup(swaggerDocument));
I still can't solve this problem, but maybe this new error can help you find the solution. I'm also looking for that.
EDIT: It's solved.
This is the code that solved my problem:
server.ts
import path from 'path';
import cors from 'cors';
import bodyParser from 'body-parser';
import mongoose from 'mongoose';
import routes from './routes';
const express = require("express");
const app = express();
const ROOT_FOLDER = path.join(__dirname, '..');
const SRC_FOLDER = path.join(ROOT_FOLDER, 'src');
// parse requests of content-type - application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: true }));
// parse requests of content-type - application/json
app.use(bodyParser.json());
app.use(cors());
app.use(routes);
if (process.env.NODE_ENV !== 'production') {
require('dotenv').config();
}
mongoose.connect(process.env.MONGODB_URI || "", {
dbName: "WordsThatIKnowMongoDB"
})
.then(() => console.debug("Database connected!"))
.catch(err => { console.debug(err) });
const swaggerUi = require('swagger-ui-express');
const swaggerDocument = require('./swagger.json');
const options = { customCssUrl: '/public/swagger-ui.css', customSiteTitle: "The Words That I Know API - Swagger" };
app.use('/public', express.static(path.join(SRC_FOLDER, 'public')));
app.use('/', swaggerUi.serve);
app.get('/', swaggerUi.setup(swaggerDocument, options));
app.listen(5000, () => {
console.debug("Running on port 5000.");
});
export default app;
Don't forget to put the styles from Swagger at '/public/swagger-ui.css'. Create a public folder inside src and include a swagger-ui.css file. Inside of this, past swagger styles. You can find swagger styles using inspect on browser, and going to source tab. There you'll find the swagger-ui.css file; remove the commented line after pasting the styles code.
If you prefer an easy way to get the styles code, get this file. https://github.com/deywersonp/ghibli-50-api/blob/main/src/public/css/swagger-ui.css

hey I got the same problem ! the solution I use isn't optimal but it worked
knowing the css file is been wel process localy,all i did is to add custom css to my swagger-ui documentation. so as the css is working localy I copied all the css ( inspected web browser saw the file swagger-ui.css file source code ) , created a css file , paste the css to it , then i added my css file to my static folder.
here is how to add costum css
const options = { customCssUrl: '/public/css/swagger-ui.css',};
router.use('/api-docs-ui', function(req, res, next){
swaggerDocument.host = req.get('host');
req.swaggerDoc = swaggerDocument;
next();
}, swaggerUi.serve, swaggerUi.setup(swaggerDocument, options));
here is how you define your statics files
app.use('/public/css', express.static('public/css'));
so now localy I have 2 css file working but on vercel just one is working!
hope it could help

Also you can use a CDN for you Swagger styles if you don't wont to put the css files into your public folder.

Related

Unable to load chrome extention with playwright for automation

Hello guys I just started using playwright and JavaScript. I am having a trouble figuring out how to load chrome extension from local folder while opening browser for automation. I want to automate chrome extension but so far the only clue that I have got is to use this code
const { chromium } = require('playwright');
(async () => {
const pathToExtension = require('path').join(__dirname, 'my-extension');
const userDataDir = '/tmp/test-user-data-dir';
const browserContext = await chromium.launchPersistentContext(userDataDir,{
headless: false,
args: [
`--disable-extensions-except=${pathToExtension}`,
`--load-extension=${pathToExtension}`
]
});
const backgroundPage = browserContext.backgroundPages()[0];
// Test the background page as you would any other page.
await browserContext.close();
})();
which I am unable to understand that how this would be configured and in which file should I add this code. Can anybody help me in this regard please?

Use Mapbox with offline maps in Electron app

Is it possible to have an electron app showing offline map?
I downloaded the mbtiles and stored in the electron app, but I'm not able to show it in the angular side of the electron app.
I have the following code:
<mgl-map
class="map"
id = "map"
[style]="'mbtiles://assets/resources/downloaded.mbtiles'"
[center]="[mapCenter.longitude, mapCenter.latitude]"
(load) = "onLoad($event)"
(dragEnd)="mapDrag()"
[doubleClickZoom]="false"
[bearing]="[bearing]"
[zoom]="[zoom]"
[pitch]="[pitch]">
</mgl-map>
But I get the error
zone-evergreen.js:1068 Fetch API cannot load mbtiles://User/hello/path/to/file.mbtiles. URL scheme "mbtiles" is not supported.
So, in order to make it works in an online way I have to change the style for
[style]="'mapbox://styles/mapbox/streets-v9'"
Is it possible to make it works serving the mbtiles from the nodejs code or in other way?
To serve up your local mbtiles file, you need to register the mbtiles protocol in main.js. Something like this should do it:
const electron = require('electron');
const protocol = electron.protocol;
const path = require('path');
protocol.registerHttpProtocol('mbtiles', (req, cb) => {
const url = 'assets/resources/downloaded.mbtiles';
cb({ path: path.normalize(`${__dirname}/${url}`) })
});
You can read up on protocol handlers in electron here: https://www.electronjs.org/docs/api/protocol#protocol

Broadcast channel api not triggering, workbox

I am new with workbox and have been trying to integrate it with my react app. Caching works fine but seems like broadcastUpdate is not triggering when content of file is changed.
This is the code snipped in the service worker:
workbox.routing.registerRoute(/\.(?:css)$/,
workbox.strategies.staleWhileRevalidate({
cacheName: 'css-cache',
broadcastUpdate: {
channelName: 'api-updates'
},
plugins: [
new workbox.broadcastUpdate.Plugin(
'api-updates'
)
]
})
);
And I am listening for it in my main file(index.js).Code snippet there is
const updatesChannel = new BroadcastChannel('api-updates');
updatesChannel.onmessage = function(e) {
console.log('Received', e.data);
alert('updated');
};
When the content of css file changes then no message is received in updatesChannel.
Any help will be much appreciated. Thanks.
I tried this as well, and same results for me.. it turned out that the headers were not readable by the client. This is a CORS thing. What solved it for me was to explicitly add these headers (or the ones you use) in the Django settings:
CORS_EXPOSE_HEADERS = ['Allow', 'Content-Length', 'ETag', 'Last-Modified', ]

How to queue post request using workbox?

Using following js in my service worker from workboxjs sample for my testing:
importScripts('https://unpkg.com/workbox-sw#0.0.2/build/importScripts/workbox-sw.dev.v0.0.2.js');
I want to try out how to queue the post requests in offline mode using workbox-sw, so once the network is available it process the request from the queue!
Q 1:
I think I need to import additional libraries to define my routes for post methods as shown here on github issue #634
How can use import on browser? I tried using importScripts but it doesn't work.
import * as worker from 'workbox-sw';
import 'workbox-routing';
Q 2:
Do I need any additional libraries for background sync, so post methods are queued?
I'd recommend using it as part of the larger workbox-sw framework, as a plugin. Here's a version of the sample code, modified to use importScripts() to pull in the Workbox code from a CDN. Alternatively, rather than using the pre-packaged bundles over a CDN, you could use the ES2015 module syntax and then a bundler like Rollup or Webpack to include the relevant code from your local node_modules into the final service worker file.
importScripts('https://unpkg.com/workbox-runtime-caching#1.3.0');
importScripts('https://unpkg.com/workbox-routing#1.3.0');
importScripts('https://unpkg.com/workbox-background-sync#1.3.0');
let bgQueue = new workbox.backgroundSync.QueuePlugin({
callbacks: {
replayDidSucceed: async(hash, res) => {
self.registration.showNotification('Background sync demo', {
body: 'Product has been purchased.',
icon: '/images/shop-icon-384.png',
});
},
replayDidFail: (hash) => {},
requestWillEnqueue: (reqData) => {},
requestWillDequeue: (reqData) => {},
},
});
const requestWrapper = new workbox.runtimeCaching.RequestWrapper({
plugins: [bgQueue],
});
const route = new workbox.routing.RegExpRoute({
regExp: new RegExp('^https://jsonplaceholder.typicode.com'),
handler: new workbox.runtimeCaching.NetworkOnly({requestWrapper}),
});
const router = new workbox.routing.Router();
router.registerRoute({route});

Browsersync Middleware to Replace String

I am trying to create a Browsersync middleware to replace a string in HTML files before they are served to the browser.
I'm not entirely sure this is even possible.
So far I am able to identify when a HTML file is being requested by:
function hublMiddleware (req, res, next) {
var parsed = require("url").parse(req.url);
if (parsed.pathname.match(/\.html$/)) {}
next();
};
I can put a console.log() inside the if statement so I know it's working.
But from here I am genuinely stuck. I have searched for examples of how this may be done, e.g.
res.removeHeader('Content-Length');
res.pipe($.replace(/({{\s|\s}})|({%.*%})/g, '<!---->'))
.pipe(res);
return next();
But to no avail.
I should say I am using Browsersync with Gulp. Any help with this would be much appreciated!
This one does exactly what you want:
bs-rewrite-rules
Here's how I used it:
gulp.task('serve', function () {
browserSync({
port: 8000,
server: {
baseDir: './'
},
plugins: ['bs-rewrite-rules'],
rewriteRules: [
{
match: 'YOUR_GOOGLE_MAPS_API_KEY',
replace:'<MY_ACTUAL_API_KEY>'
}
]
});
gulp.watch(['*.html', 'css/**/*.css', 'js/**/*.js'], reload);
});

Resources