AJAX Call using Enyo Framework - enyo

I am trying to make an ajax call using the enyo framework and I am running headlong in to a problem. The error message I am getting is 0. That's it just a 0. I made sure my link to the json file was correct and I built this jsfiddle to test it out http://jsfiddle.net/mmahon512/CPU8n/2/ Any help is greatly appreciated. My host is GoDaddy and I made sure that I added the json extension to my web config correctly. The link to the json file is correct and it returns valid json. I checked it using jsonlint. Here is what the code looks like on jsfiddle:
enyo.kind({
name: "AjaxSample",
components: [
{ kind: "Button", content: "Fetch Users", ontap: "fetch" },
{ name: "repos", content: "Not loaded...", allowHtml: true }
],
fetch: function() {
var ajax = new enyo.Ajax({
url: "http://atxapps.com/_sites/atxapps.com/dev/jetstream/assets/dataUsers.json"
});
ajax.go();
ajax.response(this, "gotResponse");
ajax.error(this, this.gotError);
},
gotResponse: function(inSender, inResponse) {
var output = "";
for(i = 0; i < inResponse.length; i++) {
output += inResponse[i].Id + "";
}
output += Date.now();
this.$.repos.setContent(output);
},
gotError: function(inSender, inError) {
alert(inError);
this.$.repos.setContent(inError + " " + Date.now());
}
});

Looks like a CORS issue. I see the following in the console:
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin fiddle.jshell.net is therefore not allowed access.
I wrapped it as a jsonp request successfully.
http://jsfiddle.net/CPU8n/3/
enyo.kind({
name: "AjaxSample",
components: [
{ kind: "Button", content: "Fetch Users", ontap: "fetch" },
{ name: "repos", content: "Not loaded...", allowHtml: true }
],
fetch: function() {
var ajax = new enyo.JsonpRequest({
url: "http://jsonpwrapper.com/?urls%5B%5D=http%3A%2F%2Fatxapps.com%2F_sites%2Fatxapps.com%2Fdev%2Fjetstream%2Fassets%2FdataUsers.json"
});
ajax.go();
ajax.response(this, "gotResponse");
ajax.error(this, this.gotError);
},
gotResponse: function(inSender, inResponse) {
var output = "";
var body = enyo.json.parse(inResponse[0].body); // jsonpwrapper.com wraps the results in a array with an index for each URL. The actual data is in the body param of that index but it isn't parsed (at least in this example)
for(i = 0; i < body.length; i++) {
output += body[i].Id + "<br />";
}
output += Date.now();
this.$.repos.setContent(output);
},
gotError: function(inSender, inError) {
alert(inError);
this.$.repos.setContent(inError + " " + Date.now());
}
});
If you're running this on the same server in prod, you wouldn't see the error (since it's not cross-origin). If it'll be on a different server, you can either convert the server-side to support jsonp or adds the appropriate CORS headers.

Related

Problem sending POST body to the Firestore REST API

I want to create a new document in Firestore using the REST API.
Very good examples here using Axios to send the POST request with some fields:
https://www.jeansnyman.com/posts/google-firestore-rest-api-examples/
axios.post(
"https://firestore.googleapis.com/v1/projects/<PROJECTIDHERE>/databases/(default)/documents/<COLLECTIONNAME>",
{
fields: {
title: { stringValue: this.title },
category: { stringValue: this.category },
post: { stringValue: this.post },
summary: { stringValue: this.description },
published: { booleanValue: this.published },
created: { timestampValue: new Date() },
modified: { timestampValue: new Date() }
}
}
).then(res => { console.log("Post created") })
And an example here using Python Requests:
Using the Firestore REST API to Update a Document Field
(this is a PATCH request but the field formatting is the same as in a POST request)
import requests
import json
endpoint = "https://firestore.googleapis.com/v1/projects/[PROJECT_ID]/databases/(default)/documents/[COLLECTION]/[DOCUMENT_ID]?currentDocument.exists=true&updateMask.fieldPaths=[FIELD_1]"
body = {
"fields" : {
"[FIELD_1]" : {
"stringValue" : "random new value"
}
}
}
data = json.dumps(body)
headers = {"Authorization": "Bearer [AUTH_TOKEN]"}
print(requests.patch(endpoint, data=data, headers=headers).json())
I am using Google Apps Script UrlFetchApp.fetch to send my requests. I am able to use GET requests with no problems. For example, to get all the documents in a collection (in Google Apps Script):
function firestore_get_documents(){
var options = {
headers: { 'Authorization': 'Bearer ' + ScriptApp.getOAuthToken() },
method:'GET'
}
var response = UrlFetchApp.fetch('https://firestore.googleapis.com/v1/projects/<PROJECTIDHERE>/databases/(default)/documents/myCollection', options);
var parsed = JSON.parse(response.getContentText());
return parsed;
}
This works nicely. And changing 'method' to 'POST' creates a new document in myCollection as expected. Then I try to add a POST body with some fields (or just one field):
function firestore_create_new_document(){
var options = {
headers: { 'Authorization': 'Bearer ' + ScriptApp.getOAuthToken() },
method:'POST',
payload: {fields: { title: { stringValue: 'newTitle' } } }, // If you comment out this line, it works as expected
muteHttpExceptions:true
}
var response = UrlFetchApp.fetch('https://firestore.googleapis.com/v1/projects/<PROJECTIDHERE>/databases/(default)/documents/myCollection', options);
var contentText = response.getContentText();
var parsed = JSON.parse(response.getContentText());
return parsed;
}
I get the following errors:
code: 400 message: "Request contains an invalid argument."
status: "INVALID_ARGUMENT"
details[0][#type]: "type.googleapis.com/google.rpc.BadRequest"
details[0][fieldViolations][0][field]: "{title={stringValue=newTitle}}"
details[0][fieldViolations][0][description]: "Error expanding 'fields' parameter. Cannot find matching fields for path '{title={stringValue=newTitle}}'."
Documentation is available here:
https://firebase.google.com/docs/firestore/reference/rest/v1/projects.databases.documents/createDocument
https://firebase.google.com/docs/firestore/reference/rest/v1/projects.databases.documents#Document
The problem may be the formatting of my 'fields' object - I've tried several different formats from the documentation and examples
The problem may be that the fields don't exist yet? I think I should be able to create a new document with new fields
The problem may be with the way UrlFetchApp.fetch sends my JSON body. I have tried using payload = JSON.stringify(payload_object) and that doesn't work either.
I think UrlFetchApp is doing something slightly different than Axios or Python Requests - the body is getting sent differently, and not parsing as expected.
How about the following modification?
From:
var options = {
headers: { 'Authorization': 'Bearer ' + ScriptApp.getOAuthToken() },
method:'POST',
payload: {fields: { title: { stringValue: 'newTitle' } } }, // If you comment out this line, it works as expected
muteHttpExceptions:true
}
To:
var options = {
headers: { 'Authorization': 'Bearer ' + ScriptApp.getOAuthToken() },
method:'POST',
payload: JSON.stringify({fields: { title: { stringValue: 'newTitle' } } }),
contentType: "application/json",
muteHttpExceptions:true
}
When I tested above modified request, I could confirm that it worked. But if other error occurs, please tell me.
Reference:
Class UrlFetchApp

Mapbox GL and .net core webApi

I am moving from Leaflet to Mapbox GL and have some data issues. My webApi is proven but I cannot smoothly integrate them.
The approach I gave up on, based upon their examples and my own research, looks like:
map = new mapboxgl.Map({
container: 'mapdiv',
style: 'mapbox://styles/mapbox/streets-v10'
, center: start
, zoom: $scope.zoom
, transformRequest: (url, resourceType) => {
if (resourceType === 'Source' && url.startsWith(CONFIG.API_URL)) {
return {
headers: {
'Authorization': 'Bearer ' + localStorageService.get("authorizationData")
, 'Access-Control-Allow-Origin': CONFIG.APP_URL
, 'Access-Control-Allow-Credentials': 'true'
}
}
}
}
});
This is passing my OAuth2 token (or at least I think it should be) and the Cross site scripting part CORS.
Accompanying the above with:
map.addSource(layerName, { type: 'geojson', url: getLayerURL($scope.remLayers[i]) });
map.getSource(layerName).setData(getLayerURL($scope.remLayers[i]));
Having also tried to no avail:
map.addSource(layerName, { "type": 'geojson', "data": { "type": "FeatureCollection", "features": [] }});
map.getSource(layerName).setData(getLayerURL($scope.remLayers[i]));
Although there are no errors Fiddler does not show any requests being made to my layer webApi. All the others show but Mapbox does not appear to raising them.
The Url looks like:
http://localhost:49198/api/layer/?bbox=36.686654090881355,34.72821077223763,36.74072742462159,34.73664000652042&dtype=l&id=cf0e1df7-9510-4d03-9319-d4a1a7d6646d&sessionId=9a7d7daf-76fc-4dd8-af4f-b55d341e60e4
Because this was not working I attempted to make it more manual using my existing $http calls which partially works.
map = new mapboxgl.Map({
container: 'mapdiv',
style: 'mapbox://styles/mapbox/streets-v10'
, center: start
, zoom: $scope.zoom
, transformRequest: (url, resourceType) => {
if (resourceType === 'Source' && url.startsWith(CONFIG.API_URL)) {
return {
headers: {
'Authorization': 'Bearer ' + localStorageService.get("authorizationData")
}
}
}
}
});
map.addSource(layerName,
{
"type": 'geojson',
"data": { "type": "FeatureCollection", "features": [] }
});
The tricky part is to know when to run the data retrieval call. The only place I could find was on the maps data event which now looks like:
map.on('data', function (e) {
if (e.dataType === 'source' && e.isSourceLoaded === false && e.tile === undefined) {
// See if the datasource is known
for (var i = 0; i < $scope.remLayers.length; i++) {
if (e.sourceId === $scope.remLayers[i].name) {
askForData(i)
}
}
}
});
function askForData(i) {
var data = getBBoxString(map);
var mapZoomLevel = map.getZoom();
if (checkZoom(mapZoomLevel, $scope.remLayers[i].minZoom, $scope.remLayers[i].maxZoom)) {
mapWebSvr.getData({
bbox: data, dtype: 0, id: $scope.remLayers[i].id, buffer: $scope.remLayers[i].isBuffer, sessionId
},
function (data, indexValue, indexType) {
showNewData(data, indexValue, indexType);
},
function () {
// Not done yet.
},
i,
0
);
}
}
function showNewData(ajxresponse, index, indexType) {
map.getSource($scope.remLayers[index].name).setData(ajxresponse);
map.getSource($scope.remLayers[index].name).isSourceLoaded = true;
}
This is all working with one exception. It keeps firing time and time again. Some of these calls return a lot of data for a web call so its not a solution at the moment.
Its like its never satisfied with the data even though its showing it on the map!
There is a parameter on the data event, isSourceLoaded but it does not get set to true.
I have searched for an example, have tried setting isSourceLoaded in a number of places (as with the code above) but to no avail.
Does anyone have a method accomplishing this basic data retrieval function successfully or can point out the error(s) in my code? Or even point me to a working example...
I have spent too long on this now and could do with some help.
After a bit of a run around I have a solution.
A Mapbox email pointed to populating the data in the load event - which I am now doing.
This was not however the solution I was looking for as the data needs refreshing when the map moves, zooms etc - further look ups are required.
Following a bit more a examination a solution was found.
Using the code blow on the render event will request the information when the bounding box is changed.
var renderStaticBounds = getBoundsString(map.getBounds());
map.on('render', function (e) {
if (renderStaticBounds != getBoundsString(map.getBounds())) {
renderStaticBounds = getBoundsString(map.getBounds());
for (var i = 0; i < $scope.remLayers.length; i++) {
askForData(i);
}
}
});
function getBoundsString(mapBounds) {
var left = mapBounds._sw.lng;
var bottom = mapBounds._sw.lat;
var right = mapBounds._ne.lng;
var top = mapBounds._ne.lat;
return left + ',' + bottom + ',' + right + ',' + top;
}
This hopefully will save someone some development time.

how to get respose of data webscript in share webscript js file

I have one data webscript at alfresco side which return json response.
i want this json response in share webscript to display that json data on share.
following is the my code written in getLocation.get.js file # share.
var result1 = new Array();
var connector = remote.connect("alfresco");
var data = connector.get("/com/portfolio/ds/getlocation");
// create json object from data
if(data.status == 200){
var result = jsonUtils.toJSONString(eval(data.response));
model.docprop = result ;
}else{
model.docprop = "Failed";
}
Following is the output from alfresco side
{
"subgroups": [
{
"name": "grp_pf_india_user" ,
"label": "INDIA"
},
{
"name": "grp_pf_israil_user" ,
"label": "ISRAIL"
},
{
"name": "grp_pf_usa_user" ,
"label": "USA"
}
]
}
use this code to call repo webscripts from share side by using the concept or RMI. (Alfresco.constants.PROXY_URI) = (http://host:port/share/proxy/alfresco/)
var xurl=Alfresco.constants.PROXY_URI+"HR-webscripts/createHRDocument/"+JSON.stringify(o);
//alert(xurl);
var request = $.ajax({
url: xurl ,
type: "POST",
//data: { "groupname" : o},
beforeSend : function(xhr){
/*
Alfresco.util.Ajax & alfresco/core/CoreXhr – will automatically take the token from the cookie and add it as a request header for every request.
Alfresco.forms.Form – will automatically take the token from the cookie and add it as a url parameter to when submitting an multipart/form-data request.
(When submitting a form as JSON the Alfresco.util.Ajax will be used internally)
*/
if (Alfresco.util.CSRFPolicy && Alfresco.util.CSRFPolicy.isFilterEnabled()){
xhr.setRequestHeader(Alfresco.util.CSRFPolicy.getHeader(), Alfresco.util.CSRFPolicy.getToken() );
}
},
dataType: "html"
});
request.done(function(msg) {
//alert( "Request OK: " + msg );
$("#res").html( msg );
});
request.fail(function(jqXHR, textStatus) {
alert( "Request failed: " + textStatus );
});

Parse Cloud httpRequest Stripe Subscriptions at_period_end param

Having trouble getting the format right with Parse.Cloud.httpRequest for deleting a subscription at_period_end.
I am able to successfully make this request with PostMan using x-www-form-urlencoded, key 'at_period_end' value true. (Can't post a screenshot due to my reputation sorry)
Here is my cloud-code:
Parse.Cloud.httpRequest({
method : 'DELETE',
url : 'https://' + skey + ':#' + 'api.stripe.com/v1' + '/customers/' + request.params.customerId + '/subscriptions/' + request.params.subscriptionId,
body : {
"at_period_end": true
},
success: function(httpResponse) {
if (httpResponse.status === 200) {
response.success(httpResponse);
}
else {
response.error(httpResponse);
}
},
error: function(httpResponse) {
response.error(httpResponse);
}
});
I have played around with adding a headers object with Content-Type set, but unsuccessful.
I think this is just a formatting translation issue from what I correctly entered into PostMan, to what is in my httpRequest object...
I also can't find any great information on docs on the httpRequest method so its quite frustrating :(.
Thanks heaps
***** EDIT ****** SOLUTION:
Managed to solve this using url inline parameters:
var options = request.params.options,
url = 'https://' + skey + ':#api.stripe.com/v1/customers/' + request.params.customerId + '/subscriptions/' + request.params.subscriptionId,
keys;
keys = Object.keys(options);
// This is disgusting, I need to know a better way.
for (var i = 0; i < keys.length; i++)
{
if (i === 0)
{
url += '?';
}
url += keys[i] + '=' + options[keys[i]];
if (i !== keys.length - 1)
{
url += '&';
}
}
Parse.Cloud.httpRequest({
method : 'DELETE',
url : url,
success: function(httpResponse) {
if (httpResponse.status === 200) {
response.success(httpResponse);
}
else {
response.error(httpResponse);
}
},
error: function(httpResponse) {
response.error(httpResponse);
}
});
if anyone could show me a better way to write this, that would be epic :)
Cheers
This one has always been particularly thorny for me, here is what I've been using that has worked:
Parse.Cloud.httpRequest({
method: 'DELETE',
url: 'https://api.stripe.com/v1/customers/' + request.params.stripeId + '/subscriptions/' + request.params.stripeSubscriptionId,
headers: {
'Authorization': 'Basic BASE_64_ENCODE_SECRET_KEY'
},
params: {
at_period_end: true
},
success: function(httpResponse) {
...
},
error: function(httpResponse) {
...
}
});
A couple of extra details here.
I initially had "Content-Type: application/json" as one of the headers, but this appears to not be correct, despite (I think) needing it in the past.
The base64 encode of your key can be generated with
echo -e 'sk_live_ABCDEF123456:' | openssl base64
Don't forget the colon (:) at the end of the key, it matters.
This is just a detail however, and it looks like putting the secret key directly in the URL is working as well.

How to make Chrome Extension run for each new Iframe added?

I created a Chrome Extension as a solution to override the helpText bubbles in SalesForce Console pages. The helpText bubbles show up the text without the ability to link URLs. It looks like this:
The extension is taking the helpText bubble (which in the SalesForce console window, is inside an iFrame) and makes the URL click-able. It also adds word wrap and marks the links in blue.
The solution works fine when the page loads with the initial iFrame (or iFrames) on it, meaning when you open the SalesForce console the first time (https://eu3.salesforce.com/console).
When a new tab is created at the SalesForce console, my inject script doesn't run.
Can you please assist in understanding how to inject the script on each and every new Tab SalesForce Console is creating?
The Extension as follows:
manifest.js:
{
"browser_action": {
"default_icon": "icons/icon16.png"
},
"content_scripts": [ {
"all_frames": true,
"js": [ "js/jquery/jquery.js", "src/inject/inject.js" ],
"matches": [ "https://*.salesforce.com/*", "http://*.salesforce.com/*" ]
} ],
"default_locale": "en",
"description": "This extension Fix SalesForce help bubbles",
"icons": {
"128": "icons/icon128.png",
"16": "icons/icon16.png",
"48": "icons/icon48.png"
},
"manifest_version": 2,
"name": "--Fix SalesForce bubble text--",
"permissions": [ "https://*.salesforce.com/*", "http://*.salesforce.com/*" ],
"update_url": "https://clients2.google.com/service/update2/crx",
"version": "5"
}
And this is the inject.js:
chrome.extension.sendMessage({}, function(response) {
var readyStateCheckInterval = setInterval(function() {
if (document.readyState === "complete") {
clearInterval(readyStateCheckInterval);
var frame = jQuery('#servicedesk iframe.x-border-panel');
frame = frame.contents();
function linkify(inputText) {
var replacedText, replacePattern1, replacePattern2, replacePattern3;
var originalText = inputText;
//URLs starting with http://, https://, file:// or ftp://
replacePattern1 = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&##\/%?=~_|!:,.;]*[-A-Z0-9+&##\/%=~_|])/gim;
replacedText = inputText.replace(replacePattern1, '$1');
//URLs starting with "www." (without // before it, or it'd re-link the ones done above).
replacePattern2 = /(^|[^\/f])(www\.[\S]+(\b|$))/gim;
replacedText = replacedText.replace(replacePattern2, '$1$2');
//Change email addresses to mailto:: links.
replacePattern3 = /(([a-zA-Z0-9\-\_\.])+#[a-zA-Z\_]+?(\.[a-zA-Z]{2,6})+)/gim;
replacedText = replacedText.replace(replacePattern3, '$1');
//If there are hrefs in the original text, let's split
// the text up and only work on the parts that don't have urls yet.
var count = originalText.match(/<a href/g) || [];
if(count.length > 0){
var combinedReplacedText;
//Keep delimiter when splitting
var splitInput = originalText.split(/(<\/a>)/g);
for (i = 0 ; i < splitInput.length ; i++){
if(splitInput[i].match(/<a href/g) == null){
splitInput[i] = splitInput[i].replace(replacePattern1, '$1').replace(replacePattern2, '$1$2').replace(replacePattern3, '$1');
}
}
combinedReplacedText = splitInput.join('');
return combinedReplacedText;
} else {
return replacedText;
}
}
var helpOrbReady = setInterval(function() {
var helpOrb = frame.find('.helpOrb');
if (helpOrb) {
clearInterval(helpOrbReady)
} else {
return;
}
helpOrb.on('mouseout', function(event) {
event.stopPropagation();
event.preventDefault();
setTimeout(function() {
var helpText = frame.find('.helpText')
helpText.css('display', 'block');
helpText.css('opacity', '1');
helpText.css('word-wrap', 'break-word');
var text = helpText.html()
text = text.substr(text.indexOf('http'))
text = text.substr(0, text.indexOf(' '))
var newHtml = helpText.html()
helpText.html(linkify(newHtml))
}, 500); });
}, 1000);
}
}, 1000);
});
It is possible (I have not tested it, but it sounds plausible from a few questions I've seen here) that Chrome does not automatically inject manifest-specified code into newly-created <iframe> elements.
In that case, you will have to use a background script to re-inject your script:
chrome.runtime.onMessage.addListener( function(request, sender, sendResponse) {
if(request.reinject) {
chrome.tabs.executeScript(
sender.tab.id,
{ file: "js/jquery/jquery.js", "all_frames": true },
function(){
chrome.tabs.executeScript(
sender.tab.id,
{ file: "js/inject/inject.js", "all_frames": true }
);
}
);
});
Content script:
// Before everything: include guard, ensure injected only once
if(injected) return;
var injected = true;
function onNewIframe(){
chrome.runtime.sendMessage({reinject: true});
}
Now, I have many questions about your code, which are not directly related to your question.
Why the pointless sendMessage wrapper? No-one is even listening, so your code basically returns with an error set.
Why all the intervals? Use events instead of polling.
If you are waiting on document to become ready, jQuery offers $(document).ready(...)
If you're waiting on DOM modifications, learn to use DOM Mutation Observers, as documented and as outlined here or here. This would be, by the way, the preferred way to call onNewIframe().

Resources