public async Task <PublisherResults> PublishWorkbooks(
            string projectName,
            string projectDescription,
            IEnumerable <CompletedWorkbookInfo> completedWorkbooks,
            IEnumerable <string> workbookTags)
        {
            var tableauServerInfo = _publisherSettings.TableauServerInfo;

            try
            {
                using (var restApi = await TableauServerRestApi.InitApi(tableauServerInfo, _loggerFactory.CreateLogger <TableauServerRestApi>()))
                {
                    projectName = await FindUniqueProjectName(projectName, restApi);

                    var newProjectInfo = await restApi.CreateProject(projectName, projectDescription, _publisherSettings.ParentProjectInfo.Id, _publisherSettings.ParentProjectInfo.Name);
                    await SetDefaultPermissions(restApi, newProjectInfo);

                    var tags = workbookTags?.ToList();
                    var publishingResults = completedWorkbooks
                                            .Select(cwi => PublishWorkbook(newProjectInfo.Id, cwi, restApi, tags).Result) // .Result here ensures that we publish one workbook at a time instead of trying to push 10+ of them at the same time
                                            .ToList();

                    return(new PublisherResults(tableauServerInfo.Url, tableauServerInfo.Site, restApi.ContentUrl, newProjectInfo.Name, publishingResults));
                }
            }
            catch (RestApiException ex)
            {
                _logger.LogError("Exception encountered when attempting to sign in or create the project: {exceptionMessage}", ex.Message);
                return(new PublisherResults(tableauServerInfo.Url, tableauServerInfo.Site, null, projectName, null, ex));
            }
        }
Example #2
0
        private static async Task <string> FindUniqueProjectName(string projectName, TableauServerRestApi restApi)
        {
            if (await restApi.GetSiteProjectByName(projectName) == null)
            {
                return(projectName);
            }

            var suffix = 2;

            while (await restApi.GetSiteProjectByName($"{projectName}-{suffix}") != null)
            {
                suffix++;
            }

            return($"{projectName}-{suffix}");
        }
Example #3
0
        private async Task SetDefaultPermissions(TableauServerRestApi restApi, ProjectInfo projectInfo)
        {
            var groupsToProvideWithDefaultPermissions = _publisherSettings.GroupsToProvideWithDefaultPermissions;

            if (groupsToProvideWithDefaultPermissions == null || groupsToProvideWithDefaultPermissions.Count == 0)
            {
                return;
            }

            _logger.LogDebug("Attempting to set default workbook permissions for {groupsCount} groups on project {projectName}", groupsToProvideWithDefaultPermissions.Count, projectInfo.Name);

            var groups = await restApi.GetSiteGroups();

            var groupsToUpdate = groups.Where(group => groupsToProvideWithDefaultPermissions.Contains(group.Name)).ToList();

            foreach (var groupInfo in groupsToUpdate)
            {
                await restApi.AddDefaultWorkbookPermissions(projectInfo.Id, groupInfo.Id, Permissions);

                _logger.LogDebug("Added default workbook permissions for {groupName} on project {projectName}", groupInfo.Name, projectInfo.Name);
            }

            if (groupsToUpdate.Count == 0)
            {
                var requestedGroupsAsString = string.Join("; ", groupsToProvideWithDefaultPermissions);
                _logger.LogWarning("Configuration requested to set default workbook permissions for {groupsCount} group(s), however none of them were found on Tableau Server. Groups requested: {groupsRequested}", groupsToProvideWithDefaultPermissions.Count, requestedGroupsAsString);
            }
            else if (groupsToUpdate.Count < groupsToProvideWithDefaultPermissions.Count)
            {
                var missingGroups         = groupsToProvideWithDefaultPermissions.Except(groupsToUpdate.Select(group => group.Name));
                var missingGroupsAsString = string.Join("; ", missingGroups);
                _logger.LogWarning("Configuration requested to set default workbook permissions for {groupsCount} group(s), however some of the groups were not found. Missing groups {missingGroups}", groupsToProvideWithDefaultPermissions.Count, missingGroupsAsString);
            }
            else
            {
                _logger.LogInformation("Successfully set default workbook permissions on {groupCount} group(s)", groupsToProvideWithDefaultPermissions.Count);
            }
        }
Example #4
0
 private async Task AddTagsToWorkbook(WorkbookPublishResult workbookInfo, IList <string> tagsToAdd, TableauServerRestApi restApi)
 {
     try
     {
         await Retry.DoWithRetries <RestApiException, IList <TagInfo> >(
             nameof(WorkbookPublisher),
             _logger,
             async() => await restApi.AddTagsToWorkbook(workbookInfo.PublishedWorkbookId, tagsToAdd));
     }
     catch (RestApiException ex)
     {
         _logger.LogWarning(
             "Failed to add tags to workbook '{workbookName}' ({workbookId}). Exception message: {exceptionAddingTags}",
             workbookInfo.PublishedWorkbookName ?? "(null)",
             workbookInfo.PublishedWorkbookId ?? "(null)",
             ex.Message);
     }
 }
Example #5
0
        private async Task <WorkbookPublishResult> PublishWorkbook(string projectId, CompletedWorkbookInfo completedWorkbookInfo, TableauServerRestApi restApi, IList <string> tags)
        {
            if (!completedWorkbookInfo.GeneratedSuccessfully)
            {
                return(WorkbookPublishResult.Fail(
                           completedWorkbookInfo.OriginalWorkbookName,
                           new WorkbookPublishingException($"Workbook {completedWorkbookInfo.OriginalWorkbookName} was not generated successfully. Skipping publishing", completedWorkbookInfo.Exception)));
            }

            var publishWorkbookRequest = new PublishWorkbookRequest(
                completedWorkbookInfo.WorkbookPath,
                projectId,
                completedWorkbookInfo.FinalWorkbookName,
                overwriteExistingWorkbook: true)
            {
                Credentials = _dbCreds,
            };

            WorkbookPublishResult workbookPublishResult;

            try
            {
                var retryOnExceptionPolicy = Policy
                                             .Handle <RestApiException>()
                                             .WaitAndRetryAsync(
                    new []
                {
                    TimeSpan.FromSeconds(30),
                    TimeSpan.FromSeconds(60),
                },
                    (exception, timeSpan, retryCount, context) =>
                {
                    _logger.LogDebug("Got an exception trying to publish workbook `{failedWorkbookName}`. This is retry number {retryCount}. Exception was: `{exceptionMessage}`", completedWorkbookInfo.FinalWorkbookName ?? "null", retryCount, exception?.Message ?? "null");
                });

                var handleExceptionPolicy = Policy <WorkbookPublishResult>
                                            .Handle <RestApiException>(ex => ex.IsTimeoutException)
                                            .FallbackAsync(WorkbookPublishResult.Timeout(completedWorkbookInfo.FinalWorkbookName));

                workbookPublishResult = await retryOnExceptionPolicy
                                        .WrapAsync(handleExceptionPolicy)
                                        .ExecuteAsync(async() =>
                {
                    var workbookInfo = await restApi.PublishWorkbook(publishWorkbookRequest);
                    return(WorkbookPublishResult.Success(completedWorkbookInfo.FinalWorkbookName, workbookInfo));
                });
            }
            catch (RestApiException ex)
            {
                var errorMessage = $"Failed to publish workbook `{completedWorkbookInfo.WorkbookPath}` after multiple retries. Exception was: {ex.Message}";
                _logger.LogError(errorMessage);
                return(WorkbookPublishResult.Fail(completedWorkbookInfo.OriginalWorkbookName, new WorkbookPublishingException(errorMessage)));
            }

            if (workbookPublishResult.PublishState == WorkbookPublishResult.WorkbookPublishState.Success &&
                _publisherSettings.ApplyPluginProvidedTagsToWorkbooks &&
                tags != null &&
                tags.Count > 0)
            {
                await AddTagsToWorkbook(workbookPublishResult, tags, restApi);
            }

            if (workbookPublishResult.PublishState == WorkbookPublishResult.WorkbookPublishState.Success)
            {
                _logger.LogDebug("Workbook `{publishedWorkbookName}` was published to Tableau Server as ID `{newWorkbookId}`", publishWorkbookRequest.WorkbookNameOnTableauServer, workbookPublishResult.PublishedWorkbookId);
            }
            else
            {
                _logger.LogDebug("Publishing attempt for workbook `{originalWorkbookName}` returned non-successful publishing status. Status: {publishingStatus}", publishWorkbookRequest.WorkbookNameOnTableauServer, workbookPublishResult.PublishState.ToString());
            }

            return(workbookPublishResult);
        }
        private async Task <WorkbookPublishResult> PublishWorkbook(string projectId, CompletedWorkbookInfo completedWorkbookInfo, TableauServerRestApi restApi, IList <string> tags)
        {
            if (!completedWorkbookInfo.GeneratedSuccessfully)
            {
                return(new WorkbookPublishResult(
                           completedWorkbookInfo.OriginalWorkbookName,
                           new WorkbookPublishingException($"Workbook {completedWorkbookInfo.OriginalWorkbookName} was not generated successfully. Skipping publishing", completedWorkbookInfo.Exception)));
            }

            var publishWorkbookRequest = new PublishWorkbookRequest(
                completedWorkbookInfo.WorkbookPath,
                projectId,
                completedWorkbookInfo.FinalWorkbookName,
                overwriteExistingWorkbook: true)
            {
                Credentials = _dbCreds,
            };

            WorkbookInfo workbookInfo;

            try
            {
                workbookInfo = await Retry.DoWithRetries <RestApiException, WorkbookInfo>(
                    nameof(WorkbookPublisher),
                    _logger,
                    async() => await restApi.PublishWorkbook(publishWorkbookRequest));

                _logger.LogDebug("Workbook `{publishedWorkbookName}` was published to Tableau Server as ID `{newWorkbookId}`", publishWorkbookRequest.WorkbookNameOnTableauServer, workbookInfo.Id);
            }
            catch (RestApiException ex)
            {
                var errorMessage = $"Failed to publish workbook `{completedWorkbookInfo.WorkbookPath}` after multiple retries. Exception was: {ex.Message}";
                _logger.LogError(errorMessage);
                return(new WorkbookPublishResult(completedWorkbookInfo.OriginalWorkbookName, new WorkbookPublishingException(errorMessage)));
            }

            if (_publisherSettings.ApplyPluginProvidedTagsToWorkbooks && tags != null && tags.Count > 0)
            {
                await AddTagsToWorkbook(workbookInfo, tags, restApi);
            }

            return(new WorkbookPublishResult(workbookInfo.Name));
        }