Skip to content

Commit ca234f6

Browse files
authored
Release/4.3.0
Release 4.3.0
2 parents 7fdba58 + 5d88744 commit ca234f6

File tree

229 files changed

+8970
-1349
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

229 files changed

+8970
-1349
lines changed

.github/workflows/dotnet-test.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,12 @@ jobs:
2626
run: dotnet build '${{ vars.BUILD_SOLUTION }}' -c ${{ matrix.config }}
2727
- name: Test solution with ${{ matrix.config }} configuration
2828
run: |
29-
dotnet test '${{ vars.BUILD_SOLUTION }}' --no-build -c ${{ matrix.config }} --verbosity normal --logger trx --results-directory "TestResults-${{ matrix.os }}-${{ matrix.config }}" -- RunConfiguration.TestSessionTimeout=${{ vars.MIGRATIONSDK_TEST_CANCELLATION_TIMEOUT_MILLISECONDS }}
29+
dotnet test '${{ vars.BUILD_SOLUTION }}' --no-build -c ${{ matrix.config }} --verbosity normal --logger junit --results-directory "TestResults-${{ matrix.os }}-${{ matrix.config }}" -- RunConfiguration.TestSessionTimeout=${{ vars.MIGRATIONSDK_TEST_CANCELLATION_TIMEOUT_MILLISECONDS }}
3030
- name: Upload test results
3131
# Use always() to always run this step to publish test results when there are test failures
3232
if: ${{ always() }}
3333
uses: actions/upload-artifact@v4
3434
with:
3535
name: dotnet-results-${{ matrix.os }}-${{ matrix.config }}
3636
path: TestResults-${{ matrix.os }}-${{ matrix.config }}
37-
if-no-files-found: error
37+
if-no-files-found: error

.gitignore

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,6 @@ instance/
8080
# Scrapy stuff:
8181
.scrapy
8282

83-
# Sphinx documentation
84-
docs/_build/
8583

8684
# PyBuilder
8785
.pybuilder/
@@ -179,14 +177,6 @@ launchSettings.json
179177
UpgradeLog.htm
180178
*.*.ini
181179
*.*.json
182-
/src/Python/Documentation/_build
183-
/src/Python/Documentation/_static
184-
/src/Python/Python.pyproj.user
185-
/src/Documentation/_python
186-
/src/Python/Documentation/generated
187-
/tests/Python.TestApplication/manifest.json
188-
/.git2gus
189-
/src/Python/scripts/publish-package.ps1
190180

191181
# Public repo ignores
192182
.github/pull_request_template.md

Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<Nullable>enable</Nullable>
55
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
66
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
7-
<Version>4.2.0</Version>
7+
<Version>4.3.0</Version>
88
<Authors>Salesforce, Inc.</Authors>
99
<Company>Salesforce, Inc.</Company>
1010
<Copyright>Copyright (c) 2024, Salesforce, Inc. and its licensors</Copyright>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System;
2+
using Microsoft.Extensions.Logging;
3+
using Tableau.Migration.Api.Rest.Models;
4+
using Tableau.Migration.Content;
5+
using Tableau.Migration.Engine;
6+
using Tableau.Migration.Engine.Hooks.Filters;
7+
using Tableau.Migration.Resources;
8+
9+
namespace Csharp.ExampleApplication.Hooks.Filters
10+
{
11+
#region class
12+
public class SharedCustomViewFilter : ContentFilterBase<ICustomView>
13+
{
14+
public SharedCustomViewFilter(
15+
ISharedResourcesLocalizer localizer,
16+
ILogger<IContentFilter<ICustomView>> logger)
17+
: base(localizer, logger) { }
18+
19+
public override bool ShouldMigrate(ContentMigrationItem<ICustomView> item)
20+
{
21+
return !item.SourceItem.Shared;
22+
}
23+
}
24+
#endregion
25+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Threading;
5+
using System.Threading.Tasks;
6+
using Microsoft.Extensions.Logging;
7+
using Tableau.Migration;
8+
using Tableau.Migration.Content;
9+
using Tableau.Migration.Engine.Hooks.Transformers;
10+
using Tableau.Migration.Resources;
11+
12+
namespace Csharp.ExampleApplication.Hooks.Transformers
13+
{
14+
#region class
15+
public class CustomViewExcludeDefaultUserTransformer(
16+
ISharedResourcesLocalizer localizer,
17+
ILogger<CustomViewExcludeDefaultUserTransformer> logger)
18+
: ContentTransformerBase<IPublishableCustomView>(localizer, logger)
19+
{
20+
public IList<string> ExcludeUsernames { get; } = new List<string>() {"User1", "User2"};
21+
22+
23+
private readonly ILogger<CustomViewExcludeDefaultUserTransformer>? _logger = logger;
24+
25+
public override async Task<IPublishableCustomView?> TransformAsync(IPublishableCustomView itemToTransform, CancellationToken cancel)
26+
{
27+
var newDefaultUsers = itemToTransform.DefaultUsers.Where(user => !ExcludeUsernames.Contains(user.Name)).ToList();
28+
29+
itemToTransform.DefaultUsers = newDefaultUsers;
30+
31+
_logger?.LogInformation(
32+
@"Excluding default users {newDefaultUsers}",
33+
newDefaultUsers);
34+
35+
return await Task.FromResult(itemToTransform);
36+
}
37+
}
38+
#endregion
39+
}

examples/Csharp.ExampleApplication/MyMigrationApplication.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,10 @@ public async Task StartAsync(CancellationToken cancel)
113113
#region UnlicensedUsersFilter-Registration
114114
_planBuilder.Filters.Add<UnlicensedUsersFilter, IUser>();
115115
#endregion
116+
117+
#region SharedCustomViewFilter-Registration
118+
_planBuilder.Filters.Add<SharedCustomViewFilter, ICustomView>();
119+
#endregion
116120

117121
// Add post-publish hooks
118122
#region UpdatePermissionsHook-Registration
@@ -138,6 +142,10 @@ public async Task StartAsync(CancellationToken cancel)
138142
#region StartAtTransformer-Registration
139143
_planBuilder.Transformers.Add<SimpleScheduleStartAtTransformer<ICloudExtractRefreshTask>, ICloudExtractRefreshTask>();
140144
#endregion
145+
146+
#region CustomViewDefaultUsersTransformer-Registration
147+
_planBuilder.Transformers.Add<CustomViewExcludeDefaultUserTransformer, IPublishableCustomView>();
148+
#endregion
141149

142150
// Add migration action completed hooks
143151
#region LogMigrationActionsHook-Registration

examples/Csharp.ExampleApplication/Program.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ public static IServiceCollection AddCustomizations(this IServiceCollection servi
6868
#region UnlicensedUsersFilter-DI
6969
services.AddScoped<UnlicensedUsersFilter>();
7070
#endregion
71+
72+
#region SharedCustomViewFilter-DI
73+
services.AddScoped<SharedCustomViewFilter>();
74+
#endregion
7175

7276
#region UpdatePermissionsHook-DI
7377
services.AddScoped(typeof(UpdatePermissionsHook<,>));
@@ -90,6 +94,10 @@ public static IServiceCollection AddCustomizations(this IServiceCollection servi
9094
#region StartAtTransformer-DI
9195
services.AddScoped(typeof(SimpleScheduleStartAtTransformer<>));
9296
#endregion
97+
98+
#region CustomViewDefaultUsersTransformer-DI
99+
services.AddScoped<CustomViewExcludeDefaultUserTransformer>();
100+
#endregion
93101

94102
#region LogMigrationActionsHook-DI
95103
services.AddScoped<LogMigrationActionsHook>();
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from tableau_migration import (
2+
ICustomView,
3+
ContentMigrationItem,
4+
ContentFilterBase)
5+
6+
7+
class SharedCustomViewFilter(ContentFilterBase[ICustomView]):
8+
def should_migrate(self, item: ContentMigrationItem[ICustomView]) -> bool:
9+
if item.source_item.shared == True:
10+
return False
11+
return True

examples/Python.ExampleApplication/Hooks/mappings/project_rename_mapping.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@ def map(self, ctx: ContentMappingContext[IProject]) -> ContentMappingContext[IPr
1111

1212
new_location = ctx.content_item.location.rename("Production")
1313

14-
ctx.map_to(new_location)
14+
ctx = ctx.map_to(new_location)
1515

1616
return ctx
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from tableau_migration import (
2+
ContentTransformerBase,
3+
IContentReference,
4+
IPublishableCustomView
5+
)
6+
7+
class CustomViewDefaultUsersTransformer(ContentTransformerBase[IPublishableCustomView]):
8+
9+
#Pass in list of users retrieved from Users API
10+
default_users = []
11+
12+
def transform(self, itemToTransform: IPublishableCustomView) -> IPublishableCustomView:
13+
itemToTransform.default_users = self.default_users
14+
return itemToTransform

examples/Python.ExampleApplication/Python.ExampleApplication.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,15 @@
88

99
import configparser # configuration parser
1010
import os # environment variables
11-
import sys # system utility
1211
import tableau_migration # Tableau Migration SDK
13-
import print_result
12+
from print_result import print_result
1413

1514
from threading import Thread # threading
1615

1716
from tableau_migration import (
1817
MigrationManifestSerializer,
1918
MigrationManifest
20-
)
19+
)
2120

2221
serializer = MigrationManifestSerializer()
2322

@@ -40,12 +39,14 @@ def migrate():
4039
server_url = config['SOURCE']['URL'],
4140
site_content_url = config['SOURCE']['SITE_CONTENT_URL'],
4241
access_token_name = config['SOURCE']['ACCESS_TOKEN_NAME'],
43-
access_token = os.environ.get('TABLEAU_MIGRATION_SOURCE_TOKEN', config['SOURCE']['ACCESS_TOKEN'])) \
42+
access_token = os.environ.get('TABLEAU_MIGRATION_SOURCE_TOKEN', config['SOURCE']['ACCESS_TOKEN']),
43+
create_api_simulator = os.environ.get('TABLEAU_MIGRATION_SOURCE_SIMULATION', 'False') == 'True') \
4444
.to_destination_tableau_cloud(
4545
pod_url = config['DESTINATION']['URL'],
4646
site_content_url = config['DESTINATION']['SITE_CONTENT_URL'],
4747
access_token_name = config['DESTINATION']['ACCESS_TOKEN_NAME'],
48-
access_token = os.environ.get('TABLEAU_MIGRATION_DESTINATION_TOKEN', config['DESTINATION']['ACCESS_TOKEN'])) \
48+
access_token = os.environ.get('TABLEAU_MIGRATION_DESTINATION_TOKEN', config['DESTINATION']['ACCESS_TOKEN']),
49+
create_api_simulator = os.environ.get('TABLEAU_MIGRATION_DESTINATION_SIMULATION', 'False') == 'True') \
4950
.for_server_to_cloud() \
5051
.with_tableau_id_authentication_type() \
5152
.with_tableau_cloud_usernames(config['USERS']['EMAIL_DOMAIN'])
@@ -101,7 +102,7 @@ def load_manifest(manifest_path: str) -> MigrationManifest | None:
101102
while not done:
102103
try:
103104
migration_thread.join(1)
104-
sys.exit(0)
105+
done = True
105106
except KeyboardInterrupt:
106107
# Ctrl+C was caught, request migration to cancel.
107108
print("Caught Ctrl+C, shutting down...")

examples/Python.ExampleApplication/Python.ExampleApplication.pyproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,15 @@
3030
<Compile Include="hooks\mappings\project_rename_mapping.py" />
3131
<Compile Include="hooks\migration_action_completed\log_migration_actions_hook.py" />
3232
<Compile Include="hooks\post_publish\bulk_logging_hook.py" />
33+
<Compile Include="Hooks\transformers\custom_view_default_users_transformer.py" />
3334
<Compile Include="hooks\transformers\encrypt_extracts_transformer.py" />
3435
<Compile Include="hooks\transformers\migrated_tag_transformer.py" />
3536
<Compile Include="Hooks\transformers\schedule_startat_transformer.py" />
3637
<Compile Include="print_result.py" />
3738
<Compile Include="Python.ExampleApplication.py" />
3839
</ItemGroup>
3940
<ItemGroup>
41+
<Content Include=".env" />
4042
<Content Include="config*.ini" />
4143
<Content Include="pyproject.toml" />
4244
<Content Include="requirements.txt" />

examples/Python.ExampleApplication/config.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22
URL = http://server
33
SITE_CONTENT_URL =
44
ACCESS_TOKEN_NAME = MyServerTokenName
5+
ACCESS_TOKEN =
56

67
[DESTINATION]
78
URL = https://pod.online.tableau.com
89
SITE_CONTENT_URL = mycloudsite
910
ACCESS_TOKEN_NAME = MyCloudTokenName
11+
ACCESS_TOKEN =
1012

1113
[USERS]
1214
EMAIL_DOMAIN = mycompany.com

examples/Python.ExampleApplication/print_result.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
import tableau_migration
21
from tableau_migration.migration import PyMigrationResult
32
from tableau_migration import IMigrationManifestEntry, MigrationManifestEntryStatus
43
from Tableau.Migration.Engine.Pipelines import ServerToCloudMigrationPipeline
54

6-
def print_result(self, result: PyMigrationResult):
5+
def print_result(result: PyMigrationResult):
76
"""Prints the result of a migration."""
8-
self.logger.info(f'Result: {result.status}')
7+
print(f'Result: {result.status}')
98

109
for pipeline_content_type in ServerToCloudMigrationPipeline.ContentTypes:
1110
content_type = pipeline_content_type.ContentType
@@ -41,4 +40,4 @@ def print_result(self, result: PyMigrationResult):
4140
\t{count_pending}/{count_total} pending
4241
'''
4342

44-
self.logger.info(output)
43+
print(output)
Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
1-
setuptools==70.1.1
21
configparser==7.0.0
32
tableau_migration
4-
cffi==1.16.0
5-
pycparser==2.22
6-
pythonnet==3.0.3
7-
typing_extensions==4.12.2
83
python-dotenv==1.0.1

src/Documentation/api-csharp/index.md

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,40 +6,7 @@ Welcome to the C# API Reference for the Migration SDK.
66

77
The following code samples are for writing a simple migration app using the Migration SDK. For details on configuring and customizing the Migration SDK to your specific needs, see [Articles](~/articles/index.md) and [Code Samples](~/samples/index.md).
88

9-
### [Program.cs](#tab/program-cs)
10-
11-
[!code-csharp[](../../../examples/Csharp.ExampleApplication/Program.cs#namespace)]
12-
13-
### [Startup code](#tab/startup-cde)
14-
15-
[!code-csharp[](../../../examples/Csharp.ExampleApplication/MyMigrationApplication.cs#namespace)]
16-
17-
### [Config classes](#tab/config-classes)
18-
19-
[!code-csharp[](../../../examples/Csharp.ExampleApplication/Config/MyMigrationApplicationOptions.cs#namespace)]
20-
21-
[!code-csharp[](../../../examples/Csharp.ExampleApplication/Config/EndpointOptions.cs#namespace)]
22-
23-
### [Config file](#tab/appsettings)
24-
25-
```json
26-
{
27-
"source": {
28-
"serverUrl": "http://server",
29-
"siteContentUrl": "",
30-
"accessTokenName": "my server token name",
31-
"accessToken": "my-secret-server-pat"
32-
},
33-
"destination": {
34-
"serverUrl": "https://pod.online.tableau.com",
35-
"siteContentUrl": "site-name",
36-
"accessTokenName": "my cloud token name",
37-
"accessToken": "my-secret-cloud-pat"
38-
}
39-
}
40-
```
41-
42-
---
9+
[!include[](~/includes/csharp-getting-started.md)]
4310

4411
## Suggested Reading
4512

src/Documentation/api-python/index.md

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,4 @@ There are advanced features of the Migration SDK that the Python Wrapper cannot
2424

2525
The following code samples are for writing a simple migration app using the Migration SDK. For details on configuring and customizing the Migration SDK to your specific needs, see [Articles](~/articles/index.md) and [Code Samples](~/samples/index.md).
2626

27-
### [Startup Script](#tab/startup)
28-
29-
[!code-python[](../../../examples/Python.ExampleApplication/Python.ExampleApplication.py)]
30-
31-
### [config.ini](#tab/config)
32-
33-
> [!Important]
34-
> The values below should not be quoted. So ***no*** `'` or `"`.
35-
36-
[!code-ini[](../../../examples/Python.ExampleApplication/config.ini)]
37-
38-
### [requirements.txt](#tab/reqs)
39-
40-
[!code-text[](../../../examples/Python.ExampleApplication/requirements.txt#L3-)]
41-
42-
---
27+
[!include[](~/includes/python-getting-started.md)]

src/Documentation/articles/configuration.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,20 @@ Supported Content Types:
448448

449449
*Description:* The Migration SDK uses [Microsoft.Extensions.Http.Resilience](https://learn.microsoft.com/en-us/dotnet/core/resilience) as a resilience and transient-fault layer. The SDK uses the **PerFileTransferRequestTimeout** property to define the maximum duration of FileTransfer requests.
450450

451+
452+
### Network.UserAgentComment
453+
454+
*Reference:* [`NetworkOptions.UserAgentComment`](xref:Tableau.Migration.Config.NetworkOptions#Tableau_Migration_Config_NetworkOptions_UserAgentComment).
455+
456+
*Default:* [`NetworkOptions.Defaults.USER_AGENT_COMMENT`](xref:Tableau.Migration.Config.NetworkOptions.Defaults#Tableau_Migration_Config_NetworkOptions_Defaults_USER_AGENT_COMMENT).
457+
458+
*Python Environment Variable:* `MigrationSDK__Network__UserAgentComment`
459+
460+
*Reload on Edit?:* **No**. Any changes to this configuration will reflect on the next time the application starts.
461+
462+
*Description:* The Migration SDK appends the **UserAgentComment** property to the User-Agent header in all HTTP requests. This property is only used to assist in server-side debugging and it not typically set.
463+
464+
451465
### DefaultPermissionsContentTypes.UrlSegments
452466

453467
*Reference:* [`DefaultPermissionsContentTypeOptions.UrlSegments`](xref:Tableau.Migration.Config.DefaultPermissionsContentTypeOptions#Tableau_Migration_Config_DefaultPermissionsContentTypeOptions_UrlSegments).

0 commit comments

Comments
 (0)