/
AssetDirectoryOperation.cs
143 lines (122 loc) · 6.1 KB
/
AssetDirectoryOperation.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
using Inedo.AssetDirectories;
using Inedo.Extensibility.SecureResources;
using Inedo.Extensions.PackageSources;
#nullable enable
namespace Inedo.Extensions.Operations.ProGet.AssetDirectories
{
public abstract class AssetDirectoryOperation : ExecuteOperation
{
private volatile int percent = -1;
private volatile string? message;
private protected AssetDirectoryOperation()
{
}
public abstract string? Path { get; set; }
[ScriptAlias("Source")]
[DisplayName("Source")]
[SuggestableValue(typeof(AssetSourceSuggestionProvider))]
public string? AssetSourceId { get; set; }
[ScriptAlias("Resource")]
[Category("Connection/Identity")]
[DisplayName("Secure resource (legacy)")]
public string? ResourceName { get; set; }
[ScriptAlias("EndpointUrl")]
[DisplayName("API endpoint URL")]
[Category("Connection/Identity")]
[PlaceholderText("Use URL from secure resource")]
public string? ApiUrl { get; set; }
[ScriptAlias("ApiKey")]
[DisplayName("API key")]
[Category("Connection/Identity")]
[FieldEditMode(FieldEditMode.Password)]
[PlaceholderText("Use token from secure credentials")]
public string? ApiKey { get; set; }
[ScriptAlias("UserName")]
[DisplayName("User name")]
[Category("Connection/Identity")]
[PlaceholderText("Use user name from secure credentials")]
public string? UserName { get; set; }
[ScriptAlias("Password")]
[Category("Connection/Identity")]
[FieldEditMode(FieldEditMode.Password)]
[PlaceholderText("Use password from secure credentials")]
public string? Password { get; set; }
#pragma warning disable CS0618 // Type or member is obsolete
private string? LegacySecureResourceName => this.ResourceName;
#pragma warning restore CS0618 // Type or member is obsolete
public override OperationProgress GetProgress() => new(AH.NullIf(this.percent, -1), this.message);
public override Task ExecuteAsync(IOperationExecutionContext context)
{
if (!string.IsNullOrEmpty(this.AssetSourceId))
{
var id = new AssetSourceId(this.AssetSourceId);
switch (id.Format)
{
case AssetSourceIdFormat.SecureResource:
this.GetLegacySecureResource(context, id.GetResourceName());
break;
case AssetSourceIdFormat.ProGetAssetDirectory:
this.GetCredentials(context, id.GetProGetServiceCredentialName(), id.GetAssetDirectoryName());
break;
default:
this.LogError($"Unexpected source format: {id.Format}");
return Complete;
}
}
else if (!string.IsNullOrWhiteSpace(this.LegacySecureResourceName))
{
this.GetLegacySecureResource(context, this.LegacySecureResourceName);
}
if (string.IsNullOrWhiteSpace(this.ApiUrl))
{
this.LogError("EndpointUrl must be specified in either the operation or the referenced secure resource.");
return Complete;
}
var client = new AssetDirectoryClient(this.ApiUrl, AH.NullIf(this.ApiKey, string.Empty), AH.NullIf(this.UserName, string.Empty), AH.NullIf(this.Password, string.Empty));
return this.ExecuteAsync(client, context);
}
private protected abstract Task ExecuteAsync(AssetDirectoryClient client, IOperationExecutionContext context);
private protected void ProgressReceived(int? percent, string message)
{
this.percent = percent ?? -1;
this.message = message;
}
private void GetLegacySecureResource(ICredentialResolutionContext context, string secureResourceName)
{
if (!context.TryGetSecureResource(SecureResourceType.General, secureResourceName, out var r) || r is not ProGetAssetDirectorySecureResource resource)
throw new ExecutionFailureException($"Resource {secureResourceName} is not a ProGet secure resource.");
if (string.IsNullOrWhiteSpace(this.ApiUrl))
this.ApiUrl = resource.EndpointUrl;
if (!string.IsNullOrWhiteSpace(resource.RootFolder))
this.Path = PathEx.Combine('/', resource.RootFolder, this.Path ?? string.Empty);
switch (resource.GetCredentials(context))
{
case TokenCredentials tokenCredentials:
if (string.IsNullOrWhiteSpace(this.ApiKey))
this.ApiKey = AH.Unprotect(tokenCredentials.Token);
break;
case UsernamePasswordCredentials userCredentials:
if (string.IsNullOrWhiteSpace(this.UserName))
this.UserName = userCredentials.UserName;
if (string.IsNullOrEmpty(this.Password))
this.Password = AH.Unprotect(userCredentials.Password);
break;
}
}
private bool GetCredentials(ICredentialResolutionContext context, string credentialName, string feedName)
{
var creds = SecureCredentials.TryCreate(credentialName, context);
if (creds is not ProGetServiceCredentials credentials)
throw new ExecutionFailureException($"Credential {credentialName} is not a ProGet service credential.");
if (!string.IsNullOrEmpty(this.ApiUrl) && !string.IsNullOrEmpty(credentials.ServiceUrl))
this.ApiUrl = PathEx.Combine('/', credentials.ServiceUrl, Uri.EscapeDataString(feedName));
if (!string.IsNullOrEmpty(this.ApiKey))
this.ApiKey = credentials.APIKey;
if (!string.IsNullOrEmpty(this.UserName))
this.UserName = credentials.UserName;
if (!string.IsNullOrEmpty(this.Password))
this.Password = credentials.Password;
return true;
}
}
}