public async Task DigitalTwins_Lifecycle() { DigitalTwinsClient client = GetClient(); string roomTwinId = await GetUniqueTwinIdAsync(client, TestAssetSettings.RoomTwinIdPrefix).ConfigureAwait(false); string floorModelId = await GetUniqueModelIdAsync(client, TestAssetSettings.FloorModelIdPrefix).ConfigureAwait(false); string roomModelId = await GetUniqueModelIdAsync(client, TestAssetSettings.RoomModelIdPrefix).ConfigureAwait(false); try { // arrange // create room model string roomModel = TestAssetsHelper.GetRoomModelPayload(roomModelId, floorModelId); await client.CreateModelsAsync(new List <string> { roomModel }).ConfigureAwait(false); // act // create room twin string roomTwin = TestAssetsHelper.GetRoomTwinPayload(roomModelId); await client.CreateDigitalTwinAsync(roomTwinId, roomTwin).ConfigureAwait(false); // get twin await client.GetDigitalTwinAsync(roomTwinId).ConfigureAwait(false); // update twin string updateTwin = TestAssetsHelper.GetRoomTwinUpdatePayload(); await client.UpdateDigitalTwinAsync(roomTwinId, updateTwin).ConfigureAwait(false); // delete a twin await client.DeleteDigitalTwinAsync(roomTwinId).ConfigureAwait(false); // assert Func <Task> act = async() => { await client.GetDigitalTwinAsync(roomTwinId).ConfigureAwait(false); }; act.Should().Throw <RequestFailedException>() .And.Status.Should().Be((int)HttpStatusCode.NotFound); } finally { // cleanup try { if (!string.IsNullOrWhiteSpace(roomModelId)) { await client.DeleteModelAsync(roomModelId).ConfigureAwait(false); } } catch (Exception ex) { Assert.Fail($"Test clean up failed: {ex.Message}"); } } }
public static async Task <string> FindOrCreateTwinAsync(string dtmi, string regId, ILogger log) { // Create Digital Twins client var cred = new ManagedIdentityCredential(adtAppId); var client = new DigitalTwinsClient( new Uri(adtInstanceUrl), cred, new DigitalTwinsClientOptions { Transport = new HttpClientTransport(singletonHttpClientInstance) }); // Find existing DigitalTwin with registration ID try { // Get DigitalTwin with Id 'regId' BasicDigitalTwin existingDt = await client.GetDigitalTwinAsync <BasicDigitalTwin>(regId).ConfigureAwait(false); // Check to make sure it is of the correct model type if (StringComparer.OrdinalIgnoreCase.Equals(dtmi, existingDt.Metadata.ModelId)) { log.LogInformation($"DigitalTwin {existingDt.Id} already exists"); return(existingDt.Id); } // Found DigitalTwin but it is not of the correct model type log.LogInformation($"Found DigitalTwin {existingDt.Id} but it is not of model {dtmi}"); } catch (RequestFailedException ex) when(ex.Status == (int)HttpStatusCode.NotFound) { log.LogDebug($"Did not find DigitalTwin {regId}"); } // Either the DigitalTwin was not found, or we found it but it is of a different model type // Create or replace it with what it needs to be, meaning if it was not found a brand new DigitalTwin will be created // and if it was of a different model, it will replace that existing DigitalTwin // If it was intended to only create the DigitalTwin if there is no matching DigitalTwin with the same Id, // ETag.All could have been used as the ifNonMatch parameter to the CreateOrReplaceDigitalTwinAsync method call. // Read more in the CreateOrReplaceDigitalTwinAsync documentation here: // https://docs.microsoft.com/en-us/dotnet/api/azure.digitaltwins.core.digitaltwinsclient.createorreplacedigitaltwinasync?view=azure-dotnet BasicDigitalTwin dt = await client.CreateOrReplaceDigitalTwinAsync( regId, new BasicDigitalTwin { Metadata = { ModelId = dtmi }, Contents = { { "Temperature", 0.0 } } } ).ConfigureAwait(false); log.LogInformation($"Digital Twin {dt.Id} created."); return(dt.Id); }
// </UpdateRelationshipMethod> // <FetchAndPrintMethod> private static async Task CustomMethod_FetchAndPrintTwinAsync(string twin_Id, DigitalTwinsClient client) { Response <BasicDigitalTwin> res = await client.GetDigitalTwinAsync <BasicDigitalTwin>(twin_Id); // <UseFindOutgoingRelationships> await CustomMethod_FindOutgoingRelationshipsAsync(client, twin_Id); // </UseFindOutgoingRelationships> // <UseFindIncomingRelationships> await CustomMethod_FindIncomingRelationshipsAsync(client, twin_Id); // </UseFindIncomingRelationships> return; }
public void DigitalTwins_TwinNotExist_ThrowsNotFoundException() { // arrange DigitalTwinsClient client = GetClient(); // act Func <Task> act = async() => { await client.GetDigitalTwinAsync <BasicDigitalTwin>("someNonExistantTwin").ConfigureAwait(false); }; // assert act.Should().Throw <RequestFailedException>() .And.Status.Should().Be((int)HttpStatusCode.NotFound); }
public void DigitalTwins_IncorrectCredentials_ThrowsUnauthorizedException() { // arrange DigitalTwinsClient unauthorizedClient = GetFakeClient(); // act Func <Task> act = async() => { await unauthorizedClient.GetDigitalTwinAsync <BasicDigitalTwin>("someNonExistantTwin").ConfigureAwait(false); }; // assert act.Should().Throw <RequestFailedException>() .And.Status.Should().Be((int)HttpStatusCode.Unauthorized); }
private static async Task PropagateStateToAzureMapsAsync(string capabilityTwinId, string capabilityName, JToken capabilityValue, ILogger log) { // Find parent to which the capability applies, using isCapabilityOf relationships List <string> parentTwinIds = new List <string>(); AsyncPageable <BasicRelationship> rels = dtClient.GetRelationshipsAsync <BasicRelationship>(capabilityTwinId, "isCapabilityOf"); await foreach (BasicRelationship relationship in rels) { parentTwinIds.Add(relationship.TargetId); } if (parentTwinIds.Count != 1) { log.LogInformation($"Capability twin {capabilityTwinId} isn't assigned to exactly one parent."); return; } string parentTwinId = parentTwinIds.First(); Response <BasicDigitalTwin> parentTwinResponse = await dtClient.GetDigitalTwinAsync <BasicDigitalTwin>(parentTwinId); BasicDigitalTwin parentTwin = parentTwinResponse.Value; // TODO: Based on parent twin, get Azure Maps Feature ID (either from ADT model using externalIds property, or by querying Azure Maps feature service ) string AzureMapsFeatureID = "NONEXISTENT_FIXME"; // Load the Azure Maps stateset ID (configured in an env. variable JSON fragment) that corresponds to this capability name if (!azureMapsStateSets.ContainsKey(capabilityName)) { log.LogInformation($"There is no configured Azure Maps stateset supporting the capability type {capabilityName}."); return; } string statesetID = azureMapsStateSets[capabilityName].ToString(); // Update the maps feature stateset JObject postcontent = new JObject( new JProperty( "States", new JArray( new JObject( new JProperty("keyName", capabilityName), new JProperty("value", capabilityValue.ToString()), new JProperty("eventTimestamp", DateTime.UtcNow.ToString("s")))))); var response = await httpClient.PostAsync( $"https://us.atlas.microsoft.com/featureState/state?api-version=1.0&statesetID={statesetID}&featureID={AzureMapsFeatureID}&subscription-key={azureMapsSubscriptionKey}", new StringContent(postcontent.ToString())); log.LogInformation($"Azure Maps response:\n\t" + await response.Content.ReadAsStringAsync()); }
public async Task DigitalTwinOperationsWithCustomObjectSerializer_Succeeds() { // arrange var serializer = new TestObjectSerializer(); DigitalTwinsClientOptions options = new DigitalTwinsClientOptions { Serializer = serializer }; DigitalTwinsClient client = GetClient(options); serializer.WasDeserializeCalled.Should().BeFalse(); serializer.WasSerializeCalled.Should().BeFalse(); string roomTwinId = await GetUniqueTwinIdAsync(client, TestAssetDefaults.RoomTwinIdPrefix).ConfigureAwait(false); string floorModelId = await GetUniqueModelIdAsync(client, TestAssetDefaults.FloorModelIdPrefix).ConfigureAwait(false); string roomModelId = await GetUniqueModelIdAsync(client, TestAssetDefaults.RoomModelIdPrefix).ConfigureAwait(false); // create room model string roomModel = TestAssetsHelper.GetRoomModelPayload(roomModelId, floorModelId); await CreateAndListModelsAsync(client, new List <string> { roomModel }).ConfigureAwait(false); // act // create room twin BasicDigitalTwin roomTwin = TestAssetsHelper.GetRoomTwinPayload(roomModelId); await client.CreateOrReplaceDigitalTwinAsync(roomTwinId, roomTwin).ConfigureAwait(false); roomTwin = await client.GetDigitalTwinAsync <BasicDigitalTwin>(roomTwinId).ConfigureAwait(false); // assert roomTwin.Should().NotBeNull(); serializer.WasDeserializeCalled.Should().BeTrue(); serializer.WasSerializeCalled.Should().BeTrue(); }
private static async Task <BasicDigitalTwin> FetchAndPrintTwinAsync(string twinId, DigitalTwinsClient client) { // <GetTwin> BasicDigitalTwin twin; // <GetTwinCall> Response <BasicDigitalTwin> twinResponse = await client.GetDigitalTwinAsync(twinId); twin = twinResponse.Value; // </GetTwinCall> Console.WriteLine($"Model id: {twin.Metadata.ModelId}"); foreach (string prop in twin.Contents.Keys) { if (twin.Contents.TryGetValue(prop, out object value)) { Console.WriteLine($"Property '{prop}': {value}"); } } // </GetTwin> return(twin); }
public static async Task GetTwin(DigitalTwinsClient client) { Console.Write("twinId: "); string twinId = Console.ReadLine(); try { Response <string> res = await client.GetDigitalTwinAsync(twinId); if (res != null) { Console.WriteLine(Program.PrettifyJson(res.Value.ToString())); } } catch (RequestFailedException e) { Console.WriteLine($"Error {e.Status}: {e.Message}"); } catch (Exception ex) { Console.WriteLine($"Error: {ex}"); } }
public async Task DigitalTwins_PatchTwinFailsIfInvalidETagProvided() { DigitalTwinsClient client = GetClient(); string roomTwinId = await GetUniqueTwinIdAsync(client, TestAssetDefaults.RoomTwinIdPrefix).ConfigureAwait(false); string floorModelId = await GetUniqueModelIdAsync(client, TestAssetDefaults.FloorModelIdPrefix).ConfigureAwait(false); string roomModelId = await GetUniqueModelIdAsync(client, TestAssetDefaults.RoomModelIdPrefix).ConfigureAwait(false); try { // arrange // create room model string roomModel = TestAssetsHelper.GetRoomModelPayload(roomModelId, floorModelId); await client.CreateModelsAsync(new List <string> { roomModel }).ConfigureAwait(false); // act // create room twin BasicDigitalTwin roomTwin = TestAssetsHelper.GetRoomTwinPayload(roomModelId); await client.CreateOrReplaceDigitalTwinAsync <BasicDigitalTwin>(roomTwinId, roomTwin).ConfigureAwait(false); // get twin ETag?etagBeforeUpdate = (await client.GetDigitalTwinAsync <BasicDigitalTwin>(roomTwinId).ConfigureAwait(false)).Value.ETag; Assert.IsNotNull(etagBeforeUpdate); // update twin once to make the previous etag fall out of date JsonPatchDocument updateTwinPatchDocument = new JsonPatchDocument(); updateTwinPatchDocument.AppendAdd("/Humidity", 30); updateTwinPatchDocument.AppendReplace("/Temperature", 70); updateTwinPatchDocument.AppendRemove("/EmployeeId"); await client.UpdateDigitalTwinAsync(roomTwinId, updateTwinPatchDocument, ETag.All).ConfigureAwait(false); // update twin again, but with an out of date etag, which should cause a 412 from service JsonPatchDocument secondUpdateTwinPatchDocument = new JsonPatchDocument(); secondUpdateTwinPatchDocument.AppendReplace("/Humidity", 80); Func <Task> act = async() => { await client.UpdateDigitalTwinAsync(roomTwinId, secondUpdateTwinPatchDocument, etagBeforeUpdate).ConfigureAwait(false); }; act.Should().Throw <RequestFailedException>() .And.Status.Should().Be((int)HttpStatusCode.PreconditionFailed); } finally { // cleanup try { if (!string.IsNullOrWhiteSpace(roomModelId)) { await client.DeleteModelAsync(roomModelId).ConfigureAwait(false); } } catch (Exception ex) { Assert.Fail($"Test clean up failed: {ex.Message}"); } } }
public async Task Component_UpdateComponentFailsWhenIfMatchHeaderOutOfDate() { // arrange DigitalTwinsClient client = GetClient(); string wifiModelId = await GetUniqueModelIdAsync(client, TestAssetDefaults.WifiModelIdPrefix).ConfigureAwait(false); string roomWithWifiModelId = await GetUniqueModelIdAsync(client, TestAssetDefaults.RoomWithWifiModelIdPrefix).ConfigureAwait(false); string roomWithWifiTwinId = await GetUniqueTwinIdAsync(client, TestAssetDefaults.RoomWithWifiTwinIdPrefix).ConfigureAwait(false); string wifiComponentName = "wifiAccessPoint"; try { // CREATE // create roomWithWifi model string wifiModel = TestAssetsHelper.GetWifiModelPayload(wifiModelId); // create wifi model string roomWithWifiModel = TestAssetsHelper.GetRoomWithWifiModelPayload(roomWithWifiModelId, wifiModelId, wifiComponentName); await CreateAndListModelsAsync(client, new List <string> { roomWithWifiModel, wifiModel }).ConfigureAwait(false); // create room digital twin BasicDigitalTwin roomWithWifiTwin = TestAssetsHelper.GetRoomWithWifiTwinPayload(roomWithWifiModelId, wifiComponentName); await client.CreateOrReplaceDigitalTwinAsync <BasicDigitalTwin>(roomWithWifiTwinId, roomWithWifiTwin); // Get the component Response <object> getComponentResponse = await client .GetComponentAsync <object>( roomWithWifiTwinId, wifiComponentName) .ConfigureAwait(false); ETag?etagBeforeUpdate = (await client.GetDigitalTwinAsync <BasicDigitalTwin>(roomWithWifiTwinId)).Value.ETag; // Patch component JsonPatchDocument componentUpdatePatchDocument = new JsonPatchDocument(); componentUpdatePatchDocument.AppendReplace("/Network", "New Network"); Response updateComponentResponse = await client .UpdateComponentAsync( roomWithWifiTwinId, wifiComponentName, componentUpdatePatchDocument) .ConfigureAwait(false); // Patch component again, but with the now out of date ETag JsonPatchDocument secondComponentUpdatePatchDocument = new JsonPatchDocument(); secondComponentUpdatePatchDocument.AppendReplace("/Network", "Even newer Network"); Func <Task> act = async() => { await client .UpdateComponentAsync( roomWithWifiTwinId, wifiComponentName, secondComponentUpdatePatchDocument, etagBeforeUpdate) .ConfigureAwait(false); }; act.Should().Throw <RequestFailedException>() .And.Status.Should().Be((int)HttpStatusCode.PreconditionFailed); } catch (Exception ex) { Assert.Fail($"Failure in executing a step in the test case: {ex.Message}."); } finally { // clean up try { if (!string.IsNullOrWhiteSpace(roomWithWifiTwinId)) { await client.DeleteDigitalTwinAsync(roomWithWifiTwinId).ConfigureAwait(false); } if (!string.IsNullOrWhiteSpace(roomWithWifiModelId)) { await client.DeleteModelAsync(roomWithWifiModelId).ConfigureAwait(false); } if (!string.IsNullOrWhiteSpace(wifiModelId)) { await client.DeleteModelAsync(wifiModelId).ConfigureAwait(false); } } catch (Exception ex) { Assert.Fail($"Test clean up failed: {ex.Message}"); } } }
public async Task DigitalTwins_Lifecycle() { DigitalTwinsClient client = GetClient(); string roomTwinId = await GetUniqueTwinIdAsync(client, TestAssetDefaults.RoomTwinIdPrefix).ConfigureAwait(false); string floorModelId = await GetUniqueModelIdAsync(client, TestAssetDefaults.FloorModelIdPrefix).ConfigureAwait(false); string roomModelId = await GetUniqueModelIdAsync(client, TestAssetDefaults.RoomModelIdPrefix).ConfigureAwait(false); try { // arrange // create room model string roomModel = TestAssetsHelper.GetRoomModelPayload(roomModelId, floorModelId); await client.CreateModelsAsync(new List <string> { roomModel }).ConfigureAwait(false); // act // create room twin BasicDigitalTwin roomTwin = TestAssetsHelper.GetRoomTwinPayload(roomModelId); await client.CreateOrReplaceDigitalTwinAsync <BasicDigitalTwin>(roomTwinId, roomTwin).ConfigureAwait(false); // get twin await client.GetDigitalTwinAsync <BasicDigitalTwin>(roomTwinId).ConfigureAwait(false); // update twin JsonPatchDocument updateTwinPatchDocument = new JsonPatchDocument(); updateTwinPatchDocument.AppendAdd("/Humidity", 30); updateTwinPatchDocument.AppendReplace("/Temperature", 70); updateTwinPatchDocument.AppendRemove("/EmployeeId"); var requestOptions = new UpdateDigitalTwinOptions { IfMatch = "*" }; await client.UpdateDigitalTwinAsync(roomTwinId, updateTwinPatchDocument, requestOptions).ConfigureAwait(false); // delete a twin await client.DeleteDigitalTwinAsync(roomTwinId).ConfigureAwait(false); // assert Func <Task> act = async() => { await client.GetDigitalTwinAsync <BasicDigitalTwin>(roomTwinId).ConfigureAwait(false); }; act.Should().Throw <RequestFailedException>() .And.Status.Should().Be((int)HttpStatusCode.NotFound); } finally { // cleanup try { if (!string.IsNullOrWhiteSpace(roomModelId)) { await client.DeleteModelAsync(roomModelId).ConfigureAwait(false); } } catch (Exception ex) { Assert.Fail($"Test clean up failed: {ex.Message}"); } } }
public async Task TestNewtonsoftObjectSerializerWithDigitalTwins() { DigitalTwinsClient defaultClient = GetClient(); string roomTwinId = await GetUniqueTwinIdAsync(defaultClient, TestAssetDefaults.RoomTwinIdPrefix).ConfigureAwait(false); string floorModelId = await GetUniqueModelIdAsync(defaultClient, TestAssetDefaults.FloorModelIdPrefix).ConfigureAwait(false); string roomModelId = await GetUniqueModelIdAsync(defaultClient, TestAssetDefaults.RoomModelIdPrefix).ConfigureAwait(false); try { // arrange // create room model string roomModel = TestAssetsHelper.GetRoomModelPayload(roomModelId, floorModelId); await CreateAndListModelsAsync(defaultClient, new List <string> { roomModel }).ConfigureAwait(false); // act // create room twin BasicDigitalTwin roomTwin = TestAssetsHelper.GetRoomTwinPayload(roomModelId); await defaultClient.CreateOrReplaceDigitalTwinAsync <BasicDigitalTwin>(roomTwinId, roomTwin).ConfigureAwait(false); // Create a client with NewtonsoftJsonObjectSerializer configured as the serializer. DigitalTwinsClient testClient = GetClient( new DigitalTwinsClientOptions { Serializer = new NewtonsoftJsonObjectSerializer() }); // Get digital twin using the simple DigitalTwin model annotated with Newtonsoft attributes SimpleNewtonsoftDtModel getResponse = await testClient.GetDigitalTwinAsync <SimpleNewtonsoftDtModel>(roomTwinId).ConfigureAwait(false); getResponse.Id.Should().NotBeNullOrEmpty("Digital twin ID should not be null or empty"); // Query DigitalTwins using the simple DigitalTwin model annotated with Newtonsoft attributes AsyncPageable <SimpleNewtonsoftDtModel> queryResponse = testClient.QueryAsync <SimpleNewtonsoftDtModel>("SELECT * FROM DIGITALTWINS"); await foreach (SimpleNewtonsoftDtModel twin in queryResponse) { twin.Id.Should().NotBeNullOrEmpty("Digital twin Id should not be null or empty"); } } finally { // cleanup try { // delete twin if (!string.IsNullOrWhiteSpace(roomTwinId)) { await defaultClient.DeleteDigitalTwinAsync(roomTwinId).ConfigureAwait(false); } // delete models if (!string.IsNullOrWhiteSpace(roomModelId)) { await defaultClient.DeleteModelAsync(roomModelId).ConfigureAwait(false); } } catch (Exception ex) { Assert.Fail($"Test clean up failed: {ex.Message}"); } } }
public async Task Component_UpdateComponentSucceedsWhenIfMatchHeaderIsCorrect() { // arrange DigitalTwinsClient client = GetClient(); string wifiModelId = await GetUniqueModelIdAsync(client, TestAssetDefaults.WifiModelIdPrefix).ConfigureAwait(false); string roomWithWifiModelId = await GetUniqueModelIdAsync(client, TestAssetDefaults.RoomWithWifiModelIdPrefix).ConfigureAwait(false); string roomWithWifiTwinId = await GetUniqueTwinIdAsync(client, TestAssetDefaults.RoomWithWifiTwinIdPrefix).ConfigureAwait(false); string wifiComponentName = "wifiAccessPoint"; try { // CREATE // create roomWithWifi model string wifiModel = TestAssetsHelper.GetWifiModelPayload(wifiModelId); // create wifi model string roomWithWifiModel = TestAssetsHelper.GetRoomWithWifiModelPayload(roomWithWifiModelId, wifiModelId, wifiComponentName); await client.CreateModelsAsync(new List <string> { roomWithWifiModel, wifiModel }).ConfigureAwait(false); // create room digital twin BasicDigitalTwin roomWithWifiTwin = TestAssetsHelper.GetRoomWithWifiTwinPayload(roomWithWifiModelId, wifiComponentName); await client.CreateOrReplaceDigitalTwinAsync <BasicDigitalTwin>(roomWithWifiTwinId, roomWithWifiTwin); // Get the component Response <object> getComponentResponse = await client .GetComponentAsync <object>( roomWithWifiTwinId, wifiComponentName) .ConfigureAwait(false); // Patch component JsonPatchDocument componentUpdatePatchDocument = new JsonPatchDocument(); componentUpdatePatchDocument.AppendReplace("/Network", "New Network"); Response updateComponentResponse = await client .UpdateComponentAsync( roomWithWifiTwinId, wifiComponentName, componentUpdatePatchDocument) .ConfigureAwait(false); // Get the latest ETag ETag?etagBeforeUpdate = (await client.GetDigitalTwinAsync <BasicDigitalTwin>(roomWithWifiTwinId)).Value.ETag; Assert.IsNotNull(etagBeforeUpdate); // Patch component again, but with the now out of date ETag JsonPatchDocument secondComponentUpdatePatchDocument = new JsonPatchDocument(); componentUpdatePatchDocument.AppendReplace("/Network", "Even newer Network"); try { await client .UpdateComponentAsync( roomWithWifiTwinId, wifiComponentName, secondComponentUpdatePatchDocument, etagBeforeUpdate) .ConfigureAwait(false); } catch (RequestFailedException ex) when(ex.Status == (int)HttpStatusCode.PreconditionFailed) { throw new AssertionException("UpdateComponent should not have thrown PreconditionFailed because the ETag was up to date", ex); } } finally { // clean up try { if (!string.IsNullOrWhiteSpace(roomWithWifiTwinId)) { await client.DeleteDigitalTwinAsync(roomWithWifiTwinId).ConfigureAwait(false); } if (!string.IsNullOrWhiteSpace(roomWithWifiModelId)) { await client.DeleteModelAsync(roomWithWifiModelId).ConfigureAwait(false); } if (!string.IsNullOrWhiteSpace(wifiModelId)) { await client.DeleteModelAsync(wifiModelId).ConfigureAwait(false); } } catch (Exception ex) { Assert.Fail($"Test clean up failed: {ex.Message}"); } } }
internal static async Task <string> GetUniqueTwinIdAsync(string baseName, DigitalTwinsClient client, [CallerMemberName] string caller = "") { return(await GetUniqueIdAsync(baseName, (twinId) => client.GetDigitalTwinAsync(twinId)).ConfigureAwait(false)); }
public async Task DigitalTwins_PatchTwinSucceedsIfCorrectETagProvided() { DigitalTwinsClient client = GetClient(); string roomTwinId = await GetUniqueTwinIdAsync(client, TestAssetDefaults.RoomTwinIdPrefix).ConfigureAwait(false); string floorModelId = await GetUniqueModelIdAsync(client, TestAssetDefaults.FloorModelIdPrefix).ConfigureAwait(false); string roomModelId = await GetUniqueModelIdAsync(client, TestAssetDefaults.RoomModelIdPrefix).ConfigureAwait(false); try { // arrange // create room model string roomModel = TestAssetsHelper.GetRoomModelPayload(roomModelId, floorModelId); await CreateAndListModelsAsync(client, new List <string> { roomModel }).ConfigureAwait(false); // act // create room twin BasicDigitalTwin roomTwin = TestAssetsHelper.GetRoomTwinPayload(roomModelId); await client.CreateOrReplaceDigitalTwinAsync <BasicDigitalTwin>(roomTwinId, roomTwin).ConfigureAwait(false); // update twin once JsonPatchDocument updateTwinPatchDocument = new JsonPatchDocument(); updateTwinPatchDocument.AppendAdd("/Humidity", 30); updateTwinPatchDocument.AppendReplace("/Temperature", 70); updateTwinPatchDocument.AppendRemove("/EmployeeId"); await client.UpdateDigitalTwinAsync(roomTwinId, updateTwinPatchDocument, ETag.All).ConfigureAwait(false); // get twin ETag?etagBeforeUpdate = (await client.GetDigitalTwinAsync <BasicDigitalTwin>(roomTwinId).ConfigureAwait(false)).Value.ETag; Assert.IsNotNull(etagBeforeUpdate); // update twin again, but with the correct etag JsonPatchDocument secondUpdateTwinPatchDocument = new JsonPatchDocument(); secondUpdateTwinPatchDocument.AppendReplace("/Humidity", 80); try { await client.UpdateDigitalTwinAsync(roomTwinId, secondUpdateTwinPatchDocument, etagBeforeUpdate).ConfigureAwait(false); } catch (RequestFailedException ex) when(ex.Status == (int)HttpStatusCode.PreconditionFailed) { throw new AssertionException("UpdateDigitalTwin should not have thrown PreconditionFailed because the ETag was up to date", ex); } } catch (Exception ex) { Assert.Fail($"Failure in executing a step in the test case: {ex.Message}."); } finally { // cleanup try { if (!string.IsNullOrWhiteSpace(roomModelId)) { await client.DeleteModelAsync(roomModelId).ConfigureAwait(false); } } catch (Exception ex) { Assert.Fail($"Test clean up failed: {ex.Message}"); } } }
public static async Task Run( [EventHubTrigger("lifecycleevents", Connection = "EVENTHUB_CONNECTIONSTRING")] EventData[] events, ILogger log) { var exceptions = new List <Exception>(events.Length); // Create Digital Twin client var cred = new ManagedIdentityCredential(adtAppId); var client = new DigitalTwinsClient( new Uri(adtInstanceUrl), cred, new DigitalTwinsClientOptions { Transport = new HttpClientTransport(singletonHttpClientInstance) }); foreach (EventData eventData in events) { try { //log.LogDebug($"EventData: {System.Text.Json.JsonSerializer.Serialize(eventData)}"); string opType = eventData.Properties["opType"] as string; if (opType == "deleteDeviceIdentity") { string deviceId = eventData.Properties["deviceId"] as string; try { // Find twin based on the original Registration ID BasicDigitalTwin digitalTwin = await client.GetDigitalTwinAsync <BasicDigitalTwin>(deviceId); // In order to delete the twin, all relationships must first be removed await DeleteAllRelationshipsAsync(client, digitalTwin.Id, log); // Delete the twin await client.DeleteDigitalTwinAsync(digitalTwin.Id, digitalTwin.ETag); log.LogInformation($"Twin {digitalTwin.Id} deleted in DT"); } catch (RequestFailedException e) when(e.Status == (int)HttpStatusCode.NotFound) { log.LogWarning($"Twin {deviceId} not found in DT"); } } } catch (Exception e) { // We need to keep processing the rest of the batch - capture this exception and continue. exceptions.Add(e); } } if (exceptions.Count > 1) { throw new AggregateException(exceptions); } if (exceptions.Count == 1) { throw exceptions.Single(); } }
internal static async Task <string> GetUniqueTwinIdAsync(string baseName, DigitalTwinsClient client) { return(await GetUniqueIdAsync(baseName, (twinId) => client.GetDigitalTwinAsync(twinId))); }
public async Task <Thermostat> Execute(GetThermostat query, CancellationToken cancellationToken) { var response = await _twins.GetDigitalTwinAsync <Thermostat>(query.Id, cancellationToken); return(response.Value); }
/// <summary> /// Creates a digital twin with Component and upates Component /// </summary> public async Task RunSamplesAsync(DigitalTwinsClient client) { PrintHeader("COMPONENT SAMPLE"); // For the purpose of this example we will create temporary models using a random model Ids. // We have to make sure these model Ids are unique within the DT instance. string componentModelId = await GetUniqueModelIdAsync(SamplesConstants.TemporaryComponentModelPrefix, client); string modelId = await GetUniqueModelIdAsync(SamplesConstants.TemporaryModelPrefix, client); string basicDtId = await GetUniqueTwinIdAsync(SamplesConstants.TemporaryTwinPrefix, client); string newComponentModelPayload = SamplesConstants.TemporaryComponentModelPayload .Replace(SamplesConstants.ComponentId, componentModelId); string newModelPayload = SamplesConstants.TemporaryModelWithComponentPayload .Replace(SamplesConstants.ModelId, modelId) .Replace(SamplesConstants.ComponentId, componentModelId); // Then we create models await client.CreateModelsAsync( new[] { newComponentModelPayload, newModelPayload }); Console.WriteLine($"Created models {componentModelId} and {modelId}."); #region Snippet:DigitalTwinsSampleCreateBasicTwin // Create digital twin with component payload using the BasicDigitalTwin serialization helper var basicTwin = new BasicDigitalTwin { Id = basicDtId, // model Id of digital twin Metadata = { ModelId = modelId }, Contents = { // digital twin properties { "Prop1", "Value1" }, { "Prop2", 987 }, // component { "Component1", new BasicDigitalTwinComponent { // component properties Contents = { { "ComponentProp1", "Component value 1" }, { "ComponentProp2", 123 }, }, } }, }, }; Response <BasicDigitalTwin> createDigitalTwinResponse = await client.CreateOrReplaceDigitalTwinAsync(basicDtId, basicTwin); Console.WriteLine($"Created digital twin '{createDigitalTwinResponse.Value.Id}'."); #endregion Snippet:DigitalTwinsSampleCreateBasicTwin // You can also get a digital twin as a BasicDigitalTwin type. // It works well for basic stuff, but as you can see it gets more difficult when delving into // more complex properties, like components. #region Snippet:DigitalTwinsSampleGetBasicDigitalTwin Response <BasicDigitalTwin> getBasicDtResponse = await client.GetDigitalTwinAsync <BasicDigitalTwin>(basicDtId); if (getBasicDtResponse.GetRawResponse().Status == (int)HttpStatusCode.OK) { BasicDigitalTwin basicDt = getBasicDtResponse.Value; // Must cast Component1 as a JsonElement and get its raw text in order to deserialize it as a dictionary string component1RawText = ((JsonElement)basicDt.Contents["Component1"]).GetRawText(); IDictionary <string, object> component1 = JsonSerializer.Deserialize <IDictionary <string, object> >(component1RawText); Console.WriteLine($"Retrieved and deserialized digital twin {basicDt.Id}:\n\t" + $"ETag: {basicDt.ETag}\n\t" + $"Prop1: {basicDt.Contents["Prop1"]}\n\t" + $"Prop2: {basicDt.Contents["Prop2"]}\n\t" + $"ComponentProp1: {component1["ComponentProp1"]}\n\t" + $"ComponentProp2: {component1["ComponentProp2"]}"); } #endregion Snippet:DigitalTwinsSampleGetBasicDigitalTwin string customDtId = await GetUniqueTwinIdAsync(SamplesConstants.TemporaryTwinPrefix, client); // Alternatively, you can create your own custom data types to serialize and deserialize your digital twins. // By specifying your properties and types directly, it requires less code or knowledge of the type for // interaction. #region Snippet:DigitalTwinsSampleCreateCustomTwin var customTwin = new CustomDigitalTwin { Id = customDtId, Metadata = { ModelId = modelId }, Prop1 = "Prop1 val", Prop2 = 987, Component1 = new MyCustomComponent { ComponentProp1 = "Component prop1 val", ComponentProp2 = 123, }, }; Response <CustomDigitalTwin> createCustomDigitalTwinResponse = await client.CreateOrReplaceDigitalTwinAsync(customDtId, customTwin); Console.WriteLine($"Created digital twin '{createCustomDigitalTwinResponse.Value.Id}'."); #endregion Snippet:DigitalTwinsSampleCreateCustomTwin // Getting a digital twin as a custom data type is extremely easy. // Custom types provide the best possible experience. #region Snippet:DigitalTwinsSampleGetCustomDigitalTwin Response <CustomDigitalTwin> getCustomDtResponse = await client.GetDigitalTwinAsync <CustomDigitalTwin>(customDtId); CustomDigitalTwin customDt = getCustomDtResponse.Value; Console.WriteLine($"Retrieved and deserialized digital twin {customDt.Id}:\n\t" + $"ETag: {customDt.ETag}\n\t" + $"Prop1: {customDt.Prop1}\n\t" + $"Prop2: {customDt.Prop2}\n\t" + $"ComponentProp1: {customDt.Component1.ComponentProp1}\n\t" + $"ComponentProp2: {customDt.Component1.ComponentProp2}"); #endregion Snippet:DigitalTwinsSampleGetCustomDigitalTwin #region Snippet:DigitalTwinsSampleUpdateComponent // Update Component1 by replacing the property ComponentProp1 value, // using an optional utility to build the payload. var componentJsonPatchDocument = new JsonPatchDocument(); componentJsonPatchDocument.AppendReplace("/ComponentProp1", "Some new value"); await client.UpdateComponentAsync(basicDtId, "Component1", componentJsonPatchDocument); Console.WriteLine($"Updated component for digital twin '{basicDtId}'."); #endregion Snippet:DigitalTwinsSampleUpdateComponent // Get Component #region Snippet:DigitalTwinsSampleGetComponent await client.GetComponentAsync <MyCustomComponent>(basicDtId, SamplesConstants.ComponentName); Console.WriteLine($"Retrieved component for digital twin '{basicDtId}'."); #endregion Snippet:DigitalTwinsSampleGetComponent // Clean up try { await client.DeleteDigitalTwinAsync(basicDtId); await client.DeleteDigitalTwinAsync(customDtId); } catch (RequestFailedException ex) { Console.WriteLine($"Failed to delete digital twin due to {ex}"); } try { await client.DeleteModelAsync(modelId); await client.DeleteModelAsync(componentModelId); } catch (RequestFailedException ex) { Console.WriteLine($"Failed to delete models due to {ex}"); } }
public async Task DigitalTwins_DeleteTwinFailsIfMatchProvidesOutdatedEtag() { DigitalTwinsClient client = GetClient(); string roomTwinId = await GetUniqueTwinIdAsync(client, TestAssetDefaults.RoomTwinIdPrefix).ConfigureAwait(false); string floorModelId = await GetUniqueModelIdAsync(client, TestAssetDefaults.FloorModelIdPrefix).ConfigureAwait(false); string roomModelId = await GetUniqueModelIdAsync(client, TestAssetDefaults.RoomModelIdPrefix).ConfigureAwait(false); try { // arrange // create room model string roomModel = TestAssetsHelper.GetRoomModelPayload(roomModelId, floorModelId); await client.CreateModelsAsync(new List <string> { roomModel }).ConfigureAwait(false); // act // create room twin BasicDigitalTwin roomTwin = TestAssetsHelper.GetRoomTwinPayload(roomModelId); await client.CreateOrReplaceDigitalTwinAsync <BasicDigitalTwin>(roomTwinId, roomTwin).ConfigureAwait(false); // get twin ETag?etagBeforeUpdate = (await client.GetDigitalTwinAsync <BasicDigitalTwin>(roomTwinId).ConfigureAwait(false)).Value.ETag; // update twin JsonPatchDocument updateTwinPatchDocument = new JsonPatchDocument(); updateTwinPatchDocument.AppendAdd("/Humidity", 30); updateTwinPatchDocument.AppendReplace("/Temperature", 70); updateTwinPatchDocument.AppendRemove("/EmployeeId"); await client.UpdateDigitalTwinAsync(roomTwinId, updateTwinPatchDocument, ETag.All).ConfigureAwait(false); // assert Func <Task> act = async() => { // since the ETag is out of date, this call should throw a 412 await client.DeleteDigitalTwinAsync(roomTwinId, etagBeforeUpdate).ConfigureAwait(false); }; act.Should().Throw <RequestFailedException>() .And.Status.Should().Be((int)HttpStatusCode.PreconditionFailed); } finally { // cleanup try { if (!string.IsNullOrWhiteSpace(roomModelId)) { await client.DeleteModelAsync(roomModelId).ConfigureAwait(false); } } catch (Exception ex) { Assert.Fail($"Test clean up failed: {ex.Message}"); } } }
public async Task <string> GetUniqueTwinIdAsync(DigitalTwinsClient dtClient, string baseName) { return(await GetUniqueIdAsync(baseName, (twinId) => dtClient.GetDigitalTwinAsync <BasicDigitalTwin>(twinId)).ConfigureAwait(false)); }
public async Task DigitalTwins_DeleteTwinSucceedsIfMatchProvidesCorrectEtag() { DigitalTwinsClient client = GetClient(); string roomTwinId = await GetUniqueTwinIdAsync(client, TestAssetDefaults.RoomTwinIdPrefix).ConfigureAwait(false); string floorModelId = await GetUniqueModelIdAsync(client, TestAssetDefaults.FloorModelIdPrefix).ConfigureAwait(false); string roomModelId = await GetUniqueModelIdAsync(client, TestAssetDefaults.RoomModelIdPrefix).ConfigureAwait(false); try { // arrange // create room model string roomModel = TestAssetsHelper.GetRoomModelPayload(roomModelId, floorModelId); await client.CreateModelsAsync(new List <string> { roomModel }).ConfigureAwait(false); // act // create room twin BasicDigitalTwin roomTwin = TestAssetsHelper.GetRoomTwinPayload(roomModelId); await client.CreateOrReplaceDigitalTwinAsync <BasicDigitalTwin>(roomTwinId, roomTwin).ConfigureAwait(false); // update twin JsonPatchDocument updateTwinPatchDocument = new JsonPatchDocument(); updateTwinPatchDocument.AppendAdd("/Humidity", 30); updateTwinPatchDocument.AppendReplace("/Temperature", 70); updateTwinPatchDocument.AppendRemove("/EmployeeId"); await client.UpdateDigitalTwinAsync(roomTwinId, updateTwinPatchDocument, ETag.All).ConfigureAwait(false); // get twin ETag?correctETag = (await client.GetDigitalTwinAsync <BasicDigitalTwin>(roomTwinId).ConfigureAwait(false)).Value.ETag; Assert.IsNotNull(correctETag); try { // since the ETag is not out of date, this call should not throw a 412 await client.DeleteDigitalTwinAsync(roomTwinId, correctETag).ConfigureAwait(false); } catch (RequestFailedException ex) when(ex.Status == (int)HttpStatusCode.PreconditionFailed) { throw new AssertionException("UpdateRelationship should not have thrown PreconditionFailed because the ETag was up to date", ex); } } finally { // cleanup try { if (!string.IsNullOrWhiteSpace(roomModelId)) { await client.DeleteModelAsync(roomModelId).ConfigureAwait(false); } } catch (Exception ex) { Assert.Fail($"Test clean up failed: {ex.Message}"); } } }