/// <summary> /// Get a page's worth of Data Sources /// </summary> /// <param name="onlineDatasources"></param> /// <param name="pageToRequest">Page # we are requesting (1 based)</param> /// <param name="totalNumberPages">Total # of pages of data that Server can return us</param> private void ExecuteRequest_ForPage(List <SiteDatasource> onlineDatasources, int pageToRequest, out int totalNumberPages) { int pageSize = _onlineUrls.PageSize; //Create a web request, in including the users logged-in auth information in the request headers var urlQuery = _onlineUrls.Url_DatasourcesList(_onlineSession, pageSize, pageToRequest); _onlineSession.StatusLog.AddStatus("Web request: " + urlQuery, -10); var xmlDoc = ResourceSafe_PerformWebRequest_GetXmlDocument(urlQuery, "get datasources list"); //Get all the workbook nodes var nsManager = XmlHelper.CreateTableauXmlNamespaceManager("iwsOnline"); var datasources = xmlDoc.SelectNodes("//iwsOnline:datasource", nsManager); //Get information for each of the data sources foreach (XmlNode itemXml in datasources) { try { var ds = new SiteDatasource(itemXml); onlineDatasources.Add(ds); } catch { AppDiagnostics.Assert(false, "Datasource parse error"); _onlineSession.StatusLog.AddError("Error parsing datasource: " + itemXml.InnerXml); } } //end: foreach //------------------------------------------------------------------- //Get the updated page-count //------------------------------------------------------------------- totalNumberPages = DownloadPaginationHelper.GetNumberOfPagesFromPagination( xmlDoc.SelectSingleNode("//iwsOnline:pagination", nsManager), pageSize); }
/// <summary> /// Attempts to upload a single file a Tableau Server, and then make it a published data source /// </summary> /// <param name="localFilePath"></param> /// <param name="projectId"></param> /// <param name="dbCredentials">If not NULL, then these are the DB credentials we want to associate with the content we are uploading</param> /// <returns></returns> private bool AttemptUploadSingleFile( string localFilePath, string projectId, CredentialManager.Credential dbCredentials, DatasourcePublishSettings publishSettings) { string uploadSessionId; try { var fileUploader = new UploadFile(Urls, Login, localFilePath, HttpClientFactory, UploadChunkSizeBytes, UploadChunkDelaySeconds); uploadSessionId = fileUploader.ExecuteRequest(); } catch (Exception exFileUpload) { Login.Logger.Error("Unexpected error attempting to upload file " + localFilePath + ", " + exFileUpload.Message); throw exFileUpload; } SiteDatasource dataSource = null; Login.Logger.Information("File chunks upload successful. Next step, make it a published datasource"); try { string fileName = Path.GetFileNameWithoutExtension(localFilePath); string uploadType = RemoveFileExtensionDot(Path.GetExtension(localFilePath).ToLower()); dataSource = FinalizePublish( uploadSessionId, FileIOHelper.Undo_GenerateWindowsSafeFilename(fileName), //[2016-05-06] If the name has escapted characters, unescape them uploadType, projectId, dbCredentials); UploadeDatasources.Add(dataSource); Login.Logger.Information("Upload content details: " + dataSource.ToString()); Login.Logger.Information("Success! Uploaded datasource " + Path.GetFileName(localFilePath)); } catch (Exception exPublishFinalize) { Login.Logger.Error("Unexpected error finalizing publish of file " + localFilePath + ", " + exPublishFinalize.Message); throw exPublishFinalize;; } //See if we want to reassign ownership of the datasource if (AttemptOwnershipAssignment) { try { AttemptOwnerReassignment(dataSource, publishSettings, SiteUsers); } catch (Exception exOwnershipAssignment) { Login.Logger.Error("Unexpected error reassigning ownership of published datasource " + dataSource.Name + ", " + exOwnershipAssignment.Message); LogManualAction_ReassignOwnership(dataSource.Name); throw exOwnershipAssignment; } } return(true); //Success }
/// <summary> /// Create the request for to download Data sources from the Tableau REST API /// </summary> /// <param name="onlineUrls">Tableau Server Information</param> /// <param name="logInInfo">Tableau Sign In Information</param> /// <param name="datasource">List of Tableau Data sources to save to disk</param> /// <param name="localSavePath">File system location where data sources should be saved</param> /// <param name="project"></param> public DownloadDatasource(TableauServerUrls onlineUrls, TableauServerSignIn logInInfo, SiteDatasource datasource, string localSavePath, SiteProject project) : base(logInInfo) { _onlineUrls = onlineUrls; _datasource = datasource; _localSavePath = localSavePath; _downloadToProjectDirectory = project; }
/// <summary> /// URL to download a datasource /// </summary> /// <param name="siteUrlSegment"></param> /// <returns></returns> public string Url_DatasourceDownload(TableauServerSignIn session, SiteDatasource contentInfo) { string workingText = UrlDownloadDatasourceTemplate; workingText = workingText.Replace("{{iwsSiteId}}", session.SiteId); workingText = workingText.Replace("{{iwsRepositoryId}}", contentInfo.Id); ValidateTemplateReplaceComplete(workingText); return(workingText); }
/// <summary> /// URL to download a datasource /// </summary> /// <param name="logInInfo">Tableau Sign In Information</param> /// <param name="datasource">Tableau Data Source</param> /// <returns></returns> public string Url_DatasourceDownload(TableauServerSignIn logInInfo, SiteDatasource datasource) { string workingText = _urlDownloadDatasourceTemplate; workingText = workingText.Replace("{{iwsSiteId}}", logInInfo.SiteId); workingText = workingText.Replace("{{iwsRepositoryId}}", datasource.Id); _ValidateTemplateReplaceComplete(workingText); return(workingText); }
public string UrlUpdateDatasourceConnection(TableauServerSignIn session, SiteDatasource datasourceToUpdate, SiteConnection connectionToUpdate) { string workingText = UrlUpdateDatasourceConnection_p; workingText = workingText.Replace("{{iwsSiteId}}", session.SiteId); workingText = workingText.Replace("{{iwsDatasourceId}}", datasourceToUpdate.Id); workingText = workingText.Replace("{{iwsConnectionId}}", connectionToUpdate.Id); ValidateTemplateReplaceComplete(workingText); return(workingText); }
public void UpdateServerAddress(SiteDatasource datasourceToUpdate, SiteConnection connectionToUpdate, string serverAddress) { var sb = new StringBuilder(); var xmlWriter = XmlWriter.Create(sb, XmlHelper.XmlSettingsForWebRequests); xmlWriter.WriteStartElement("tsRequest"); xmlWriter.WriteStartElement("connection"); xmlWriter.WriteAttributeString("serverAddress", serverAddress); xmlWriter.WriteEndElement(); //</connection> xmlWriter.WriteEndElement(); // </tsRequest> xmlWriter.Dispose(); var xmlText = sb.ToString(); var urlQuery = Urls.UrlUpdateDatasourceConnection(Login, datasourceToUpdate, connectionToUpdate); var webRequest = CreateLoggedInRequest(urlQuery, HttpMethod.Put); SendHttpRequest(webRequest, xmlText); }
/// <summary> /// Save Datasource metadata in a XML file along-side the workbook file /// </summary> /// <param name="wb">Information about the workbook we have downloaded</param> /// <param name="localDatasourcePath">Local path to the twb/twbx of the workbook</param> /// <param name="userLookups">If non-NULL contains the mapping of users/ids so we can look up the owner</param> internal static void CreateSettingsFile(SiteDatasource ds, string localDatasourcePath, KeyedLookup<SiteUser> userLookups) { string contentOwnerName = null; //Start off assuming we have no content owner information if (userLookups != null) { contentOwnerName = WorkbookPublishSettings.helper_LookUpOwnerId(ds.OwnerId, userLookups); } var xml = System.Xml.XmlWriter.Create(PathForSettingsFile(localDatasourcePath)); xml.WriteStartDocument(); xml.WriteStartElement(XmlElement_DatasourceInfo); //If we have an owner name, write it out if (!string.IsNullOrWhiteSpace(contentOwnerName)) { XmlHelper.WriteValueElement(xml, WorkbookPublishSettings.XmlElement_ContentOwner, contentOwnerName); } xml.WriteEndElement(); //end: WorkbookInfo xml.WriteEndDocument(); xml.Close(); }
/// <summary> /// Save Datasource metadata in a XML file along-side the workbook file /// </summary> /// <param name="wb">Information about the workbook we have downloaded</param> /// <param name="localDatasourcePath">Local path to the twb/twbx of the workbook</param> /// <param name="userLookups">If non-NULL contains the mapping of users/ids so we can look up the owner</param> internal static void CreateSettingsFile(SiteDatasource ds, string localDatasourcePath, KeyedLookup <SiteUser> userLookups) { string contentOwnerName = null; //Start off assuming we have no content owner information if (userLookups != null) { contentOwnerName = WorkbookPublishSettings.helper_LookUpOwnerId(ds.OwnerId, userLookups); } var xml = System.Xml.XmlWriter.Create(PathForSettingsFile(localDatasourcePath)); xml.WriteStartDocument(); xml.WriteStartElement(XmlElement_DatasourceInfo); //If we have an owner name, write it out if (!string.IsNullOrWhiteSpace(contentOwnerName)) { XmlHelper.WriteValueElement(xml, WorkbookPublishSettings.XmlElement_ContentOwner, contentOwnerName); } xml.WriteEndElement(); //end: WorkbookInfo xml.WriteEndDocument(); xml.Close(); }
/// <summary> /// Get a page's worth of Data Sources /// </summary> /// <param name="onlineDatasources"></param> /// <param name="pageToRequest">Page # we are requesting (1 based)</param> /// <param name="totalNumberPages">Total # of pages of data that Server can return us</param> private void ExecuteRequest_ForPage(List <SiteDatasource> onlineDatasources, int pageToRequest, out int totalNumberPages) { int pageSize = Urls.PageSize; //Create a web request, in including the users logged-in auth information in the request headers var urlQuery = Urls.Url_DatasourcesList(Login, pageSize, pageToRequest); var request = CreateLoggedInRequest(urlQuery, HttpMethod.Get); Login.Logger.Information("Web request: " + urlQuery); var response = SendHttpRequest(request); var xmlDoc = GetHttpResponseAsXml(response); //Get all the nodes var xDoc = xmlDoc.ToXDocument(); var datasources = xDoc.Root.Descendants(XName.Get("datasource", XmlNamespace)); //Get information for each of the data sources foreach (var itemXml in datasources) { try { var itemXmlNode = itemXml.ToXmlNode(); var ds = new SiteDatasource(itemXmlNode, XmlNamespace); onlineDatasources.Add(ds); } catch { Login.Logger.Error("Error parsing datasource: " + itemXml.ToXmlNode()); } } //end: foreach //------------------------------------------------------------------- //Get the updated page-count //------------------------------------------------------------------- var paginationElement = xDoc.Root.Descendants(XName.Get("pagination", XmlNamespace)).FirstOrDefault(); totalNumberPages = GetPageCount(paginationElement, pageSize); }
/// <summary> /// Assign ownership /// </summary> /// <param name="datasource"></param> /// <param name="publishSettings"></param> /// <param name="siteUsers"></param> /// <returns>TRUE: The server content has the correct owner now. FALSE: We were unable to give the server content the correct owner</returns> private bool AttemptOwnerReassignment(SiteDatasource datasource, DatasourcePublishSettings publishSettings, IEnumerable <SiteUser> siteUsers) { this.StatusLog.AddStatusHeader("Attempting ownership assignement for Datasource " + datasource.Name + "/" + datasource.Id); //Something went wrong if we don't have a set of site users to do the look up if (siteUsers == null) { throw new ArgumentException("No set of site users provided for lookup"); } //Look the local meta data to see what the desired name is var desiredOwnerName = publishSettings.OwnerName; if (string.IsNullOrEmpty(desiredOwnerName)) { this.StatusLog.AddStatus("Skipping owner assignment. The local file system has no metadata with an owner information for " + datasource.Name); LogManualAction_ReassignOwnership(datasource.Name, "none specified", "No client ownership information was specified"); return(true); //Since there is no ownership preference stated locally, then ownership we assigned during upload was fine. } //Look on the list of users in the target site/server, and try to find a match // //NOTE: We are doing a CASE INSENSITIVE name comparison. We assume that there are not 2 users with the same name on server w/differet cases // Because if this, we want to be flexible and allow that our source/destination servers may have the user name specified with a differnt // case. -- This is less secure than a case-sensitive comparison, but is almost always what we want when porting content between servers var desiredServerUser = SiteUser.FindUserWithName(siteUsers, desiredOwnerName, StringComparison.InvariantCultureIgnoreCase); if (desiredServerUser == null) { this.StatusLog.AddError("The local file has a workbook/user mapping: " + datasource.Name + "/" + desiredOwnerName + ", but this user does not exist on the target site"); LogManualAction_ReassignOwnership(datasource.Name, desiredOwnerName, "The target site does not contain a user name that matches the owner specified by the local metadata"); return(false); //Not a run time error, but we have manual steps to perform } //If the server content is already correct, then there is nothing to do if (desiredServerUser.Id == datasource.OwnerId) { this.StatusLog.AddStatus("Workbook " + datasource.Name + "/" + datasource.Id + ", already has correct ownership. No update requried"); return(true); } //Lets tell server to update the owner var changeOwnership = new SendUpdateDatasourceOwner(_onlineUrls, _onlineSession, datasource.Id, desiredServerUser.Id); SiteDatasource updatedDatasource; try { this.StatusLog.AddStatus("Server request to change Datasource ownership, ds: " + datasource.Name + "/" + datasource.Id + ", user:"******"/" + desiredServerUser.Id); updatedDatasource = changeOwnership.ExecuteRequest(); } catch (Exception exChangeOnwnerhsip) { throw exChangeOnwnerhsip; //Unexpected error, send it upward } //Sanity check the result we got back: Double check to make sure we have the expected owner. if (updatedDatasource.OwnerId != desiredServerUser.Id) { this.StatusLog.AddError("Unexpected server error! Updated workbook Owner Id does not match expected. ds: " + datasource.Name + "/" + datasource.Id + ", " + "expected user: "******", " + "actual user: " + updatedDatasource.OwnerId ); } return(true); }
/// <summary> /// URL to download a datasource /// </summary> /// <param name="siteUrlSegment"></param> /// <returns></returns> public string Url_DatasourceDownload(TableauServerSignIn session, SiteDatasource contentInfo) { string workingText = _urlDownloadDatasourceTemplate; workingText = workingText.Replace("{{iwsSiteId}}", session.SiteId); workingText = workingText.Replace("{{iwsRepositoryId}}", contentInfo.Id); ValidateTemplateReplaceComplete(workingText); return workingText; }
/// <summary> /// Update the owner of a single datasource /// </summary> /// <param name="siteSignIn"></param> /// <param name="contentItem"></param> /// <param name="userOldOwner"></param> /// <param name="userNewOwner"></param> private void Execute_ProvisionOwnership_SingleUserChange_SingleDatasource_inner(TableauServerSignIn siteSignIn, SiteDatasource contentItem, SiteUser userOldOwner, SiteUser userNewOwner) { var updateContentOwner = new SendUpdateDatasourceOwner(siteSignIn, contentItem.Id, userNewOwner.Id); updateContentOwner.ExecuteRequest(); CSVRecord_ContentOwnershipModified("datasource", contentItem.Name, userOldOwner.Name, userNewOwner.Name); }
/// <summary> /// Change the ownership for a single Datasource /// </summary> /// <param name="contentItem"></param> /// <param name="userOldOwner"></param> /// <param name="userNewOwner"></param> private void Execute_ProvisionOwnership_SingleUserChange_SingleDatasource(TableauServerSignIn siteSignIn, SiteDatasource contentItem, SiteUser userOldOwner, SiteUser userNewOwner) { try { Execute_ProvisionOwnership_SingleUserChange_SingleDatasource_inner(siteSignIn, contentItem, userOldOwner, userNewOwner); } catch (Exception ex) { _statusLogs.AddError("Error attempting to change content ownership, " + "datasource: " + contentItem.Name + "from: " + userOldOwner.Name + ", to:" + userNewOwner.Name + ", error: " + ex.ToString()); CSVRecord_ErrorUpdatingContentOwnership("datasource", contentItem.Name, userOldOwner.Name, userNewOwner.Name, ex.Message); } }
/// <summary> /// Get a page's worth of Data Sources /// </summary> /// <param name="onlineDatasources"></param> /// <param name="pageToRequest">Page # we are requesting (1 based)</param> /// <param name="totalNumberPages">Total # of pages of data that Server can return us</param> private void ExecuteRequest_ForPage(List<SiteDatasource> onlineDatasources, int pageToRequest, out int totalNumberPages) { int pageSize =_onlineUrls.PageSize; //Create a web request, in including the users logged-in auth information in the request headers var urlQuery = _onlineUrls.Url_DatasourcesList(_onlineSession, pageSize, pageToRequest); var webRequest = CreateLoggedInWebRequest(urlQuery); webRequest.Method = "GET"; _onlineSession.StatusLog.AddStatus("Web request: " + urlQuery, -10); var response = GetWebReponseLogErrors(webRequest, "get datasources list"); var xmlDoc = GetWebResponseAsXml(response); //Get all the workbook nodes var nsManager = XmlHelper.CreateTableauXmlNamespaceManager("iwsOnline"); var datasources = xmlDoc.SelectNodes("//iwsOnline:datasource", nsManager); //Get information for each of the data sources foreach (XmlNode itemXml in datasources) { try { var ds = new SiteDatasource(itemXml); onlineDatasources.Add(ds); } catch { AppDiagnostics.Assert(false, "Datasource parse error"); _onlineSession.StatusLog.AddError("Error parsing datasource: " + itemXml.InnerXml); } } //end: foreach //------------------------------------------------------------------- //Get the updated page-count //------------------------------------------------------------------- totalNumberPages =DownloadPaginationHelper.GetNumberOfPagesFromPagination( xmlDoc.SelectSingleNode("//iwsOnline:pagination", nsManager), pageSize); }
/// <summary> /// Assign ownership /// </summary> /// <param name="datasource"></param> /// <param name="publishSettings"></param> /// <param name="siteUsers"></param> /// <returns>TRUE: The server content has the correct owner now. FALSE: We were unable to give the server content the correct owner</returns> private bool AttemptOwnerReassignment(SiteDatasource datasource, DatasourcePublishSettings publishSettings, IEnumerable<SiteUser> siteUsers) { this.StatusLog.AddStatusHeader("Attempting ownership assignement for Datasource " + datasource.Name + "/" + datasource.Id); //Something went wrong if we don't have a set of site users to do the look up if (siteUsers == null) { throw new ArgumentException("No set of site users provided for lookup"); } //Look the local meta data to see what the desired name is var desiredOwnerName = publishSettings.OwnerName; if (string.IsNullOrEmpty(desiredOwnerName)) { this.StatusLog.AddStatus("Skipping owner assignment. The local file system has no metadata with an owner information for " + datasource.Name); LogManualAction_ReassignOwnership(datasource.Name, "none specified", "No client ownership information was specified"); return true; //Since there is no ownership preference stated locally, then ownership we assigned during upload was fine. } //Look on the list of users in the target site/server, and try to find a match // //NOTE: We are doing a CASE INSENSITIVE name comparison. We assume that there are not 2 users with the same name on server w/differet cases // Because if this, we want to be flexible and allow that our source/destination servers may have the user name specified with a differnt // case. -- This is less secure than a case-sensitive comparison, but is almost always what we want when porting content between servers var desiredServerUser = SiteUser.FindUserWithName(siteUsers, desiredOwnerName, StringComparison.InvariantCultureIgnoreCase); if (desiredServerUser == null) { this.StatusLog.AddError("The local file has a workbook/user mapping: " + datasource.Name + "/" + desiredOwnerName + ", but this user does not exist on the target site"); LogManualAction_ReassignOwnership(datasource.Name, desiredOwnerName, "The target site does not contain a user name that matches the owner specified by the local metadata"); return false; //Not a run time error, but we have manual steps to perform } //If the server content is already correct, then there is nothing to do if (desiredServerUser.Id == datasource.OwnerId) { this.StatusLog.AddStatus("Workbook " + datasource.Name + "/" + datasource.Id + ", already has correct ownership. No update requried"); return true; } //Lets tell server to update the owner var changeOwnership = new SendUpdateDatasourceOwner(_onlineUrls, _onlineSession, datasource.Id, desiredServerUser.Id); SiteDatasource updatedDatasource; try { this.StatusLog.AddStatus("Server request to change Datasource ownership, ds: " + datasource.Name + "/" + datasource.Id + ", user:"******"/" + desiredServerUser.Id); updatedDatasource = changeOwnership.ExecuteRequest(); } catch (Exception exChangeOnwnerhsip) { throw exChangeOnwnerhsip; //Unexpected error, send it upward } //Sanity check the result we got back: Double check to make sure we have the expected owner. if (updatedDatasource.OwnerId != desiredServerUser.Id) { this.StatusLog.AddError("Unexpected server error! Updated workbook Owner Id does not match expected. ds: " + datasource.Name + "/" + datasource.Id + ", " + "expected user: "******", " + "actual user: " + updatedDatasource.OwnerId ); } return true; }