/// <summary> /// <see cref="ITableauApiService.DownloadFileAsync"/> /// </summary> public async Task <TableauFileBytes> DownloadFileBytesAsync(string url) { _logger?.Debug($"Downloading file from {url}"); var localPath = null as string; using ( var response = await SendRequestAsync <HttpResponseMessage>(url, HttpMethod.Get).ConfigureAwait(false) ) { if ( !response.Content.Headers.TryGetValues( "Content-Disposition", out IEnumerable <string> contentDispositionValues ) ) { var responseString = Encoding.UTF8.GetString( await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false) ); _logger?.Error("Error downloading file:"); _logger?.Error(responseString); throw new Exception($"Error downloading file: {responseString}"); } response.Content.Headers.TryGetValues("Content-Type", out IEnumerable <string> contentTypeValues); // format => name="tableau_datasource"; filename="filename.ext" var responseContentDispositionName = contentDispositionValues.First() .Split(';') .First() .Split('=') .Last() .Trim('"'); var responseFileName = contentDispositionValues.First() .Split(';') .Last() .Split('=') .Last() .Trim('"'); var bytes = await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false); var fileDownload = new TableauFileBytes( responseFileName, bytes, contentTypeValues.FirstOrDefault(), responseContentDispositionName ); return(fileDownload); } }
/// <summary> /// <see cref="ITableauWorkbookService.PublishWorkbookAsync"/> /// </summary> public async Task <TableauWorkbook> PublishWorkbookAsync( TableauWorkbook workbook, TableauFileBytes fileBytes, bool overwrite = false ) { _logger?.Debug($"Publishing workbook {workbook.Name}"); if (String.IsNullOrWhiteSpace(workbook.Project?.Id)) { throw new InvalidDataException("Project ID must be specified"); } var url = _tableauApiService.SiteUrl.AppendUri($"workbooks?skipConnectionCheck=true&overwrite={overwrite.ToString().ToLower()}"); var workbookJson = new JObject(); workbookJson["workbook"] = JToken.Parse(workbook.ToRequestString()); // note: the content-disposition name header *must* be 'request_payload', the Tableau API // is hard-coded to look for the multipart segment with this name for the workbook meta info. // similarly, the content-disposition name header *must* be 'tableau_workbook' for the segment // that contains the workbook file data var responseString = await _tableauApiService.UploadFileAsync( url, fileBytes, workbookJson.ToString(), "application/json", "request_payload" ).ConfigureAwait(false); var responseJson = JToken.Parse(responseString); var responseWorkbook = JsonConvert.DeserializeObject <TableauWorkbook>( responseJson.Value <JObject>("workbook").ToString() ); _logger?.Debug($"Workbook {workbook.Name} published, id {responseWorkbook.Id}"); return(responseWorkbook); }
/// <summary> /// <see cref="ITableauDatasourceService.PublishDatasourceAsync"/> /// </summary> public async Task <TableauDatasource> PublishDatasourceAsync( TableauDatasource datasource, TableauFileBytes fileBytes, bool overwrite = false ) { _logger?.Debug($"Publishing datasource {datasource.Name}"); if (String.IsNullOrWhiteSpace(datasource.Project?.Id)) { throw new InvalidDataException("Project ID must be specified"); } var url = _tableauApiService.SiteUrl.AppendUri($"datasources?overwrite={overwrite.ToString().ToLower()}"); var datasourceJson = new JObject(); datasourceJson["datasource"] = JToken.Parse(datasource.ToRequestString()); // note: the content-disposition name header *must* be 'request_payload', the Tableau API // is hard-coded to look for the multipart segment with this name for the datasource meta info. // similarly, the content-disposition name header *must* be 'tableau_datasource' for the segment // that contains the datasource file data var responseString = await _tableauApiService.UploadFileAsync( url, fileBytes, datasourceJson.ToString(), "application/json", "request_payload" ).ConfigureAwait(false); var responseJson = JToken.Parse(responseString); var responseDatasource = JsonConvert.DeserializeObject <TableauDatasource>( responseJson.Value <JObject>("datasource").ToString() ); _logger?.Debug($"Datasource {datasource.Name} published, id {responseDatasource.Id}"); return(responseDatasource); }
/// <summary> /// <see cref="ITableauApiService.UploadFileAsync"/> /// </summary> public async Task <string> UploadFileAsync( string url, TableauFileBytes fileBytes, string content = null, string contentType = null, string contentDispositionName = null ) { _logger?.Debug($"Uploading stream to {url}"); using (var memoryStream = new MemoryStream(fileBytes.Bytes)) using (var streamContent = new StreamContent(memoryStream)) using (var formContent = new MultipartContent("mixed")) using (var request = new HttpRequestMessage(HttpMethod.Post, url)) { if (!String.IsNullOrWhiteSpace(content)) { var stringContent = new StringContent(content); stringContent.Headers.Clear(); if (!String.IsNullOrWhiteSpace(contentType)) { stringContent.Headers.TryAddWithoutValidation("Content-Type", contentType); } if (!String.IsNullOrWhiteSpace(contentDispositionName)) { stringContent.Headers.TryAddWithoutValidation( "Content-Disposition", $"name=\"{contentDispositionName}\"" ); } formContent.Add(stringContent); } if (!String.IsNullOrWhiteSpace(fileBytes.ContentType)) { streamContent.Headers.TryAddWithoutValidation("Content-Type", fileBytes.ContentType); } string contentDisposition = $"filename=\"{fileBytes.Name}\""; if (!String.IsNullOrWhiteSpace(fileBytes.ContentDispositionName)) { contentDisposition = $"name=\"{fileBytes.ContentDispositionName}\"; {contentDisposition}"; } streamContent.Headers.TryAddWithoutValidation( "Content-Disposition", contentDisposition ); formContent.Add(streamContent); request.Content = formContent; using (HttpResponseMessage response = await _httpClient.SendAsync(request).ConfigureAwait(false)) { if (response.IsSuccessStatusCode) { return(Encoding.UTF8.GetString( await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false) )); } else { throw new HttpRequestException( response.StatusCode.ToString() + ": " + await response.Content.ReadAsStringAsync().ConfigureAwait(false) ); } } } }