Skip to content

Commit 6fcad13

Browse files
committed
Standardize on IChildResource and SetParent
Child resources, such as a ServerVolume or ImageMetadata often need references back to the parent. It should always be protected and only exposed through IChildResource. The ApiBuilder is responsible for setting a parent reference or id. The type should be reference, and then try casting to a full object if additional functionality is needed. The child won't always have a full object available, if it must have one, then the method should be moved elsewhere.
1 parent e501ccf commit 6fcad13

11 files changed

Lines changed: 73 additions & 40 deletions

File tree

src/corelib/Compute/v2_1/ComputeApiBuilder.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -907,13 +907,14 @@ public virtual async Task<TPage> ListImageDetailsAsync<TPage>(Url url, Cancellat
907907

908908
/// <summary />
909909
public virtual async Task<T> ListServerVolumesAsync<T>(string serverId, CancellationToken cancellationToken = default(CancellationToken))
910-
where T : IEnumerable<IServiceResource>
910+
where T : IEnumerable<IServiceResource>, IEnumerable<IChildResource>
911911
{
912-
var result = await BuidListServerVolumesAsync(serverId, cancellationToken)
912+
var results = await BuidListServerVolumesAsync(serverId, cancellationToken)
913913
.SendAsync()
914914
.ReceiveJson<T>();
915-
result.PropogateOwner(this);
916-
return result;
915+
results.PropogateOwner(this);
916+
results.SetParent(serverId);
917+
return results;
917918
}
918919

919920
/// <summary />
@@ -930,12 +931,13 @@ public virtual async Task<TPage> ListImageDetailsAsync<TPage>(Url url, Cancellat
930931

931932
/// <summary />
932933
public virtual async Task<T> AttachVolumeAsync<T>(string serverId, object request, CancellationToken cancellationToken = default(CancellationToken))
933-
where T : IServiceResource
934+
where T : IServiceResource, IChildResource
934935
{
935936
T result = await BuildAttachVolumeAsync(serverId, request, cancellationToken)
936937
.SendAsync()
937938
.ReceiveJson<T>();
938939
result.PropogateOwner(this);
940+
result.SetParent(serverId);
939941
return result;
940942
}
941943

src/corelib/Compute/v2_1/Image.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ public ImageType Type
9696
[OnDeserialized]
9797
private void OnDeserialized(StreamingContext context)
9898
{
99-
Metadata.Image = this;
99+
Metadata.SetParent(this);
100100
}
101101
}
102102
}

src/corelib/Compute/v2_1/ImageMetadata.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,27 @@ public class ImageMetadata : Dictionary<string, string>, IHaveExtraData, IChildR
1515
{
1616
/// <summary />
1717
[JsonIgnore]
18-
internal protected Image Image { get; set; }
18+
protected ImageReference Image { get; set; }
1919

2020
/// <summary />
2121
[JsonExtensionData]
2222
IDictionary<string, JToken> IHaveExtraData.Data { get; set; } = new Dictionary<string, JToken>();
2323

2424
object IServiceResource.Owner { get; set; }
2525

26+
internal void SetParent(ImageReference parent)
27+
{
28+
Image = parent;
29+
}
30+
2631
void IChildResource.SetParent(string parentId)
2732
{
28-
Image = new Image {Id = parentId};
33+
SetParent(new Image {Id = parentId});
34+
}
35+
36+
void IChildResource.SetParent(object parent)
37+
{
38+
SetParent((Image)parent);
2939
}
3040

3141
/// <summary />

src/corelib/Compute/v2_1/Server.cs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -175,22 +175,24 @@ public string AdminPassword
175175
result.CopyProperties(this);
176176
}
177177

178-
/// <inheritdoc />
179-
public override async Task<ServerVolume> AttachVolumeAsync(ServerVolumeDefinition volume, CancellationToken cancellationToken = new CancellationToken())
178+
/// <summary />
179+
/// <exception cref="InvalidOperationException">When this instance was not constructed by the <see cref="ComputeService"/>, as it is missing the appropriate internal state to execute service calls.</exception>
180+
public virtual async Task<ServerVolume> AttachVolumeAsync(ServerVolumeDefinition volume, CancellationToken cancellationToken = default(CancellationToken))
180181
{
181-
var result = await base.AttachVolumeAsync(volume, cancellationToken);
182-
result.Server = this;
182+
var compute = this.GetOwnerOrThrow<ComputeApiBuilder>();
183+
var result = await compute.AttachVolumeAsync<ServerVolume>(Id, volume, cancellationToken).ConfigureAwait(false);
183184
AttachedVolumes.Add(result);
185+
((IChildResource)result).SetParent(this);
184186
return result;
185187
}
186-
188+
187189
/// <summary />
188190
[OnDeserialized]
189191
private void OnDeserializedMethod(StreamingContext context)
190192
{
191-
foreach (var volume in AttachedVolumes)
193+
foreach (IChildResource volume in AttachedVolumes)
192194
{
193-
volume.Server = this;
195+
volume.SetParent(this);
194196
}
195197
}
196198
}

src/corelib/Compute/v2_1/ServerExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,8 @@ public static void Evacuate(this ServerReference server, EvacuateServerRequest r
8181
server.EvacuateAsync(request).ForceSynchronous();
8282
}
8383

84-
/// <inheritdoc cref="ServerReference.AttachVolumeAsync"/>
85-
public static ServerVolume AttachVolume(this ServerReference server, ServerVolumeDefinition volume)
84+
/// <inheritdoc cref="Server.AttachVolumeAsync"/>
85+
public static ServerVolume AttachVolume(this Server server, ServerVolumeDefinition volume)
8686
{
8787
return server.AttachVolumeAsync(volume).ForceSynchronous();
8888
}

src/corelib/Compute/v2_1/ServerReference.cs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -103,14 +103,6 @@ public class ServerReference : IHaveExtraData, IServiceResource
103103
await compute.EvacuateServerAsync(Id, request, cancellationToken).ConfigureAwait(false);
104104
}
105105

106-
/// <summary />
107-
/// <exception cref="InvalidOperationException">When this instance was not constructed by the <see cref="ComputeService"/>, as it is missing the appropriate internal state to execute service calls.</exception>
108-
public virtual async Task<ServerVolume> AttachVolumeAsync(ServerVolumeDefinition volume, CancellationToken cancellationToken = default(CancellationToken))
109-
{
110-
var compute = this.GetOwnerOrThrow<ComputeApiBuilder>();
111-
return await compute.AttachVolumeAsync<ServerVolume>(Id, volume, cancellationToken).ConfigureAwait(false);
112-
}
113-
114106
/// <summary />
115107
/// <exception cref="InvalidOperationException">When this instance was not constructed by the <see cref="ComputeService"/>, as it is missing the appropriate internal state to execute service calls.</exception>
116108
public async Task<IEnumerable<ServerVolume>> ListVolumesAsync(CancellationToken cancellationToken = default(CancellationToken))

src/corelib/Compute/v2_1/ServerVolumeReference.cs

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,26 @@ public class ServerVolumeReference : IHaveExtraData, IChildResource
1515
{
1616
/// <summary />
1717
[JsonIgnore]
18-
protected internal Server Server { get; set; }
18+
protected ServerReference ServerRef { get; set; }
1919

2020
/// <summary />
2121
public Identifier Id { get; set; }
2222

2323
object IServiceResource.Owner { get; set; }
2424

25+
internal void SetParent(ServerReference parent)
26+
{
27+
ServerRef = parent;
28+
}
29+
30+
void IChildResource.SetParent(object parent)
31+
{
32+
SetParent((Server)parent);
33+
}
34+
2535
void IChildResource.SetParent(string parentId)
2636
{
27-
Server = new Server { Id = parentId };
37+
SetParent(new ServerReference {Id = parentId});
2838
}
2939

3040
/// <summary />
@@ -34,7 +44,7 @@ void IChildResource.SetParent(string parentId)
3444
/// <summary />
3545
protected void AssertServerIsSet([CallerMemberName]string callerName = "")
3646
{
37-
if (Server != null)
47+
if (ServerRef != null)
3848
return;
3949

4050
throw new InvalidOperationException(string.Format($"{callerName} can only be used on instances which were constructed by the ComputeServer. Use ComputeService.{callerName} instead."));
@@ -47,8 +57,8 @@ protected void AssertServerIsSet([CallerMemberName]string callerName = "")
4757
AssertServerIsSet();
4858

4959
var compute = this.GetOwnerOrThrow<ComputeApiBuilder>();
50-
var result = await compute.GetServerVolumeAsync<ServerVolume>(Server.Id, Id, cancellationToken);
51-
result.Server = Server;
60+
var result = await compute.GetServerVolumeAsync<ServerVolume>(ServerRef.Id, Id, cancellationToken);
61+
result.ServerRef = ServerRef;
5262
return result;
5363
}
5464

@@ -59,11 +69,15 @@ protected void AssertServerIsSet([CallerMemberName]string callerName = "")
5969
AssertServerIsSet();
6070

6171
var compute = this.GetOwnerOrThrow<ComputeApiBuilder>();
62-
await compute.DetachVolumeAsync(Server.Id, Id, cancellationToken);
72+
await compute.DetachVolumeAsync(ServerRef.Id, Id, cancellationToken);
6373

64-
var attachedVolume = Server.AttachedVolumes.FirstOrDefault(v => v.Id == Id);
65-
if(attachedVolume != null)
66-
Server.AttachedVolumes.Remove(attachedVolume);
74+
var server = ServerRef as Server;
75+
if (server != null)
76+
{
77+
var attachedVolume = server.AttachedVolumes.FirstOrDefault(v => v.Id == Id);
78+
if (attachedVolume != null)
79+
server.AttachedVolumes.Remove(attachedVolume);
80+
}
6781
}
6882
}
6983
}

src/corelib/Extensions/TypeExtensions.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,15 @@ public static void PropogateOwner(this IEnumerable<IServiceResource> resources,
9494
resource.PropogateOwner(owner);
9595
}
9696
}
97+
98+
/// <summary />
99+
public static void SetParent(this IEnumerable<IChildResource> resources, string parentId)
100+
{
101+
foreach (var resource in resources)
102+
{
103+
resource.SetParent(parentId);
104+
}
105+
}
97106
}
98107

99108
}

src/corelib/Serialization/IServiceResource.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ public interface IServiceResource
2020
/// </summary>
2121
public interface IChildResource : IServiceResource
2222
{
23+
/// <summary>
24+
/// Called after deserialization to bootstrap a link from the child back to the parent resource.
25+
/// </summary>
26+
void SetParent(object parent);
27+
2328
/// <summary>
2429
/// Called after deserialization to bootstrap a link from the child back to the parent resource.
2530
/// </summary>

src/testing/integration/Compute/v2_1/ServerTests.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ public async Task ServerVolumesTest()
310310
Trace.WriteLine($"Created server named: {server.Name}");
311311

312312
Trace.WriteLine("Creating a test volume...");
313-
var volume = _testData.BlockStorage.CreateVolume();
313+
var volume = await _testData.CreateVolume();
314314
Identifier volumeId = volume.Id;
315315
_testData.BlockStorage.StorageProvider.WaitForVolumeAvailable(volumeId);
316316

@@ -327,12 +327,12 @@ public async Task ServerVolumesTest()
327327
Assert.NotNull(attachedVolume);
328328

329329
Trace.WriteLine("Retrieving attached volume details...");
330-
attachedVolume = await volumeRef.GetServerVolumeAsync();
331-
Assert.Equal(volumeId, attachedVolume.VolumeId);
332-
Assert.Equal(server.Id, attachedVolume.ServerId);
330+
var serverVolume = await volumeRef.GetServerVolumeAsync();
331+
Assert.Equal(volumeId, serverVolume.VolumeId);
332+
Assert.Equal(server.Id, serverVolume.ServerId);
333333

334334
Trace.WriteLine("Detaching the volume...");
335-
await attachedVolume.DetachAsync();
335+
await volumeRef.DetachAsync();
336336
Assert.False(server.AttachedVolumes.Any(v => v.Id == volumeId));
337337
_testData.BlockStorage.StorageProvider.WaitForVolumeAvailable(volumeId);
338338
}

0 commit comments

Comments
 (0)