Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add helpers for accessing sample resources #14706

Merged
merged 3 commits into from
Sep 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions sdk/digitaltwins/azure-digitaltwins-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@
</dependencies>

<build>
<resources>
<resource>
<directory>src/samples/resources</directory>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maven has inbuilt references to src/main/resources and src/test/resources. We need to add reference to src/samples/resources explicitly.

</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.digitaltwins.core;

import com.azure.core.http.policy.HttpLogDetailLevel;
import com.azure.core.http.policy.HttpLogOptions;
import com.azure.digitaltwins.core.implementation.models.ErrorResponseException;
import com.azure.identity.ClientSecretCredentialBuilder;

import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class DigitalTwinsLifecycleSample {
private static final String tenantId = System.getenv("TENANT_ID");
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: Add a command line options helper similar to .NET

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add the "TODO:" as a comment so we can find it and resolve it later?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will take care of this

private static final String clientId = System.getenv("CLIENT_ID");
private static final String clientSecret = System.getenv("CLIENT_SECRET");
private static final String endpoint = System.getenv("DIGITAL_TWINS_ENDPOINT");

private static final int MaxWaitTimeAsyncOperationsInSeconds = 10;

private static final URL DtdlDirectoryUrl = DigitalTwinsLifecycleSample.class.getClassLoader().getResource("DTDL");
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This path to "resources" is returned as a URL

private static final Path DtDlDirectoryPath;
private static final Path TwinsPath;
private static final Path ModelsPath;
private static final Path RelationshipsPath;

private static final DigitalTwinsAsyncClient client;

static {
try {
assert DtdlDirectoryUrl != null;
DtDlDirectoryPath = Paths.get(DtdlDirectoryUrl.toURI());
} catch (URISyntaxException e) {
System.err.println("Unable to convert the DTDL directory URL to URI: " + e);
throw new RuntimeException(e);
}
TwinsPath = Paths.get(DtDlDirectoryPath.toString(), "DigitalTwins");
ModelsPath = Paths.get(DtDlDirectoryPath.toString(), "Models");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ModelPath and RelationshipsPath is not used here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will be, in the rest of the sample (this sample creates models and relationships) :)

RelationshipsPath = Paths.get(DtDlDirectoryPath.toString(), "Relationships");

client = new DigitalTwinsClientBuilder()
.tokenCredential(
new ClientSecretCredentialBuilder()
.tenantId(tenantId)
.clientId(clientId)
.clientSecret(clientSecret)
.build()
)
.endpoint(endpoint)
.httpLogOptions(
new HttpLogOptions()
.setLogLevel(HttpLogDetailLevel.NONE))
.buildAsyncClient();
}

public static void main(String[] args) throws IOException, InterruptedException {
// Ensure existing twins with the same name are deleted first
deleteTwins();

// Create twin counterparts for all the models
createTwins();
}

public static void deleteTwins() throws IOException, InterruptedException {
System.out.println("DELETE DIGITAL TWINS");
Map<String, String> twins = FileHelper.loadAllFilesInPath(TwinsPath);
final Semaphore deleteTwinsSemaphore = new Semaphore(0);

// Call APIs to delete the twins. For each async operation, once the operation is completed successfully, a semaphore is released.
twins
.forEach((twinId, twinContent) -> client.deleteDigitalTwin(twinId)
.doOnSuccess(aVoid -> System.out.println("Deleted digital twin: " + twinId))
.doOnError(throwable -> {
// If digital twin does not exist, ignore.
if (!(throwable instanceof ErrorResponseException) || !((ErrorResponseException) throwable).getValue().getError().getCode().equals("DigitalTwinNotFound")) {
System.err.println("Could not delete digital twin " + twinId + " due to " + throwable);
}
})
.doOnTerminate(deleteTwinsSemaphore::release)
.subscribe());

// Verify that a semaphore has been released for each async operation, signifying that the async call has completed.
boolean created = deleteTwinsSemaphore.tryAcquire(twins.size(), MaxWaitTimeAsyncOperationsInSeconds, TimeUnit.SECONDS);
System.out.println("Twins deleted: " + created);
}

public static void createTwins() throws IOException, InterruptedException {
System.out.println("CREATE DIGITAL TWINS");
Map<String, String> twins = FileHelper.loadAllFilesInPath(TwinsPath);
final Semaphore createTwinsSemaphore = new Semaphore(0);

// Call APIs to create the twins. For each async operation, once the operation is completed successfully, a semaphore is released.
twins
.forEach((twinId, twinContent) -> client.createDigitalTwinWithResponse(twinId, twinContent)
.doOnSuccess(response -> System.out.println("Created digital twin: " + twinId + "\n\t Body: " + response.getValue()))
.doOnError(throwable -> System.err.println("Could not create digital twin " + twinId + " due to " + throwable))
.doOnTerminate(createTwinsSemaphore::release)
.subscribe());

// Verify that a semaphore has been released for each async operation, signifying that the async call has completed.
boolean created = createTwinsSemaphore.tryAcquire(twins.size(), MaxWaitTimeAsyncOperationsInSeconds, TimeUnit.SECONDS);
System.out.println("Twins created: " + created);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.digitaltwins.core;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class FileHelper {
public static Map<String, String> loadAllFilesInPath(Path path) throws IOException {
Map<String, String> fileContents = new HashMap<>();

Stream<Path> paths = Files.walk(path);
paths
.filter(filePath -> filePath.toFile().getName().endsWith(".json"))
.forEach(filePath -> {
try {
Stream<String> lines = Files.lines(filePath);
String fileAsString = lines.collect(Collectors.joining());
lines.close();

fileContents.put(getFileNameFromPath(filePath), cleanupJsonString(fileAsString));
} catch (IOException e) {
e.printStackTrace();
}
});

return fileContents;
}

public static String cleanupJsonString(String jsonString) {
// Remove newline characters, empty spaces and unwanted unicode characters
return jsonString.replaceAll("([\\r\\n\\s+\\uFEFF-\\uFFFF])", "");
}

public static String getFileNameFromPath(Path path) {
String fileName = path.getFileName().toString();
if (fileName.indexOf(".") > 0) {
fileName = fileName.substring(0, fileName.lastIndexOf("."));
}

return fileName;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.digitaltwins.core;

public class SamplesConstants {
public static final String RoomModelId = "dtmi:samples:Room;1";
public static final String WifiModelId = "dtmi:samples:Wifi;1";
public static final String BuildingModelId = "dtmi:samples:Building;1";
public static final String FloorModelId = "dtmi:samples:Floor;1";
public static final String HvacModelId = "dtmi:samples:HVAC;1";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"$metadata": {
"$model": "dtmi:samples:Building;1"
},
"AverageTemperature": 68,
"TemperatureUnit": "Celsius"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"$metadata": {
"$model": "dtmi:samples:Building;1"
},
"AverageTemperature": 68,
"TemperatureUnit": "Celsius"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"$metadata": {
"$model": "dtmi:samples:Floor;1"
},
"AverageTemperature": 75
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"$metadata": {
"$model": "dtmi:samples:HVAC;1"
},
"Efficiency": 94,
"TargetTemperature": 72,
"TargetHumidity": 30
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"$metadata": {
"$model": "dtmi:samples:Room;1"
},
"Temperature": 80,
"Humidity": 25,
"IsOccupied": true,
"EmployeeId": "Employee1",
"wifiAccessPoint": {
"$metadata": {
},
"RouterName": "Cisco1",
"Network": "Room1"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"@id": "dtmi:samples:Building;1",
"@type": "Interface",
"@context": "dtmi:dtdl:context;2",
"displayName": "Building",
"contents": [
{
"@type": "Relationship",
"name": "has",
"target": "dtmi:samples:Floor;1",
"properties": [
{
"@type": "Property",
"name": "isAccessRestricted",
"schema": "boolean"
}
]
},
{
"@type": "Relationship",
"name": "isEquippedWith",
"target": "dtmi:samples:HVAC;1"
},
{
"@type": "Property",
"name": "AverageTemperature",
"schema": "double"
},
{
"@type": "Property",
"name": "TemperatureUnit",
"schema": "string"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"@id": "dtmi:samples:Floor;1",
"@type": "Interface",
"@context": "dtmi:dtdl:context;2",
"displayName": "Floor",
"contents": [
{
"@type": "Relationship",
"name": "contains",
"target": "dtmi:samples:Room;1"
},
{
"@type": "Property",
"name": "AverageTemperature",
"schema": "double"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"@id": "dtmi:samples:HVAC;1",
"@type": "Interface",
"@context": "dtmi:dtdl:context;2",
"displayName": "HVAC",
"contents": [
{
"@type": "Property",
"name": "Efficiency",
"schema": "double"
},
{
"@type": "Property",
"name": "TargetTemperature",
"schema": "double"
},
{
"@type": "Property",
"name": "TargetHumidity",
"schema": "double"
},
{
"@type": "Relationship",
"name": "controlsTemperature",
"target": "dtmi:samples:Floor;1"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"@id": "dtmi:samples:Room;1",
"@type": "Interface",
"@context": "dtmi:dtdl:context;2",
"displayName": "Room",
"contents": [
{
"@type": "Property",
"name": "Temperature",
"schema": "double"
},
{
"@type": "Property",
"name": "Humidity",
"schema": "double"
},
{
"@type": "Property",
"name": "IsOccupied",
"schema": "boolean"
},
{
"@type": "Property",
"name": "EmployeeId",
"schema": "string"
},
{
"@type": "Component",
"name": "wifiAccessPoint",
"schema": "dtmi:samples:Wifi;1"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"@id": "dtmi:samples:Wifi;1",
"@type": "Interface",
"@context": "dtmi:dtdl:context;2",
"displayName": "Wifi",
"contents": [
{
"@type": "Property",
"name": "RouterName",
"schema": "string"
},
{
"@type": "Property",
"name": "Network",
"schema": "string"
}
]
}
Loading