How do we authenticate Quickbooks Online in a Console Application? - c#

I'm trying to invoke Quick Books Online API in a console application where i need to get the bearer token first. Below is the code snippet where i'm trying to get authorization code first for subsequent access token calls. I'm getting a HTML response instead of json object with auth code.
Also, What are the grant types does QBO support ?
HttpClientHandler httpClientHandler = new HttpClientHandler();
HttpClient httpClient = new HttpClient(httpClientHandler,false);
httpClient.BaseAddress = new Uri("https://appcenter.intuit.com/connect/oauth2");
List<KeyValuePair<string, string>> param = new List<KeyValuePair<string, string>>();
param.Add(new KeyValuePair<string, string>("response_type","code"));
param.Add(new KeyValuePair<string, string>("client_id", "AB5********26"));
param.Add(new KeyValuePair<string, string>("scope", "com.intuit.quickbooks.accounting"));
param.Add(new KeyValuePair<string, string>("redirect_uri", "https://developer.intuit.com/v2/OAuth2Playground/RedirectUrl"));
var resp = httpClient.PostAsync("", new FormUrlEncodedContent(param)).GetAwaiter().GetResult();
var result = resp.Content.ReadAsStringAsync().GetAwaiter().GetResult();
HTML Response for the request i sent..
<!DOCTYPE html>
<html class="dj_mac is-not-mobile" data-shell-type="node">
<!-- node-shell -->
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="google-site-verIfication" content="hiEXDzwqUxxMY5KZkAkeHBn6J0gy2Ne1gJdm77RkGbk">
<meta name="msapplication-TileColor" content="#0098cd">
<meta charset="utf-8" />
<link rel="preload"
href="https://plugin.intuitcdn.net/sbg-web-shell-ui/11.24.0/bower_components/document-register-element/build/document-register-element.js"
as="script">
<link rel="preload" href="https://plugin.intuitcdn.net/sbg-web-shell-ui/11.24.0/dojo/dojo.js" as="script">
<link rel="preload" href="https://plugin.intuitcdn.net/sbg-web-shell-ui/11.24.0/shell/boot.js" as="script">
<link rel="preload" href="https://plugin.intuitcdn.net/sbg-web-shell-ui/11.24.0/dojo/resources/blank.gif"
as="image">
<link rel="preload" href="https://plugin.intuitcdn.net/react-dom/16.9.0/umd/react-dom.production.min.js"
as="script">
<link rel="preload" href="https://plugin.intuitcdn.net/react/16.9.0/umd/react.production.min.js" as="script">
<script>
(function() {var e = document.createEvent("Event");e.initEvent("load", true, false);window.dispatchEvent(e);})();
</script>
<link rel="preload" href="https://plugin.intuitcdn.net/ua-parser-js/0.7.20/dist/ua-parser.min.js" as="script">
<meta id="viewPortMetaTag" name="viewport"
content="width=device-width, height=device-height, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=0">
<meta name="application-name" content="QuickBooks App Store">
<meta name="apple-mobile-web-app-title" content="QuickBooks App Store">
<title>QuickBooks App Store</title>
<script type="text/javascript" src="https://plugin.intuitcdn.net/sbg-web-shell-ui/11.24.0/dojo/dojo.js"></script>
</head>
<script type="text/javascript"
src="https://plugin.intuitcdn.net/sbg-web-shell-ui/11.24.0/bower_components/document-register-element/build/document-register-element.js">
</script>
</head>
<body class="en-us">
<script type="text/javascript" nonce="bTjY6kTz1OvXs0b/7WA0RA==">
try {
require({cache: {}});
require(["shell/base/loader", "shell/applications/default/config"], function(loader, config) {
var runtime = {"isWebpack":false,"embedded":false,"ecosystem":false,"accessDeniedPages":[48],"hiddenPages":[]};
loader.load({
useLayers: true,
storageSecondaryKey: "",
storagePrimaryKey: "",
platformPlugin: appContext.pluginsInfo && appContext.pluginsInfo.plugins ? appContext.pluginsInfo.plugins["qbo-ui-platform"] : null,
layers: config.getLayers(runtime)
}, config.getAppHandler(runtime));
});
} catch (error){
console.error(error);
require({cache: {}});
require(["shell/base/loader", "shell/applications/default/config"], function(loader, config) {
var runtime = {"isWebpack":false,"embedded":false,"ecosystem":false,"accessDeniedPages":[48],"hiddenPages":[]};
loader.load({
useLayers: true,
storageSecondaryKey: "",
storagePrimaryKey: "",
platformPlugin: appContext.pluginsInfo && appContext.pluginsInfo.plugins ? appContext.pluginsInfo.plugins["qbo-ui-platform"] : null,
layers: config.getLayers(runtime)
}, config.getAppHandler(runtime));
});
};
</script>
</body>
</html>

using Intuit.Ipp.Core;
using Intuit.Ipp.OAuth2PlatformClient;
using Intuit.Ipp.Security;
using System;
using System.Collections.Generic;
using System.Text;
namespace QuickBooksToken
{
public class GetAccessTokenQbo
{
public static string GetAccessToken()
{
System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12;
var oauth2Client = new OAuth2Client(client Id, client Secret, "https://developer.intuit.com/v2/OAuth2Playground/RedirectUrl", environment); // environment is “sandbox” or “production”
var previousRefreshToken = ReadRefreshTokenFromWhereItIsStored();
var tokenResp = oauth2Client.RefreshTokenAsync(previousRefreshToken);
tokenResp.Wait();
var data = tokenResp.Result;
if (!String.IsNullOrEmpty(data.Error) || String.IsNullOrEmpty(data.RefreshToken) ||
String.IsNullOrEmpty(data.AccessToken))
{
throw new Exception("Refresh token failed - " + data.Error);
}
// If we've got a new refresh_token store it in the file
if (previousRefreshToken != data.RefreshToken)
{
Console.WriteLine("Writing new refresh token : " + data.RefreshToken);
WriteNewRefreshTokenToWhereItIsStored(data.RefreshToken);
return data.AccessToken
}
return data.AccessToken;
}
private static string ReadRefreshTokenFromWhereItIsStored()
{
return "Refresh token"; //hard code your refresh token
}
private static string WriteNewRefreshTokenToWhereItIsStored(string refreshToken)
{
return refreshToken;
}
public static ServiceContext GetServiceContext()
{
string accessToken = GetAccessToken();// Code from above
var oauthValidator = new OAuth2RequestValidator(accessToken);
ServiceContext qboContext = new ServiceContext(realm Id, IntuitServicesType.QBO, oauthValidator);
return qboContext;
}
}
}
Install this nuget library IppDotNetSdkForQuickBooksApiV3 (version 14.4.4).
In my case environment is sandbox. You can use (https://developer.intuit.com/app/developer/playground) link to get values for client secret, client Id, realm Id and a new refresh token to hard code in your c# application.

Related

Web Scraping Java Sites with HtmlAgilityPack

Let me start out by saying that I am no pro at web scraping. I can do the basics on most platforms, but that's about it.
I am trying to create the foundation for a web application that can helps users reinforce their language learning by generating additional data, metrics, as well as create new tools for self-testing. The Duolingo website is not offering up any sort of API so my next thought for now is just to scrape https://www.duome.eu/. I wrote a quick little scraper but didn't realize that the site was java. In the following example, it is my wish to collect all of the words from the Words tab that contain anchors:
using System;
using HtmlAgilityPack;
using System.Net.Http;
using System.Text.RegularExpressions;
namespace DuolingoUpdate
{
class Program
{
static void Main(string[] args)
{
string userName = "Podus";
UpdateDuolingoUser(userName);
Console.ReadLine();
}
private static async void UpdateDuolingoUser(string userName)
{
string url = "https://www.duome.eu/" + userName + "/progress/";
// Create the http client connection
HttpClient httpClient = new HttpClient();
var html = await httpClient.GetStringAsync(url);
// Store the html client data in an object
HtmlDocument htmlDocument = new HtmlDocument();
htmlDocument.LoadHtml(html);
//var words = htmlDocument.DocumentNode.Descendants("div")
// .Where(node => node.GetAttributeValue("id", "")
// .Equals("words")).ToList();
//var wordList = words[0].Descendants("a")
// .Where(node => node.GetAttributeValue("class", "")
// .Contains("wA")).ToList();
Console.WriteLine(html);
}
}
}
The html object of the above code contains:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="google" value="notranslate">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Duolingo · Podus # duome.eu</title>
<link rel="stylesheet" href="/style.css?1548418871" />
<link href="/favicon.ico" rel="shortcut icon" type="image/x-icon" />
<script src="//code.jquery.com/jquery-3.3.1.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
if("".length==0){
var visitortime = new Date();
var visitortimezone = "GMT " + -visitortime.getTimezoneOffset()/60;
//localStorage.tz = visitortimezone;
//timezone = Date.parse(localStorage.tz);
//timezone = localStorage.tz;
//console.log(timezone);
$.ajax({
type: "GET",
url: "/tz.php",
data: 'time='+ visitortimezone,
success: function(){
location.reload();
}
});
}
});
</script>
</head>
<body>
<noscript>Click here to adjsut XP charts to your local timezone. </noscript>
<!-- Yandex.Metrika counter --> <script type="text/javascript" > (function (d, w, c) { (w[c] = w[c] || []).push(function() { try { w.yaCounter47765476 = new Ya.Metrika({ id:47765476, clickmap:true, trackLinks:true, accurateTrackBounce:true }); } catch(e) { } }); var n = d.getElementsByTagName("script")[0], s = d.createElement("script"), f = function () { n.parentNode.insertBefore(s, n); }; s.type = "text/javascript"; s.async = true; s.src = "https://mc.yandex.ru/metrika/watch.js"; if (w.opera == "[object Opera]") { d.addEventListener("DOMContentLoaded", f, false); } else { f(); } })(document, window, "yandex_metrika_callbacks"); </script> <noscript><div><img src="https://mc.yandex.ru/watch/47765476" style="position:absolute; left:-9999px;" alt="" /></div></noscript> <!-- /Yandex.Metrika counter -->
</body>
</html>
But if you go to the actual url https://www.duome.eu/Podus/progress/, the site contains a ton of script. So upon inspection the first problem is that I am not getting the html that I see in the browser. The second problem is that if you view source, its nothing like what is in inspect and I don't see anything in source that would lead me to isolate the data from div id="words".
Given my lackluster knowledge of java built web pages, how do I do this, or is it even possible?
You can access dualingo profile data in JSON format via https://www.duolingo.com/users/<username>
eg. https://www.duolingo.com/users/Podus
This should be much easier than trying to scrape the duome profile page manually.

How to populate dropdownlist using WCF Rest AngularJs

How to populate dropdownlist using WCF Rest Service:
First, this is my code to get service:
service.js
(function (app) {
app.service("GetCategoryService", function ($http) {
this.GetCategory = function () {
return $http.get("http://localhost:51458/ServiceRequest.svc/GetCategory/");
};
});
})(angular.module('entry'));
Entry.Ctrl
(function (app) {
'use strict';
app.controller('entryCtrl', entryCtrl);
entryCtrl.$inject = ['$scope'];
function entryCtrl($scope) {
$scope.pageClass = 'page-entry';
//To Get Category
$scope.Category = function () {
var promiseGet = GetCategoryService.GetCategory();
promiseGet.then(function (pl) { $scope.GetCategory = pl.data },
function (errorPl) {
console.log('Some Error in Getting Records.', errorPl);
});
}
}
})(angular.module('entry'));
This is entry.html
<div class="dropdown">
<select ng-model="Category" ng-options="item.ITEM_TEXT for item in Category"></select>
</div>
WCF Output JSON like:
[{"ITEM_TEXT":"INTERNAL APP"},{"ITEM_TEXT":"INTERNAL IT"}]
I don't know how to passing this to html, what I'm doing like this is not working. please help. thank
Make sure you have set ng-model different from the array name, also inject the service into your controller,
entryCtrl.$inject = ['$scope', 'GetCategoryService'];
DEMO
var app = angular.module('todoApp', []);
app.controller("dobController", ["$scope",
function($scope) {
$scope.Category = [{"ITEM_TEXT":"INTERNAL APP"},{"ITEM_TEXT":"INTERNAL IT"}];
}
]);
<!DOCTYPE html>
<html ng-app="todoApp">
<head>
<title>To Do List</title>
<link href="skeleton.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.min.js"></script>
<script src="MainViewController.js"></script>
</head>
<body ng-controller="dobController">
<select ng-model="selected" ng-init="selected = Category[0]" ng-options="item.ITEM_TEXT for item in Category"></select>
</body>
</html>

Extract JSON from string in .NET

The input string is mix of some text with valid JSON:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<TITLE>Title</TITLE>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<META HTTP-EQUIV="Content-language" CONTENT="en">
<META HTTP-EQUIV="keywords" CONTENT="search words">
<META HTTP-EQUIV="Expires" CONTENT="0">
<script SRC="include/datepicker.js" LANGUAGE="JavaScript" TYPE="text/javascript"></script>
<script SRC="include/jsfunctions.js" LANGUAGE="JavaScript" TYPE="text/javascript"></script>
<link REL="stylesheet" TYPE="text/css" HREF="css/datepicker.css">
<script language="javascript" type="text/javascript">
function limitText(limitField, limitCount, limitNum) {
if (limitField.value.length > limitNum) {
limitField.value = limitField.value.substring(0, limitNum);
} else {
limitCount.value = limitNum - limitField.value.length;
}
}
</script>
{"List":[{"ID":"175114","Number":"28992"]}
The task is to deserialize the JSON part of it into some object. The string can begin with some text, but it surely contains the valid JSON. I've tried to use JSON validation REGEX, but there was a problem parsing such pattern in .NET.
So in the end I'd wanted to get only:
{
"List": [{
"ID": "175114",
"Number": "28992"
}]
}
Clarification 1:
There is only single JSON object in whole the messy string, but the text can contain {}(its actually HTML and can contain javascripts with <script> function(){..... )
You can use this method
public object ExtractJsonObject(string mixedString)
{
for (var i = mixedString.IndexOf('{'); i > -1; i = mixedString.IndexOf('{', i + 1))
{
for (var j = mixedString.LastIndexOf('}'); j > -1; j = mixedString.LastIndexOf("}", j -1))
{
var jsonProbe = mixedString.Substring(i, j - i + 1);
try
{
return JsonConvert.DeserializeObject(jsonProbe);
}
catch
{
}
}
}
return null;
}
The key idea is to search all { and } pairs and probe them, if they contain valid JSON. The first valid JSON occurrence is converted to an object and returned.
Use regex to find all possible JSON structures:
\{(.|\s)*\}
Regex example
Then iterate all these matches unitil you find a match that will not cause an exception:
JsonConvert.SerializeObject(match);
If you know the format of the JSON structure, use JsonSchema.

Google map not properly load in web browser control

I have one html file like Map.Html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title></title>
<script type="text/javascript" src="http://maps.google.com.mx/maps/api/js?sensor=true"></script>
<script type="text/javascript">
var geocoder;
var map;
function initialize() {
geocoder = new google.maps.Geocoder();
var myOptions = {
zoom: 8,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
var address = "Ahmedabad, India" //change the address in order to search the google maps
geocoder.geocode({ 'address': address }, function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
map.setCenter(results[0].geometry.location);
var marker = new google.maps.Marker({
map: map,
position: results[0].geometry.location
});
var infoWindow = new google.maps.InfoWindow({
content: 'Hello'
});
google.maps.event.addListener(marker, "click", function (e) {
infoWindow.open(map, marker);
});
} else {
alert("Geocode was not successful for the following reason: " + status);
}
});
map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
}
</script>
</head>
<body onload="initialize()">
<div id="map_canvas" style="width:100%; height:100%"></div>
</body>
</html>
then i load this html file in webbrowser control DocumentText using below code:
using (StreamReader reader = new StreamReader(System.Windows.Forms.Application.StartupPath + "\\Map.html"))
{
_mapHTML = reader.ReadToEnd();
}
webBrowser1.DocumentText = _mapHTML;
but map not load properly.
zoom in/out, Map/Satellite option, Marker are showing but one white layer on map display.
SOLVED -- for me at least.
For some reason, the Webbrowser control is defaulting to a bad version (experimental?). I changed my initialization script to specify the current 3.3 version and everything is back to normal.
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?v=3.3"></script>

WinForms CefSharp Browser LocalStorage not working

I'm trying to use CefSharp browser control at WinForms application along with the LocalStorage mechanism.
The problem is that the browser control in the application changes to LocalStorage don't affect other browser windows and it doesn't get changes from other chrome browser windows. The HTML works inside native chrome browser and changes localstorage and get changes notifications. What do I miss?
C# Code:
public Form1()
{
InitializeComponent();
CefSharp.Cef.Initialize();
_browser = new ChromiumWebBrowser(URL_TO_LOAD);
_browser.BrowserSettings = new CefSharp.BrowserSettings()
{
ApplicationCacheDisabled = false,
FileAccessFromFileUrlsAllowed = true,
JavascriptDisabled = false,
LocalStorageDisabled = false,
WebSecurityDisabled = true,
JavaScriptOpenWindowsDisabled = false,
JavascriptDomPasteDisabled = false
};
_browser.Load(URL_TO_LOAD);
splitContainer1.Panel1.Controls.Add(_browser);
}
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=620"/>
<title>HTML5 Demo: Storage Events</title>
</head>
<body>
<div>
<p>
<label for="data">Your test data:</label> <input type="text"
name="data" value="" placeholder="change me" id="data" />
</p>
<p id="fromEvent">Waiting for data via<code>storage</code>event...
</p>
</div>
<SCRIPT type="text/javascript">
var addEvent = (function () {
if (document.addEventListener) {
return function (el, type, fn) {
if (el && el.nodeName || el === window) {
el.addEventListener(type, fn, false);
} else if (el && el.length) {
for (var i = 0; i < el.length; i++) {
addEvent(el[i], type, fn);
}
}
};
} else {
return function (el, type, fn) {
if (el && el.nodeName || el === window) {
el.attachEvent('on' + type, function () { return fn.call(el, window.event); });
} else if (el && el.length) {
for (var i = 0; i < el.length; i++) {
addEvent(el[i], type, fn);
}
}
};
}
})();
</SCRIPT>
<script>
alert("localStorage: " + localStorage);
var dataInput = document.getElementById('data'), output = document
.getElementById('fromEvent');
addEvent(window, 'storage', function(event) {
alert('change notification');
if (event.key == 'storage-event-test') {
output.innerHTML = event.newValue;
}
});
addEvent(dataInput, 'keyup', function() {
localStorage.setItem('storage-event-test', this.value);
});
var curStorageVal = localStorage.getItem('storage-event-test');
if(curStorageVal != null && curStorageVal != '')
{
output.innerHTML = curStorageVal;
}
</script>
</body>
</html>
Unless you set a path for the cache location in a CefSharp.CefSettings object and pass it to the Cef.Initialize() method, every time your app is started, the browser(s) will create a new cache instance, which will be discarded when your app exits.
The cache instance also holds your localStorage data, as well as any cookies (you might want to keep a user signed in?) and other things as well.
I had the same problem, but found a solution here: https://github.com/cefsharp/CefSharp/blob/v39.0.2/CefSharp.Example/CefExample.cs#L30-L32
One minimal solution is to add this to your application's startup procedure, like in your Program.Main or in your case, your Form1 class's constructor:
var settings = new CefSharp.CefSettings
{
CachePath = "cache"
};
CefSharp.Cef.Initialize(settings);
1.Get these from NuGet package manager :
using CefSharp;
using CefSharp.WinForms;
2.Create instance for you CefSetting :
CefSettings settings = new CefSettings();
settings.CachePath = #"C:\localstorage";

Categories

Resources