how to make bot to search particular intent when a user selects particular selection using luis c# iam trying using switch case please guide me - chatbot

iam trying to call particular intent when someone selects any intent eg:if user selects cmtools intent then the user can ask questions related to cmtools only. Other than cmtools if user asks question then the bot should rply Sorry not a good match.Please help me with the code to call intents from switch case or any other idea is appreciated.
Thanks in advance!
using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.Collections.Generic;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.Luis;
using Microsoft.Bot.Builder.Luis.Models;
using Newtonsoft.Json;
using System.Text;
namespace Microsoft.Bot.Sample.LuisBot
{
[LuisModel("id","key")]
[Serializable]
public class BasicLuisDialog : LuisDialog<object>
{
// QnA Maker global settings
// assumes all KBs are created with same Azure service
static string qnamaker_endpointKey = "endpointkey";
static string qnamaker_endpointDomain = "chatbot";
// QnA Maker Knowledge base
static string AIS_KB= "id1";
static string Speed_KB = "id2";
static string CMTools_KB = "id3";
// QnA Maker Finance Knowledge base
// Instantiate the knowledge bases
public QnAMakerService AIS_KBQnAService = new QnAMakerService("https://" + qnamaker_endpointDomain + ".azurewebsites.net", AIS_KB, qnamaker_endpointKey);
public QnAMakerService Speed_KBQnAService = new QnAMakerService("https://" + qnamaker_endpointDomain + ".azurewebsites.net", Speed_KB, qnamaker_endpointKey);
public QnAMakerService CMTools_KBQnAService = new QnAMakerService("https://" + qnamaker_endpointDomain + ".azurewebsites.net", CMTools_KB, qnamaker_endpointKey);
public const string AIS_KBIntent = "AIS_KB";
public const string Speed_KBIntent = "Speed_KB"; // new intent
public const string CMTools_KBIntent = "CMTools_KB"; // new intent
public class Metadata
{
public string name { get; set; }
public string value { get; set; }
}
public class Answer
{
public IList<string> questions { get; set; }
public string answer { get; set; }
public double score { get; set; }
public int id { get; set; }
public string source { get; set; }
public IList<object> keywords { get; set; }
public IList<Metadata> metadata { get; set; }
}
public class QnAAnswer
{
public IList<Answer> answers { get; set; }
}
[Serializable]
public class QnAMakerService
{
private string qnaServiceHostName;
private string knowledgeBaseId;
private string endpointKey;
public QnAMakerService(string hostName, string kbId, string endpointkey)
{
qnaServiceHostName = hostName;
knowledgeBaseId = kbId;
endpointKey = endpointkey;
}
async Task<string> Post(string uri, string body)
{
using (var client = new HttpClient())
using (var request = new HttpRequestMessage())
{
request.Method = HttpMethod.Post;
request.RequestUri = new Uri(uri);
request.Content = new StringContent(body, Encoding.UTF8, "application/json");
request.Headers.Add("Authorization", "EndpointKey " + endpointKey);
var response = await client.SendAsync(request);
return await response.Content.ReadAsStringAsync();
}
}
public async Task<string> GetAnswer(string question)
{
string uri = qnaServiceHostName + "/qnamaker/knowledgebases/" + knowledgeBaseId + "/generateAnswer";
string questionJSON = #"{'question': '" + question + "'}";
var response = await Post(uri, questionJSON);
var answers = JsonConvert.DeserializeObject<QnAAnswer>(response);
if (answers.answers.Count > 0)
{
return answers.answers[0].answer;
}
else
{
return "No good match found.";
}
}
}
public enum Selection
{
CMTools, AIS, Speed
}
[LuisIntent("AppSelection")]
private async Task AppSelection(IDialogContext context, LuisResult result)
{
var options = new Selection[] { Selection.CMTools, Selection.AIS, Selection.Speed };
var descriptions = new string[] { "CMTools", "AIS", "Speed" };
PromptDialog.Choice<Selection>(context, ResumeAfterOrderSelectionClarification,options, "Please choose your application?", descriptions: descriptions );
}
private async Task ResumeAfterOrderSelectionClarification(IDialogContext context, IAwaitable<Selection> result,LuisResult result1)
{
var selection = await result;
await context.PostAsync($"Thanks for choosing {selection}. how can I help you ?");
switch(selection){
case CMTools:
await context.PostAsync($"CMTools");
//code needed to call CMTools_KB intent
break;
case AIS:
await context.PostAsync($"AIS_KB");
//code needed to call AIS_KB intent
break;
case Speed:
await context.PostAsync($"Speed_KB");
// Ask the HR knowledge base
//code needed to call Speed_KB intent
break;
default:
await context.PostAsync($"testing.........");
}
}
[LuisIntent("")]
[LuisIntent("None")]
public async Task NoneIntent(IDialogContext context, LuisResult result)
{
HttpClient client = new HttpClient();
await this.ShowLuisResult(context, result);
}
[LuisIntent("AIS_KB")]
public async Task AIS_KBIntent(IDialogContext context, LuisResult result)
{
// Ask the HR knowledge base
var qnaMakerAnswer = await AIS_KBQnAService.GetAnswer(result.Query);
await context.PostAsync($"{qnaMakerAnswer}");
context.Wait(MessageReceived);
}
[LuisIntent("Speed_KB")]
public async Task Speed_KBIntent(IDialogContext context, LuisResult result)
{
// Ask the HR knowledge base
var qnaMakerAnswer = await Speed_KBQnAService.GetAnswer(result.Query);
await context.PostAsync($"{qnaMakerAnswer}");
context.Wait(MessageReceived);
}
[LuisIntent("CMTools_KB")]
public async Task CMTools_KBIntent(IDialogContext context, LuisResult result)
{
// Ask the HR knowledge base
var qnaMakerAnswer = await CMTools_KBQnAService.GetAnswer(result.Query);
await context.PostAsync($"{qnaMakerAnswer}");
context.Wait(MessageReceived);
}
private async Task ShowLuisResult(IDialogContext context, LuisResult result)
{
await context.PostAsync($"Sorry , I am not able to help you with this questions,Please connect our L2 Support Group");
var qnaMakerAnswer = await CMTools_KBQnAService.GetAnswer(result.Query);
await context.PostAsync($"{qnaMakerAnswer}");
await context.PostAsync($"it is finished ");
context.Wait(MessageReceived);
}
}
}

Related

Xamarin forms - Cannot get object from REST API to xaml page

I am developing an Xamarin.Forms app in VS 2019. My REST API is hosted on GoDaddy.
When I call the api I get back my json converted object fine in my viewmodel. But the object is null
from my xaml page. See this code:
public class NewOrderViewModel : BaseViewModel
{
public NewOrderDetails NewOrderDetails { get; set; }
public ICommand OkCommand { get; private set;}
public ICommand CancelCommand { get; private set; }
readonly IPageService _pageService;
public NewOrderViewModel(IPageService pageService, int custId)
{
_pageService = pageService;
OkCommand = new Command(NewOrder);
CancelCommand = new Command(CancelOrder);
NewOrderDetails = new NewOrderDetails();
LoadNewOrderDetails(custId);
}
private async void LoadNewOrderDetails(int custId)
{
using (var client = new HttpClient(new System.Net.Http.HttpClientHandler()))
{
var response = await client.GetStringAsync("http://api.lates.com.au/api/Customers/" + custId.ToString());
var customer = JsonConvert.DeserializeObject<Customer>(response);
await _pageService.DisplayAlert("Value", customer.CustomerName, "OK"); //This confirms the correct customer is returned.
NewOrderDetails.CustomerName = customer.CustomerName;
foreach (var cd in customer.CustomerDepartments)
{
NewOrderDetails.CustomerDepartments.Add(cd);
}
NewOrderDetails.OrderDate = DateTime.Today;
NewOrderDetails.DeliveryDate = DateTime.Today;
NewOrderDetails.CustomerId = custId;
}
}
private void NewOrder()
{
_pageService.PopAsync();
_pageService.PushModalAsync(new CustomerOrder());
}
private void CancelOrder()
{
_pageService.PopAsync();
}
}
public partial class NewOrder : ContentPage
{
public NewOrder()
{
InitializeComponent();
imgAddIcon.Source = FileImageSource.FromFile("AddDocument64By64.png");
}
protected override void OnAppearing()
{
BindingContext = new NewOrderViewModel(new PageService(), 1);
//If i put a break point here the NewOrderDetails property of NewOrderViewModel is null - WHY???
}
}
It seems to be something to do with asynchronous timing. Let me know if you need more info.
Malcolm
If i put a break point here the NewOrderDetails property of
NewOrderViewModel is null - WHY???
At that time your break point hit, the data in NewOrderDetails has not be set because the httpRequest is still requesting and you have to await the request finish to get the data from Api.
To solve your problem, you have to implement INotifyPropertyChanged in both NewOrderDetails and NewOrderViewModel to notify the View update value after you get the data from Api. I will give you some code snippets:
In NewOrderDetails :
public class NewOrderDetails : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public NewOrderDetails()
{
}
public string CustomerName
{
set
{
if (customerName != value)
{
customerName = value;
OnPropertyChanged("CustomerName");
}
}
get
{
return customerName;
}
}
string customerName { get; set; }
}
In NewOrderViewModel :
public class NewOrderViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public NewOrderDetails NewOrderDetaila
{
set
{
if (newOrderDetails != value)
{
newOrderDetails = value;
OnPropertyChanged("NewOrderDetaila");
}
}
get
{
return newOrderDetails;
}
}
NewOrderDetails newOrderDetails { get; set; }
public NewOrderViewModel( int custId)
{
NewOrderDetaila = new NewOrderDetails();
LoadNewOrderDetails(custId);
}
private async void LoadNewOrderDetails(int custId)
{
//...
NewOrderDetaila.CustomerName = "133";
//...
}
}
And in Xaml binding:
<Label Text="{Binding NewOrderDetaila.CustomerName}"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
Try and let me know if it works for you.
One problem in your code is here:
using (var client = new HttpClient(new System.Net.Http.HttpClientHandler()))
{
var response = await client.GetStringAsync("http://api.lates.com.au/api/Customers/" + custId.ToString());
var customer = JsonConvert.DeserializeObject<Customer>(response);
await _pageService.DisplayAlert("Value", customer.CustomerName, "OK"); //This confirms the correct customer is returned.
NewOrderDetails.CustomerName = customer.CustomerName;
foreach (var cd in customer.CustomerDepartments)
{
NewOrderDetails.CustomerDepartments.Add(cd);
}
NewOrderDetails.OrderDate = DateTime.Today;
NewOrderDetails.DeliveryDate = DateTime.Today;
NewOrderDetails.CustomerId = custId;
}
HttpClient should be defined as static class, and reused during your application lifetime. Disposing and recreating HttpClient leads to socket errors. Your code is causing multiple requests. I suggest also move this method to Task, that returns the object.
Example method:
internal class SendData
{
private static HttpClient client = new HttpClient();
internal static async Task<string> MakeServerRequest(string url, string content)
{
try
{
var request = new StringContent(content, Encoding.UTF8, "application/json");
var result = await client.PostAsync(url, request);
var response = await result.Content.ReadAsStringAsync();
return response;
}
catch (Exception ex)
{
YOUR ADDITIONAL LOGIC HERE
return null;
}
}
}
This will return JSON string that you can serialize to object, and do whatever your app requires.

How to pass a class as parameter to Web Api

I want to pass a class as parameter to my Web API. Here is my code:
Web API:
[Route("api/[controller]")]
public class ValuesController : Controller
{
[HttpPost]
public string Post(ClusteringObject _cluesteringObject)
{
return _cluesteringObject.NumberOfCluster.ToString();
}
}
public class ClusteringObject
{
public string Data { get; set; }
public int NumberOfCluster { get; set; }
}
And my test console App code:
class Program
{
static void Main(string[] args)
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:57961/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
var testData = new ClusteringObject()
{
Data = "asdf",
NumberOfCluster = 1
};
HttpResponseMessage response = client.PostAsJsonAsync("api/values", testData).Result;
string res = "";
using (HttpContent content = response.Content)
{
Task<string> result = content.ReadAsStringAsync();
res = result.Result;
}
}
}
public class ClusteringObject
{
public string Data { get; set; }
public int NumberOfCluster { get; set; }
}
My post action returns 0. It seems, I couldn't pass my object to Web API thats why it shows default values of each properties. How can I pass instance of ClusteringObject to my Web API ?
PostAsJsonAsync will send ClusteringObject as Body in the request, I suggest you try
FromBody like
public string Post([FromBody]ClusteringObject _cluesteringObject)

Serial communication(Serial UART) in raspberry-pi with windows 10 iot core and sim 900a

We were building and an app that could send and receive sms from sim900a module interfacing with raspberry-pi with windows 10 iot core.
In windows form application:-
We are going to read the serial data by making a call to ReadExisting method of SerialPort instance; which returns a partial response, so we have to loop through and append the data until the serial data that we received contain a substring “OK” or “\r\n>” means we have completely read the AT command response.
do
{
if (receiveNow.WaitOne(timeout, false))
{
string data = port.ReadExisting();
serialPortData += data;
}
}while (!serialPortData.EndsWith("\r\nOK\r\n") &&
!serialPortData.EndsWith("\r\n> ") &&
!serialPortData.EndsWith("\r\nERROR\r\n"));
How to do the same thing in universal windows platform(uwp)
I have used these commands but it reads partially(till \r \n), remaining part is not being read.
Task<UInt32> loadAsyncTask;
uint ReadBufferLength = 1024;
// Set InputStreamOptions to complete the asynchronous read operation when one or more bytes is available
dataReaderObject.InputStreamOptions = InputStreamOptions.Partial;
// Create a task object to wait for data on the serialPort.InputStream
loadAsyncTask = dataReaderObject.LoadAsync(ReadBufferLength).AsTask();
// Launch the task and wait
UInt32 bytesRead = await loadAsyncTask;
if (bytesRead > 0)
{
rcvdText.Text = dataReaderObject.ReadString(bytesRead);
status.Text = "bytes read successfully!";
}
Have a look at SerialDevice class. It can be created from USB vendorID/productID or by name (e.g. COM1). It provides stream based access to the serial port.
I´m not experienced with it (work in progress) but it seems like its the way to go for UWP.
This is my current attempt to serial communication in UWP (still untested!):
using SerialCommunication.Contracts.DataTypes;
using SerialCommunication.Contracts.Interfaces;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Windows.Devices.Enumeration;
using Windows.Devices.SerialCommunication;
using Windows.Storage.Streams;
namespace SerialCommunication
{
public class SerialPort : ISerialPort, IDisposable
{
public SerialPort(string portName)
{
PortName = portName;
AdvancedQuery = SerialDevice.GetDeviceSelector(portName);
}
public SerialPort(ushort vendorId, ushort productId)
{
PortName = $"{nameof(vendorId)}={vendorId} {nameof(productId)}={productId}";
AdvancedQuery = SerialDevice.GetDeviceSelectorFromUsbVidPid(vendorId, productId);
}
public void Dispose() => Disconnect();
public string PortName { get; }
public string AdvancedQuery { get; }
public bool IsConnected => serialPort != null;
public int WriteTimeoutMilliseconds { get; set; }
public int ReadTimeoutMilliseconds { get; set; }
public uint BaudRate { get; set; }
public SerialParity Parity { get; set; } = SerialParity.None;
public SerialStopBitCount StopBits { get; set; } = SerialStopBitCount.One;
public ushort DataBits { get; set; } = 8;
public SerialHandshake Handshake { get; set; } = SerialHandshake.None;
public InputStreamOptions InputStreamOptions { get; set; } = InputStreamOptions.ReadAhead;
public UnicodeEncoding Encoding { get; set; } = UnicodeEncoding.Utf8;
public ByteOrder ByteOrder { get; set; } = ByteOrder.LittleEndian;
public bool Connect()
{
lock (serialPortLock)
{
CreateSerialDevice().Wait();
// serial port
serialPort.WriteTimeout = TimeSpan.FromMilliseconds(WriteTimeoutMilliseconds);
serialPort.ReadTimeout = TimeSpan.FromMilliseconds(ReadTimeoutMilliseconds);
serialPort.BaudRate = BaudRate;
serialPort.Parity = Parity;
serialPort.StopBits = StopBits;
serialPort.DataBits = DataBits;
serialPort.Handshake = Handshake;
// output stream
dataWriter = new DataWriter(serialPort.OutputStream);
dataWriter.UnicodeEncoding = Encoding;
dataWriter.ByteOrder = ByteOrder;
// input stream
dataReader = new DataReader(serialPort.InputStream);
dataReader.InputStreamOptions = InputStreamOptions;
dataReader.UnicodeEncoding = Encoding;
dataReader.ByteOrder = ByteOrder;
// start reader
ReadWorker();
return IsConnected;
}
}
public void Disconnect()
{
lock (serialPortLock)
{
if (serialPort == null) return;
continuousReadData = false;
serialPort?.Dispose();
serialPort = null;
}
}
private async Task CreateSerialDevice()
{
var foundDevices = await DeviceInformation.FindAllAsync(AdvancedQuery);
if (foundDevices.Count == 0) throw new SerialPortException($"No device found: {nameof(PortName)}={PortName} {nameof(AdvancedQuery)}={AdvancedQuery}");
var deviceId = foundDevices[0].Id;
serialPort = await SerialDevice.FromIdAsync(deviceId);
if (serialPort == null) throw new SerialPortException($"Error creating device: {nameof(PortName)}={PortName} {nameof(AdvancedQuery)}={AdvancedQuery} {nameof(deviceId)}={deviceId}");
}
public void Write(byte[] bytes, int index = 0, int count = -1)
{
if (count < 0) count = bytes.Length - index;
byte[] tmp = new byte[count];
Array.Copy(bytes, index, tmp, 0, count);
WriteBytes(tmp);
}
public void InjectAndDispatch(byte[] receivedBytes)
{
lock (listeners)
{
foreach (var listener in listeners)
if (listener.IsActive)
listener.AddBytes(receivedBytes);
}
}
private async void ReadWorker()
{
continuousReadData = true;
while (continuousReadData)
{
await dataReader.LoadAsync(1);
byte[] receivedBytes = new byte[dataReader.UnconsumedBufferLength];
dataReader.ReadBytes(receivedBytes);
lock (listeners)
foreach (var listener in listeners)
if (listener.IsActive)
listener.AddBytes(receivedBytes);
}
dataReader.Dispose();
}
private async void WriteBytes(params byte[] bytes)
{
dataWriter.WriteBytes(bytes);
await dataWriter.StoreAsync();
await dataWriter.FlushAsync();
}
private readonly object serialPortLock = new object();
private SerialDevice serialPort;
private DataWriter dataWriter;
private DataReader dataReader;
private volatile bool continuousReadData = true;
#region listeners
public IListener AddListener()
{
lock (listenersLock)
{
var listener = new Listener();
listeners.Add(listener);
return listener;
}
}
public void RemoveListener(IListener listener)
{
lock (listenersLock)
listeners.Remove(listener as IListenerInternal);
}
private readonly object listenersLock = new object();
private readonly List<IListenerInternal> listeners = new List<IListenerInternal>();
class Listener : IListenerInternal
{
private bool _IsActive;
public bool IsActive
{
get { return _IsActive; }
set
{
if (_IsActive != value)
{
_IsActive = value;
Clear();
}
}
}
public void AddBytes(byte[] bytes)
{
lock (receivedBytesLock)
receivedBytes.AddRange(bytes);
BytesReceived?.Invoke(this, EventArgs.Empty);
}
public event EventHandler BytesReceived;
public byte[] GetReceivedBytesAndClear()
{
lock (receivedBytesLock)
{
var bytes = receivedBytes.ToArray();
receivedBytes.Clear();
return bytes;
}
}
public byte[] GetReceivedBytes()
{
lock (receivedBytesLock)
return receivedBytes.ToArray();
}
public void Clear()
{
lock (receivedBytesLock)
receivedBytes.Clear();
}
public void Trim(int length)
{
lock (receivedBytesLock)
{
var count = receivedBytes.Count;
if (count > length)
receivedBytes.RemoveRange(0, count - length);
}
}
private readonly object receivedBytesLock = new object();
private readonly List<byte> receivedBytes = new List<byte>();
}
#endregion
}
}
Furthermore I had trouble using the System.IO.SerialPort (of .NET Framework) with strings (ReadExisting etc). It seems like sometimes the encoding makes it hard to create good results. I always use it with byte arrays - less trouble, more fun!

Signalr & WebSocketSharp in Unity3d

I've currently built a simple Signalr Hub which I'm pushing messages to from a Unity5 project. Given that SignalR2 client doesn't work with Unity5 I'm using websocketsharp in order to intercept the websocket frames. The messages are being pushed to the Hub successfully, but when I attempt to call a method on the client, I do not get the payload string, only the message identifier {"I": 0}
Looking through the SignalR documentation, it looks like this gets sent last, but I have no idea how I can get a hold it it. I'm sure its something simple, but for the life of me I can't figure it out.
UPDATE
Upon request, I've added the code for the project below...
SignalRClient.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using Newtonsoft.Json;
using WebSocketSharp;
namespace Assets.Scripts
{
class SignalRClient
{
private WebSocket _ws;
private string _connectionToken;
private Dictionary<string, UnTypedActionContainer> _actionMap;
private readonly string _socketUrl = "http://localhost/";
private readonly string _socket = "ws://localhost/";
public SignalRClient()
{
_actionMap = new Dictionary<string, UnTypedActionContainer>();
var webRequest = (HttpWebRequest)WebRequest.Create(_socketUrl + "/signalr/negotiate?connectionData=%5B%7B%22name%22%3A%22myHub%22%7D%5D&clientProtocol=1.3");
var response = (HttpWebResponse)webRequest.GetResponse();
using (var sr = new StreamReader(response.GetResponseStream()))
{
var payload = sr.ReadToEnd();
UnityEngine.Debug.Log(payload);
_connectionToken = Uri.EscapeDataString(JsonConvert.DeserializeObject<NegotiateResponse>(payload).ConnectionToken);
//UnityEngine.Debug.Log(_connectionToken);
}
}
public void Open()
{
_ws = _ws == null
? new WebSocket(_socket + "signalr/connect?transport=webSockets&connectionToken=" + _connectionToken)
: new WebSocket(_socket + "signalr/reconnect?transport=webSockets&connectionToken=" + _connectionToken);
AttachAndConnect();
}
public void Close()
{
_ws.Close();
}
public void SendMessage(string name, string message)
{
//{"H":"chathub","M":"Send","A":["tester","hello"],"I":0}
var payload = new RollerBallWrapper()
{
H = "myhub",
M = "Send",
A = new[] { name, message },
I = 12
};
var wsPacket = JsonConvert.SerializeObject(payload);
_ws.Send(wsPacket);
}
private void AttachAndConnect()
{
_ws.OnClose += _ws_OnClose;
_ws.OnError += _ws_OnError;
_ws.OnMessage += _ws_OnMessage;
_ws.OnOpen += _ws_OnOpen;
_ws.Connect();
}
void _ws_OnOpen(object sender, EventArgs e)
{
UnityEngine.Debug.Log("Opened Connection");
}
//
// This seems to be retriving the last frame containing the Identifier
void _ws_OnMessage(object sender, MessageEventArgs e)
{
//UnityEngine.Debug.Log(e.Data); // Returns {"I":"0"} ????
}
void _ws_OnError(object sender, WebSocketSharp.ErrorEventArgs e)
{
UnityEngine.Debug.Log(e.Message);
}
void _ws_OnClose(object sender, CloseEventArgs e)
{
UnityEngine.Debug.Log(e.Reason + " Code: " + e.Code + " WasClean: " + e.WasClean);
}
public void On<T>(string method, Action<T> callback) where T : class
{
_actionMap.Add(method, new UnTypedActionContainer
{
Action = new Action<object>(x =>
{
callback(x as T);
}),
ActionType = typeof(T)
});
}
}
internal class UnTypedActionContainer
{
public Action<object> Action { get; set; }
public Type ActionType { get; set; }
}
class MessageWrapper
{
public string C { get; set; }
public RollerBallWrapper[] M { get; set; }
}
class RollerBallWrapper
{
public string H { get; set; }
public string M { get; set; }
public string[] A { get; set; }
public int I { get; set; }
}
}
MyHub.cs
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.SignalR;
public class MyHub : Hub
{
public void Send(string name, string message)
{
var myConn = Context.ConnectionId;
Clients.All.broadcastMessage("John", "Hello");
}
}
The problem is the websocket connection. I had the following:
new WebSocket(_socket + "signalr/connect?transport=webSockets&connectionToken=" + _connectionToken)
Which was missing 2 critical querystring parameters: connectionData and tid in addition to the connectionToken and transport. I wrongly assumed that these weren't needed.
I hope this helps anyone who didn't read the documentation like me :)

Creating a new Team Project using the REST api

Ive been banging my head against this all week.
Creating a new Team Project using the REST api.
Everywhere i look, the response is the same, and it always involves using command line and xml.
But why?
On the visual studio online pages this can be found:
https://www.visualstudio.com/en-us/integrate/api/tfs/projects
(Specifically looking at the part labelled "Create a team project")
So why does this exist if it cant be used?
Or am i missing something?
If anyone knows any examples of using this i would greatly appreciate it.
Ive been using the Microsoft.TeamFoundation.WorkItemTracking.Client namespaces etc... and have been happily creating new work items to projects
and Ive even managed to use the API to pull down lists of projects too.
using code from this example (scroll to bottom of page)
https://www.visualstudio.com/en-us/integrate/get-started/rest/basics
but i cannot for the life of me post a new team project.
At this point i am open to any suggestions, i created an account here just to ask (i love this site) :(
As requested, some code:
static async Task<string> PostProjectAsync(HttpClient _client, string _apiUrl, string _apiVersion)
{
var responseBody = string.Empty;
HttpContent hc = new StringContent(#"
{
""name"": ""Testprojectfromconsole"",
""description"": ""Posted from console application using the tfs API""
}
");
//TODO: make a class that matches the json layout that the api is expecting
//then see if you have any better luck with that instead of this horrid horrid mess
ProjectPost newproj = new ProjectPost();
newproj.Name = #"Test Project -From console";
newproj.Description = #"Hopefully this has been posted from the console app, delete it later on if need be.";
newproj.Capabilities.VersionControl.SourceControlType = #"TFS"; //probably wrong
newproj.Capabilities.ProcessTemplate.TemplateTypeId = #"default"; //also probably wrong
string json = JsonConvert.SerializeObject(newproj);
try
{
using (HttpResponseMessage response = _client.PostAsync(_apiUrl + _apiVersion, hc).Result)
{
response.EnsureSuccessStatusCode();
responseBody = await response.Content.ReadAsStringAsync();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
return responseBody;
}
At the moment I'm passing a HttpContent called "hc" to the postasync, but if i switch it for the json object, the postasync stops working (because it wants httpcontent not a json)
before this methodis called, the client is set up as so:
client.DefaultRequestHeaders.Accept.Add(
new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
//Set alternate credentials
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",
Convert.ToBase64String(
System.Text.ASCIIEncoding.ASCII.GetBytes(
string.Format("{0}:{1}", ALTUSERNAME, ALTPASSWORD))));
Console.WriteLine("<--------------Getting projects from tfs!-------------->");
Console.WriteLine("<----------------Hold on to your butts!---------------->");
responseBody = await GetAsync(client, BASEURL + "projects", APIVERS);
Console.WriteLine(responseBody.ToString());
Console.WriteLine("<----------------Making a new project!----------------->");
Console.WriteLine("<----------------Hold on to your butts!---------------->");
responseBody = await PostProjectAsync(client, BASEURL + "projects", APIVERS);
Console.WriteLine(responseBody.ToString());
oh, and the URL is such:
static string PN1 = #"Test Project -From Web";
static string PN2 = #"Another Test Project -From Web";
static string COL = #"DefaultCollection";
static string BASEURL = "https://{0}.visualstudio.com/DefaultCollection/_apis/";
// Get the alternate credentials that you'll use to access the Visual Studio Online account.
static string ALTUSERNAME = "myusername";
static string ALTPASSWORD = "mypassword!";
//Your visual studio account name
static string ACCOUNT = "ourserver";
//Api version query parameter
static string APIVERS = "?api-version=1.0";
Here is the code that I've used. This is made for .net 3.5 but I found solutions with .net 4.5.1:
private const string PROJECT_TEMPLATE_AGILE = "adcc42ab-9882-485e-a3ed-7678f01f66bc";
private const string PROJECT_TEMPLATE_SCRUM = "6b724908-ef14-45cf-84f8-768b5384da45";
private const string PROJECT_TEMPLATE_CMMI = "27450541-8e31-4150-9947-dc59f998fc01";
VsoTeamProject project = new VsoTeamProject(
newFolderName,
comment,
new Capabilities(new VersionControl("Tfvc"), new ProcessTemplate(projectTemplateId)));
CreateTeamProject(project, "POST", false); // this calls PostResponse method
Here is the main method:
private void PostResponse(VsoTeamProject project, string method, bool useProjectName)
{
string projectState = "wellFormed";
if(method.Equals("DELETE"))
{
projectState = "deleting";
}
var requestUriString = ConstructUrl(
useProjectName ? project.TeamProjectId : string.Empty,
string.Empty,
new Dictionary<string, object>());
var httpWebRequest = (HttpWebRequest)WebRequest.Create(requestUriString);
httpWebRequest.ContentType = "application/json";
httpWebRequest.Method = method;
string autorization = TFSImplementor.LoginName + ":" + TFSImplementor.Password;
byte[] binaryAuthorization = Encoding.UTF8.GetBytes(autorization);
autorization = Convert.ToBase64String(binaryAuthorization);
autorization = "Basic " + autorization;
httpWebRequest.Headers.Add("AUTHORIZATION", autorization);
if(method.Equals("POST"))
{
using(var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
string json = JsonConvert.SerializeObject(project);
streamWriter.Write(json);
}
}
try
{
var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
if(httpResponse.StatusCode == HttpStatusCode.Accepted)
{
Task<WebResponse> responseTask = Task.Factory.FromAsync<WebResponse>(httpWebRequest.BeginGetResponse, httpWebRequest.EndGetResponse, null);
using(var responseStream = responseTask.Result.GetResponseStream())
{
var reader = new StreamReader(responseStream);
var t = reader.ReadToEnd();
ProjectStatus json = JsonConvert.DeserializeObject<ProjectStatus>(t);
if(json.status.Equals("queued"))
{
while(true)
{
if(CheckTeamProjectState(project.ProjectName, true, projectState))
{
break;
}
}
}
}
}
}
catch(WebException e)
{
using(WebResponse response = e.Response)
{
using(Stream data = response.GetResponseStream())
{
using(var reader = new StreamReader(data))
{
string text = reader.ReadToEnd();
Logger.Error(text);
Logger.Exception(e);
if(method.Equals("DELETE"))
{
throw new Exception("Failed to delete project, check log for more details");
}
throw new Exception("Failed to create project, check log for more details");
}
}
}
}
}
Here are the classes that you can use:
private class VsoTeamProject
{
#region Fields
private readonly string m_name;
private readonly string m_comment;
private readonly string m_teamProjectId;
private readonly Capabilities m_capabilities;
#endregion
#region Constructors
public VsoTeamProject(string teamProjectId, string name)
{
m_teamProjectId = teamProjectId;
m_name = name;
}
public VsoTeamProject(string projectName, string description, Capabilities capabilities)
{
m_name = projectName;
m_comment = description;
m_capabilities = capabilities;
}
#endregion
#region Properties
[JsonProperty("name")]
protected internal string ProjectName
{
get
{
return m_name;
}
}
[JsonProperty("description")]
protected internal string Description
{
get
{
return m_comment;
}
}
protected internal string TeamProjectId
{
get
{
return m_teamProjectId;
}
}
[JsonProperty("capabilities")]
protected internal Capabilities Capabilities
{
get
{
return m_capabilities;
}
}
#endregion
}
private class ProjectStatus
{
public string id { get; set; }
public string status { get; set; }
public string url { get; set; }
public string name { get; set; }
public string state { get; set; }
public string message { get; set; }
}
private class Capabilities
{
public Capabilities(VersionControl versionControl, ProcessTemplate template)
{
VersionControl = versionControl;
ProcessTemplate = template;
}
[JsonProperty("processTemplate")]
public ProcessTemplate ProcessTemplate { get; private set; }
[JsonProperty("versioncontrol")]
public VersionControl VersionControl { get; private set; }
}
private class VersionControl
{
public VersionControl(object type)
{
SourceControlType = type;
}
[JsonProperty("sourceControlType")]
public object SourceControlType { get; private set; }
}
private class ProcessTemplate
{
public ProcessTemplate(string templateTypeId)
{
TemplateTypeId = templateTypeId;
}
[JsonProperty("templateTypeId")]
public string TemplateTypeId { get; private set; }
}
I've this PostResponse method also for deleting the projects from VSO. It works like a charm.
I don't know if you are still interested in an answer to this (as it's been 3 years now), but the issue isn't your code: it's the documentation.
When you create a project using the API, the fields listed in the documentation don't tell you all of the required fields.
If you tried the request in Postman, here's what you would get back:
message:"The project information supplied to project create is invalid. You must provide all and only these properties/capabilities: name, description, visibility, capabilities.versioncontrol.sourceControlType, capabilities.processTemplate.templateTypeId."
Where the project template type id = 6b724908-ef14-45cf-84f8-768b5384da45