I am new to API coding and working on a module which signs in to Tableau Server and gets the list of workbooks stored in a site. The code is to be written in C# using the Tableau Rest API.
I was able to sign in to Tableau server successfully using the Rest API. However, I was not able to query the workbooks. Below is my code.
class Program
{
static HttpClient client = new HttpClient();
public static string site_id { get; set;}
public static string token { get; set; }
static void Main(string[] args)
{
CallWebAPIAsync().Wait();
}
static async Task CallWebAPIAsync()
{
using (var client = new HttpClient())
{
client.Timeout = TimeSpan.FromMilliseconds(Timeout.Infinite);
client.BaseAddress = new Uri("https://my server url.com");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/octet-stream"));
SignIn(client);
Workbooks(client, site_id, token);
}
Console.Read();
}
public class CustomXmlMediaTypeFormatter : XmlMediaTypeFormatter
{
public CustomXmlMediaTypeFormatter()
{
UseXmlSerializer = true;
WriterSettings.OmitXmlDeclaration = false;
}
}
public static tableauCredentialsType SignIn(HttpClient client)
{
var name = "Administrator";
var password = "password";
var site = "";
var tableauCredentialsType = new tsRequest()
{
Item = new tableauCredentialsType
{
name = name,
password = password,
site = new siteType() { contentUrl = site }
}
};
var httpContent = new ObjectContent<tsRequest>(tableauCredentialsType, new CustomXmlMediaTypeFormatter());
var httpResponseMessage = client.PostAsync("/api/3.2/auth/signin", httpContent).Result;
if (httpResponseMessage.StatusCode != System.Net.HttpStatusCode.OK)
throw new Exception(string.Format("Unable to login to the server", site, name));
var responseLogin = httpResponseMessage.Content.ReadAsAsync<tsResponse>(new List<MediaTypeFormatter>() { new CustomXmlMediaTypeFormatter() }).Result;
var resultTableauCredentialsType = ((tableauCredentialsType)responseLogin.Items[0]);
site_id = resultTableauCredentialsType.site.id;
token = resultTableauCredentialsType.token;
return resultTableauCredentialsType;
}
public static IList<workbookType> Workbooks(HttpClient client, string siteid, string auth_token, int pageSize = 100, int pageNumber =1)
{
IList<workbookType> results = new List<workbookType>();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("x-tableau-auth", auth_token);
var query = client.GetAsync(string.Format("/api/3.2/sites/{0}/workbooks", siteid, pageSize, pageNumber)).Result;
if(query.StatusCode == System.Net.HttpStatusCode.OK)
{
var result = query.Content.ReadAsAsync<tsResponse>(new List<MediaTypeFormatter>() { new CustomXmlMediaTypeFormatter() }).Result;
var pagination = (paginationType)result.Items[0];
var workbooks = ((workbookListType)result.Items[1]).workbook.AsEnumerable();
for (int i=2; i < pageNumber; i++)
{
query = client.GetAsync(string.Format("/api/3.2/sites/{0}/workbooks", siteid, pageSize, i)).Result;
if(query.StatusCode == System.Net.HttpStatusCode.OK)
{
result = query.Content.ReadAsAsync<tsResponse>(new List<MediaTypeFormatter>() { new CustomXmlMediaTypeFormatter() }).Result;
workbooks = workbooks.Concat(((workbookListType)result.Items[1]).workbook.AsEnumerable());
}
}
results = workbooks.ToList();
}
return results;
}
}
When I try to run the Workbooks function in the above code I am getting 401-Unauthorized error. I came to know that I need to pass the X-tableau_auth so that I will have the authentication done while querying for the workbooks. However, I was not able to get that properly.
Can anyone help me with this?
The header information can be passed using the DefaultRequestHeaders Add method.
You can try something like
client.DefaultRequestHeaders.Add("x-tableau-auth", "tokenvalue");
Also the api parameters is incorrect. You can refer to the API guide provided by Tableau
Did you check the Url that you're using to fetch the list of workbooks? I checked with Tableau's API documentation, the Url format looks different, try updating your code
string.Format("/api/{0}/sites/site-id/workbooks", siteid, pageSize, i)
to
string.Format("/api/3.4/sites/{0}/workbooks", siteid)
Here I assumed you're using 3.4 version api.
I'd also recommend using some Rest Clients to test these Apis before you code.
Related
I recently upgraded RestSharp to version 107.3.0. I had to modify my request some, but the Web API gets the request and returns, but it hangs there waiting for the response...
private async Task<bool> AuthenticateUser(string username, string password)
{
var encryption = new Encryption64();
var encrypt = encryption.Encrypt(password, _key);
var client = new RestClient(UserSettings.URL);
var uri = $"users/authenticate/{username}/";
var pass = new PasswordDTO
{
Password = encrypt
};
var request = new RestRequest(uri)
.AddJsonBody(pass);
//var json = JsonConvert.SerializeObject(pass);
//request.AddParameter("application/json; charset=utf-8", json, ParameterType.RequestBody);
var response = await client.PostAsync<bool>(request);
return response;
}
The line await client.PostAsync<bool>(request) never completes. No errors in the Debug window either. This worked before I upgraded. What am I doing wrong?
I am not sure if this is entirely correct... but I fiddled with it until I got it working.
private bool AuthenticateUser(string username, string password)
{
using (var client = new RestClient(UserSettings.URL))
{
var encryption = new Encryption64();
var encrypt = encryption.Encrypt(password, _key);
var uri = $"users/authenticate/{username}/";
var pass = new PasswordDTO
{
Password = encrypt
};
var request = new RestRequest(uri)
.AddJsonBody(pass);
var response = client.PostAsync<bool>(request).Result;
return response;
}
}
UPDATE
private async Task<bool> AuthenticateUser(string username, string password)
{
using (var client = new RestClient(UserSettings.URL))
{
var encryption = new Encryption64();
var encrypt = encryption.Encrypt(password, _key);
var uri = $"users/authenticate/{username}/";
var pass = new PasswordDTO
{
Password = encrypt
};
var request = new RestRequest(uri)
.AddJsonBody(pass);
var response = await client.PostAsync<bool>(request);
return response;
}
}
public async Task<bool> AuthenticateUserAsync([FromUri] string username, [FromBody] PasswordDTO pass)
{
Log.Logger.ForContext<UserController>().Information("{User} is Logging In", username);
using (var context = new DatabaseContext())
{
var user = await context.bma_users
.AsNoTracking()
.FirstOrDefaultAsync(p => p.username == username);
if (user is null)
return false;
return (user.password == pass.Password);
}
}
I still welcome any ideas for improvement.
I am using rest end point and don't get folder id on creation. It should be in response but not there.
Folder is getting created successfully.
url: https://www.googleapis.com/drive/v3/files
public static async void CreateFolder(string accessToken, string brandFolderName)
{
var gDriveItems = await GetFoldersByBrand(accessToken,brandFolderName);
if (gDriveItems.Any(x=>x.Name.ToLower() == brandFolderName.ToLower()))
{
return;
}
var request = new HttpRequestMessage(HttpMethod.Post, "drive/v3/files");
request.Headers.Add("Authorization", "Bearer "+ accessToken);
request.Headers.Add("Accept","application/json");
JsonObject jsonFolderObject = new JsonObject();
jsonFolderObject.Add("name", brandFolderName);
jsonFolderObject.Add("mimeType", "application/vnd.google-apps.folder");
var data = JsonConvert.SerializeObject(jsonFolderObject);
request.Content = new StringContent(data, Encoding.UTF8, "application/json");
var responce = await _httpClient.SendAsync(request);
var mm = responce.Content.ReadAsStringAsync();
responce.EnsureSuccessStatusCode();
}
For reference, here's my code to add a new folder.
I use Google.Apis.Drive.v3
service is an instance of DriveService with the relevant scope to create a folder/file.
This will give me the Id of the new folder in the result
private static async Task<File> CreateRankingsFolder(DriveService service,
string driveId,
string parentId,
string folderName = "YOURFOLDERNAME")
{
File result = null;
try
{
File body = new File();
body.Name = folderName;
body.MimeType = "application/vnd.google-apps.folder";
body.DriveId = driveId;
if (!string.IsNullOrEmpty(parentId))
{
var _parents = new List<string>()
{
parentId
};
body.Parents = _parents;
}
// service is an authorized Drive API service instance
var req = service.Files.Create(body);
result = await req.ExecuteAsync();
}
catch(Exception e)
{
}
return result;
}
I am working on an asp.net web application where I have to create users directly in Tableau Server using Rest API and C#. The users are local users and not AD or SAML users.
I have coded the part however, I am getting Unauthorised as the result. Below is my code.
public static string site_id { get; set; }
public static string token { get; set; }
static void Main(string[] args)
{
CallWebAPIAsync().Wait();
}
static async Task CallWebAPIAsync()
{
using (var client = new HttpClient())
{
client.Timeout = TimeSpan.FromMilliseconds(Timeout.Infinite);
client.BaseAddress = new Uri("https://serverurl.com");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/octet-stream"));
SignIn(client);
Users(client, site_id, token);
CreateUser(client, site_id, token);
}
Console.Read();
}
public class CustomXmlMediaTypeFormatter : XmlMediaTypeFormatter
{
public CustomXmlMediaTypeFormatter()
{
UseXmlSerializer = true;
WriterSettings.OmitXmlDeclaration = false;
}
}
public static tableauCredentialsType SignIn(HttpClient client)
{
var name = "Administrator";
var password = "Password";
var site = string.Empty;
var tableauCredentialsType = new tsRequest()
{
Item = new tableauCredentialsType
{
name = name,
password = password,
site = new siteType() { contentUrl = site }
}
};
var httpContent = new ObjectContent<tsRequest>(tableauCredentialsType, new CustomXmlMediaTypeFormatter());
var httpResponseMessage = client.PostAsync("/api/3.2/auth/signin", httpContent).Result;
if (httpResponseMessage.StatusCode != System.Net.HttpStatusCode.OK)
throw new Exception(string.Format("Unable to login to the server", site, name));
var responseLogin = httpResponseMessage.Content.ReadAsAsync<tsResponse>(new List<MediaTypeFormatter>() { new CustomXmlMediaTypeFormatter() }).Result;
var resultTableauCredentialsType = ((tableauCredentialsType)responseLogin.Items[0]);
site_id = resultTableauCredentialsType.site.id;
token = resultTableauCredentialsType.token;
return resultTableauCredentialsType;
}
public static userType CreateUser(HttpClient client, string siteid, string auth_token)
{
var name = "user#domain.com";
var userType = new tsRequest()
{
Item = new userType
{
name = name,
siteRole = siteRoleType.ExplorerCanPublish,
authSetting = siteUserAuthSettingType.ServerDefault
}
};
var httpContent = new ObjectContent<tsRequest>(userType, new CustomXmlMediaTypeFormatter());
client.DefaultRequestHeaders.Add("x-tableau-auth", auth_token);
var uri = string.Format("/api/3.2/sites/{0}/users", siteid);
var httpResponseMessage = client.PostAsync(uri, httpContent).Result;
if (httpResponseMessage.StatusCode != System.Net.HttpStatusCode.OK)
throw new Exception(string.Format("Unable to create user in the server", siteid, name));
var responseLogin = httpResponseMessage.Content.ReadAsAsync<tsResponse>(new List<MediaTypeFormatter>() { new CustomXmlMediaTypeFormatter() }).Result;
var resultTableauCredentialsType = ((userType)responseLogin.Items[0]);
return resultTableauCredentialsType;
}
I was able to successfully login into the server and was also able to provide the x-tableau-auth to my client headers.
The code is working fine except the post sync method in Create user method. Once the code executes, it throws UnAuthorized error as the response from the server. My user is the administrator in Tableau Server and when I query the workbooks and users in Tableau Server with the same x-tableau-auth, it's working fine.
I am not good in API programming and need some help in solving out this issue. Any help is highly appreciated.
I'm learning how to create WEB-API client
I've created some simple API:
[HttpGet]
public IHttpActionResult GetInfo()
{
return Ok("Its working!");
}
[HttpPost]
public IHttpActionResult PostInfo(ClientDataDto dto)
{
try
{
someMethod(dto.IdKlienta, dto.Haslo, dto.IdZgloszenia, dto.HardwareInfo, dto.SoftwareInfo);
return Ok("sent");
}
catch
{
return BadRequest();
}
}
For now I just trying to call GET method.
When I use Fiddler with addr
localhost:someport/api/Client2
its working
but when i try to do it by client, which code is below:
private static HttpClient client = new HttpClient();
static void Main(string[] args)
{
#region TESTONLY
var debug = new XMLData();
string HardwareInfoXML = debug.HardwareXML;
string SoftInfoXML = debug.SoftwareXML;
int id_zgloszenia = 20;
int idKlienta = 25;
//haslo = "202cb962ac59075b964b07152d234b70";
#endregion
var data = new ClientDataDto() { HardwareInfo = HardwareInfoXML, SoftwareInfo = SoftInfoXML, IdKlienta = idKlienta, IdZgloszenia = id_zgloszenia };
RunAsync(data);
}
private static async Task RunAsync(ClientDataDto data)
{
var stringContent = new StringContent(JsonConvert.SerializeObject(data), Encoding.UTF8, "application/json");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.BaseAddress = new Uri(#"http://localhost:7774/api/client2/");
var url = new Uri(#"http://localhost:7774/api/client2/");
var res1 = await client.GetAsync(url);
var res = await client.PostAsync(url, stringContent);
res.EnsureSuccessStatusCode();
}
Application closing without any info at
var res1 = await client.GetAsync(url);
I have checked to see all exceptions in Debug exception Windows, but it is just closing after trying call GetAsync
PostASync doesn't work too.
What is wrong here?
i'm really sorry that i've posted simpe problem.
sulotion is to add .Wait() on RunAsync(data);
RunAsync(data).Wait();
We have an ASP.NET WebAPI which generates a custom document using the TFS SDK. We attempted to wrap the entire call stack for the SDK inside a C# impersonation wrapper. Our document is returned corrupted because the TFS client is not authenticating under the IIS Network Service but is correctly returned with data when we set the App Pool identity to a specific user. The EnsureAuthenticated comes back true. The TestManagementService is not null. The ProjectName parameter is not empty and has a valid ProjectName;
[Route("generatetestplan")]
[HttpPost]
public HttpResponseMessage Post([FromBody]TestPlanRequest Request)
{
var wi = (WindowsIdentity)HttpContext.Current.User.Identity;
HttpResponseMessage Res = new HttpResponseMessage(HttpStatusCode.OK);
WindowsIdentity.RunImpersonated(wi.AccessToken, () =>
{
using (var handler = new HttpClientHandler { UseDefaultCredentials = true })
using (var client = new HttpClient(handler))
{
Res = GenerateTestPlan(Request, Res);
}
});
return Res;
}
public HttpResponseMessage GenerateTestPlan(TestPlanRequest Request, HttpResponseMessage Res)
{
var TestResultsGen = new TestResultsGenerator(Request.ProjectName, Request.TestPlanId);
TestResultsGen.Generate();
var Bytes = TestResultsGen.FBytes;
Res.Content = new ByteArrayContent(Bytes);
Res.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
return Res;
}
public TestResultsGenerator(string ProjectName, int TestPlanId)
{
TfsTeamProjectCollection = AuthenticateToCollection();
this.TestPlanId = TestPlanId;
this.ProjectName = ProjectName;
try
{
TestManagementService = TfsTeamProjectCollection.GetService<ITestManagementService>();
TeamProject = TestManagementService.GetTeamProject(ProjectName);
}
catch(Exception e)
{
logger.Error(DateTime.Now.ToString() + " Test Service Error: " + e.ToString());
}
}
public static TfsTeamProjectCollection AuthenticateToCollection()
{
var server = ConfigurationManager.AppSettings["TFS"];
TfsTeamProjectCollection TfsCollection = new TfsTeamProjectCollection(new Uri(server), new Microsoft.VisualStudio.Services.Common.VssCredentials());
try
{
TfsCollection.EnsureAuthenticated();
}
catch (Exception e)
{
logger.Error(e.ToString());
AuthenticateToCollection();
}
return Tfs