How to serve a polymer application from dart shelf static file handler? - dart

I'm trying to serve a polymer application using a shelf static server. I create next structure:
polymerapp
- pubspec.yml
- bin
- server.dart
- web
- index.html
- lib
- main_app.dart
- main_app.html
Inside server.dart I put this code:
import 'dart:io' show Platform;
import 'dart:async' show runZoned;
import 'package:path/path.dart' show join, dirname;
import 'package:shelf/shelf_io.dart' as io;
import 'package:shelf_static/shelf_static.dart';
void main() {
// Assumes the server lives in bin/ and that `pub build` ran
var pathToBuild = join(dirname(Platform.script.toFilePath()),
'..', 'web');
var handler = createStaticHandler(pathToBuild,
defaultDocument: 'index.html');
var portEnv = Platform.environment['PORT'];
var port = portEnv == null ? 9999 : int.parse(portEnv);
runZoned(() {
io.serve(handler, '0.0.0.0', port);
print("Serving $pathToBuild on port $port");
},
onError: (e, stackTrace) => print('Oh noes! $e $stackTrace'));
}
the rest is the template polymer application created by dart editor.
The problem is that when I try to access localhost:9999 from the browser it shows me the next errors:
Failed to load resource: the server responded with a status of 404 (Not Found)
http://localhost:9999/packages/paper_elements/roboto.html
Failed to load resource: the server responded with a status of 404 (Not Found)
http://localhost:9999/packages/polymertest/main_app.html
Failed to load resource: the server responded with a status of 404 (Not Found)
http://localhost:9999/packages/polymer/init.dart
An error occurred loading file: package:polymer/init.dart
I want to do this for a faster way of development. In that case I don't need to build the polymer-dart application every time that I made a change.

You can pass serveFilesOutsidePath: true to createStaticHandler()
var handler = createStaticHandler(pathToBuild,
defaultDocument: 'index.html',
serveFilesOutsidePath: true);
Also, during development you can use pub serve with shelf_proxy for incremental build. See here for an example.

The combination of shelf_proxy in dev shelf_static in prod is very useful. The clever dart team came up with the idea of combining those and I borrowed the idea in mojito. You can use it as follows
import 'package:mojito/mojito.dart';
final app = mojito.init();
app.router..addStaticAssetHandler('/ui');
The code for that is here which you can copy if you prefer

Related

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

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.

I can not precache with workbox

I can not precache with workbox.
Now I am trying to cache in workbox's injectManifest mode.
It has been confirmed that the runtime cache described in the file specified by swSrc is working.
However, files named with globDirectory and globPatterns are not precached. Specifically, specifying globPatterns: ['** / *. {Js, css, html, png}'] results in an error.
Please tell me how to get rid of this error.
workbox uses workbox-build.
The following shows each version.
workbox-build: 3.6.3
node: 11.11
It is running on localhost.
injectManifest.js
const workboxBuild = require('workbox-build');
async function injectManifest() {
try {
await workboxBuild
.injectManifest({
globDirectory: DIST_PUBLIC,
globPatterns: ['**/*.{js,css,html,png}'],
swSrc: path.join(DIST_PUBLIC, 'sw.template.js'),
swDest: path.join(DIST_PUBLIC, 'sw.js'),
})
.then(() => {
console.log('Service worker has been generated.');
});
} catch (e) {
console.log(e);
}
}
injectManifest();
sw.template.js
importScripts('https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js');
if (workbox) {
console.log(`Yay! Workbox is loaded πŸŽ‰`);
workbox.core.skipWaiting();
workbox.routing.registerRoute(new RegExp('.*.*'), new workbox.strategies.staleWhileRevalidate());
} else {
console.log(`Boo! Workbox didn't load 😬`);
}
Error
AssertionError [ERR_ASSERTION]: Unable to find a place to inject the manifest. Please ensure that your service worker file contains the following:/(\.precacheAndRoute\()\s*\[\s*\]\s*(\)|,)/
at Object._callee$ (/Users/hoge/web/node_modules/workbox-build/build/entry-points/inject-manifest.js:82:13)
at tryCatch (/Users/hoge/web/node_modules/babel-runtime/node_modules/regenerator-runtime/runtime.js:62:40)
at Generator.invoke [as _invoke] (/Users/hoge/web/node_modules/babel-runtime/node_modules/regenerator-runtime/runtime.js:296:22)
at Generator.prototype.(anonymous function) [as next] (/Users/hoge/web/node_modules/babel-runtime/node_modules/regenerator-runtime/runtime.js:114:21)
at step (/Users/hoge/web/node_modules/babel-runtime/helpers/asyncToGenerator.js:17:30)
at /Users/hoge/web/node_modules/babel-runtime/helpers/asyncToGenerator.js:28:13
Please tell me how to get rid of this error.
This error indicates that injectManifest does not know where to inject the list of resources to be pre-cached in your service worker.
Quoting the documentation:
When workbox injectManifest is run, it looks for a specific string
(precaching.precacheAndRoute([]) by default) in your source service
worker file. It replaces the empty array with a list of URLs to
precache and writes the service worker file to its destination
location, based on the configuration options in config.js. The rest
of the code in your source service worker is left untouched.
So, most likely, all you have to do get rid of this error is to add the expected line in your service worker template:
importScripts('https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js');
if (workbox) {
console.log(`Yay! Workbox is loaded πŸŽ‰`);
workbox.core.skipWaiting();
workbox.precaching.precacheAndRoute([]); // URLs to precache injected by workbox build
workbox.routing.registerRoute(new RegExp('.*.*'), new workbox.strategies.staleWhileRevalidate());
} else {
console.log(`Boo! Workbox didn't load 😬`);
}

Ionic 3 Response with status: 0 for URL: null

I have an Ionic 3 app. I recently added the iOS platform.
When i run it on iOS (emulator and device) all the server requests that has headers fail with the error "Response with status: 0 for URL: null". On Android those requests works fine.
If I do the requests without headers i get the expected response from server.
I know the problem is with WKWebView and CORS. The server has the CORS configured correctly. I do the requests with #angular/http module.
Let's see some code.
This is my provider for doing requests to server:
import { Injectable } from '#angular/core';
import { Content } from 'ionic-angular';
import { Http, Headers, RequestOptions, URLSearchParams } from '#angular/http';
import { HTTP } from '#ionic-native/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import { Globalization } from '#ionic-native/globalization';
import { Globals } from '../providers/globals';
...
/// Here we have one example Request (it's inside a method)
/// Here I create the URL for the request
let url = this.server_url + this.server_functions.doSearch;
/// Now I create the Headers for the Request
let headers = new Headers();
/// As You can see, I have tried to pass diferent headers to server
// headers.append("Access-Control-Allow-Origin","*");
// headers.append("Origin", "https://localhost:8080");
// headers.append("Access-Control-Allow-Methods","POST, GET, OPTIONS, PUT");
// headers.append("Accept","application/json");
// headers.append("Content-Type","application/json; charset=utf-8");
/// If the user is logged in I have to send this headers to server
if ( !this.globals.guestMode ) {
headers.append("TokenAuth", this.globals.getLogRegData()["TokenAuth"]);
headers.append("IdAuth", this.globals.getLogRegData()["IdAuth"]);
}
/// And here we start the GET Request
this.http.get( url, { headers: headers } ).map(res => res.json()).subscribe(
data => {
// console.log( JSON.stringify(data) );
callback( data );
},
err => {
console.log("ELOL: "+err);
}
);
By the other way, I decided to try the #ionic-native/http module (as you can see in the imports) to avoid the WKWebView and CORS problems, but when I do the request with it, I got this error:
WARN: Native: tried calling HTTP.get, but the HTTP plugin is not installed.
WARN: Install the HTTP plugin: 'ionic cordova plugin add cordova-plugin-advanced-http'
This is how I do the Request with the native plugin:
this.httpnative.get(url, {}, {})
.then(data => {
console.log(data.status);
console.log(data.data); // data received by server
console.log(data.headers);
})
.catch(error => {
console.log(error.status);
console.log(error.error); // error message as string
console.log(error.headers);
});
This is a fragment of my app.module.ts:
import { HttpModule } from '#angular/http';
import { HTTP } from '#ionic-native/http';
...
#NgModule({
...
imports: [
...
HttpModule,
HTTP,
...
],
})
I hope some one can bring me some light on this, because I'm so lost in the paths of Ionic.
Thank You.
To avoid CORS problem specially in iOS you must use #ionic-native/http plugin which is actually Advanced HTTP plugin for API calling.
Follow below steps to use this plugin
Step 1: Add Http native plugin
$ ionic cordova plugin add cordova-plugin-advanced-http
$ npm install --save #ionic-native/http
Installation Link : HTTP
Step 2: Import HTTP native plugin in your file where you wants to cal API.
import { HTTP, HTTPResponse } from '#ionic-native/http';
Step 3: How to use this plugin for API call ?
constructor(public httpPlugin: HTTP) {
}
//Set header like this
this.httpPlugin.setHeader("content-type", "application/json");
//Call API
this.httpPlugin.get(this.url, {}, {}).then((response) => {
//Got your server response
}).catch(error => {
//Got error
});
Hope this will help you.
I have same issue in android, rest api not working in android emulator or device and give error: Response with 0 for Url: null.
and finally i got solution that there is cordova platform android version issue, if there is cordova Android version is 6.3 then it’s working properly but it there is cordova Android version is 7.0 or 7.1.1 then it’s not working for me.
So, I think in ios you should check your ios platform version and then try again.
You can check ios or android version using cli command.
ionic info
Also check your all cordova plugins is installed or not. If someone is missing then install it manually and check again.
You can check your cordova plugins using cli command.
cordova plugin -ls
I think it is helpful you.
Thanks

Finding out if a specific external website page exists (dart)

I'm trying to find out if a specific webpage exists. I'm using the dart:html library in the dart file where I'm interested in this information.
For example, I want to find out if a page like this exists: https://developer.mozilla.org/en-US/docs/Web/API/HTMLTextAreaElement.autofocus
How could I do this in dart ?
Just fetch the site and if you get an error response, the site doesn't exist (or is currently down).
import 'dart:html';
main() async {
var url = 'http://www.google.com/somepage.html';
var response = await HttpRequest.getString(url);
// examine response for errors
}
try at DartPad

websocket connection not happening using grails event-push plugin

I am trying to integrate grails events-push plugin to push events to browser however its not working. I made below changes for it
BuildConfig.groovy
grails.tomcat.nio = true
compile ":events-push:1.0.M7"
Config.groovy
events.push.servlet.initParams = [
'org.atmosphere.cpr.cometSupport': 'org.atmosphere.container.Tomcat7CometSupport',
"org.atmosphere.cpr.CometSupport.maxInactiveActivity": "100000"
]
tomcat.nio=true
Deleted context.xml(generated by plugin) from META-INF folder as tomcat was not working with it
In Client side i.e angular js
window.grailsEvents = new grails.Events('http://localhost:8080');
I managed to start my application successfully. I also below message in log
DEBUG cpr.DefaultBroadcaster - Broadcaster eventsbus doesn't have any associated resource. Message will be cached in the configured BroadcasterCache
Bu when I open my application in browser websocket do not work.
In serve end I see below meesage
2014-05-01 15:19:56,365 [http-nio-8080-exec-3] DEBUG cpr.AsynchronousProcessor - Timing out the connection for request AtmosphereRequest{ contextPath= servletPath=/g-eventsbus pathInfo=/eventsbus requestURI=/g-eventsbus/eventsbus requestURL=http://localhost:8080/g-eventsbus/eventsbus destroyable=false}
2014-05-01 15:19:56,366 [http-nio-8080-exec-3] WARN websocket.DefaultWebSocketProcessor - Unable to retrieve AtmosphereResource for org.apache.catalina.websocket.WsOutbound#269dd750
2014-05-01 15:19:57,783 [http-nio-8080-exec-5] DEBUG cpr.AsynchronousProcessor - Timing out the connection for request AtmosphereRequest{ contextPath= servletPath=/g-eventsbus pathInfo=/eventsbus requestURI=/g-eventsbus/eventsbus requestURL=http://localhost:8080/g-eventsbus/eventsbus destroyable=false}
I browser console end I see
WebSocket connection to 'ws://localhost:8080/g-eventsbus/eventsbus?X-Atmosphere-tracking-id=0&X-Atmosphere-Framework=1.1.0.beta3&X-Atmosphere-Transport=websocket&X-Atmosphere-TrackMessageSize=true&X-Cache-Date=0&topics=eventsbus' failed: WebSocket is closed before the connection is established.
Guys please help me I am struggling with this plugin from long time.
I'm using grails-events-push and almost everything works well.
In BuildConfig:
grails.servlet.version = "3.0"
grails.tomcat.nio=true
...
dependencies {
...
compile 'org.grails.plugins:events:1.0.0.BUILD-SNAPSHOT'
compile 'org.atmosphere:atmosphere-runtime:2.1.4'
}
plugins {
...
build ":tomcat:7.0.52.1"
runtime ":events-push:1.0.0.BUILD-SNAPSHOT"
}
You have to create one file to declare your events: mine is EasyRestaurantEvents.groovy
import static reactor.event.selector.Selectors.*
includes = ['push']
doWithReactor = {
reactor('grailsReactor'){
ext 'browser', [
(R('oneMessage-([0-9]+)')) : true
]
}
reactor('browser'){
ext 'browser', [
'oneMessageFromBrowser' : true
]
}
}
In the controller or service I can send an event in this way:
event('oneMessage-' + someId, mapObject)
In the client app I can receive this message in this way:
grailsEvents.on("oneMessage-666",
function(event){
alert("oneMessage was received for client 666");
});
In the server app, I can receive a message from the browser, in this way:
import reactor.spring.annotation.ReplyTo
import reactor.spring.annotation.Selector
class OneService {
#Selector(reactor = 'browser')
#ReplyTo
def oneMessageFromBrowser(Map data){
//do some work
}
}
To send an event from the browser yo can do:
grailsEvents.send('oneMessageFromBrowser', {message:'hello from browser'});
I hope this helps! I struggled with this plugin a lot! =(
But is very easy to use (when you make it work)
PS: I used another application created in angular to communicate with the server so I have to import the js manually:
"atmosphere.js": 2.1.5-javascript
"jquery.atmosphere.js": 2.1.5-jquery
Thanks mpccolorado for you reply. I got it working actually issue was in JS grails.Events should be created with globalTopicName.
var grailsEvents = new grails.Events(GRAILS_EVENT_URL, {globalTopicName: 'newReview'});

Resources