Skip to content

Commit

Permalink
samples(adt): add a sample for using a custom twin type for serializa…
Browse files Browse the repository at this point in the history
…tion (#12432)
  • Loading branch information
David R. Williamson authored Jun 2, 2020
1 parent db2ea3a commit 188cec8
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public async Task RunSamplesAsync()

string componentModelId = await GetUniqueModelIdAsync(SamplesConstants.TemporaryComponentModelPrefix, DigitalTwinsClient).ConfigureAwait(false);
string modelId = await GetUniqueModelIdAsync(SamplesConstants.TemporaryModelPrefix, DigitalTwinsClient).ConfigureAwait(false);
string dtId = await GetUniqueTwinIdAsync(SamplesConstants.TemporaryTwinPrefix, DigitalTwinsClient).ConfigureAwait(false);
string dtId1 = await GetUniqueTwinIdAsync(SamplesConstants.TemporaryTwinPrefix, DigitalTwinsClient).ConfigureAwait(false);

string newComponentModelPayload = SamplesConstants.TemporaryComponentModelPayload
.Replace(SamplesConstants.ComponentId, componentModelId);
Expand All @@ -51,7 +51,10 @@ public async Task RunSamplesAsync()

// Create digital twin with Component payload using the BasicDigitalTwin serialization helper

var basicDigitalTwin = new BasicDigitalTwin();
var basicDigitalTwin = new BasicDigitalTwin
{
Id = dtId1
};
basicDigitalTwin.Metadata.ModelId = modelId;
basicDigitalTwin.CustomProperties.Add("Prop1", "Value1");
basicDigitalTwin.CustomProperties.Add("Prop2", "Value2");
Expand All @@ -63,40 +66,71 @@ public async Task RunSamplesAsync()

basicDigitalTwin.CustomProperties.Add("Component1", componentMetadata);

string dtPayload = JsonSerializer.Serialize(basicDigitalTwin, new JsonSerializerOptions { IgnoreNullValues = true });
string dt1Payload = JsonSerializer.Serialize(basicDigitalTwin);

Response<string> createDtResponse = await DigitalTwinsClient.CreateDigitalTwinAsync(dtId, dtPayload).ConfigureAwait(false);
Console.WriteLine($"Created digital twin {dtId} with response {createDtResponse.GetRawResponse().Status}.");
Response<string> createDt1Response = await DigitalTwinsClient.CreateDigitalTwinAsync(dtId1, dt1Payload).ConfigureAwait(false);
Console.WriteLine($"Created digital twin {dtId1} with response {createDt1Response.GetRawResponse().Status}.");

#endregion Snippet:DigitalTwinsSampleCreateBasicTwin

#region Snippet:DigitalTwinsSampleCreateCustomTwin

string dtId2 = await GetUniqueTwinIdAsync(SamplesConstants.TemporaryTwinPrefix, DigitalTwinsClient).ConfigureAwait(false);
var customDigitalTwin = new CustomDigitalTwin
{
Id = dtId2,
Metadata = new CustomDigitalTwinMetadata { ModelId = modelId },
Prop1 = "Prop1 val",
Prop2 = "Prop2 val",
Component1 = new Component1
{
Metadata = new Component1Metadata { ModelId = componentModelId },
ComponentProp1 = "Component prop1 val",
ComponentProp2 = "Component prop2 val",
}
};
string dt2Payload = JsonSerializer.Serialize(customDigitalTwin);

Response<string> createDt2Response = await DigitalTwinsClient.CreateDigitalTwinAsync(dtId2, dt2Payload).ConfigureAwait(false);
Console.WriteLine($"Created digital twin {dtId2} with response {createDt2Response.GetRawResponse().Status}.");

#endregion Snippet:DigitalTwinsSampleCreateCustomTwin

#region Snippet:DigitalTwinsSampleUpdateComponent

// Update Component1 by replacing the property ComponentProp1 value
var componentUpdateUtility = new UpdateOperationsUtility();
componentUpdateUtility.AppendReplaceOp("/ComponentProp1", "Some new value");
string updatePayload = componentUpdateUtility.Serialize();

Response<string> response = await DigitalTwinsClient.UpdateComponentAsync(dtId, "Component1", updatePayload);
Response<string> response = await DigitalTwinsClient.UpdateComponentAsync(dtId1, "Component1", updatePayload);

Console.WriteLine($"Updated component for digital twin {dtId}. Update response status: {response.GetRawResponse().Status}");
Console.WriteLine($"Updated component for digital twin {dtId1}. Update response status: {response.GetRawResponse().Status}");

#endregion Snippet:DigitalTwinsSampleUpdateComponent

// Get Component

#region Snippet:DigitalTwinsSampleGetComponent

response = await DigitalTwinsClient.GetComponentAsync(dtId, SamplesConstants.ComponentPath).ConfigureAwait(false);
response = await DigitalTwinsClient.GetComponentAsync(dtId1, SamplesConstants.ComponentPath).ConfigureAwait(false);

Console.WriteLine($"Get component for digital twin: \n{response.Value}. Get response status: {response.GetRawResponse().Status}");

#endregion Snippet:DigitalTwinsSampleGetComponent

// Now delete a Twin
await DigitalTwinsClient.DeleteDigitalTwinAsync(dtId).ConfigureAwait(false);
// Clean up

try
{
await DigitalTwinsClient.DeleteDigitalTwinAsync(dtId1).ConfigureAwait(false);
await DigitalTwinsClient.DeleteDigitalTwinAsync(dtId2).ConfigureAwait(false);
}
catch (RequestFailedException ex)
{
Console.WriteLine($"Failed to delete digital twin due to {ex}");
}

// Delete models
try
{
await DigitalTwinsClient.DeleteModelAsync(modelId).ConfigureAwait(false);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Text.Json.Serialization;
using Azure.DigitalTwins.Core.Serialization;

namespace Azure.DigitalTwins.Core.Samples
{
/// <summary>
/// Custom type for a sample illustrating how someone can create their own class to match a digital twin model type
/// for serialization, instead of using <see cref="BasicDigitalTwin"/>.
/// </summary>
internal class CustomDigitalTwin
{
[JsonPropertyName("$dtId")]
public string Id { get; set; }

[JsonPropertyName("$metadata")]
public CustomDigitalTwinMetadata Metadata { get; set; }

[JsonPropertyName("Prop1")]
public string Prop1 { get; set; }

[JsonPropertyName("Prop2")]
public string Prop2 { get; set; }

[JsonPropertyName("Component1")]
public Component1 Component1 { get; set; }
}

internal class Component1
{
[JsonPropertyName("$metadata")]
public Component1Metadata Metadata { get; set; }

[JsonPropertyName("ComponentProp1")]
public string ComponentProp1 { get; set; }

[JsonPropertyName("ComponentProp2")]
public string ComponentProp2 { get; set; }
}

internal class Metadata
{
[JsonPropertyName("$model")]
public string ModelId { get; set; }
}

internal class CustomDigitalTwinMetadata : Metadata
{
[JsonPropertyName("Prop1")]
public WritableProperty Prop1 { get; set; }

[JsonPropertyName("Prop2")]
public WritableProperty Prop2 { get; set; }
}

internal class Component1Metadata : Metadata
{
[JsonPropertyName("ComponentProp1")]
public WritableProperty ComponentProp1 { get; set; }

[JsonPropertyName("ComponentProp2")]
public WritableProperty ComponentProp2 { get; set; }
}
}
44 changes: 37 additions & 7 deletions sdk/digitaltwins/Azure.DigitalTwins.Core/samples/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,16 @@ catch (Exception ex)

For Creating Twin you will need to provide Id of a digital Twin such as `myTwin` and the application/json digital twin based on the model created earlier. You can look at sample application/json [here](https://github.com/Azure/azure-sdk-for-net-pr/tree/feature/IoT-ADT/sdk/digitaltwins/Azure.DigitalTwins.Core/samples/DigitalTwinsClientSample/DTDL/DigitalTwins "DigitalTwin").

One option is to use the provided class BasicDigitalTwin for serialization and deserialization.
It uses functionality from the `System.Text.Json` library to maintain any unmapped json properties to a dictionary.

```C# Snippet:DigitalTwinsSampleCreateBasicTwin
// Create digital twin with Component payload using the BasicDigitalTwin serialization helper
var basicDigitalTwin = new BasicDigitalTwin();
var basicDigitalTwin = new BasicDigitalTwin
{
Id = dtId1
};
basicDigitalTwin.Metadata.ModelId = modelId;
basicDigitalTwin.CustomProperties.Add("Prop1", "Value1");
basicDigitalTwin.CustomProperties.Add("Prop2", "Value2");
Expand All @@ -136,10 +142,34 @@ componentMetadata.CustomProperties.Add("ComponentProp2", "ComponentValue2");

basicDigitalTwin.CustomProperties.Add("Component1", componentMetadata);

string dtPayload = JsonSerializer.Serialize(basicDigitalTwin, new JsonSerializerOptions { IgnoreNullValues = true });
string dt1Payload = JsonSerializer.Serialize(basicDigitalTwin);

Response<string> createDt1Response = await DigitalTwinsClient.CreateDigitalTwinAsync(dtId1, dt1Payload).ConfigureAwait(false);
Console.WriteLine($"Created digital twin {dtId1} with response {createDt1Response.GetRawResponse().Status}.");
```

For known twin model types, it may be best to create your own class that maps all the properties.
This makes your code more readable, and properties easier to work with.

```C# Snippet:DigitalTwinsSampleCreateCustomTwin
string dtId2 = await GetUniqueTwinIdAsync(SamplesConstants.TemporaryTwinPrefix, DigitalTwinsClient).ConfigureAwait(false);
var customDigitalTwin = new CustomDigitalTwin
{
Id = dtId2,
Metadata = new CustomDigitalTwinMetadata { ModelId = modelId },
Prop1 = "Prop1 val",
Prop2 = "Prop2 val",
Component1 = new Component1
{
Metadata = new Component1Metadata { ModelId = componentModelId },
ComponentProp1 = "Component prop1 val",
ComponentProp2 = "Component prop2 val",
}
};
string dt2Payload = JsonSerializer.Serialize(customDigitalTwin);

Response<string> createDtResponse = await DigitalTwinsClient.CreateDigitalTwinAsync(dtId, dtPayload).ConfigureAwait(false);
Console.WriteLine($"Created digital twin {dtId} with response {createDtResponse.GetRawResponse().Status}.");
Response<string> createDt2Response = await DigitalTwinsClient.CreateDigitalTwinAsync(dtId2, dt2Payload).ConfigureAwait(false);
Console.WriteLine($"Created digital twin {dtId2} with response {createDt2Response.GetRawResponse().Status}.");
```

### Query Digital Twin
Expand Down Expand Up @@ -212,17 +242,17 @@ var componentUpdateUtility = new UpdateOperationsUtility();
componentUpdateUtility.AppendReplaceOp("/ComponentProp1", "Some new value");
string updatePayload = componentUpdateUtility.Serialize();

Response<string> response = await DigitalTwinsClient.UpdateComponentAsync(dtId, "Component1", updatePayload);
Response<string> response = await DigitalTwinsClient.UpdateComponentAsync(dtId1, "Component1", updatePayload);

Console.WriteLine($"Updated component for digital twin {dtId}. Update response status: {response.GetRawResponse().Status}");
Console.WriteLine($"Updated component for digital twin {dtId1}. Update response status: {response.GetRawResponse().Status}");
```

### Get Digital Twin Component

Get a component by providing name of a component and id of digital twin it belongs to.

```C# Snippet:DigitalTwinsSampleGetComponent
response = await DigitalTwinsClient.GetComponentAsync(dtId, SamplesConstants.ComponentPath).ConfigureAwait(false);
response = await DigitalTwinsClient.GetComponentAsync(dtId1, SamplesConstants.ComponentPath).ConfigureAwait(false);

Console.WriteLine($"Get component for digital twin: \n{response.Value}. Get response status: {response.GetRawResponse().Status}");
```
Expand Down
42 changes: 21 additions & 21 deletions sdk/digitaltwins/Azure.DigitalTwins.Core/src/DigitalTwinsClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,25 +144,25 @@ public virtual Response<string> GetDigitalTwin(string digitalTwinId, Cancellatio
/// <returns>The created application/json digital twin and the http response.</returns>
/// <remarks>The digital twin must be the serialization of an instance of <see cref="Serialization.BasicDigitalTwin"/> or the serialization of an extension of that type.</remarks>
/// <example>
/// <code snippet="Snippet:DigitalTwinsSampleCreateBasicTwin">
/// // Create digital twin with Component payload using the BasicDigitalTwin serialization helper
///
/// var basicDigitalTwin = new BasicDigitalTwin();
/// basicDigitalTwin.Metadata.ModelId = modelId;
/// basicDigitalTwin.CustomProperties.Add(&quot;Prop1&quot;, &quot;Value1&quot;);
/// basicDigitalTwin.CustomProperties.Add(&quot;Prop2&quot;, &quot;Value2&quot;);
///
/// var componentMetadata = new ModelProperties();
/// componentMetadata.Metadata.ModelId = componentModelId;
/// componentMetadata.CustomProperties.Add(&quot;ComponentProp1&quot;, &quot;ComponentValue1&quot;);
/// componentMetadata.CustomProperties.Add(&quot;ComponentProp2&quot;, &quot;ComponentValue2&quot;);
///
/// basicDigitalTwin.CustomProperties.Add(&quot;Component1&quot;, componentMetadata);
///
/// string dtPayload = JsonSerializer.Serialize(basicDigitalTwin, new JsonSerializerOptions { IgnoreNullValues = true });
/// <code snippet="Snippet:DigitalTwinsSampleCreateCustomTwin">
/// string dtId2 = await GetUniqueTwinIdAsync(SamplesConstants.TemporaryTwinPrefix, DigitalTwinsClient).ConfigureAwait(false);
/// var customDigitalTwin = new CustomDigitalTwin
/// {
/// Id = dtId2,
/// Metadata = new CustomDigitalTwinMetadata { ModelId = modelId },
/// Prop1 = &quot;Prop1 val&quot;,
/// Prop2 = &quot;Prop2 val&quot;,
/// Component1 = new Component1
/// {
/// Metadata = new Component1Metadata { ModelId = componentModelId },
/// ComponentProp1 = &quot;Component prop1 val&quot;,
/// ComponentProp2 = &quot;Component prop2 val&quot;,
/// }
/// };
/// string dt2Payload = JsonSerializer.Serialize(customDigitalTwin);
///
/// Response&lt;string&gt; createDtResponse = await DigitalTwinsClient.CreateDigitalTwinAsync(dtId, dtPayload).ConfigureAwait(false);
/// Console.WriteLine($&quot;Created digital twin {dtId} with response {createDtResponse.GetRawResponse().Status}.&quot;);
/// Response&lt;string&gt; createDt2Response = await DigitalTwinsClient.CreateDigitalTwinAsync(dtId2, dt2Payload).ConfigureAwait(false);
/// Console.WriteLine($&quot;Created digital twin {dtId2} with response {createDt2Response.GetRawResponse().Status}.&quot;);
/// </code>
/// </example>
public virtual Task<Response<string>> CreateDigitalTwinAsync(string digitalTwinId, string digitalTwin, CancellationToken cancellationToken = default)
Expand Down Expand Up @@ -254,7 +254,7 @@ public virtual Response<string> UpdateDigitalTwin(string digitalTwinId, string d
/// <returns>Json string representation of the component corresponding to the provided componentPath and the HTTP response.</returns>
/// <example>
/// <code snippet="Snippet:DigitalTwinsSampleGetComponent">
/// response = await DigitalTwinsClient.GetComponentAsync(dtId, SamplesConstants.ComponentPath).ConfigureAwait(false);
/// response = await DigitalTwinsClient.GetComponentAsync(dtId1, SamplesConstants.ComponentPath).ConfigureAwait(false);
///
/// Console.WriteLine($&quot;Get component for digital twin: \n{response.Value}. Get response status: {response.GetRawResponse().Status}&quot;);
/// </code>
Expand Down Expand Up @@ -292,9 +292,9 @@ public virtual Response<string> GetComponent(string digitalTwinId, string compon
/// componentUpdateUtility.AppendReplaceOp(&quot;/ComponentProp1&quot;, &quot;Some new value&quot;);
/// string updatePayload = componentUpdateUtility.Serialize();
///
/// Response&lt;string&gt; response = await DigitalTwinsClient.UpdateComponentAsync(dtId, &quot;Component1&quot;, updatePayload);
/// Response&lt;string&gt; response = await DigitalTwinsClient.UpdateComponentAsync(dtId1, &quot;Component1&quot;, updatePayload);
///
/// Console.WriteLine($&quot;Updated component for digital twin {dtId}. Update response status: {response.GetRawResponse().Status}&quot;);
/// Console.WriteLine($&quot;Updated component for digital twin {dtId1}. Update response status: {response.GetRawResponse().Status}&quot;);
/// </code>
/// </example>
public virtual Task<Response<string>> UpdateComponentAsync(string digitalTwinId, string componentPath, string componentUpdateOperations, RequestOptions requestOptions = default, CancellationToken cancellationToken = default)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ namespace Azure.DigitalTwins.Core.Serialization
/// <code snippet="Snippet:DigitalTwinsSampleCreateBasicTwin">
/// // Create digital twin with Component payload using the BasicDigitalTwin serialization helper
///
/// var basicDigitalTwin = new BasicDigitalTwin();
/// var basicDigitalTwin = new BasicDigitalTwin
/// {
/// Id = dtId1
/// };
/// basicDigitalTwin.Metadata.ModelId = modelId;
/// basicDigitalTwin.CustomProperties.Add(&quot;Prop1&quot;, &quot;Value1&quot;);
/// basicDigitalTwin.CustomProperties.Add(&quot;Prop2&quot;, &quot;Value2&quot;);
Expand All @@ -24,10 +27,10 @@ namespace Azure.DigitalTwins.Core.Serialization
///
/// basicDigitalTwin.CustomProperties.Add(&quot;Component1&quot;, componentMetadata);
///
/// string dtPayload = JsonSerializer.Serialize(basicDigitalTwin, new JsonSerializerOptions { IgnoreNullValues = true });
/// string dt1Payload = JsonSerializer.Serialize(basicDigitalTwin);
///
/// Response&lt;string&gt; createDtResponse = await DigitalTwinsClient.CreateDigitalTwinAsync(dtId, dtPayload).ConfigureAwait(false);
/// Console.WriteLine($&quot;Created digital twin {dtId} with response {createDtResponse.GetRawResponse().Status}.&quot;);
/// Response&lt;string&gt; createDt1Response = await DigitalTwinsClient.CreateDigitalTwinAsync(dtId1, dt1Payload).ConfigureAwait(false);
/// Console.WriteLine($&quot;Created digital twin {dtId1} with response {createDt1Response.GetRawResponse().Status}.&quot;);
/// </code>
/// </example>
public class BasicDigitalTwin : ModelProperties
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,18 @@ namespace Azure.DigitalTwins.Core.Serialization
{
/// <summary>
/// An optional, helper class for deserializing a digital twin.
/// The $metadata class on a <see cref="BasicDigitalTwin"/>.
/// The $metadata class on a <see cref="BasicDigitalTwin"/> and <see cref="ModelProperties"/>.
/// </summary>
public class DigitalTwinMetadata
{
/// <summary>
/// The Id of the model that this digital twin is modeled by.
/// The Id of the model that the digital twin or component is modeled by.
/// </summary>
[JsonPropertyName("$model")]
public string ModelId { get; set; }

/// <summary>
/// Additional, model-defined properties.
/// Model-defined writable properties' request state.
/// </summary>
/// <remarks>For your convenience, the value of each dictionary object can be turned into an instance of <see cref="WritableProperty"/>.</remarks>
[JsonExtensionData]
Expand Down
Loading

0 comments on commit 188cec8

Please sign in to comment.