Sending a json document using a supplied certificate - json

I have been supplied a certificate that is required to send a json claim to an uri that has an api for sending the claim. The line in error is: handler.ClientCertificates.Add(certificate)
The following is my code:
Public Shared Sub Main()
Dim path As String = "E:\DRDRW_Update\Web Based Billing\vendorsupplied.pfx"
Dim password As String = "Password"
Dim strGateway As String = "https://MCE/api/WebServiceClaim"
Dim collection = New X509Certificate2Collection()
collection.Import(path, password, X509KeyStorageFlags.Exportable)
Dim certificate = collection(0)
Dim PathClaim As String = "E:\Sample Claim Submission JSON.txt"
Dim fi As New IO.FileInfo(PathClaim)
Dim jsonclaim As String = IO.File.ReadAllText(fi.FullName)
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12
Dim handler As New WebRequestHandler()
handler.ClientCertificates.Add(certificate)
' custom certificate validation handler to ignore untrusted remote certificate
ServicePointManager.ServerCertificateValidationCallback = New RemoteCertificateValidationCallback(AddressOf ValidateServerCertificate)
Using client = New HttpClient(handler)
Dim serializedProduct = JsonConvert.SerializeObject(jsonclaim)
Dim content = New StringContent(serializedProduct, Encoding.UTF8, "application/json")
content.Headers.Add("header1", "header2") ' require header
content.Headers.Add("token", "xxxxxxx-yyyy-zzzz")
Dim result = client.PostAsync(strGateway, content).Result ' ensures task is synchronous
' deserialize the saveresultmodel from the WS response and check for claim validation errors
Dim success As Boolean = False
If result.IsSuccessStatusCode Then
Dim resultResult As String = result.Content.ReadAsStringAsync().Result
Dim claimResult = JsonConvert.DeserializeObject(resultResult)
If claimResult.Errors.Count = 0 Then
success = True
Else
' output error results to console
For Each [error] In claimResult.Errors
Console.WriteLine(JsonConvert.SerializeObject([error]))
Next [error]
End If
End If
End Using
End Sub
Public Class WebRequestHandler
Inherits HttpClientHandler
End Class

The issue is that you have declared your own class for the WebRequestHandler. This is not the same as the System.Net.Http.WebRequestHandler.
Visual Studio had a "nice" feature offering to do this for you when it discovers a missing reference with something like "Generate Class for WebRequestHandler." This usually happens when you copy/paste code from another source and the reference was not fully defined. I never take this option and find it a weird way to approach writing code.
You do need to change your code to reference the correct handler like so.
Dim handler As New System.Net.Http.WebRequestHandler
The reason you are getting an error with that is that your project now needs a reference to another assembly (dll) with that class in it. To figure this out, I used google to find the documentation page here and noted this text: Assembly:
System.Net.Http.WebRequest (in System.Net.Http.WebRequest.dll).
This tells us to add the System.Net.Http.WebRequest reference to the project in order to access this class.

I had two issues, the first was answered by #GMan80013 by putting a reference to System.Net.Http.WebRequest.dll. The other was caused by the use of self-signing certificates. The ServicePointManager.ServerCertificateValidationCallback function needs to return true to ignore the issue of certificate validation caused by self-signed certificates.

Related

Deserialize and serialize Json data in Winform

I have a winform with a combobox and some textboxes. I get the Json data via REST API and deserialize it via Json.NET and a class file.
The JsonHelper
Imports Newtonsoft.Json
Public Module JsonHelper
Public Function FromClass(Of T)(data As T, Optional isEmptyToNull As Boolean = False, Optional jsonSettings As JsonSerializerSettings = Nothing) As String
Dim response As String = String.Empty
If Not EqualityComparer(Of T).Default.Equals(data, Nothing) Then
response = JsonConvert.SerializeObject(data, jsonSettings)
End If
Return If(isEmptyToNull, (If(response = "{}", "null", response)), response)
End Function
Public Function ToClass(Of T)(data As String, Optional jsonSettings As JsonSerializerSettings = Nothing) As T
Dim response = Nothing
If Not String.IsNullOrEmpty(data) Then
response = If(jsonSettings Is Nothing,
JsonConvert.DeserializeObject(Of T)(data),
JsonConvert.DeserializeObject(Of T)(data, jsonSettings))
End If
Return response
End Function
End Module
Imports Newtonsoft.Json
Namespace Models
Public Class Header
<JsonProperty("Name")>
Public Property Name As String
<JsonProperty("DisplayAt")>
Public Property DisplayAt As String
End Class
The Class file
Public Class DataSource
<JsonProperty("Id")>
Public Property Id As String
<JsonProperty("Name")>
Public Property Name As String
<JsonProperty("Headers")>
Public Property Headers As Header()
<JsonProperty("Rows")>
Public Property Rows As String()()
<JsonProperty("TotalRows")>
Public Property TotalRows As Integer
<JsonProperty("LastUpdated")>
Public Property LastUpdated As DateTime
<JsonProperty("CompanyId")>
Public Property CompanyId As Integer
End Class
Public Class Category
<JsonProperty("DataSource")>
Public Property DataSource As DataSource
End Class
End Namespace
The combobox is populated with data from the Json file and via bindingsource the textboxes are connected to the combobox. So if you select another row, the values of the textboxes change.
The bindingsource
Dim bindingSource As BindingSource = New BindingSource()
bindingSource.DataSource = dt
bindingSource.Sort = "Weergave DESC"
ListBox1.DataSource = bindingSource
ListBox1.DisplayMember = "Weergave"
ListBox1.ValueMember = "Id"
'ListBox1.Sorted = True
txtWeergave.DataBindings.Clear()
txtWeergave.DataBindings.Add(New Binding("Text", ListBox1.DataSource, "Weergave", True, DataSourceUpdateMode.OnPropertyChanged))
txtProjectnaam.DataBindings.Clear()
txtProjectnaam.DataBindings.Add(New Binding("Text", ListBox1.DataSource, "Projectnaam", True, DataSourceUpdateMode.OnPropertyChanged))
txtProjectnummer.DataBindings.Clear()
txtProjectnummer.DataBindings.Add(New Binding("Text", ListBox1.DataSource, "Projectnummer", True, DataSourceUpdateMode.OnPropertyChanged))
txtServicemonteur.DataBindings.Clear()
txtServicemonteur.DataBindings.Add(New Binding("Text", ListBox1.DataSource, "Servicemonteur", True, DataSourceUpdateMode.OnPropertyChanged))
I wan't to make some changes to the data and update the database via a PUT request.
Therefore I made the following routine:
Private Sub PutData()
Dim request As WebRequest = WebRequest.Create("my url")
request.ContentType = "application/json"
request.Method = "PUT"
' Get the response.
Dim response As HttpWebResponse = CType(request.GetResponse(), HttpWebResponse)
' Get the stream containing content returned by the server.
Dim dataStream As Stream = response.GetResponseStream()
' Open the stream using a StreamReader for easy access.
Dim reader As New StreamReader(dataStream)
' Read the content.
Dim responseFromServer As String = reader.ReadToEnd()
'Dim JObject As Object
Dim rawJson As String = responseFromServer
Dim dataSource As String = JsonHelper.FromClass(Of Category)(rawJson).DataSource
End Sub
The following things are not clear for me.
If I make changes in the textbox, the changes are also made in the combobox because they have been connected with eachother via a bindingsource. But does the values also change in the Class file?
How can I make changes to the values of the textboxes and update the datasource via a PUT request?
It's not completely clear becacuse some things are missing from your question, such as your binding configuration, but..
Generally I wouldn't expect things to be that "when I change the textbox the combo will change". Let's take some artificial example of a Person class with Name and Ethnicity. Name is a string and would be bound to a textbox. Ethnicity is a fixed list of values, and I would expect a combo to be bound up such that the combo's datasource, displaymember and valuememeber related to the columns/properties of a list of ethnicities, and then the combo's selectedvalue be bound to the person's ethnicity setting. This way changing the selected item in the combo edits their ethnicity to one of the other values in the fixed list of ethnicities. Changing the value displayed in the name box doesn't change anything about the ethnicity. If you have a list of multiple Person that the bindingsource is managing, then you can navigate the bindingsource to show different people and edit them. It's probably easiest to see if you also add a datagridview, albeit temporarily, bound to the same bindingsource that persons are bound through; changing the current row of the grid navigates the bindingsource and the textbox/combobox will change to show the details for the new person
The PUT request is easier to answer; you formulate a block of json for the server to work on by re-serializing your edited class to JSON, and then send it as the body of the put request
request.Method = "PUT"
'formulate a body
Dim postData = JsonHelper.FromClass(editedPerson)
Dim encoding As New ASCIIEncoding() 'or whatever
Dim byte1 As Byte() = encoding.GetBytes(postData)
request.ContentType = "application/json"
request.ContentLength = byte1.Length
Dim reqStream = request.GetRequestStream()
reqStream.Write(byte1, 0, byte1.Length)
reqStream.Close()
' Get the response.
Dim response As HttpWebResponse = CType(request.GetResponse(), HttpWebResponse)
By the way, in C# at least, you can right click your project name and choose Add REST Client, give it the URL of the swagger/openapi descriptor of your web service and it will make all the client classes for you which can simplify interaction a lot. Though it's a C# only thing (it's based on autorest which I don't think supports VB), C# and VB are the same thing internally, so you can add a C# project alongside your VB one purely for purposes of creating a rest client, and then import a reference to the c# project into your VB one

lotus agent request_content how to separate fields

I have a lotus agent running with lotusscript. Form the browser I post form data to the webserver and I receive this data with the following lotusscript:request_method = doc.GetItemValue( "request_content" )(0)
But if I have a form with for example name and phonenumber. Then my agent receives this as name=bla&phonenumber=243525
How can i separate these fields actually and secondly how can I receive XML on this agent so that I can extract this and put in to a document. I googled a lot but still got no solutions.
The way you get the data differs if the client makes a GET or a POST.
If this is a get, all the parameters are in the url in a url format.
Many many ressource on the web will give you some code to parse this url and get name and value a simple search in goolge will bring : http://searchdomino.techtarget.com/tip/Parsing-URL-Parameters-with-Ease
I use generally the following code, which add in the document context the fields received on url or on post.
Dim s As NotesSession
Set s = New notessession
Set doc = s.documentcontext
Dim myQuerystring As String
If doc Is Nothing Then
logErrorEX "getting a call without document context ?!?","", doc,""
GoTo returnErr
End If
If doc.QUERY_STRING_DECODED(0)<>"" Then'it's a GET
myQuerystring = doc.QUERY_STRING_DECODED(0)
ElseIf doc.QUERY_STRING(0)<>"" Then
myQuerystring = doc.QUERY_STRING(0)
'decode it !
ElseIf doc.REQUEST_CONTENT(0)<>"" Then'it's a POST
myQuerystring = doc.REQUEST_CONTENT(0) ' WARNING this is for POST but you will have to decode !!!
'decode it !
Else
logErrorEX "getting a call with document context but without query_string?!?","", doc,""
GoTo returnErr
End if
Call ExplodeQueryString(myQuerystring, doc)
Private Sub ExplodeQueryString (QueryString As String,doc As NotesDocument )
Dim ArgsList As Variant
ArgsList = Split (QueryString, "&")
If IsArray(ArgsList) Then
debugString = debugString+"ArgsList is an array of " & UBound(ArgsList)
Else
debugString = debugString+"ArgsList is NOT an array ??? " & ArgsList
End if
Dim ArgKey As String
Dim ArgValue As String
ForAll Arg In ArgsList
If left$(Arg, 1)= "_" Or Left$(Arg, 1)= "%" Then
'ignore it
else
ArgKey = strleft(Arg, "=")
If ArgKey = "" Then
'ignore it?
else
ArgValue = strright$(Arg, "=")
' AgentArgs(ArgKey) = ArgValue
doc.Replaceitemvalue ArgKey, ArgValue
End If
End if
End ForAll
End Sub
I didn't declare some global variable like debugString to shorten in.
The format you are seeing is the convention used by all web browser software to encode field data from forms. You can use functions similar to the ExplodeQueryString function in the code posted by Emmanual to parse it. It looks to me like he is taking each "&name" portion and creating a NotesItem with that name and using it to store the value from the "=value" portion. You can do that, or you can use a List, or whatever best fits your requirements.
There is no rule against sending POST data in other formats without using the &name=value convention. It just requires agreement between whatever software is doing the sending and your software on the receiving side. If they want to send you XML in the POST data, that's fine. You can use standard XML parsing functions to deal with it. Notes comes with a NotesDOMParsesr class that you can use if you want. If you are running on Windows, you can use Microsoft.XMLDOM instead.
I wrote a class a while back that does exactly what you ask for. It splits the query string (or request content) into a list of values, with the name as the list tag.
http://blog.texasswede.com/free-code-class-to-read-url-name-value-pairs/
Here is the code (I usually put it in a script library called Class.URL):
%REM
Library Class.URL
Created Oct 9, 2014 by Karl-Henry Martinsson
Description: Lotusscript class to handle incoming URL (GET/POST).
%END REM
Option Public
Option Declare
%REM
Class URLData
Description: Class to handle URL data passed to web agent
%END REM
Class URLData
p_urldata List As String
%REM
Sub New()
Description: Create new instance of URL object from NotesDocument
%END REM
Public Sub New()
Dim session As New NotesSession
Dim webform As NotesDocument
Dim tmp As String
Dim tmparr As Variant
Dim tmparg As Variant
Dim i As Integer
'*** Get document context (in-memory NotesDocument)
Set webform = session.DocumentContext
'*** Get HTTP GET argument(s) after ?OpenAgent
tmp = FullTrim(StrRight(webform.GetItemValue("Query_String")(0),"&"))
If tmp = "" Then
'*** Get HTTP POST argument(s) after ?OpenAgent
tmp = FullTrim(StrRight(webform.GetItemValue("Request_Content")(0),"&"))
End If
'*** Separate name-value pairs from each other into array
tmparr = Split(tmp,"&")
'*** Loop through array, split each name-value/argument
For i = LBound(tmparr) To UBound(tmparr)
tmparg = Split(tmparr(i),"=")
p_urldata(LCase(tmparg(0))) = Decode(tmparg(1))
Next
End Sub
%REM
Function GetValue
Description: Get value for specified argument.
Returns a string containing the value.
%END REM
Public Function GetValue(argname As String) As String
If IsElement(p_urldata(LCase(argname))) Then
GetValue = p_urldata(LCase(argname))
Else
GetValue = ""
End If
End Function
%REM
Function IsValue
Description: Check if specified argument was passed in URL or not.
Returns boolean value (True or False).
%END REM
Public Function IsValue(argname As String) As Boolean
If IsElement(p_urldata(LCase(argname))) Then
IsValue = True
Else
IsValue = False
End If
End Function
'*** Private function for this class
'*** There is no good/complete URL decode function in Lotusscript
Private Function Decode(txt As String) As String
Dim tmp As Variant
Dim tmptxt As String
tmptxt = Replace(txt,"+"," ")
tmp = Evaluate(|#URLDecode("Domino";"| & tmptxt & |")|)
Decode = tmp(0)
End Function
End Class
And this is how you can use it:
Option Public
Option Declare
Use "Class.URL"
Sub Initialize
Dim url As URLData
'*** Create new URLData object
Set url = New URLData()
'*** MIME Header to tell browser what kind of data we will return
Print "content-type: text/html"
'*** Check reqired values for this agent
If url.IsValue("name")=False Then
Print "Missing argument 'name'."
Exit Sub
End If
'*** Process name argument
If url.GetValue("name")="" Then
Print "'Name' is empty."
Else
Print "Hello, " + url.GetValue("name") + "!"
End If
End Sub

Programatically fill TFS New Bug workitem from VBA

I am wondering if it is possible to have VBA (Access) open a TFS Bug report webpage and fill in the description ?
While I am able to open the page I have not yet found a way to populate the description and potently other fields.
Perhaps one of the experts knows?
I wouldn't try to do what you are working on.
One, trusting a buggy application to correctly report its own bugs isn't a great idea. But beyond that you will be trying to attach Access to TFS.
That being said you can do this entirely automated. Put in some error trapping and then what you are looking to do is how to call TFS APIs. But you may or may not have to install some third part tools etc.
Starting point TFS API
The way I finally did it is to use a dll to interface between access and tfs...
For anyone trying to do the same here is the code...
Imports Microsoft.TeamFoundation.Client
Imports Microsoft.TeamFoundation.WorkItemTracking.Client
Imports Microsoft.TeamFoundation.WorkItemTracking.Common
Imports System.Runtime.InteropServices
<ComClass(TFSInterOp.ClassId, TFSInterOp.InterfaceId, TFSInterOp.EventsId)> Public Class TFSInterOp
Public Const ClassId As String = "14306fc5-1492-42d6-a032-bc4348508dd3"
Public Const InterfaceId As String = "288339cb-0c2e-45fd-8005-e5fed401f0cc"
Public Const EventsId As String = "723327dc-7777-44e4-b291-9299027665eb"
Public Sub New()
End Sub
Public Function InsertBugWorkItem(Title As String, Desc As String) As String
Dim tfsServer As String = My.Settings("TfsFullPath").ToString ' {YOUR TFS SERVER PATH}
Dim strAssUser As String = My.Settings("AssignTo").ToString
Dim teamFoundationServer1 As Microsoft.TeamFoundation.Client.TfsTeamProjectCollection = Microsoft.TeamFoundation.Client.TfsTeamProjectCollectionFactory.GetTeamProjectCollection(New Uri(tfsServer))
Dim workItemStore1 As New WorkItemStore(teamFoundationServer1)
teamFoundationServer1.Authenticate()
Dim WorkItemStore As WorkItemStore = New WorkItemStore(tfsServer)
Dim tfsProject As Project = WorkItemStore.Projects(0)
Dim wIType As WorkItemType = tfsProject.WorkItemTypes("Bug")
Dim workItem As WorkItem = New WorkItem(wIType)
Dim wiStore = teamFoundationServer1.GetService(Of WorkItemStore)()
Dim Project = wiStore.Projects
Dim Area = Project.Item(0).AreaRootNodes("BITS").Id ' The project to add the work item to
' Prefill items
workItem.Title = Title
workItem.Description = Desc
workItem.AreaId = Project.Item(0).AreaRootNodes("BITS").Id
workItem.Fields("Assigned To").Value = strAssUser
workItem.Fields("System Info").Value = "Access V 1.1.25"
workItem.Fields("Repro Steps").Value = Desc
Dim result As ArrayList = workItem.Validate()
If result.Count > 0 Then
Return (result(0).ToString + "There was an error adding this work item to the work item repository")
Else
workItem.Save()
End If
' Open the new item in explorer
Dim myService = teamFoundationServer1.GetService(Of TswaClientHyperlinkService)()
Dim myUrl = myService.GetWorkItemEditorUrl(workItem.Id)
Dim oProcess As New System.Diagnostics.Process()
oProcess.StartInfo.FileName = myUrl.ToString
oProcess.Start()
Return "OK"
End Function
End Class

ServiceStack.Text reading json results not working

I am just trying to figure out the best way to deserialize a json string returned from a 3rd party api call. I read ServiceStack is fast so want to try it out. No experience and here is what I have done:
Opened Visual Studio 2013
Created new project Windows Forms Application
Installed ServiceStack.Text (based on https://servicestack.net/download)
Added a button (btnView) and textbox (txtOutput)
Add code to btnView_Click event
Private Sub btnView_Click(sender As Object, e As EventArgs) Handles btnView.Click
Me.Cursor = Cursors.WaitCursor
Dim wp As New WebPost 'this allows to pass url and return results
wp.URL = "xxxx"
Dim sJSONRetVal As String = wp.Request(String.Empty, True)
'sJSONRetVal return values looks like the following:
'{"complaints":[{"feedback_type":"abuse","subject":"Sales Agent Position"},{"feedback_type":"abuse","subject":"Sales Agent Position"}],"message":"OK","code":0}
'ServiceStack.Text example
Dim t As SMTP_Complaints = ServiceStack.Text.JsonSerializer.DeserializeFromString(Of SMTP_Complaints)(sJSONRetVal)
'For Each xi As SMTP_Complaints In t
' txtOutput.Text &= xi.mail_from & vbCrLf
'Next
wp = Nothing
txtOutput.Text = t.ToString
Me.Cursor = Cursors.Default
End Sub
Public Class SMTP_Complaints
Dim _feedback_type As String = ""
Dim _subject As String = ""
Public Property feedback_type As String
Get
Return _feedback_type
End Get
Set(value As String)
_feedback_type = value
End Set
End Property
Public Property subject As String
Get
Return _subject
End Get
Set(value As String)
_subject = value
End Set
End Property
End Class
The above doesn't seem to get any data. how would I loop through the data returned and return the data from both instances? Just not sure how I need to set this up to read the json data and then be able to output.
Based on the returned JSON of:
{"complaints":[{"feedback_type":"abuse","subject":"Sales Agent Position"},{"feedback_type":"abuse","subject":"Sales Agent Position"}],"message":"OK","code":0}
You will need two DTOs to deserialise this result.
I have used auto implemented properties here to simplify the complexity of the code. If you use an older version of VB, you'll need to expand these out to include a backing field with get and set method.
Public Class SMTP_Complaint
Public Property feedback_type As String
Public Property subject As String
End Class
Public Class SMTP_ComplaintsResponse
Public Property complaints As SMTP_Complaint()
Public Property message As String
Public Property code As Integer
End Class
You need the SMTP_ComplaintsResponse class because your complaints are wrapped in your JSON response.
Then to deserialise the response:
Dim response = JsonSerializer.DeserializeFromString(Of SMTP_ComplaintsResponse)(sJSONRetVal)
And your complaints are then accessible:
For Each complaint As var In response.complaints
Console.WriteLine("Type: {0}, Subject {1}", complaint.feedback_type, complaint.subject)
Next

Upload file with ADODB for Web-DAV "cannot find any objects or data in accordance with the name..."

I have an MS Access 2007 VBA application running on Windows 7. One crucial function is to upload files to a WebDAV server. The code below works perfectly on one PC, but fails on other PCs (and yes, each is configured the same way).
Here is a translate.google.com translation of the Norwegion error message that pops up when it fails on the other PCs:
Run-time error '-2147217895 (80040e19)': can not find any objects or data in accordance with the name, range or selection criteria within the scope of this operation
It fails on this line of code:
objRecord.Open fil, "URL=" & URL, adModeReadWrite, adCreateOverwrite, adDelayFetchStream, sUsername, sPwd
The full function code is below. It's really just reuse of the code at http://social.msdn.microsoft.com/Forums/en-US/isvvba/thread/26b8e905-33d0-438b-98a7-bb69053b931e/. Any hints would be greatly appreciated!
Function DAVPUT(ByVal URL As String, ByVal fil As String) As Boolean '
Dim sUsername As String
Dim sPwd As String
sUsername = "k#dummy.com"
sPwd = "dummy"
Dim objRecord As New ADODB.Record
Dim objStream As New ADODB.Stream
objRecord.Open fil, "URL=" & URL, adModeReadWrite, adCreateOverwrite, adDelayFetchStream, sUsername, sPwd
objStream.Type = adTypeBinary
objStream.Open objRecord, adModeWrite, adOpenStreamFromRecord
objStream.LoadFromFile fil
objStream.Flush
DoEvents
objStream.close
objRecord.close
DAVPUT = True
End Function
This post suggests you "compare the versions of MDAC local and remote". I realize I am offering a minimal, inexpert answer, but since there are not others, here it is.