diff --git a/sdk/modelsrepository/azure-iot-modelsrepository/src/main/java/com/azure/iot/modelsrepository/DtmiConventions.java b/sdk/modelsrepository/azure-iot-modelsrepository/src/main/java/com/azure/iot/modelsrepository/DtmiConventions.java index 2fbb128455152..5a49487c5b98c 100644 --- a/sdk/modelsrepository/azure-iot-modelsrepository/src/main/java/com/azure/iot/modelsrepository/DtmiConventions.java +++ b/sdk/modelsrepository/azure-iot-modelsrepository/src/main/java/com/azure/iot/modelsrepository/DtmiConventions.java @@ -74,21 +74,6 @@ public static URI getModelUri(String dtmi, URI repositoryUri, boolean expanded) } } - /** - * Converts a string to {@link URI} - * - * @param uri String format of the path - * @return {@link URI} representation of the path/uri. - * @throws IllegalArgumentException If the {@code uri} is invalid. - */ - public static URI convertToUri(String uri) throws IllegalArgumentException { - try { - return new URI(uri); - } catch (Exception e) { - throw new IllegalArgumentException("Invalid uri format", e); - } - } - static String dtmiToPath(String dtmi) { if (!isValidDtmi(dtmi)) { throw new IllegalArgumentException(String.format(StatusStrings.INVALID_DTMI_FORMAT_S, dtmi)); diff --git a/sdk/modelsrepository/azure-iot-modelsrepository/src/main/java/com/azure/iot/modelsrepository/ModelsRepositoryAsyncClient.java b/sdk/modelsrepository/azure-iot-modelsrepository/src/main/java/com/azure/iot/modelsrepository/ModelsRepositoryAsyncClient.java index 3fecab6f5ba20..61f11f3234dd1 100644 --- a/sdk/modelsrepository/azure-iot-modelsrepository/src/main/java/com/azure/iot/modelsrepository/ModelsRepositoryAsyncClient.java +++ b/sdk/modelsrepository/azure-iot-modelsrepository/src/main/java/com/azure/iot/modelsrepository/ModelsRepositoryAsyncClient.java @@ -60,8 +60,8 @@ public final class ModelsRepositoryAsyncClient { * Gets the repository uri that the client has been initialized with. * @return The target repository uri. */ - public URI getRepositoryUri() { - return this.repositoryUri; + public String getRepositoryUri() { + return this.repositoryUri.toString(); } /** diff --git a/sdk/modelsrepository/azure-iot-modelsrepository/src/main/java/com/azure/iot/modelsrepository/ModelsRepositoryClient.java b/sdk/modelsrepository/azure-iot-modelsrepository/src/main/java/com/azure/iot/modelsrepository/ModelsRepositoryClient.java index 32fa0941ad6dc..4066a83a71114 100644 --- a/sdk/modelsrepository/azure-iot-modelsrepository/src/main/java/com/azure/iot/modelsrepository/ModelsRepositoryClient.java +++ b/sdk/modelsrepository/azure-iot-modelsrepository/src/main/java/com/azure/iot/modelsrepository/ModelsRepositoryClient.java @@ -8,7 +8,6 @@ import com.azure.core.annotation.ServiceMethod; import com.azure.core.util.Context; -import java.net.URI; import java.util.Map; /** @@ -27,7 +26,7 @@ public final class ModelsRepositoryClient { * Gets the repository uri that the client has been initialized with. * @return The target repository uri. */ - public URI getRepositoryUri() { + public String getRepositoryUri() { return this.modelsRepositoryAsyncClient.getRepositoryUri(); } diff --git a/sdk/modelsrepository/azure-iot-modelsrepository/src/main/java/com/azure/iot/modelsrepository/ModelsRepositoryClientBuilder.java b/sdk/modelsrepository/azure-iot-modelsrepository/src/main/java/com/azure/iot/modelsrepository/ModelsRepositoryClientBuilder.java index 8e1a2de76e1a4..e82b0e913e793 100644 --- a/sdk/modelsrepository/azure-iot-modelsrepository/src/main/java/com/azure/iot/modelsrepository/ModelsRepositoryClientBuilder.java +++ b/sdk/modelsrepository/azure-iot-modelsrepository/src/main/java/com/azure/iot/modelsrepository/ModelsRepositoryClientBuilder.java @@ -217,8 +217,7 @@ public ModelsRepositoryClientBuilder modelDependencyResolution(ModelDependencyRe * @return the updated ModelsRepositoryClientBuilder instance for fluent building. */ public ModelsRepositoryClientBuilder repositoryEndpoint(String repositoryEndpoint) { - DtmiConventions.convertToUri(repositoryEndpoint); - this.repositoryEndpoint = DtmiConventions.convertToUri(repositoryEndpoint); + this.repositoryEndpoint = convertToUri(repositoryEndpoint); return this; } @@ -330,4 +329,19 @@ public ModelsRepositoryClientBuilder clientOptions(ClientOptions clientOptions) this.clientOptions = clientOptions; return this; } + + /** + * Converts a string to {@link URI} + * + * @param uri String format of the path + * @return {@link URI} representation of the path/uri. + * @throws IllegalArgumentException If the {@code uri} is invalid. + */ + static URI convertToUri(String uri) throws IllegalArgumentException { + try { + return new URI(uri); + } catch (Exception e) { + throw new IllegalArgumentException("Invalid URI format", e); + } + } } diff --git a/sdk/modelsrepository/azure-iot-modelsrepository/src/samples/README.md b/sdk/modelsrepository/azure-iot-modelsrepository/src/samples/README.md index 8fe8df377f4e0..8f7bf2cde7082 100644 --- a/sdk/modelsrepository/azure-iot-modelsrepository/src/samples/README.md +++ b/sdk/modelsrepository/azure-iot-modelsrepository/src/samples/README.md @@ -19,25 +19,46 @@ The samples project demonstrates the following: // When no URI is provided for instantiation, the Azure IoT Models Repository global endpoint // https://devicemodels.azure.com/ is used and the model dependency resolution // configuration is set to TryFromExpanded. -ModelsRepositoryClientBuilder clientBuilder = new ModelsRepositoryClientBuilder(); -ModelsRepositoryAsyncClient asyncClient = clientBuilder.buildAsyncClient(); -ModelsRepositoryClient syncClient = clientBuilder.buildClient(); +ModelsRepositoryAsyncClient asyncClient = new ModelsRepositoryClientBuilder() + .buildAsyncClient(); -System.out.println("Initialized the async client pointing to the global endpoint" + asyncClient.getRepositoryUri().toString()); -System.out.println("Initialized the sync client pointing to the global endpoint" + syncClient.getRepositoryUri().toString()); +ModelsRepositoryClient syncClient = new ModelsRepositoryClientBuilder() + .buildClient(); + +System.out.println("Initialized the async client pointing to the global endpoint" + asyncClient.getRepositoryUri()); +System.out.println("Initialized the sync client pointing to the global endpoint" + syncClient.getRepositoryUri()); +``` + +```java +// This form shows specifying a custom URI for the models repository with default client options. +// The default client options will enable model dependency resolution. +ModelsRepositoryAsyncClient asyncClient = new ModelsRepositoryClientBuilder() + .repositoryEndpoint("https://contoso.com/models") + .buildAsyncClient(); + +ModelsRepositoryClient syncClient = new ModelsRepositoryClientBuilder() + .repositoryEndpoint("https://contoso.com/models") + .buildClient(); + +System.out.println("Initialized the async client pointing to the custom endpoint" + asyncClient.getRepositoryUri()); +System.out.println("Initialized the sync client pointing to the custom endpoint" + syncClient.getRepositoryUri()); ``` ```java // The client will also work with a local file-system URI. This example shows initialization // with a local URI and disabling model dependency resolution. -clientBuilder - .repositoryEndpoint(LOCAL_DIRECTORY_URI) - .modelDependencyResolution(ModelDependencyResolution.DISABLED); -asyncClient = clientBuilder.buildAsyncClient(); -syncClient = clientBuilder.buildClient(); - -System.out.println("Initialized the async client pointing to the local file-system: " + asyncClient.getRepositoryUri().toString()); -System.out.println("Initialized the sync client pointing to the local file-system: " + syncClient.getRepositoryUri().toString()); +ModelsRepositoryAsyncClient asyncClient = new ModelsRepositoryClientBuilder() + .repositoryEndpoint(CLIENT_SAMPLES_DIRECTORY_PATH) + .modelDependencyResolution(ModelDependencyResolution.DISABLED) + .buildAsyncClient(); + +ModelsRepositoryClient syncClient = new ModelsRepositoryClientBuilder() + .repositoryEndpoint(CLIENT_SAMPLES_DIRECTORY_PATH) + .modelDependencyResolution(ModelDependencyResolution.DISABLED) + .buildClient(); + +System.out.println("Initialized the async client pointing to the local file-system: " + asyncClient.getRepositoryUri()); +System.out.println("Initialized the sync client pointing to the local file-system: " + syncClient.getRepositoryUri()); ``` ## Sync vs Async clients @@ -69,24 +90,20 @@ After publishing, your model(s) will be available for consumption from the globa ```java // Global endpoint client -ModelsRepositoryClientBuilder clientBuilder = new ModelsRepositoryClientBuilder(); -ModelsRepositoryAsyncClient asyncClient = clientBuilder.buildAsyncClient(); +// Global endpoint client +ModelsRepositoryAsyncClient asyncClient = new ModelsRepositoryClientBuilder() + .buildAsyncClient(); // The output of getModels will include at least the definition for the target dtmi. // If the model dependency resolution configuration is not disabled, then models in which the // target dtmi depends on will also be included in the returned Map. String targetDtmi = "dtmi:com:example:TemperatureController;1"; -CountDownLatch modelsCountdownLatch = new CountDownLatch(1); - // In this case the above dtmi has 2 model dependencies. // dtmi:com:example:Thermostat;1 and dtmi:azure:DeviceManagement:DeviceInformation;1 asyncClient.getModels(targetDtmi) .doOnSuccess(aVoid -> System.out.println("Fetched the model and dependencies for: " + targetDtmi)) - .doOnTerminate(modelsCountdownLatch::countDown) .subscribe(res -> System.out.println(String.format("%s resolved in %s interfaces.", targetDtmi, res.size()))); - -modelsCountdownLatch.await(MAX_WAIT_TIME_ASYNC_OPERATIONS_IN_SECONDS, TimeUnit.SECONDS); ``` GitHub pull-request workflows are a core aspect of the IoT Models Repository service. To submit models, the user is expected to fork and clone the global [models repository project][modelsrepository_github_repo] then iterate against the local copy. Changes would then be pushed to the fork (ideally in a new branch) and a PR created against the global repository. @@ -95,26 +112,20 @@ To support testing local changes using this workflow and similar use cases, the ```java // Local sample repository client -ModelsRepositoryClientBuilder clientBuilder = new ModelsRepositoryClientBuilder() - .repositoryEndpoint(LOCAL_DIRECTORY_URI); - -ModelsRepositoryAsyncClient asyncClient = clientBuilder.buildAsyncClient(); +ModelsRepositoryAsyncClient asyncClient = new ModelsRepositoryClientBuilder() + .repositoryEndpoint(CLIENT_SAMPLES_DIRECTORY_PATH) + .buildAsyncClient(); // The output of getModels will include at least the definition for the target dtmi. // If the model dependency resolution configuration is not disabled, then models in which the // target dtmi depends on will also be included in the returned Map. String targetDtmi = "dtmi:com:example:TemperatureController;1"; -CountDownLatch modelsCountdownLatch = new CountDownLatch(1); - // In this case the above dtmi has 2 model dependencies. // dtmi:com:example:Thermostat;1 and dtmi:azure:DeviceManagement:DeviceInformation;1 asyncClient.getModels(targetDtmi) .doOnSuccess(aVoid -> System.out.println("Fetched the model and dependencies for: " + targetDtmi)) - .doOnTerminate(modelsCountdownLatch::countDown) .subscribe(res -> System.out.println(String.format("%s resolved in %s interfaces.", targetDtmi, res.size()))); - -modelsCountdownLatch.await(MAX_WAIT_TIME_ASYNC_OPERATIONS_IN_SECONDS, TimeUnit.SECONDS); ``` You are also able to get definitions for multiple root models at a time by leveraging @@ -122,8 +133,8 @@ the `getModels` overload that supports an `Iterable`. ```java // Global endpoint client -ModelsRepositoryClientBuilder clientBuilder = new ModelsRepositoryClientBuilder(); -ModelsRepositoryAsyncClient asyncClient = clientBuilder.buildAsyncClient(); +ModelsRepositoryAsyncClient asyncClient = new ModelsRepositoryClientBuilder() + .buildAsyncClient(); // When given an Iterable of dtmis, the output of getModels() will include at // least the definitions of each dtmi enumerated in the Iterable. @@ -131,17 +142,12 @@ ModelsRepositoryAsyncClient asyncClient = clientBuilder.buildAsyncClient(); // enumerated dtmi depends on will also be included in the returned Map. Iterable dtmis = Arrays.asList("dtmi:com:example:TemperatureController;1", "dtmi:com:example:azuresphere:sampledevice;1"); -CountDownLatch modelsCountdownLatch = new CountDownLatch(1); - // In this case the dtmi "dtmi:com:example:TemperatureController;1" has 2 model dependencies // and the dtmi "dtmi:com:example:azuresphere:sampledevice;1" has no additional dependencies. // The returned Map will include 4 models. asyncClient.getModels(dtmis) .doOnSuccess(aVoid -> System.out.println("Fetched the models and dependencies for: " + String.join(", ", dtmis))) - .doOnTerminate(modelsCountdownLatch::countDown) .subscribe(res -> System.out.println(String.format("Dtmis %s resolved in %s interfaces.", String.join(", ", dtmis), res.size()))); - -modelsCountdownLatch.await(MAX_WAIT_TIME_ASYNC_OPERATIONS_IN_SECONDS, TimeUnit.SECONDS); ``` ## DtmiConventions utility functions @@ -149,6 +155,7 @@ modelsCountdownLatch.await(MAX_WAIT_TIME_ASYNC_OPERATIONS_IN_SECONDS, TimeUnit.S The IoT Models Repository applies a set of conventions for organizing digital twin models. This package exposes a class called `DtmiConventions` which exposes utility functions supporting these conventions. These same functions are used throughout the client. +`isValidDtmi` will ensure that the provided DigitalTwins Model Id (DTMI) has the correct format according to [DTDL specifications][dtdlv2_reference]. ```java // This snippet shows how to validate a given DTMI string is well-formed. @@ -161,27 +168,30 @@ String invalidDtmi = "dtmi:com:example:Thermostat"; System.out.println(String.format("Dtmi %s is a valid dtmi: %s", invalidDtmi, DtmiConventions.isValidDtmi(invalidDtmi))); ``` +`getModelUri` allows for constructing the path in which the provided DTMI is supposed to be located at based on a repository URI. +For instance: If the repository URI is `https://contoso.com/models/` and the DTMI (DigitalTwins Model Id) is `dtmi:com:example:Thermostat;1` then the path in which the file should exist will be `https://constoso.com/models/dtmi/com/example/thermostat-1.json`. + +Same pattern is expected while working with the local file system. +For instance: If the repository URI is `file:///path/to/repository` and the DTMI (DigitalTwins Model Id) is `dtmi:com:example:Thermostat;1` then the path in which the file should exist will be `file:///path/to/repository/dtmi/com/example/thermostat-1.json`. + +If the `boolean` parameter to the `getModelUri` method (`expanded`) is `true`, the result of the resolved path will have `.expanded.json` file extension instead of `.json`. This will allow for the `ModelsRepositoryClient` to only load the pre-computed dependency tree instead of the entire dependency tree. If the `.expanded.json` form of the model payload cannot be found, the client will load the full dependency tree as a fall back mechanism. + ```java // This snippet shows obtaining a fully qualified path to a model file. // Local repository example: -try { - URI localRepositoryUri = new URI("file:///path/to/repository"); - String fullyQualifiedModelUri = DtmiConventions.getModelUri("dtmi:com:example:Thermostat;1", localRepositoryUri, false).toString(); - - // Prints: file:///path/to/repository/dtmi/com/example/thermostat-1.json - System.out.println(fullyQualifiedModelUri); - - // Remote repository example - URI remoteRepositoryUri = new URI("https://contoso.com/models/"); - fullyQualifiedModelUri = DtmiConventions.getModelUri("dtmi:com:example:Thermostat;1", remoteRepositoryUri, false).toString(); - - // Prints: https://constoso.com/models/dtmi/com/example/thermostat-1.json - System.out.println(fullyQualifiedModelUri); -} catch (URISyntaxException ex) { - System.out.println("Invalid URI path has been used to instantiate the URI object. Exiting..."); - return; -} +URI localRepositoryUri = new URI("file:///path/to/repository"); +String fullyQualifiedModelUri = DtmiConventions.getModelUri("dtmi:com:example:Thermostat;1", localRepositoryUri, false).toString(); + +// Prints: file:///path/to/repository/dtmi/com/example/thermostat-1.json +System.out.println(fullyQualifiedModelUri); + +// Remote repository example with expanded enabled +URI remoteRepositoryUri = new URI("https://contoso.com/models/"); +fullyQualifiedModelUri = DtmiConventions.getModelUri("dtmi:com:example:Thermostat;1", remoteRepositoryUri, true).toString(); + +// Prints: https://constoso.com/models/dtmi/com/example/thermostat-1.expanded.json +System.out.println(fullyQualifiedModelUri); ``` diff --git a/sdk/modelsrepository/azure-iot-modelsrepository/src/samples/java/com/azure/iot/core/DtmiConventionsSamples.java b/sdk/modelsrepository/azure-iot-modelsrepository/src/samples/java/com/azure/iot/core/DtmiConventionsSamples.java index cff5fd3a8222c..2ed0d2f4d5a3d 100644 --- a/sdk/modelsrepository/azure-iot-modelsrepository/src/samples/java/com/azure/iot/core/DtmiConventionsSamples.java +++ b/sdk/modelsrepository/azure-iot-modelsrepository/src/samples/java/com/azure/iot/core/DtmiConventionsSamples.java @@ -42,11 +42,11 @@ public static void getModelUri() { // Prints: file:///path/to/repository/dtmi/com/example/thermostat-1.json System.out.println(fullyQualifiedModelUri); - // Remote repository example + // Remote repository example with expanded enabled. URI remoteRepositoryUri = new URI("https://contoso.com/models/"); - fullyQualifiedModelUri = DtmiConventions.getModelUri("dtmi:com:example:Thermostat;1", remoteRepositoryUri, false).toString(); + fullyQualifiedModelUri = DtmiConventions.getModelUri("dtmi:com:example:Thermostat;1", remoteRepositoryUri, true).toString(); - // Prints: https://constoso.com/models/dtmi/com/example/thermostat-1.json + // Prints: https://constoso.com/models/dtmi/com/example/thermostat-1.expanded.json System.out.println(fullyQualifiedModelUri); } catch (URISyntaxException ex) { System.out.println("Invalid URI path has been used to instantiate the URI object. Exiting..."); diff --git a/sdk/modelsrepository/azure-iot-modelsrepository/src/samples/java/com/azure/iot/core/Main.java b/sdk/modelsrepository/azure-iot-modelsrepository/src/samples/java/com/azure/iot/core/Main.java index fb140020090aa..33964d2a964ff 100644 --- a/sdk/modelsrepository/azure-iot-modelsrepository/src/samples/java/com/azure/iot/core/Main.java +++ b/sdk/modelsrepository/azure-iot-modelsrepository/src/samples/java/com/azure/iot/core/Main.java @@ -3,11 +3,13 @@ package com.azure.iot.core; +import java.util.Scanner; + /** * Entry point for running samples. */ public class Main { - static void main(String[] args) throws InterruptedException { + public static void main(String[] args) throws InterruptedException { // DtmiConventions samples DtmiConventionsSamples.isValidDtmi(); @@ -20,5 +22,10 @@ static void main(String[] args) throws InterruptedException { ModelResolutionSamples.getModelsFromGlobalRepository(); ModelResolutionSamples.getModelsFromLocalRepository(); ModelResolutionSamples.getMultipleModelsFromGlobalRepository(); + + Scanner userInput = new Scanner(System.in); + System.out.println("Press any key to exit."); + userInput.nextLine(); + System.exit(1); } } diff --git a/sdk/modelsrepository/azure-iot-modelsrepository/src/samples/java/com/azure/iot/core/ModelResolutionSamples.java b/sdk/modelsrepository/azure-iot-modelsrepository/src/samples/java/com/azure/iot/core/ModelResolutionSamples.java index 40c06ea163044..0ce947c1c3d52 100644 --- a/sdk/modelsrepository/azure-iot-modelsrepository/src/samples/java/com/azure/iot/core/ModelResolutionSamples.java +++ b/sdk/modelsrepository/azure-iot-modelsrepository/src/samples/java/com/azure/iot/core/ModelResolutionSamples.java @@ -9,8 +9,6 @@ import com.azure.iot.modelsrepository.ModelsRepositoryClientBuilder; import java.util.Arrays; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; public class ModelResolutionSamples { private static final String CLIENT_SAMPLES_DIRECTORY_PATH = (System.getProperty("user.dir").concat("/src/samples/resources/TestModelRepo/")).replace("\\", "/"); @@ -24,33 +22,42 @@ public static void clientInitializationSamples() { // When no URI is provided for instantiation, the Azure IoT Models Repository global endpoint // https://devicemodels.azure.com/ is used and the model dependency resolution // configuration is set to TryFromExpanded. - ModelsRepositoryClientBuilder clientBuilder = new ModelsRepositoryClientBuilder(); - ModelsRepositoryAsyncClient asyncClient = clientBuilder.buildAsyncClient(); - ModelsRepositoryClient syncClient = clientBuilder.buildClient(); + ModelsRepositoryAsyncClient asyncClient = new ModelsRepositoryClientBuilder() + .buildAsyncClient(); - System.out.println("Initialized the async client pointing to the global endpoint" + asyncClient.getRepositoryUri().toString()); - System.out.println("Initialized the sync client pointing to the global endpoint" + syncClient.getRepositoryUri().toString()); + ModelsRepositoryClient syncClient = new ModelsRepositoryClientBuilder() + .buildClient(); + + System.out.println("Initialized the async client pointing to the global endpoint" + asyncClient.getRepositoryUri()); + System.out.println("Initialized the sync client pointing to the global endpoint" + syncClient.getRepositoryUri()); // This form shows specifying a custom URI for the models repository with default client options. // The default client options will enable model dependency resolution. - clientBuilder - .repositoryEndpoint("https://contoso.com/models"); - asyncClient = clientBuilder.buildAsyncClient(); - syncClient = clientBuilder.buildClient(); + asyncClient = new ModelsRepositoryClientBuilder() + .repositoryEndpoint("https://contoso.com/models") + .buildAsyncClient(); + + syncClient = new ModelsRepositoryClientBuilder() + .repositoryEndpoint("https://contoso.com/models") + .buildClient(); System.out.println("Initialized the async client pointing to the custom endpoint" + asyncClient.getRepositoryUri().toString()); System.out.println("Initialized the sync client pointing to the custom endpoint" + syncClient.getRepositoryUri().toString()); // The client will also work with a local file-system URI. This example shows initialization // with a local URI and disabling model dependency resolution. - clientBuilder + asyncClient = new ModelsRepositoryClientBuilder() + .repositoryEndpoint(CLIENT_SAMPLES_DIRECTORY_PATH) + .modelDependencyResolution(ModelDependencyResolution.DISABLED) + .buildAsyncClient(); + + syncClient = new ModelsRepositoryClientBuilder() .repositoryEndpoint(CLIENT_SAMPLES_DIRECTORY_PATH) - .modelDependencyResolution(ModelDependencyResolution.DISABLED); - asyncClient = clientBuilder.buildAsyncClient(); - syncClient = clientBuilder.buildClient(); + .modelDependencyResolution(ModelDependencyResolution.DISABLED) + .buildClient(); - System.out.println("Initialized the async client pointing to the local file-system: " + asyncClient.getRepositoryUri().toString()); - System.out.println("Initialized the sync client pointing to the local file-system: " + syncClient.getRepositoryUri().toString()); + System.out.println("Initialized the async client pointing to the local file-system: " + asyncClient.getRepositoryUri()); + System.out.println("Initialized the sync client pointing to the local file-system: " + syncClient.getRepositoryUri()); } /** @@ -59,24 +66,19 @@ public static void clientInitializationSamples() { */ public static void getModelsFromGlobalRepository() throws InterruptedException { // Global endpoint client - ModelsRepositoryClientBuilder clientBuilder = new ModelsRepositoryClientBuilder(); - ModelsRepositoryAsyncClient asyncClient = clientBuilder.buildAsyncClient(); + ModelsRepositoryAsyncClient asyncClient = new ModelsRepositoryClientBuilder() + .buildAsyncClient(); // The output of getModels will include at least the definition for the target dtmi. // If the model dependency resolution configuration is not disabled, then models in which the // target dtmi depends on will also be included in the returned Map. String targetDtmi = "dtmi:com:example:TemperatureController;1"; - CountDownLatch modelsCountdownLatch = new CountDownLatch(1); - // In this case the above dtmi has 2 model dependencies. // dtmi:com:example:Thermostat;1 and dtmi:azure:DeviceManagement:DeviceInformation;1 asyncClient.getModels(targetDtmi) .doOnSuccess(aVoid -> System.out.println("Fetched the model and dependencies for: " + targetDtmi)) - .doOnTerminate(modelsCountdownLatch::countDown) .subscribe(res -> System.out.println(String.format("%s resolved in %s interfaces.", targetDtmi, res.size()))); - - modelsCountdownLatch.await(MAX_WAIT_TIME_ASYNC_OPERATIONS_IN_SECONDS, TimeUnit.SECONDS); } /** @@ -85,8 +87,8 @@ public static void getModelsFromGlobalRepository() throws InterruptedException { */ public static void getMultipleModelsFromGlobalRepository() throws InterruptedException { // Global endpoint client - ModelsRepositoryClientBuilder clientBuilder = new ModelsRepositoryClientBuilder(); - ModelsRepositoryAsyncClient asyncClient = clientBuilder.buildAsyncClient(); + ModelsRepositoryAsyncClient asyncClient = new ModelsRepositoryClientBuilder() + .buildAsyncClient(); // When given an Iterable of dtmis, the output of getModels() will include at // least the definitions of each dtmi enumerated in the Iterable. @@ -94,17 +96,12 @@ public static void getMultipleModelsFromGlobalRepository() throws InterruptedExc // enumerated dtmi depends on will also be included in the returned Map. Iterable dtmis = Arrays.asList("dtmi:com:example:TemperatureController;1", "dtmi:com:example:azuresphere:sampledevice;1"); - CountDownLatch modelsCountdownLatch = new CountDownLatch(1); - // In this case the dtmi "dtmi:com:example:TemperatureController;1" has 2 model dependencies // and the dtmi "dtmi:com:example:azuresphere:sampledevice;1" has no additional dependencies. // The returned Map will include 4 models. asyncClient.getModels(dtmis) .doOnSuccess(aVoid -> System.out.println("Fetched the models and dependencies for: " + String.join(", ", dtmis))) - .doOnTerminate(modelsCountdownLatch::countDown) .subscribe(res -> System.out.println(String.format("Dtmis %s resolved in %s interfaces.", String.join(", ", dtmis), res.size()))); - - modelsCountdownLatch.await(MAX_WAIT_TIME_ASYNC_OPERATIONS_IN_SECONDS, TimeUnit.SECONDS); } /** @@ -113,25 +110,19 @@ public static void getMultipleModelsFromGlobalRepository() throws InterruptedExc */ public static void getModelsFromLocalRepository() throws InterruptedException { // Local sample repository client - ModelsRepositoryClientBuilder clientBuilder = new ModelsRepositoryClientBuilder() - .repositoryEndpoint(CLIENT_SAMPLES_DIRECTORY_PATH); - - ModelsRepositoryAsyncClient asyncClient = clientBuilder.buildAsyncClient(); + ModelsRepositoryAsyncClient asyncClient = new ModelsRepositoryClientBuilder() + .repositoryEndpoint(CLIENT_SAMPLES_DIRECTORY_PATH) + .buildAsyncClient(); // The output of getModels will include at least the definition for the target dtmi. // If the model dependency resolution configuration is not disabled, then models in which the // target dtmi depends on will also be included in the returned Map. String targetDtmi = "dtmi:com:example:TemperatureController;1"; - CountDownLatch modelsCountdownLatch = new CountDownLatch(1); - // In this case the above dtmi has 2 model dependencies. // dtmi:com:example:Thermostat;1 and dtmi:azure:DeviceManagement:DeviceInformation;1 asyncClient.getModels(targetDtmi) .doOnSuccess(aVoid -> System.out.println("Fetched the model and dependencies for: " + targetDtmi)) - .doOnTerminate(modelsCountdownLatch::countDown) .subscribe(res -> System.out.println(String.format("%s resolved in %s interfaces.", targetDtmi, res.size()))); - - modelsCountdownLatch.await(MAX_WAIT_TIME_ASYNC_OPERATIONS_IN_SECONDS, TimeUnit.SECONDS); } } diff --git a/sdk/modelsrepository/azure-iot-modelsrepository/src/test/java/com/azure/iot/modelsrepository/DtmiConventionTests.java b/sdk/modelsrepository/azure-iot-modelsrepository/src/test/java/com/azure/iot/modelsrepository/DtmiConventionTests.java index 2337d9cd7f9ae..e6a85d3ca2e0f 100644 --- a/sdk/modelsrepository/azure-iot-modelsrepository/src/test/java/com/azure/iot/modelsrepository/DtmiConventionTests.java +++ b/sdk/modelsrepository/azure-iot-modelsrepository/src/test/java/com/azure/iot/modelsrepository/DtmiConventionTests.java @@ -42,7 +42,7 @@ public void dtmiToPathTest(String input, String expected) { public void getModelUriTests(String repository, String expectedUri) { final String dtmi = "dtmi:com:example:Thermostat;1"; - URI repositoryUri = DtmiConventions.convertToUri(repository); + URI repositoryUri = TestHelper.convertToUri(repository); if (expectedUri == null || expectedUri.isEmpty()) { Assertions.assertThrows(IllegalArgumentException.class, () -> DtmiConventions.getModelUri(dtmi, repositoryUri, false)); diff --git a/sdk/modelsrepository/azure-iot-modelsrepository/src/test/java/com/azure/iot/modelsrepository/TestHelper.java b/sdk/modelsrepository/azure-iot-modelsrepository/src/test/java/com/azure/iot/modelsrepository/TestHelper.java index d8c1b43a97b35..736cf59272970 100644 --- a/sdk/modelsrepository/azure-iot-modelsrepository/src/test/java/com/azure/iot/modelsrepository/TestHelper.java +++ b/sdk/modelsrepository/azure-iot-modelsrepository/src/test/java/com/azure/iot/modelsrepository/TestHelper.java @@ -9,6 +9,7 @@ import com.azure.iot.modelsrepository.implementation.ModelsRepositoryConstants; import org.junit.jupiter.params.provider.Arguments; +import java.net.URI; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -81,4 +82,19 @@ private static boolean shouldServiceVersionBeTested(ModelsRepositoryServiceVersi return Arrays.stream(configuredServiceVersionList).anyMatch(configuredServiceVersion -> serviceVersion.getVersion().equals(configuredServiceVersion.trim())); } + + /** + * Converts a string to {@link URI} + * + * @param uri String format of the path + * @return {@link URI} representation of the path/uri. + * @throws IllegalArgumentException If the {@code uri} is invalid. + */ + public static URI convertToUri(String uri) throws IllegalArgumentException { + try { + return new URI(uri); + } catch (Exception e) { + throw new IllegalArgumentException("Invalid URI format", e); + } + } }