Grails Events Push plugin not getting a response from server event - grails

I have been trying to get the grails events push plugin to work but cant seem to get response data from the server when the savedToDo event is called. I can see the logs in my console for the data coming to the server and executing the service code, but the client doesnt seem to be getting back a response. My code is as follows :
MyService.groovy
package eventspush
import grails.converters.JSON
import grails.events.*
class MyService {
//will receive client events from 'saveTodo' topic
#Listener(namespace='browser')
def saveTodo(Map data){
log.info(data)
data.moreData = "There we go...."
event('savedTodo', data) // will trigger registered browsers on 'savedTodo' topic
}
}
conf/MyEvents.groovy
events = {
"savedTodo" browser: true, browserFilter: { message, request ->
return true
}
}
index.gsp
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<r:require modules="grailsEvents"/>
<meta name='layout' content='main'/>
<r:script>
$(document).ready(function () {
/*
Register a grailsEvents handler for this window, constructor can take a root URL,
a path to event-bus servlet and options. There are sensible defaults for each argument
*/
window.grailsEvents = new grails.Events("${createLink(uri: '')}", {logLevel:"debug", transport:'websocket'});
var data = new Object();
data.name="some name"
grailsEvents.send('saveTodo', data); //will send data to server topic 'saveTodo'
grailsEvents.on('savedTodo', function (data) {
console.log(data)
}, {});
});
</r:script>
</head>
My problem is that I am not seeing any data being logged in the console. Does anybody have any idea what I am doing wrong?
Edit, I have worked out what was wrong and I have updated the MyEvents.groovy accordingly, the above code is now working

It seems that you have added the 'savedTodo' mapping to the PushEvents.groovy file within the Plugin directory. This is wrong!
You should add your own *Events.groovy file within the conf folder of your own project and leave the PushEvents.groovy file of the plugin untouched!
The other code looks fine so far ;)
There's a typo within your JavaScript section in index.gsp:
it reads:
grailsEvents.send('saveTodo', data);
when it should be:
grailsEvents.send('savedTodo', data);
you omitted the "d" ... ;)

So I found out what what was causing the data not being pushed to the client.
I edited the line in MyService.groovy from being
event('savedTodo', data)
to
event(topic:'savedTodo', data:data)
It is now working as expected

Related

Notify a web page served from service-worker cache that it has been served from SW cache

I developped a service worker which is serving pages from network first (and caching it) or, when offline, serving it from cache.
Ideally, I would like to inform the user (with a banner, or something like this) that the page has been served from the cache because we detected that he was offline.
Do you have an idea on how to implement this ?
Some ideas I had (but didn't really succeeded to implement) :
Inject some code in the cached response body (like, injecting some JS code triggering a offline-detected event which may or may not have an event listener on the webpage, depending on if I want to do something or not on this webpage while offline).
=> I didn't found how to append stuff in response's body coming from the cache to do this.
Send a postMessage from service worker to the webpage telling that it has been rendered using a cached content.
=> It doesn't seem to be possible as I don't have any MessagePort available in ServiceWorker's fetch event, making it impossible to send any postMessage() to it.
If you have any idea on how to implement this, I would be very happy to discuss about it.
Thanks in advance :)
I looked at different solutions :
Use navigator.onLine flag on HTML page : this is not reliable on my case, as my service worker might served cached page (because of, let's say, a timeout) whilst the browser can consider to be online
Inject custom headers when serving HTML response from cache : I don't see how it might work since response headers are generally not accessible on clientside
Call service-worker's postMessage() few seconds after content is served : problem is, in that case, that fetch event on the "starting" HTML page, doesn't have any clientId yet (we have a chicken & egg problem here, since service worker is not yet attached to the client at the moment the root html page is served from cache)
The only remaining solution I found was to inject some code in the cached response.
Something like this on the service worker side (the main idea is to inject some code on cached response body, triggering a served-offline-page event once DOM content has been loaded) :
async function createClonedResponseWithOfflinePageInjectedEvent(response) {
const contentReader = response.body.getReader();
let content = '', readResult = {done: false, value: undefined };
while(!readResult.done) {
readResult = await contentReader.read();
content += readResult.value?new TextDecoder().decode(readResult.value):'';
}
// Important part here, as we're "cloning" response by injecting some JS code in it
content = content.replace("</body>", `
<script type='text/javascript'>
document.addEventListener('DOMContentLoaded', () => document.dispatchEvent(new Event('served-offline-page')));
</script>
</body>
`);
return new Response(content, {
headers: response.headers,
status: response.status,
statusText: response.statusText
});
}
async function serveResponseFromCache(cache, request) {
try {
const response = await cache.match(request);
if(response) {
console.info("Used cached asset for : "+request.url);
// isCacheableHTMLPage() will return true on html pages where we're ok to "inject" some js code to notify about
if(isCacheableHTMLPage(request.url)) {
return createClonedResponseWithOfflinePageInjectedEvent(response);
} else {
return response;
}
} else {
console.error("Asset neither available from network nor from cache : "+request.url);
// Redirecting on offline page
return new Response(null, {
headers: {
'Location': '/offline.html'
},
status: 307
})
}
}catch(error) {
console.error("Error while matching request "+request.url+" from cache : "+error);
}
}
On the HTML page, this is simple, we only have to write this in the page :
document.addEventListener('served-offline-page', () => {
console.log("It seems like this page has been served as some offline content !");
})

Get Response from CREATE_STREAM

We upload a document from SAPUI5 to our SAP System using the CREATE_STREAM Method of the oData Service in ABAP. The creation of the document works fine.
What we would like to achieve is to get the response back to SAPUI5. Especially when there is an error during the creation of the document in the backend.
In Frontend we use the uploadSet Control.
...oUploadSet.uploadItem(oItem);
In the Backend we create a message with
...lo_message_container->add_message( iv_msg_type = /iwbep/cl_cos_logger=>error
iv_msg_number = '018'
iv_msg_id = lv_msg_id
iv_add_to_response_header = abap_true
)....
We can find the created message in the error protocol of our gateway server (/IWFND/ERROR_LOG). But how can this message be retrieved in SAPUI5 and used in the MessageManger Control?
We tried the onUploadCompleted Control but we can't find any response data there.
Can somebody explain how the response or a message header from the CREAT_STREAM method can be used in SAPUI5?
The "new" UploadSet control is kinda half-baked imo. The response will get lost in some internal method. This internal method will then trigger onUploadCompleted and you get nothing but useless information.
Lucky for us we can easily overwrite this internal stuff. UploadSet has an aggregation Uploader. We have to provide our own Uploader. Problem solved. Here is the line that needs to be modified.
sap.ui.define([
"sap/m/upload/Uploader",
...
], function (Uploader, ...) {
return Uploader.extend("my.custom.control.Uploader", {
uploadItem: function (oItem, aHeaders) {
// beginning of the method. take it from the official sources
oXhr.onreadystatechange = function () {
const oHandler = that._mRequestHandlers[oItem.getId()];
if (this.readyState === window.XMLHttpRequest.DONE && !oHandler.aborted) {
// we need to return the xhr object. it contains the response!
that.fireUploadCompleted({ item: oItem, xhr: oXhr });
}
};
// .. rest of the method
}
});
});
Use it like this
<mvc:View xmlns:custom="my.custom.control" ....>
<UploadSet items="....">
.....
<uploader>
<custom:Uploader uploadUrl="......"
uploadCompleted=".onUploadCompleted"
uploadStarted=".onUploadStarted" />
</uploader>
</UploadSet>
Edit: Your own uploader also means implementing your own event handlers (uploadAborted, uploadCompleted, uploadProgressed, uploadStarted). See the official documentation for more information about the events.

YouTube API Academy

I just completed the YouTube API tutorials on Codecademy and successfully managed to display results relating to a given 'q' value in the console window provided using the following code:
// Helper function to display JavaScript value on HTML page.
function showResponse(response) {
var responseString = JSON.stringify(response, '', 2);
document.getElementById('response').innerHTML += responseString;
}
// Called automatically when JavaScript client library is loaded.
function onClientLoad() {
gapi.client.load('youtube', 'v3', onYouTubeApiLoad);
}
// Called automatically when YouTube API interface is loaded (see line 9).
function onYouTubeApiLoad() {
// This API key is intended for use only in this lesson.
// See http://goo.gl/PdPA1 to get a key for your own applications.
gapi.client.setApiKey('AIzaSyCR5In4DZaTP6IEZQ0r1JceuvluJRzQNLE');
search();
}
function search() {
// Use the JavaScript client library to create a search.list() API call.
var request = gapi.client.youtube.search.list({
part: 'snippet',
q: "Hello",
});
// Send the request to the API server,
// and invoke onSearchRepsonse() with the response.
request.execute(onSearchResponse);
}
// Called automatically with the response of the YouTube API request.
function onSearchResponse(response) {
showResponse(response);
}
and:
<!DOCTYPE html>
<html>
<head>
<script src="search.js" type="text/javascript"></script>
<script src="https://apis.google.com/js/client.js?onload=onClientLoad" type="text/javascript"></script>
</head>
<body>
<pre id="response"></pre>
</body>
</html>
The problem I am having now is that I have taken this code and put it into my own local files with the intention of furthering my understanding and manipulating it work in a way which suits me, however it just returns a blank page. I assume that it works on Codecademy because they use a particular environment and the code used perhaps only works within that environment, I am surprised they wouldn't provide information on what changes would be required to use this outside of their given environment and was hoping someone could shed some light on this? Perhaps I am altogether wrong, if so, any insight would be appreciated.
Browser Console Output:
Failed to execute 'postMessage' on 'DOMWindow': The target origin provided ('file://') does not match the recipient window's origin ('null').
I also had the same problem but it was resolved when I used Xampp. What you have to do is install xampp on your machine and then locate its directory. After You will find a folder named "htdocs". Just move your folder containing both js and HTML file into this folder. Now you have to open Xampp Control Panel and click on start button for both - Apache and SQL server. Now open your browser and type in the URL:
http://localhost/"(Your htdocs directory name containing both of your pages)"
After this, click on .html file and you are done.

Can Meteor's Backbone Router handle POST requests?

I have a Meteor app which uses a router as per the sample Todos app, eg.:
var TodosRouter = Backbone.Router.extend({
routes: {
":list_id": "main"
},
main: function (list_id) {
...
},
...
});
Router = new TodosRouter;
I have an external service that lets me know when it is finished via a POST request to a URL that I specify. On receiving the POST request, it needs to perform some functions. Can I set this up consistent with the above framework? If not, am I better off writing a separate non-Meteor (eg. nodejs) app to handle the POSTs, or is there a way to make it work within Meteor?
I know this, this and this StackOverflow question are similar, but none of the answers explains how to do it with the Backbone.Router.
Thanks!
Here's how I migrated from the Backbone.Router to Iron-Router. I couldn't see a way to use them both at once.
First, I put code like in #Christian Fritz's answer, in the server folder:
Router.map(function () {
this.route('foo', {
path: '/foo',
where: 'server',
action: function() {
if (this.request.method == 'POST') {
console.log('post request received');
} else {
this.response.writeHead(404);
}
}
})
});
That handles the POST request, as tested by including a console.log in the action (which will write to the terminal window where meteor is running), and typing:
curl --data "p=bar" localhost:3000/foo
Then I replaced the Backbone.Router call with code in the client folder, as described in the Iron-Router docs, eg.
Router.configure({
layoutTemplate: 'layout'
});
Router.map(function () {
this.route('home', {
path: '/',
template: 'homepage',
});
});
Then I had to rewrite my templates so that pages were generated from different templates, instead of from different Session variable settings. This actually simplified my code, eg. instead of:
<body>
{{#if onPage 'home'}}
{{> homepage }}
{{/if}}
{{#if onPage 'help'}}
{{> helppage }}
{{/if}}
...
</body>
and
UI.registerHelper("onPage", function(name) {
return Session.equals('page', name);
});
it's just:
<template name="layout">
{{>yield}}
</template>
Any javascript that needs to direct the user to a specific page uses Router.go() with the route name:
Router.go('home');
Beyond that, I found a few places where I used to be able to assume my collections .findOne() had found one, but seem to have broken with Iron-Router. Wrapping these in ifs fixed them.
In Meteor 0.8, to check what page you are on you can use (see here):
Router.current().lookupProperty('template')

Consume WCF Rest Service in ASP.net using jquery

I am trying to consume a wcf rest api in a asp.net project using jquery. for doing so i have done:
Created a WCF Rest service source code can be downloaded from here.
Created a ASP.Net project to consume that restAPI using jquery. source code here.
In ASP .Net project on the click of button I am trying to call a REST service. But every time I gets two issues:
calling var jsondata = JSON.stringify(request); in TestHTML5.js throws an error saying "Microsoft JScript runtime error: 'JSON' is undefined"
When I press ignore it continues towards WCF Rest API call but it always returns error (Not Found) function. Rest API never gets called.
Thanks for every one's help in advance.
ANSWER:
Solution and source link can be found on this link.
I have looked at the sample code you provided and the problem is that you are violating the same origin policy restriction. You cannot perform cross domain AJAX calls. In your example the service is hosted on http://localhost:35798 and the web application calling it on http://localhost:23590 which is not possible. You will have to host both the service and the calling application in the same ASP.NET project. You seem to have attempted to enable CORS on the client side using ($.support.cors = true;) but on your service doesn't support CORS.
Another issue saw with your calling page (TestHTML5.htm) is the fact that you have included jquery twice (once the minified and once the standard version) and you have included your script (TestHTML5.js) after jquery. You should fix your script references. And yet another issue is the following line <script type="text/javascript"/> which is invalid.
So start by fixing your markup (I have removed all the CSS noise you had in your markup in order to focus on the important parts):
<!DOCTYPE html>
<html dir="ltr" lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title>SignUp Form</title>
<script type="text/javascript" src="../Scripts/jquery-1.7.2.min.js"></script>
<script type="text/javascript" src="../Scripts/TestHTML5.js"></script>
</head>
<body>
<button id="Send" onclick="testHTML5OnClick();">
Send Me ID!
</button>
</body>
</html>
and then in your TestHTML5.js you could also clean a little bit. For example your service is listening for the following url pattern json/{id} and accepting only GET verbs and you are attempting to use POST which is not possible. In addition to that you are attempting to use the JSON.stringify method which doesn't make any sense with the GET verb. You should simply send the id as part of the url portion as you defined in your service.
function testHTML5OnClick() {
var id = 5;
var url = "../RestServiceImpl.svc/json/" + id;
var type = 'GET';
callLoginService(url);
}
function callLoginService(url, type) {
$.ajax({
type: type,
url: url,
success: serviceSucceeded,
error: serviceFailed
});
}
function serviceSucceeded(result) {
alert(JSON.stringify(result));
}
function serviceFailed(result) {
alert('Service call failed: ' + result.status + '' + result.statusText);
}
Did u add this reference?
script type="text/javascript" src="../../json.js"></script>
I have same problem and search i get this and this result

Resources