Trouble Writing Firefox Add on (Registering Observers, Javascript) - http

I am writing an add-on for firefox but I seem to have a few issues with the channel. I am trying to write a program that receives a websites certificate information and saves it in a specific format. It does not pick up the information I need.
Heres my code:(Sorry for putting a lot there, but its all essential.)
const {Cc,Ci,Cu} = require("chrome");
let console = (Cu.import("resource://gre/modules/devtools/Console.jsm", {})).console;
console.log("Importing console.\n");
// Cu.import("resource://gre/modules/XPCOMUtils.jsm");
this._loadHandler = function(e) {myExtension.onPageLoad(e); };
var utils = require('sdk/window/utils');
var gBrowser = utils.getMostRecentBrowserWindow().getBrowser();
gBrowser.addEventListener("load", this._loadHandler, true);
// We register an observer on page load, through page load event listener. Is this round-about?
// Can we register it directly?
var myExtension = {
onPageLoad: function(aEvent) {
var doc = aEvent.originalTarget; // doc is document that triggered "onload" event
var i = 0;
console.log("Page has been loaded.\n");
this.registerObserver("http-on-examine-response");
this.initialized = true;
},
onPageUnload: function(aEvent) {
console.log("Page has been unloaded.\n");
this.unregisterObserver("http-on-examine-response");
},
registerObserver: function(topic) {
var observerService = Cc["#mozilla.org/observer-service;1"]
.getService(Ci.nsIObserverService);
observerService.addObserver(this, topic, false);
console.log("Observer Registered.\n");
},
unregisterObserver: function(topic) {
var observerService = Cc["#mozilla.org/observer-service;1"]
.getService(Ci.nsIObserverService);
observerService.removeObserver(this, topic);
console.log("Observer Unregistered.\n");
},
//Observation Function
observe: function(channel, topic, data) {
console.log("Running Observe....\n");
this.examine_cert(channel);
},
examine_cert: function(channel) {
channel.QueryInterface(Ci.nsIHttpChannel);
//Assigns the channel of the http resource in the host port
var host = channel.URI.hostPort;
//Checks for security info
var si = channel.securityInfo;
if (!si){
console.log("No securityInfo for "+ host +" , returning\n");
return;
}
//Gets Certificates status
si.QueryInterface(Ci.nsISSLStatusProvider);
var st = si.SSLStatus;
if (!st){
console.log(st + "\n");
console.log("No SSLStatus for "+ host +" , returning\n");
return;
}
st.QueryInterface(Ci.nsISSLStatus);
var cert = st.serverCert;
if (!cert){
console.log("No serverCert for "+ host +" , returning\n");
return;
}
// by now we must have a non-empty cert, print it and the status
console.log( "status: " + st + "\n");
console.log( "cert : " + cert.commonName + "\n");
//certobj.ciphername = st.cipherName; // apparently, parsed out of cert when SSLStatus is made
//certobj.keyLength = st.keyLength;
//certobj.secretKeyLength = st.secretKeyLength;
},
// Recursively descend into the object, looking for displayName matching re;
// also, validate objects as we go.
findASN1Object: function (struc, re) {
if (!struc){
console.log("\nCertificate does not have a valid structure.\n");
return;
}
// Shortcut: object with displayName matching re found, return it immediately
if (re.test(struc.displayName)) return struc;
var s = Ci;
try {
s = struc.QueryInterface(Ci.nsIASN1Sequence);
}
catch (e) {
console.log("\nCertificate failed nsIASN1Sequence conversion\n");
}
if ( !s || ! s.isValidContainer) {
console.log("\nCertificate does not have a valid container.\n");
return;
}
// check all the objects recursively
for (var i=0; i<s.ASN1Objects.length; i++) {
struc = s.ASN1Objects.queryElementAt(i, Ci.nsIASN1Object);
var res = this.findASN1Object(struc, re);
if (res) return res;
}
},
//------ Object containment:
// nsIHttpChannel > securityInfo (nsISSLStatusProvider) > SSLStatus (nsISSLStatus) > serverCert (nsIX509Cert)
fillCertObj: function(obj, cert) {
obj.cert = cert;
console.log("\nCert:" + cert + "\n");
obj.notBefore = cert.validity.notBefore;
obj.notAfter = cert.validity.notAfter;
if (cert.issuer) {
obj.issuerMd5Fingerprint = cert.issuer.md5Fingerprint;
obj.issuerSha1Fingerprint = cert.issuer.sha1Fingerprint;
}
else {
//console.log("no issuer: "+ [cert.commonName, cert.issuer, cert.sha1Fingerprint]); \
console.log("\nThe Certificate doesn't have an Issuer.\n");
}
var keys = ["commonName", "organization", "organizationalUnit", "serialNumber","emailAddress",
"issuerCommonName", "issuerOrganization", "issuerOrganizationUnit","md5Fingerprint", "sha1Fingerprint" ];
for (var i in keys){
obj[keys[i]] = cert[keys[i]];
}
obj.subjectAltName = [];
var san = this.findASN1Object(cert.ASN1Structure, /^Certificate Subject Alt Name$/);
if (san) {
//this.log("SAN:", [san.displayName, san.displayValue]);
var m, re = /DNS Name: ((?:\*\.)?[a-z0-9.-]+)/g;
// *.smth1.smth2.smth3....smthn where smth has only a-z,0-9,or - or
// smth1.smth2.smth3....smthn where smth has only a-z,0-9,or -
// push domain names contained in displayValue onto the list subjectAltName
while (m = re.exec(san.displayValue))
obj.subjectAltName.push(m[1]);
}
console.log("Filled Certificate.\n");
},
init_cert: function(){
var certobj = this.newCertObj();
//certobj.host = host;
//certobj.ciphername = st.cipherName;
//certobj.keyLength = st.keyLength;
//certobj.secretKeyLength = st.secretKeyLength;
//this.fillCertObj(certobj.now, cert);
console.log(certobj);
},
newCertObj: function() {
console.log("Creating Empty Certificate.\n");
return {
flags: 0,
host: "",
warn: {},
now: {
commonName: "",
organization: "",
organizationalUnit: "",
serialNumber: "",
emailAddress: "",
notBefore: "",
notAfter: "",
issuerCommonName: "",
issuerOrganization: "",
issuerOrganizationUnit: "",
md5Fingerprint: "",
sha1Fingerprint: "",
issuerMd5Fingerprint: "",
issuerSha1Fingerprint: "",
cert: null,
},
old: {
commonName: "",
organization: "",
organizationalUnit: "",
serialNumber: "",
emailAddress: "",
notBefore: "",
notAfter: "",
issuerCommonName: "",
issuerOrganization: "",
issuerOrganizationUnit: "",
md5Fingerprint: "",
sha1Fingerprint: "",
issuerMd5Fingerprint: "",
issuerSha1Fingerprint: "",
cert: null,
},
};
},
}

It's not wise to register and unregister observer on page load because it will be catching http requests from everywhere. So register it on startup of addon and unregister it on shutdown/unload of addon.
And you should check the loadContext to see for what window/tab/frame/ajax/background is making the request. See here for more on loadContext: https://stackoverflow.com/a/25223307/1828637
But anyways this should work the way you have it meaning reg on page load and unreg on page unload:
const {
Cc, Ci, Cu
} = require("chrome");
let console = (Cu.import("resource://gre/modules/devtools/Console.jsm", {})).console;
console.log("Importing console.\n");
// Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
this._loadHandler = function(e) {
myExtension.onPageLoad(e);
};
var utils = require('sdk/window/utils');
var gBrowser = utils.getMostRecentBrowserWindow().getBrowser();
gBrowser.addEventListener("load", this._loadHandler, true);
// We register an observer on page load, through page load event listener. Is this round-about?
// Can we register it directly?
var myExtension = {
observers: {
'http-on-examine-response': {
observe: function(aSubject, aTopic, aData) {
console.log("Running Observe....\n");
myExtension.examine_cert(aSubject);
},
reg: function() {
Services.obs.addObserver(myExtension.observers['http-on-examine-response'], 'http-on-examine-response', false);
},
unreg: function() {
Services.obs.removeObserver(myExtension.observers['http-on-examine-response'], 'http-on-examine-response');
}
}
},
onPageLoad: function(aEvent) {
var doc = aEvent.originalTarget; // doc is document that triggered "onload" event
var i = 0;
console.log("Page has been loaded.\n");
myExtension.observers['http-on-examine-response'].reg();
this.initialized = true;
},
onPageUnload: function(aEvent) {
console.log("Page has been unloaded.\n");
myExtension.observers['http-on-examine-response'].unreg();
},
examine_cert: function(channel) {
channel.QueryInterface(Ci.nsIHttpChannel);
//Assigns the channel of the http resource in the host port
var host = channel.URI.hostPort;
//Checks for security info
var si = channel.securityInfo;
if (!si) {
console.log("No securityInfo for " + host + " , returning\n");
return;
}
//Gets Certificates status
si.QueryInterface(Ci.nsISSLStatusProvider);
var st = si.SSLStatus;
if (!st) {
console.log(st + "\n");
console.log("No SSLStatus for " + host + " , returning\n");
return;
}
st.QueryInterface(Ci.nsISSLStatus);
var cert = st.serverCert;
if (!cert) {
console.log("No serverCert for " + host + " , returning\n");
return;
}
// by now we must have a non-empty cert, print it and the status
console.log("status: " + st + "\n");
console.log("cert : " + cert.commonName + "\n");
//certobj.ciphername = st.cipherName; // apparently, parsed out of cert when SSLStatus is made
//certobj.keyLength = st.keyLength;
//certobj.secretKeyLength = st.secretKeyLength;
},
// Recursively descend into the object, looking for displayName matching re;
// also, validate objects as we go.
findASN1Object: function(struc, re) {
if (!struc) {
console.log("\nCertificate does not have a valid structure.\n");
return;
}
// Shortcut: object with displayName matching re found, return it immediately
if (re.test(struc.displayName)) return struc;
var s = Ci;
try {
s = struc.QueryInterface(Ci.nsIASN1Sequence);
} catch (e) {
console.log("\nCertificate failed nsIASN1Sequence conversion\n");
}
if (!s || !s.isValidContainer) {
console.log("\nCertificate does not have a valid container.\n");
return;
}
// check all the objects recursively
for (var i = 0; i < s.ASN1Objects.length; i++) {
struc = s.ASN1Objects.queryElementAt(i, Ci.nsIASN1Object);
var res = this.findASN1Object(struc, re);
if (res) return res;
}
},
//------ Object containment:
// nsIHttpChannel > securityInfo (nsISSLStatusProvider) > SSLStatus (nsISSLStatus) > serverCert (nsIX509Cert)
fillCertObj: function(obj, cert) {
obj.cert = cert;
console.log("\nCert:" + cert + "\n");
obj.notBefore = cert.validity.notBefore;
obj.notAfter = cert.validity.notAfter;
if (cert.issuer) {
obj.issuerMd5Fingerprint = cert.issuer.md5Fingerprint;
obj.issuerSha1Fingerprint = cert.issuer.sha1Fingerprint;
} else {
//console.log("no issuer: "+ [cert.commonName, cert.issuer, cert.sha1Fingerprint]); \
console.log("\nThe Certificate doesn't have an Issuer.\n");
}
var keys = ["commonName", "organization", "organizationalUnit", "serialNumber", "emailAddress",
"issuerCommonName", "issuerOrganization", "issuerOrganizationUnit", "md5Fingerprint", "sha1Fingerprint"
];
for (var i in keys) {
obj[keys[i]] = cert[keys[i]];
}
obj.subjectAltName = [];
var san = this.findASN1Object(cert.ASN1Structure, /^Certificate Subject Alt Name$/);
if (san) {
//this.log("SAN:", [san.displayName, san.displayValue]);
var m, re = /DNS Name: ((?:\*\.)?[a-z0-9.-]+)/g;
// *.smth1.smth2.smth3....smthn where smth has only a-z,0-9,or - or
// smth1.smth2.smth3....smthn where smth has only a-z,0-9,or -
// push domain names contained in displayValue onto the list subjectAltName
while (m = re.exec(san.displayValue))
obj.subjectAltName.push(m[1]);
}
console.log("Filled Certificate.\n");
},
init_cert: function() {
var certobj = this.newCertObj();
//certobj.host = host;
//certobj.ciphername = st.cipherName;
//certobj.keyLength = st.keyLength;
//certobj.secretKeyLength = st.secretKeyLength;
//this.fillCertObj(certobj.now, cert);
console.log(certobj);
},
newCertObj: function() {
console.log("Creating Empty Certificate.\n");
return {
flags: 0,
host: "",
warn: {},
now: {
commonName: "",
organization: "",
organizationalUnit: "",
serialNumber: "",
emailAddress: "",
notBefore: "",
notAfter: "",
issuerCommonName: "",
issuerOrganization: "",
issuerOrganizationUnit: "",
md5Fingerprint: "",
sha1Fingerprint: "",
issuerMd5Fingerprint: "",
issuerSha1Fingerprint: "",
cert: null,
},
old: {
commonName: "",
organization: "",
organizationalUnit: "",
serialNumber: "",
emailAddress: "",
notBefore: "",
notAfter: "",
issuerCommonName: "",
issuerOrganization: "",
issuerOrganizationUnit: "",
md5Fingerprint: "",
sha1Fingerprint: "",
issuerMd5Fingerprint: "",
issuerSha1Fingerprint: "",
cert: null,
},
};
},
}
lastly why are you wrapping in the pseudo-class myExtension? When doing addon-sdk or bootstrap addons you don't have to do that anymore. It makes things easier so you don't have to track what scope you are in.

Related

Can't track events of Looker Embedded Dashborard (SSO embedded) in Nest.JS application

I have next.js project, on server side I generate Looker-sso url and on client side I use this url for embedded dashboard.
Here is how I generate SSO url at Next.js server side
function getLookerSsoUrl(embeddedUserParams: UserInfo, dashboardId: string) : string {
/** Returns Looker Embedded sso url. */
const url_data : LookerEmbeddedUrlOptions = {
"host": LOOKER_HOST,
"secret": LOOKER_SECRET,
"first_name": embeddedUserParams.firstName,
"last_name": embeddedUserParams.lastName,
"external_user_id": embeddedUserParams.firstName + " " + embeddedUserParams.lastName,
"external_group_id": embeddedUserParams.companyName + " group",
"group_ids": [1],
"access_filters": {},
"models": ['db_analytics'],
"permissions": [
'see_user_dashboards',
'see_lookml_dashboards',
'access_data',
'see_looks'],
"user_attributes": {
"name": embeddedUserParams.firstName + " " + embeddedUserParams.lastName,
"first_name": embeddedUserParams.firstName,
"last_name": embeddedUserParams.lastName,
"email": embeddedUserParams.email,
"company_id": embeddedUserParams.companyId,
"company_name": embeddedUserParams.companyName,
"id": embeddedUserParams.id
},
"session_length": 15 * 60,
"embed_url": `/embed/dashboards/${dashboardId}?embed_domain=http://localhost&company_id=${embeddedUserParams.companyId}`,
"force_logout_login": true
}
return "https://" + getSignedEmbedUrl(url_data)
}
function getSignedEmbedUrl(options : LookerEmbeddedUrlOptions) : string {
/** Build sso url with all Looker options and secret key and returns it. */
// looker options
const secret = options.secret
const host = options.host
// user options
const json_external_user_id = JSON.stringify(options.external_user_id)
const json_first_name = JSON.stringify(options.first_name)
const json_last_name = JSON.stringify(options.last_name)
const json_permissions = JSON.stringify(options.permissions)
const json_models = JSON.stringify(options.models)
const json_group_ids = JSON.stringify(options.group_ids)
const json_external_group_id = JSON.stringify(options.external_group_id || "")
const json_user_attributes = JSON.stringify(options.user_attributes || {})
const json_access_filters = JSON.stringify(options.access_filters)
// url/session specific options
const embed_path = '/login/embed/' + encodeURIComponent(options.embed_url)
const json_session_length = JSON.stringify(options.session_length)
const json_force_logout_login = JSON.stringify(options.force_logout_login)
// computed options
const json_time = JSON.stringify(Math.floor((new Date()).getTime() / 1000))
const json_nonce = JSON.stringify(getNonce(16))
// compute signature
let string_to_sign = ""
string_to_sign += host + "\n"
string_to_sign += embed_path + "\n"
string_to_sign += json_nonce + "\n"
string_to_sign += json_time + "\n"
string_to_sign += json_session_length + "\n"
string_to_sign += json_external_user_id + "\n"
string_to_sign += json_permissions + "\n"
string_to_sign += json_models + "\n"
string_to_sign += json_group_ids + "\n"
string_to_sign += json_external_group_id + "\n"
string_to_sign += json_user_attributes + "\n"
string_to_sign += json_access_filters
const signature = createHmac('sha1', secret).update(ForceUnicodeEncoding(string_to_sign)).digest('base64').trim()
// construct query string
const query_params = {
nonce: json_nonce,
time: json_time,
session_length: json_session_length,
external_user_id: json_external_user_id,
permissions: json_permissions,
models: json_models,
access_filters: json_access_filters,
first_name: json_first_name,
last_name: json_last_name,
group_ids: json_group_ids,
external_group_id: json_external_group_id,
user_attributes: json_user_attributes,
force_logout_login: json_force_logout_login,
signature: signature
}
const query_string = stringify(query_params)
return host + embed_path + '?' + query_string
}
function getNonce(len : number) : string {
/** Returns nonce characters. */
let text = ""
const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
for (let i = 0; i < len; i++)
text += possible.charAt(Math.floor(Math.random() * possible.length))
return text
}
function ForceUnicodeEncoding(string : string) : string {
/** Decodes a URI component previously created by encodeURIComponent. */
return decodeURIComponent(encodeURIComponent(string))
}
Here is client side code:
import React, {useCallback} from "react"
import {LookerEmbedSDK} from "#looker/embed-sdk"
import {ENV_VAR} from "env"
type embeddedDashboardType = {
id : number
setError? : ((errorMsg : string) => void)
}
export const EmbeddedDashboard = ({id, setError} : embeddedDashboardType) => {
const canceller = (event : any) => {
return { cancel: !event.modal }
}
document.addEventListener("click", ()=>console.log("r"))
const embedCtrRef = useCallback((el) => {
const authUrl = "/api/looker-sso"
if (el) {
LookerEmbedSDK.init("...", authUrl)
LookerEmbedSDK.createDashboardWithId(id)
.withNext()
.appendTo(el)
.on("drillmenu:click", canceller)
.on("drillmodal:explore", canceller)
.on("dashboard:tile:explore", canceller)
.on("look:ready", ()=> console.log("ready"))
.on("explore:ready", ()=>console.log("ready"))
.on("dashboard:run:start", ()=>console.log("ready"))
.on("dashboard:tile:view", canceller)
.build()
.connect()
.catch((error) => {
console.error("Connection error", error)
if (setError){
setError(error)
}
})
}
}, [])
return <>
<style jsx>{`
:global(.container-dashboard) {
width: 100%;
height: 95vh;
}
:global(.container-dashboard > iframe) {
width: 100%;
height: 100%;
}
`}</style>
<div className="container-dashboard" ref={embedCtrRef} />
</>
}
The dashboard displays correctly, but I cant track events, that happen in Looker Iframe
I except, that I would see clicks, which are made on looker iframe

DynamoDB table seed works in cli but not AWS-SDK

I have a table that has more than 25 items and wrote a basic script to break them into sub arrays of 25 items each then loops thru that collection of sub arrays to run a batch write item command in the AWS DynamoDB Client. The issue I am getting is a returned validation error. When I run the same seed file via the aws-cli it seeds the table perfectly. This makes me think it has something to do with my script. See anything I am missing? Thanks in advance!
var { DynamoDB } = require('aws-sdk');
var db = new DynamoDB.DocumentClient({
region: 'localhost',
endpoint: 'http://localhost:8000',
});
const allItems = require('./allItems.json');
const tableName = 'some-table-name';
console.log({ tableName, allItems });
var batches = [];
var currentBatch = [];
var count = 0;
for (let i = 0; i < allItems.length; i++) {
//push item to the current batch
count++;
currentBatch.push(allItems[i]);
if (count === 25) {
batches.push(currentBatch);
currentBatch = [];
}
}
//if there are still items left in the curr batch, add to the collection of batches
if (currentBatch.length > 0 && currentBatch.length !== 25) {
batches.push(currentBatch);
}
var completedRequests = 0;
var errors = false;
//request handler for DynamoDB
function requestHandler(err, data) {
console.log('In the request handler...');
return function (err, data) {
completedRequests++;
errors = errors ? true : err;
//log error
if (errors) {
console.error('Request caused a DB error.');
console.error('ERROR: ' + err);
console.error(JSON.stringify(err, null, 2));
} else {
var res = {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Methods': 'GET,POST,OPTIONS',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Credentials': true,
},
body: JSON.stringify(data),
isBase64Encoded: false,
};
console.log(`Success: returned ${data}`);
return res;
}
if (completedRequests == batches.length) {
return errors;
}
};
}
//Make request
var params;
for (let j = 0; j < batches.length; j++) {
//items go in params.RequestedItems.id array
//format for the items is {PutRequest : {Item: ITEM_OBJECT}}
params = '{"RequestItems": {"' + tableName + '": []}}';
params = JSON.parse(params);
params.RequestItems[tableName] = batches[j];
console.log('before db.batchWriteItem: ', params);
try {
//send to db
db.batchWrite(params, requestHandler(params));
} catch{
console.error(err)
}
}
Here is the formatted request object and the error:
before db.batchWriteItem:
{ RequestItems:
{ 'some-table-name': [ [Object], [Object], [Object], [Object] ] }
}
In the request handler...
Request caused a DB error.
ERROR: ValidationException: Invalid attribute value type
{
"message": "Invalid attribute value type",
"code": "ValidationException",
"time": "2020-08-04T10:51:13.751Z",
"requestId": "dd49628c-6ee9-4275-9349-6edca29636fd",
"statusCode": 400,
"retryable": false,
"retryDelay": 47.94198279972915
}
You are using the DocumentClient in the nodejs code. This will automatically convert the data format used by DynamoDB to a more easily consumable format.
e.g.
{
"id": {
"S": "A string value"
}
}
would become
{
"id": "A string value"
}
The CLI does not perform this data conversion.
You can use the regular DynamoDB client to not perform this conversion in Nodejs. e.g. const db = new Dynamodb()

Sqlite not INSERTING dynamic JSON data

I am using Ionic v1 and trying to INSERT a few JSON records into a SQLite database on Android Kitkat. If I use a static variable it works but only inserts first item in array, but if I try to make the variable names dynamic I get an error:
Cannot read property 'mac_id' of undefined
I have tried the two following code segments, first works but only inserts the first item in the JSON array, second does not working:
Works:
But only inserts first item (multiple times)
for ( var i = 0; i < data.length; i++)
{
var mac_id = data[i].mac_id;
var device_type = data[i].device_type;
var machine_id = data[i].machine_id;
var machine_name = data[i].machine_name;
var date_created = data[i].date_created;
db.transaction(function (tx) {
var query = "INSERT INTO devices (mac_id, device_type,machine_id,machine_name,date_created) VALUES (?,?,?,?,?)";
tx.executeSql(query, [mac_id , device_type , machine_id , machine_name , date_created ], function(tx, res) {
},
function(tx, error) {
console.log(' device INSERT error: ' + error.message);
});
}, function(error) {
console.log(' device transaction error: ' + error.message);
}, function() {
console.log('device INSERT ok');
});
}
Not Working
for ( var i = 0; i < data.length; i++)
{
db.transaction(function (tx) {
var query = "INSERT INTO devices (mac_id, device_type,machine_id,machine_name,date_created) VALUES (?,?,?,?,?)";
tx.executeSql(query, [data[i].mac_id, data[i].device_type, data[i].machine_id, data[i].machine_name, data[i].date_created ], function(tx, res) {
},
function(tx, error) {
console.log(' device INSERT error: ' + error.message);
});
}, function(error) {
console.log(' device transaction error: ' + error.message);
}, function() {
console.log('device INSERT ok');
});
}
JSON array
[
{
"id": "3",
"mac_id": "fsdf324324",
"device_type": "redvfsdfds",
"machine_id": "3",
"machine_name": "sdfsdfsdf",
"date_created": "3322342"
},
{
"id": "2",
"mac_id": "243434",
"device_type": "fredssd",
"machine_id": "2",
"machine_name": "fdsfsdf",
"date_created": "43434"
},
{
"id": "1",
"mac_id": "1324324234",
"device_type": "bweight",
"machine_id": "1",
"machine_name": "dffdgf",
"date_created": "4324234"
}
]
db.transaction(function (tx) {
for ( var i = 0; i < $scope.data.length; i++)
{
var query = "INSERT INTO devices (mac_id, device_type,machine_id,machine_name,date_created) VALUES (?,?,?,?,?)";
tx.executeSql(query, [data[i].mac_id, data[i].device_type, data[i].machine_id, data[i].machine_name, data[i].date_created ], function(tx, res) {
},function(tx, error) {
console.log(' device INSERT error: ' + error.message);
});
}
}, function(error) {
console.log(' device transaction error: ' + error.message);
}, function() {
console.log('device INSERT ok');
});
You should use for loop inside transaction . Use it like this . hope this helps :)

Apache Cordova using SQLite - Can't wait to return data

function outDb(outData) {
var temp;
var db = window.sqlitePlugin.openDatabase({ name: "test" });
db.executeSql("SELECT " + outData + " FROM test", [], function (res) {
switch (outData) {
case "goal": temp = res.rows.item(0).GOAL;
break;
case "curDate": temp = res.rows.item(0).CURDATE;
break;
case "Day1": temp = res.rows.item(0).DAY1;
break;
case "Day2": temp = res.rows.item(0).DAY2;
break;
case "Day3": temp = res.rows.item(0).DAY3;
break;
default: temp = "is default";
}
});
return temp;
}
I want to save data into myData;
var myData = outDb("goal");
But this is not working, because the return statement happens faster than the data is assigned to my variable.
What can I do?
You should use callback function because Cordova-sqlite-storage works asynchronously.
function getFromDB(_n, fn) {
var db = ...
db.transaction(function(tx) {
var query = "SELECT * FROM <table_name> WHERE <field_name> = ?";
tx.executeSql(query, [_n], function (tx, rs) {
for(var x = 0; x < rs.rows.length; x++) {
_res = rs.rows.item(x).value;
fn(_res);
}
}, function (tx, error) {
console.log('select error: ' + error.message);
});
}, function (error) {
console.log('transaction error: ' + error.message);
}, function () {
console.log('transaction ok.');
});}
getFromDB(<name>, function(val) {
console.log(val);});
All sqlite commands are event driven. That means, that you have to wait for values until the event is finished.
In your case: You will never get a return value, because your return is outside of the success function.
You could do a rewrite of your function like:
function outDb(outData) {
var temp;
var db = window.sqlitePlugin.openDatabase({ name: "test" });
db.executeSql("SELECT " + outData + " FROM test", [], function (res) {
switch (outData) {
case "goal": temp = res.rows.item(0).GOAL;
break;
case "curDate": temp = res.rows.item(0).CURDATE;
break;
case "Day1": temp = res.rows.item(0).DAY1;
break;
case "Day2": temp = res.rows.item(0).DAY2;
break;
case "Day3": temp = res.rows.item(0).DAY3;
break;
default: temp = "is default";
}
return temp;
});
}
A much better way is like I showed in this answer:
How to compact SQL instructions in Cordova?

Atmosphere #Disconnect does not get called when we close browser window

Using the chat example, when I close the browser window, I do not ever see the #Disconnect method called.
Here is my service:-
package org.atmosphere.samples.chat;
import java.io.IOException;
import org.atmosphere.config.service.Disconnect;
import org.atmosphere.config.service.ManagedService;
import org.atmosphere.config.service.Ready;
import org.atmosphere.cpr.AtmosphereResource;
import org.atmosphere.cpr.AtmosphereResourceEvent;
import org.atmosphere.cpr.AtmosphereResourceEventListenerAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
#ManagedService(path = "{room: [a-zA-Z][a-zA-Z_0-9]*}")
public class Chat
{
private static final Logger LOGGER = LoggerFactory.getLogger(Chat.class);
#Ready
public void onReady(final AtmosphereResource inAtmosphereResource)
{
LOGGER.info("Browser {} connected.", inAtmosphereResource.uuid());
}
#Disconnect
public void onDisconnect(AtmosphereResourceEvent event)
{
if (event.isCancelled())
{
LOGGER.info("Browser {} unexpectedly disconnected", event.getResource().uuid());
}
else if (event.isClosedByClient())
{
LOGGER.info("Browser {} closed the connection", event.getResource().uuid());
}
else
{
LOGGER.info("close event was called, but I know not why");
}
}
#org.atmosphere.config.service.Message(encoders = { JacksonEncoder.class }, decoders = { JacksonDecoder.class })
public Message onMessage(Message message) throws IOException
{
LOGGER.info("{} just send {}", message.getAuthor(), message.getMessage());
return message;
}
}
Here is my JS:-
$(function () {
"use strict";
var header = $('#header');
var content = $('#content');
var input = $('#input');
var status = $('#status');
console.log(document.location.hash);
var bookingNumber = document.location.hash.substring(1, document.location.hash.length);
console.log(bookingNumber);
var myName = false;
var author = null;
var logged = false;
var socket = $.atmosphere;
var subSocket;
var transport = 'websocket';
// We are now ready to cut the request
var request = { url: document.location.toString().replace(/#.*/, "") + 'chat/' + bookingNumber,
contentType : "application/json",
trackMessageLength : true,
shared : true,
logLevel : "debug",
transport : transport ,
fallbackTransport: 'long-polling'};
request.onOpen = function(response) {
content.html($('>p<', { text: 'Atmosphere connected using ' + response.transport }));
input.removeAttr('disabled').focus();
status.text('Choose name:');
transport = response.transport;
if (response.transport == "local") {
subSocket.pushLocal("Name?");
}
};
request.onTransportFailure = function(errorMsg, request) {
jQuery.atmosphere.info(errorMsg);
if (window.EventSource) {
request.fallbackTransport = "sse";
transport = "see";
}
header.html($('<h3>', { text: 'Atmosphere Chat. Default transport is WebSocket, fallback is ' + request.fallbackTransport }));
};
request.onMessage = function (response) {
// We need to be logged first.
if (!myName) return;
var message = response.responseBody;
try {
var json = jQuery.parseJSON(message);
} catch (e) {
console.log('This doesn\'t look like a valid JSON: ', message.data);
return;
}
if (!logged) {
logged = true;
status.text(myName + ': ').css('color', 'blue');
input.removeAttr('disabled').focus();
subSocket.pushLocal(myName);
} else {
input.removeAttr('disabled');
var me = json.author == author;
var date = typeof(json.time) == 'string' ? parseInt(json.time) : json.time;
addMessage(json.author, json.message, me ? 'blue' : 'black', new Date(date));
}
};
request.onClose = function(response) {
logged = false;
}
subSocket = socket.subscribe(request);
input.keydown(function(e) {
if (e.keyCode === 13) {
var msg = $(this).val();
if (author == null) {
author = msg;
}
subSocket.push(jQuery.stringifyJSON({ author: author, message: msg }));
$(this).val('');
input.attr('disabled', 'disabled');
if (myName === false) {
myName = msg;
}
}
});
function addMessage(author, message, color, datetime) {
content.append('<p><span style="color:' + color + '">' + author + '</span> # ' +
+ (datetime.getHours() < 10 ? '0' + datetime.getHours() : datetime.getHours()) + ':'
+ (datetime.getMinutes() < 10 ? '0' + datetime.getMinutes() : datetime.getMinutes())
+ ': ' + message + '</p>');
}
});
I am running on glassfish 3, with web sockets and comet enabled.
UPDATE I forgot to mention that I am using v 2.1.0RC1
tested with GF 3.1.2.2:
Blockquote
[#|2013-12-02T10:43:24.743-0500|INFO|glassfish3.1.2|javax.enterprise.system.std.com.sun.enterprise.server.logging|_ThreadID=25;_ThreadName=http-thread-pool-8080(4);|10:43:24.743 [http-thread-pool-8080(4)] INFO org.atmosphere.samples.chat.Chat - Browser 0f7f5596-bd25-4dda-a488-1357da8487f5 closed the connection
Let's havce the discussion on the Atmosphere ML in case you are still experiencing issue. I've tested with the sample BTW.
-- Jeanfrancois

Resources