Google Picker API + Angular2 getting Failed to execute 'postMessage' on 'DOMWindow' - javascript

I am looking to integrate Google Picker API in my Angular 2 application.
My HTML Code:
<h1>
{{title}}
</h1>
<button (click)="onApiLoad()">Click Me!</button>
My Component Typescript Code:
import { Component } from '#angular/core';
declare var gapi:any;
declare var google:any;
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app works!';
developerKey = 'AIzaSyDFf3LoI2iGoq21Py5mx0YdRZC4-wOu2jQ';
clientId = "487432935414-ekonc03orunp50312oqmlrauutorpb53.apps.googleusercontent.com"
scope = ['https://www.googleapis.com/auth/drive.readonly'];
pickerApiLoaded = false;
oauthToken?: any;
constructor() { }
onApiLoad() {
gapi.load('auth', {'callback': this.onAuthApiLoad.bind(this)});
gapi.load('picker', {'callback': this.onPickerApiLoad.bind(this)});
}
onAuthApiLoad() {
gapi.auth.authorize(
{
'client_id': this.clientId,
'scope': this.scope,
'immediate': false
},
this.handleAuthResult);
}
onPickerApiLoad() {
this.pickerApiLoaded = true;
this.createPicker();
}
handleAuthResult(authResult) {
if (authResult && !authResult.error) {
if (authResult.access_token) {
var pickerBuilder = new google.picker.PickerBuilder();
var picker = pickerBuilder.
enableFeature(google.picker.Feature.NAV_HIDDEN).
setOAuthToken(authResult.access_token).
addView(google.picker.ViewId.PHOTOS).
setDeveloperKey('AIzaSyDFf3LoI2iGoq21Py5mx0YdRZC4-wOu2jQ').
setCallback(function (e){
var url = 'nothing';
console.log(url);
if (e[google.picker.Response.ACTION] == google.picker.Action.PICKED) {
var doc = e[google.picker.Response.DOCUMENTS][0];
url = doc[google.picker.Document.URL];
}
var message = 'You picked: ' + url;
console.log(message);
}).
build();
picker.setVisible(true);
}
}
}
createPicker() {
if (this.pickerApiLoaded && this.oauthToken) {
var picker = new google.picker.PickerBuilder().
addView(google.picker.ViewId.DOCUMENTS).
setOAuthToken(this.oauthToken).
setDeveloperKey(this.developerKey).
setCallback(this.pickerCallback).
build();
picker.setVisible(true);
}
}
pickerCallback(data) {
var url = 'nothing';
console.log(url);
if (data[google.picker.Response.ACTION] == google.picker.Action.PICKED) {
var doc = data[google.picker.Response.DOCUMENTS][0];
url = doc[google.picker.Document.URL];
}
var message = 'You picked: ' + url;
alert(message);
}
I am able to login to the google and getting access token, but after that I am unable to see documents in google drive.
Instead of that I am getting below errors:
cb=gapi.loaded_0:96 Failed to execute 'postMessage' on 'DOMWindow': The target origin provided ('https://docs.google.com') does not match the recipient window's origin ('https://localhost:4200').
Invalid 'X-Frame-Options' header encountered when loading 'https://docs.google.com/picker?protocol=gadgets&origin=https%3A%2F%2Flocalhost%3A4200&navHidden=true&oauth_token=ya29.GlskBRj74kFwZFmKi3ecOvMG9yGYjoTnaaxpf84M_qSOwANu_m3uSvzciXupB35CXxHvfHgNQuS_pfVG9lwRnrKA4JQiU5mTX3hNEWxBQ69cJTMyTnn0LzsQJOgB&developerKey=AIzaSyDFf3LoI2iGoq21Py5mx0YdRZC4-wOu2jQ&hostId=localhost&relayUrl=https%3A%2F%2Flocalhost%3A4200%2Ffavicon.ico&nav=((%22photos%22))&rpctoken=vcwxj7g2b339&rpcService=sz95b67m0h97&thirdParty=true': 'ALLOW-FROM https://localhost:4200' is not a recognized directive. The header will be ignored.
Output:

It's a CORS issue. Your client_id credentials should have you localhost whitelisted in https://console.developers.google.com/
Go to credentials
Click on the client ids you are using under OAuth 2.0 Client IDs
Add you localhost under Authorized JavaScript Origins i.e
http://localhost:8601

Related

Google Sheets API throwing 401 error while append rows to spreadsheet without login

Objective: Submit a form and store data to google spreadsheet
documentation: link
What I've done so far:
var CLIENT_ID = 'my_client_id.apps.googleusercontent.com';
var API_KEY = 'MY_API_KEY';
var DISCOVERY_DOCS = ["https://sheets.googleapis.com/$discovery/rest?version=v4"];
var SCOPES = "https://www.googleapis.com/auth/spreadsheets";
var authorizeButton = document.getElementById('authorize_button');
var signoutButton = document.getElementById('signout_button');
function handleClientLoad() {
gapi.load('client:auth2', initClient);
}
function initClient() {
gapi.client.init({
apiKey: API_KEY,
clientId: CLIENT_ID,
discoveryDocs: DISCOVERY_DOCS,
scope: SCOPES
})
function updateSigninStatus(isSignedIn) {
if (isSignedIn) {
//authorizeButton.style.display = 'none';
//signoutButton.style.display = 'block';
//listMajors();
} else {
//authorizeButton.style.display = 'block';
//signoutButton.style.display = 'none';
}
}
function appendPre(message) {
var pre = document.getElementById('content');
var textContent = document.createTextNode(message + '\n');
pre.appendChild(textContent);
}
function update_docs(data) {
var params = {
spreadsheetId: '1YXMlr_-I45AWM2b9QnLkuLQoI6dq6wEuVOcttOMv9hU',
range: 'A:I', // TODO: Update placeholder value.
valueInputOption: 'RAW',
insertDataOption: 'INSERT_ROWS',
};
var valueRangeBody = {
"range": 'A:I', // 9 cols
"majorDimension": 'ROWS',
"values": [
[
data[0].value,//nom,
data[1].value,//prenom,
data[2].value,//email,
data[3].value,//user_phone,
data[4].value,//company_name,
data[5].value,//user_type,
data[6].value,//account_name,
data[7].value,//password,
data[8].value,//comptes_sources,
]
]
};
var request = gapi.client.sheets.spreadsheets.values.append(params, valueRangeBody);
request.then(function(response) {
console.log(response.result);
}, function(reason) {
console.error('error: ' + reason.result.error.message);
});
}
I can successfully append rows to the spreadsheet if I'm logged in to my google account.
Question: Can I append row without logging in(if yes please provide some docs/code)?
Because if I submit the form from a private window it throws 401 error.
error message: error: Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential.
I think before you start working on this you need to understand a few things.
There is a difference between private and public data.
Public data, Searching publicly uploaded youtube videos
Private data, My person gmail account, drive account, calendar account.
Even setting the sheet to public will not help you as with it public using an api key you will only be allowed to read the sheet not update it.
Answer: No you can not append a row without the application being authenticated and having access to the data.
Assuming that this is a sheet that you personally own you could set up a service account authenticate and grant the service account access to the sheet it will then be able to make the changes for you without you having to login. However this depends upon how your application works and what language you are using. I dont think that javascript supports service account authentication.

method().bind(this) + Angular4 getting Cannot read property 'myFun' of undefined

I'm integrating Google picker API in my application. I'm following the official documentation Google Picker API
I have successfully done my job, but after adding below code I am unable to use class methods and variables. Getting Cannot read property of undefined error
gapi.load('auth', {'callback': this.onAuthApiLoad.bind(a)});
Complete code is:
onApiLoad() {
var a= this;
gapi.load('auth', {'callback': this.onAuthApiLoad.bind(a)});
gapi.load('picker');
}
onAuthApiLoad() {
gapi.auth.authorize(
{
'client_id': this.clientId,
'scope': this.scope,
'immediate': false
},
this.handleAuthResult);
}
handleAuthResult(authResult) {
if (authResult && !authResult.error) {
if (authResult.access_token) {
var pickerBuilder = new google.picker.PickerBuilder();
var picker = pickerBuilder.
enableFeature(google.picker.Feature.NAV_HIDDEN).
setOAuthToken(authResult.access_token).
addView(google.picker.ViewId.DOCS).
setCallback(this.myFun).
build();
picker.setVisible(true);
}
}
}
myFun(e){
}
I have added below line to handle auth request
this.handleAuthResult.bind(this)

"GAPI is not defined" message

I'm trying to use the Google Sheets API for inclusion in my web app, but I keep receiving a error specifying that the gapi library is not defined. I've tried delay the request to the server by using the ComponentDidMount life cycle method, and even using a timeout in that method, but I keep receiving the same error. How can I have the gapi library defined for use then in my app ?
import React from 'react';
var CLIENT_ID = '';
var SCOPES = ["https://www.googleapis.com/auth/spreadsheets.readonly"];
export default class MyNavbar extends React.Component {
constructor(props) {
super(props);
}
componentDidMount(){
this.checkAuth();
}
/**
* Check if current user has authorized this application.
*/
checkAuth(){
gapi.auth.authorize(
{
'client_id': CLIENT_ID,
'scope': SCOPES.join(' '),
'immediate': true
}, this.handleAuthResult());
}
/**
* Handle response from authorization server.
*
* #param {Object} authResult Authorization result.
*/
handleAuthResult(authResult) {
var authorizeDiv = document.getElementById('authorize-div');
if (authResult && !authResult.error) {
// Hide auth UI, then load client library.
authorizeDiv.style.display = 'none';
loadSheetsApi();
} else {
// Show auth UI, allowing the user to initiate authorization by
// clicking authorize button.
authorizeDiv.style.display = 'inline';
}
}
/**
* Initiate auth flow in response to user clicking authorize button.
*
* #param {Event} event Button click event.
*/
handleAuthClick(event) {
gapi.auth.authorize(
{client_id: CLIENT_ID, scope: SCOPES, immediate: false},
handleAuthResult);
return false;
}
/**
* Load Sheets API client library.
*/
loadSheetsApi() {
var discoveryUrl =
'https://sheets.googleapis.com/$discovery/rest?version=v4';
gapi.client.load(discoveryUrl).then(listMajors);
}
/**
* Print the names and majors of students in a sample spreadsheet:
* https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit
*/
listMajors() {
gapi.client.sheets.spreadsheets.values.get({
spreadsheetId: '1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms',
range: 'Class Data!A2:E',
}).then(function(response) {
var range = response.result;
if (range.values.length > 0) {
appendPre('Name, Major:');
for (i = 0; i < range.values.length; i++) {
var row = range.values[i];
// Print columns A and E, which correspond to indices 0 and 4.
appendPre(row[0] + ', ' + row[4]);
}
} else {
appendPre('No data found.');
}
}, function(response) {
appendPre('Error: ' + response.result.error.message);
});
}
/**
* Append a pre element to the body containing the given message
* as its text node.
*
* #param {string} message Text to be placed in pre element.
*/
appendPre(message) {
var pre = document.getElementById('output');
var textContent = document.createTextNode(message + '\n');
pre.appendChild(textContent);
}
render(){
return (
<div>
<h1>Hello World My Name Is Justin 2</h1>
<div id="authorize-div"></div>
<pre id="output"></pre>
</div>
);
}
}
Here try this.
import React from 'react';
import asyncLoad from 'react-async-loader'; // for loading script tag asyncly `npm i --save react-async-loader`
const CLIENT_ID = '';
const SCOPES = ["https://www.googleapis.com/auth/spreadsheets.readonly"];
// For making gapi object passed as props to our component
const mapScriptToProps = state => ({
// gapi will be this.props.gapi
gapi: {
globalPath: 'gapi',
url: 'https://your-gapi-url'
}
});
#asyncLoad(mapScriptToProps)
class MyNavbar extends React.Component {
constructor(props) {
super(props);
this.gapi = null;
// You need to bind methods to this class's object context. (i.e this)!
this.checkAuth = this.checkAuth.bind(this);
this.handleAuthResult = this.authResult.bind(this);
this.handleAuthClick = this.handleAuthClick.bind(this);
this.loadSheetsApi = this.loadSheetsApi.bind(this);
this.listMajors = this.listMajors.bind(this);
}
componentDidMount() {
// Check is gapi loaded?
if (this.props.gapi !== null) {
this.checkAuth();
}
}
componentWillReceiveProps({ gapi }) {
if (gapi!== null) {
this.checkAuth();
}
}
/**
* Check if current user has authorized this application.
*/
checkAuth() {
// this will give you an error of gapi is not defined because there is no
// reference of gapi found globally you cannot access global object which are
// not predefined in javascript( in this case its window.gapi ).
// properties added by Programmer cannot be accessed directly( if it's not in the same file as well as the same scope!) in commonjs modules. Because they
// don't' run in a global scope. Any variable in another module which is not
// exported, will not be available to other modules.
this.gapi = window.gapi; // you can do like this. Now you can access gapi in all methods if this class.
this
.gapi
.auth
.authorize({
'client_id': CLIENT_ID,
'scope': SCOPES.join(' '),
'immediate': true
}, this.handleAuthResult());
}
/**
* Handle response from authorization server.
*
* #param {Object} authResult Authorization result.
*/
handleAuthResult(authResult) {
var authorizeDiv = document.getElementById('authorize-div');
if (authResult && !authResult.error) {
// Hide auth UI, then load client library.
authorizeDiv.style.display = 'none';
loadSheetsApi();
} else {
// Show auth UI, allowing the user to initiate authorization by clicking
// authorize button.
authorizeDiv.style.display = 'inline';
}
}
/**
* Initiate auth flow in response to user clicking authorize button.
*
* #param {Event} event Button click event.
*/
handleAuthClick(event) {
// gapi.auth.authorize( here also gapi is not defined
this
.gapi
.auth
.authorize({
client_id: CLIENT_ID,
scope: SCOPES,
immediate: false
}, handleAuthResult);
return false;
}
/**
* Load Sheets API client library.
*/
loadSheetsApi() {
var discoveryUrl = 'https://sheets.googleapis.com/$discovery/rest?version=v4';
// also will give your error
// for gapi being not defined.
// gapi.client.load(discoveryUrl).then(listMajors);
this
.gapi
.client
.load(discoveryUrl)
.then(listMajors);
}
/**
* Print the names and majors of students in a sample spreadsheet:
* https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit
*/
listMajors() {
this.gapi
.client
.sheets
.spreadsheets
.values
.get({spreadsheetId: '1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms', range: 'Class Data!A2:E'})
.then(function (response) {
var range = response.result;
if (range.values.length > 0) {
appendPre('Name, Major:');
for (i = 0; i < range.values.length; i++) {
var row = range.values[i];
// Print columns A and E, which correspond to indices 0 and 4.
appendPre(row[0] + ', ' + row[4]);
}
} else {
appendPre('No data found.');
}
}, function (response) {
appendPre('Error: ' + response.result.error.message);
});
}
/**
* Append a pre element to the body containing the given message
* as its text node.
*
* #param {string} message Text to be placed in pre element.
*/
appendPre(message) {
// as you can see. You are accessing window.document as document.
// its fine because its defined in javascript (implicitly),
// not explicitly by programmer(here you!).
var pre = document.getElementById('output');
var textContent = document.createTextNode(message + '\n');
pre.appendChild(textContent);
}
render() {
return (
<div>
<h1>Hello World My Name Is Justin 2</h1>
<div id="authorize-div"></div>
<pre id="output"></pre>
</div>
);
}
}
export default MyNavbar;
You need to load the gapi library before bundle.js in index.html file, or better you can load the gapi js script async with react-async-loader.
Here's how you can do it :
import React, { Component, PropTypes } from 'react';
import asyncLoad from 'react-async-loader'; // for loading script tag asyncly
// For making gapi object passed as props to our component
const mapScriptToProps = state => ({
gapi: {
globalPath: 'gapi',
url: 'https://your-gapi-url'
}
});
// decorate our component
#asyncLoad(mapScriptToProps)
class yourComponent extends Component {
componentDidMount() {
// Check is gapi loaded?
if (this.props.gapi !== null) {
this.checkAuth();
}
}
componentWillReceiveProps({ gapi }) {
if (gapi!== null) {
this.checkAuth();
}
}
checkAuth = () => {
// Better check with window and make it available in component
this.gapi = window.gapi;
this.gapi.auth.authorize({
'client_id': CLIENT_ID,
'scope': SCOPES.join(' '),
'immediate': true
}, this.handleAuthResult);
}
handleAuthResult = (authData) => {
// Your auth loagic...
}
render() {
return ( <div>
{ this.props.gapi &&
<YourSpreadsheetOrWhatever data={ this.getData() } />
}
</div>)
}
}
Try replacing your code with this and check if it works.
I found this project https://github.com/rlancer/gapi-starter it may be helpful.
Google Login & API + ReactJS + Flow + Webpack starter kit
Google API's are great but they were designed before the module partner of Javascript programing became popular. This starter fixes that handing Google login and library loading for you.
Get the Code!
git clone https://github.com/rlancer/gapi-starter.git
cd gapi-starter
npm install
You need to load the gapi library before bundle.js in index.html file as :
<script src="https://apis.google.com/js/api.js"></script>
And then you just need to initialize your gapi variablesas :
let gapi = window.gapi;

Google drive Public

I am working with Google drive picker, where once an item is selected from Google drive a url is produced. The problem is that that url is only accessible by the owner, and hence not public. I want the URL to be publicly accessible.
Hence, i've looked into the following guide:
https://developers.google.com/picker/docs/reference#Response.Documents
and feel that the Document.AUDIENCE class would be best applicable, however I do not know how to add that criteria into the below google drive picker example code.
Any help would be greatly appreciated.
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<title>Google Picker Example</title>
<script type="text/javascript">
// The Browser API key obtained from the Google Developers Console.
var developerKey = 'xxxxxxxYYYYYYYY-12345678';
// The Client ID obtained from the Google Developers Console. Replace with your own Client ID.
var clientId = "1234567890-abcdefghijklmnopqrstuvwxyz.apps.googleusercontent.com"
// Scope to use to access user's photos.
var scope = ['https://www.googleapis.com/auth/photos'];
var pickerApiLoaded = false;
var oauthToken;
// Use the API Loader script to load google.picker and gapi.auth.
function onApiLoad() {
gapi.load('auth', {'callback': onAuthApiLoad});
gapi.load('picker', {'callback': onPickerApiLoad});
}
function onAuthApiLoad() {
window.gapi.auth.authorize(
{
'client_id': clientId,
'scope': scope,
'immediate': false
},
handleAuthResult);
}
function onPickerApiLoad() {
pickerApiLoaded = true;
createPicker();
}
function handleAuthResult(authResult) {
if (authResult && !authResult.error) {
oauthToken = authResult.access_token;
createPicker();
}
}
// Create and render a Picker object for picking user Photos.
function createPicker() {
if (pickerApiLoaded && oauthToken) {
var picker = new google.picker.PickerBuilder().
addView(google.picker.ViewId.PHOTOS).
setOAuthToken(oauthToken).
setDeveloperKey(developerKey).
setCallback(pickerCallback).
build();
picker.setVisible(true);
}
}
// A simple callback implementation.
function pickerCallback(data) {
var url = 'nothing';
if (data[google.picker.Response.ACTION] == google.picker.Action.PICKED) {
var doc = data[google.picker.Response.DOCUMENTS][0];
url = doc[google.picker.Document.URL];
}
var message = 'You picked: ' + url;
document.getElementById('result').innerHTML = message;
}
</script>
</head>
<body>
<div id="result"></div>
<!-- The Google API Loader script. -->
<script type="text/javascript" src="https://apis.google.com/js/api.js?onload=onApiLoad"></script>
</body>
To add public permissions to the selected file. You will need to use the drive api to add file permissions
Please see https://developers.google.com/drive/v2/reference/permissions/insert
You will need to insert a permission to the file with role set to 'reader' and the type to 'anyone'
You can refer to the javascript implementation in the example
/**
* Insert a new permission.
*
* #param {String} fileId ID of the file to insert permission for.
* #param {String} value User or group e-mail address, domain name or
* {#code null} "default" type.
* #param {String} type The value "user", "group", "domain" or "default".
* #param {String} role The value "owner", "writer" or "reader".
*/
function insertPermission(fileId, value, type, role) {
var body = {
'value': value,
'type': type,
'role': role
};
var request = gapi.client.drive.permissions.insert({
'fileId': fileId,
'resource': body
});
request.execute(function(resp) { });
}
To elaborate on #Sam's answer, here is how you would do it without using the gapi.client.drive Javascript API.
Let's say that some_id is your document id:
request = gapi.client.request({
path: '/drive/v2/files/some_id/permissions',
method: 'POST',
body: {
role: 'reader'
type: 'anyone'
}
});
request.execute(function (res) {
console.log(res);
})
Which generates a request to https://content.googleapis.com/drive/v2/files/some_id/permissions?alt=json with this body: {role: "reader", type: "anyone"}
Here is the result you will get:
{
"kind": "drive#permission",
"etag": "some etage",
"id": "anyone",
"selfLink": "https://www.googleapis.com/drive/v2/files/some_id/permissions/anyone",
"role": "reader",
"type": "anyone"
}

Problems with testing SignalR with Jasmine in Angular app

I have a serious issue here. My application relies on SignalR functionality, but because of that I am unable to write unit tests. I am new to testing frameworks and have used Jasmine only in simple cases. SignalR has proven to be too much of a challenge for me, but I need to understand how I can successfully test it.
This is my CommsApp.ts file [typescript]:
/// <reference path="References.ts" />
var commsAnimationsModule = angular.module('forge.communications.animations', ['ngAnimate']);
var commsDirectivesModule = angular.module('forge.communications.directives', []);
var commsServicesModule = angular.module('forge.communications.services', []);
var commsFiltersModule = angular.module('forge.communications.filters', []);
var commsApp = angular.module('forge.communications.CommsApp',
[
'ngRoute',
'ngAnimate',
'cfValidation',
'ui.bootstrap',
'forge.communications.animations',
'forge.communications.directives',
'forge.communications.services',
'forge.communications.filters',
'angularFileUpload',
'timeRelative'
]);
commsApp.config(function ($routeProvider: ng.route.IRouteProvider, $locationProvider: any) {
$locationProvider.html5Mode(true);
$routeProvider.
when('/scheduled-messages', {
templateUrl: '/forge/CommsApp/js/Controllers/ScheduledMessageList/ScheduledMessageList.html',
controller: 'ScheduledMessageListController'
}).
when('/geotriggered-messages', {
templateUrl: '/forge/CommsApp/js/Controllers/GeoMessageList/GeoMessageList.html',
controller: 'GeoMessageListController'
}).
when('/scheduled-message/create', {
templateUrl: '/forge/CommsApp/js/Controllers/CreateScheduledMessage/CreateScheduledMessage.html',
controller: 'CreateScheduledMessageController'
}).
when('/scheduled-message/edit/:id', {
templateUrl: '/forge/CommsApp/js/Controllers/EditScheduledMessage/EditScheduledMessage.html',
controller: 'EditScheduledMessageController'
}).
when('/geotriggered-message/create', {
templateUrl: '/forge/CommsApp/js/Controllers/CreateGeotriggeredMessage/CreateGeotriggeredMessage.html',
controller: 'CreateGeotriggeredMessageController'
}).
when('/geotriggered-message/edit/:id', {
templateUrl: '/forge/CommsApp/js/Controllers/EditGeotriggeredMessage/EditGeotriggeredMessage.html',
controller: 'EditGeotriggeredMessageController'
}).
otherwise({
redirectTo: '/scheduled-messages'
});
});
commsApp.run(function ($rootScope: ICommsRootScope, commsSignalrEventService: CommsSignalrEventService, commsMgmtHttpService: CommsMgmtHttpServiceClient) {
// set up the items on the root scope
$rootScope.SelectedLocale = 'en-ie';
$rootScope.ForgeApplicationKey = "9496B737-7AE2-4FBD-B271-A64160759177";
$rootScope.AppVersionString = "1.0.0";
$rootScope.SessionToken = getCookie("ForgeSessionToken");
commsSignalrEventService.initialize().done(() => {
// send any messages about a new user logging in to the application here.
});
// call this at app startup to pre-cache this data for the create and edit pages
commsMgmtHttpService.GetUpdateMessageEditorOptions();
});
function getCookie(name:string) {
var value = "; " + document.cookie;
var parts = value.split("; " + name + "=");
if (parts.length == 2) return parts.pop().split(";").shift();
}
This is the test file (I removed most of the code since it's not working anyway, I have been chasing my own tail here):
describe('forge.communications.CommsApp', function () {
beforeEach(module('forge.communications.CommsApp'));
var route, rootScope, proxy, commsSignalrEventService;
beforeEach(inject(function (_$route_, _$rootScope_, _commsSignalrEventService_) {
route = _$route_,
rootScope = _$rootScope_;
commsSignalrEventService = _commsSignalrEventService_;
}));
describe("should map routes to controllers and templates", function () {
it("/scheduled-messages route should be mapped to ScheduledMessageListController", function () {
expect(2).toEqual(2);
expect(route.routes['/scheduled-messages'].controller).toBe('ScheduledMessageListController');
});
});
});
This is CommsSignalREventService.ts file:
var servicesModule: ng.IModule = angular.module('forge.communications.services');
servicesModule.factory('commsSignalrEventService', function ($rootScope): CommsSignalrEventService {
return new CommsSignalrEventService($rootScope);
});
class CommsSignalrEventService {
private $rootScope: ng.IScope;
private proxy:any = null;
constructor($rootScope) {
this.$rootScope = $rootScope;
}
public initialize(): JQueryPromise<any>{
this.proxy = $.connection['communicationsCenterHub'];
console.log('proxy',this.proxy);
//scheduled messages
this.proxy.client.broadcastScheduledMessageCreatedEvent = (messageId: number) => {
this.$rootScope.$broadcast('comms-message-created', { messageId: messageId });
};
this.proxy.client.broadcastScheduledMessageUpdatedEvent = (messageId: number) => {
this.$rootScope.$broadcast('comms-message-updated', { messageId: messageId });
};
this.proxy.client.broadcastScheduledMessageStateChangedEvent = (messageId: number) => {
this.$rootScope.$broadcast('comms-message-statechanged', { messageId: messageId });
};
this.proxy.client.broadcastScheduledMessageDeletedEvent = (messageId: number) => {
this.$rootScope.$broadcast('comms-message-deleted', { messageId: messageId });
};
//geotriggered messages
this.proxy.client.broadcastGeoMessageCreatedEvent = (messageId: number) => {
this.$rootScope.$broadcast('comms-geomessage-created', { messageId: messageId });
};
this.proxy.client.broadcastGeoMessageUpdatedEvent = (messageId: number) => {
this.$rootScope.$broadcast('comms-geomessage-updated', { messageId: messageId });
};
this.proxy.client.broadcastGeoMessageStateChangedEvent = (messageId: number) => {
this.$rootScope.$broadcast('comms-geomessage-statechanged', { messageId: messageId });
};
this.proxy.client.broadcastGeoMessageDeletedEvent = (messageId: number) => {
this.$rootScope.$broadcast('comms-geomessage-deleted', { messageId: messageId });
};
var promise = $.connection.hub.start();
promise.done(function () {
//console.log('comms signalr hub started');
});
return promise;
}
public RegisterScheduledMessageCreated(messageId: number): void{
this.proxy.server.registerScheduledMessageCreated(messageId);
}
public RegisterScheduledMessageUpdated(messageId: number): void {
this.proxy.server.registerScheduledMessageUpdated(messageId);
}
public RegisterScheduledMessageDeleted(messageId: number): void {
this.proxy.server.registerScheduledMessageDeleted(messageId);
}
public RegisterScheduledMessageStateChanged(messageId: number): void {
this.proxy.server.registerScheduledMessageStateChanged(messageId);
}
public RegisterGeoMessageCreated(messageId: number): void {
this.proxy.server.registerGeoMessageCreated(messageId);
}
public RegisterGeoMessageUpdated(messageId: number): void {
this.proxy.server.registerGeoMessageUpdated(messageId);
}
public RegisterGeoMessageDeleted(messageId: number): void {
this.proxy.server.registerGeoMessageDeleted(messageId);
}
public RegisterGeoMessageStateChanged(messageId: number): void {
this.proxy.server.registerGeoMessageStateChanged(messageId);
}
}
The error I am seeing constantly in the command line, whenever I run karma, is forge.communications.CommsApp encountered a declaration exception FAILED
TypeError: Cannot read property 'client' of undefined at CommsSignalREventService, meaning that the 'proxy' variable in CommsSignalREventService.ts file is undefined:
this.proxy = $.connection['communicationsCenterHub'];
console.log('proxy', this.proxy); //resolves to UNDEFINED in tests, works fine in the app
this.proxy.client.broadcastScheduledMessageCreatedEvent = function (messageId) {
_this.$rootScope.$broadcast('comms-message-created', { messageId: messageId });
};
I will appreciate any help, because the amount of time I have put trying to figure it out is beyond ridiculous at this stage.
I think your issue is that your actual app references JavaScript dynamically generated by SignalR at runtime. This script is generally found at "~/signalr/hubs" or "~/signalr/js".
This script is what creates the $.connection['communicationsCenterHub'] proxy for you. Without a reference to this script in your test, this proxy will be undefined.
I'm presuming that you are not running the SignalR server when you are running your Jasmine tests. If this is the case you have two options:
You can simply copy the script generated by SignalR at "~/signalr/hubs" to a file and reference that in your tests. You could manually do this by pointing your browser to the generated script URL and, but you would have to update this file anytime your hub changes. You can automate this by running signalr.exe ghp /path:[path to the .dll that contains your Hub class] as a post-build step which will generate this file for you.
You can just avoid using the generated proxy altogether. Look at the "Without the generated proxy" section of the SignalR JS API reference.

Categories

Resources