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

DMR - Update documentation and enable pipelines #20064

Merged
merged 13 commits into from
Mar 23, 2021
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

### New features

- N/A
- Initial preview of Azure Models Repository SDK

### Breaking changes

Expand Down
38 changes: 38 additions & 0 deletions sdk/modelsrepository/azure-iot-modelsrepository/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Azure IoT Models Repository client library for Java

This library provides functionality for interacting with the [Azure IoT Models Repository][modelsrepository_iot_endpoint]. It also aims to provide a consistent experience working with digital twin model repositories following Azure IoT conventions.

[Source code][source]

## Getting started

The complete Microsoft Azure SDK can be downloaded from the [Microsoft Azure downloads][microsoft_sdk_download] page, and it ships with support for building deployment packages, integrating with tooling, rich command line tooling, and more.
Expand All @@ -8,18 +12,46 @@ For the best development experience, developers should use the official Microsof

### Include the Package

[//]: # ({x-version-update-start;com.azure:azure-iot-modelsrepository;current})

```xml
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-iot-modelsrepository</artifactId>
<version>1.0.0-beta.1</version>
</dependency>
```

[//]: # ({x-version-update-end})

### Prerequisites

- A models repository following [Azure IoT conventions][modelsrepository_conventions]
- The models repository can be located on the local filesystem or hosted on a webserver.
- Azure IoT hosts the global [Azure IoT Models Repository][modelsrepository_iot_endpoint] which the client will point to by default if no URI is provided.

### Authenticate the Client

Currently no authentication mechanisms are supported in the client. The global endpoint is not tied to an Azure subscription and does not support auth. All models published are meant for anonymous public consumption.

## Key concepts

The Azure IoT Models Repository enables builders to manage and share digital twin models. The models are [JSON-LD][json_ld_reference] documents defined using the Digital Twins Definition Language ([DTDL][dtdlv2_reference]).

The repository defines a pattern to store DTDL interfaces in a directory structure based on the Digital Twin Model Identifier (DTMI). You can locate an interface in the repository by converting the DTMI to a relative path. For example, the DTMI "`dtmi:com:example:Thermostat;1`" translates to `/dtmi/com/example/thermostat-1.json`.

## Examples

You can familiarize yourself with the client using [samples for IoT Models Repository][modelsrepository_samples].

## Troubleshooting

All events and errors that surface from the service calls will be logged with the ClientLogger.

## Next steps

See implementation examples with our [code samples][modelsrepository_samples].

## Contributing

This project welcomes contributions and suggestions.
Expand All @@ -36,3 +68,9 @@ For more information see the Code of Conduct FAQ or contact opencode@microsoft.c
<!-- LINKS -->
[microsoft_sdk_download]: https://azure.microsoft.com/downloads/?sdk=net
[code_of_conduct]: https://opensource.microsoft.com/codeofconduct/
[modelsrepository_iot_endpoint]: https://devicemodels.azure.com/
[source]: https://github.com/Azure/azure-sdk-for-java/tree/master/sdk/modelsrepository/azure-iot-modelsrepository/src
[modelsrepository_conventions]: https://github.com/Azure/iot-plugandplay-models-tools/wiki
[json_ld_reference]: https://json-ld.org
[dtdlv2_reference]: https://github.com/Azure/opendigitaltwins-dtdl/blob/master/DTDL/v2/dtdlv2.md
[modelsrepository_samples]: https://github.com/Azure/azure-sdk-for-java/tree/master/sdk/modelsrepository/azure-iot-modelsrepository/src/samples
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,11 @@

package com.azure.iot.modelsrepository;

import com.azure.core.util.UrlBuilder;
import com.azure.iot.modelsrepository.implementation.ModelsRepositoryConstants;
import com.azure.iot.modelsrepository.implementation.StatusStrings;

import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Locale;
import java.util.regex.Pattern;

Expand All @@ -21,8 +17,7 @@
*/
public final class DtmiConventions {

private DtmiConventions() {
}
private DtmiConventions() { }

/**
* A DTMI has three components: scheme, path, and version. Scheme and path are separated by a colon. Path and
Expand Down Expand Up @@ -56,7 +51,8 @@ public static boolean isValidDtmi(String dtmi) {
* @param dtmi DigitalTwin Model Id.
* @param repositoryUri The repository uri
* @param expanded Is model from precomputed values
* @return The model uri Will throw an {@link IllegalArgumentException} if the provided dtmi is not valid.
* @return The model uri.
* @throws IllegalArgumentException if the provided dtmi is not valid.
*/
public static URI getModelUri(String dtmi, URI repositoryUri, boolean expanded) {
String dtmiPath = dtmiToPath(dtmi);
Expand All @@ -66,33 +62,15 @@ public static URI getModelUri(String dtmi, URI repositoryUri, boolean expanded)
ModelsRepositoryConstants.JSON_EXPANDED_EXTENSION);
}

UrlBuilder urlBuilder = new UrlBuilder();
urlBuilder.setHost(repositoryUri.getHost());
urlBuilder.setScheme(repositoryUri.getScheme());
urlBuilder.setPath(repositoryUri.getPath());
urlBuilder.setQuery(repositoryUri.getQuery());

if (repositoryUri.getPort() > 0) {
urlBuilder.setPort(repositoryUri.getPort());
}

String path = urlBuilder.getPath();

if (path != null && !path.endsWith("/")) {
urlBuilder.setPath(path + "/");
}

if (urlBuilder.getPath() == null) {
urlBuilder.setPath(dtmiPath);
} else {
urlBuilder.setPath(urlBuilder.getPath() + dtmiPath);
}

String stringUri = repositoryUri.toString();
try {
return new URI(urlBuilder.toString());
} catch (Exception e) {
// No exceptions will be thrown as the input is a valid URI format.
return null;
if (stringUri.endsWith("/")) {
return new URI(stringUri + dtmiPath);
} else {
return new URI(stringUri + "/" + dtmiPath);
}
} catch (URISyntaxException e) {
throw new IllegalArgumentException("Invalid uri syntax");
}
}

Expand All @@ -103,16 +81,11 @@ public static URI getModelUri(String dtmi, URI repositoryUri, boolean expanded)
* @return {@link URI} representation of the path/uri.
* @throws IllegalArgumentException If the {@code uri} is invalid.
*/
public static URI convertToUri(String uri) {
public static URI convertToUri(String uri) throws IllegalArgumentException {
try {
return new URI(uri);
} catch (URISyntaxException ex) {
try {
Path path = Paths.get(uri).normalize();
return new File(path.toAbsolutePath().toString()).toURI();
} catch (Exception e) {
throw new IllegalArgumentException("Invalid uri format", e);
}
} catch (Exception e) {
throw new IllegalArgumentException("Invalid uri format", e);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

public class ModelsRepositoryConstants {
public static final String FILE = "file";
public static final String HTTP = "http";
public static final String JSON_EXTENSION = ".json";
public static final String JSON_EXPANDED_EXTENSION = ".expanded.json";
public static final String DEFAULT_MODELS_REPOSITORY_ENDPOINT = "https://devicemodels.azure.com";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,13 @@ public RepositoryHandler(URI repositoryUri, ModelsRepositoryAPIImpl protocolLaye
this.repositoryUri = repositoryUri;
this.logger = new ClientLogger(RepositoryHandler.class);

if (this.repositoryUri.getScheme().toLowerCase(Locale.getDefault()).startsWith(ModelsRepositoryConstants.FILE)) {
this.modelFetcher = new FileModelFetcher();
} else {
if (this.repositoryUri.getScheme() != null
&& this.repositoryUri.getScheme()
.toLowerCase(Locale.getDefault())
.startsWith(ModelsRepositoryConstants.HTTP)) {
this.modelFetcher = new HttpModelFetcher(protocolLayer);
} else {
this.modelFetcher = new FileModelFetcher();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
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/");
private static final String CLIENT_SAMPLES_DIRECTORY_PATH = (System.getProperty("user.dir").concat("/src/samples/resources/TestModelRepo/")).replace("\\", "/");

private static final int MAX_WAIT_TIME_ASYNC_OPERATIONS_IN_SECONDS = 10;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,9 @@

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.MethodSource;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.stream.Stream;

class DtmiConventionTests {

Expand All @@ -35,8 +31,15 @@ public void dtmiToPathTest(String input, String expected) {
}

@ParameterizedTest
@MethodSource("getModelUriTestsSupplier")
public void getModelUriTests(String repository, String expectedUri) throws URISyntaxException {
@CsvSource({
"https://localhost/repository/, https://localhost/repository/dtmi/com/example/thermostat-1.json",
"https://localhost/REPOSITORY, https://localhost/REPOSITORY/dtmi/com/example/thermostat-1.json",
"file:///path/to/repository/, file:///path/to/repository/dtmi/com/example/thermostat-1.json",
"file://path/to/RepoSitory, file://path/to/RepoSitory/dtmi/com/example/thermostat-1.json",
"C:/path/to/repository/, C:/path/to/repository/dtmi/com/example/thermostat-1.json",
"//server//repository, //server//repository/dtmi/com/example/thermostat-1.json"
})
public void getModelUriTests(String repository, String expectedUri) {
final String dtmi = "dtmi:com:example:Thermostat;1";

URI repositoryUri = DtmiConventions.convertToUri(repository);
Expand All @@ -50,23 +53,6 @@ public void getModelUriTests(String repository, String expectedUri) throws URISy
Assertions.assertEquals(expectedUri, modelUri.toString());
}

private static Stream<Arguments> getModelUriTestsSupplier() {
return Stream.of(
Arguments.of("https://localhost/repository/",
"https://localhost/repository/dtmi/com/example/thermostat-1.json"),
Arguments.of("https://localhost/REPOSITORY",
"https://localhost/REPOSITORY/dtmi/com/example/thermostat-1.json"),
Arguments.of("file:///path/to/repository/",
"file:///path/to/repository/dtmi/com/example/thermostat-1.json"),
Arguments.of("file://path/to/RepoSitory", "file://path/to/RepoSitory/dtmi/com/example/thermostat-1.json")

// TODO: These were disabled as they fail in Linux and macOS, likely due to '\' in the URI.
// Arguments.of("C:\\path\\to\\repository\\",
// "file:///C:/path/to/repository/dtmi/com/example/thermostat-1.json"),
// Arguments.of("\\\\server\\repository", "file:////server/repository/dtmi/com/example/thermostat-1.json")
);
}

@ParameterizedTest
@CsvSource({
"dtmi:com:example:Thermostat;1, true",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

import com.azure.core.exception.AzureException;
import com.azure.core.http.HttpClient;
import com.azure.core.test.annotation.DoNotRecord;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
Expand All @@ -22,7 +21,6 @@ class ModelRepositoryIntegrationTests extends ModelsRepositoryTestBase {

@ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS)
@MethodSource("com.azure.iot.modelsrepository.TestHelper#getTestParameters")
@DoNotRecord(skipInPlayback = true) // TODO: Remove this once playback recordings are added.
public void getModelsSingleDtmiNoDependencies(HttpClient httpClient, ModelsRepositoryServiceVersion serviceVersion, String repositoryUri) throws URISyntaxException {
final String dtmi = "dtmi:com:example:Thermostat;1";

Expand All @@ -36,7 +34,6 @@ public void getModelsSingleDtmiNoDependencies(HttpClient httpClient, ModelsRepos

@ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS)
@MethodSource("com.azure.iot.modelsrepository.TestHelper#getTestParameters")
@DoNotRecord(skipInPlayback = true) // TODO: Remove this once playback recordings are added.
public void getModelsSingleDtmiDoesNotExist(HttpClient httpClient, ModelsRepositoryServiceVersion serviceVersion, String repositoryUri) throws URISyntaxException {
final String dtmi = "dtmi:com:example:Thermostatddd;1";

Expand All @@ -49,7 +46,6 @@ public void getModelsSingleDtmiDoesNotExist(HttpClient httpClient, ModelsReposit

@ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS)
@MethodSource("com.azure.iot.modelsrepository.TestHelper#getTestParameters")
@DoNotRecord(skipInPlayback = true) // TODO: Remove this once playback recordings are added.
public void getModelsSingleDtmiWithDependencies(HttpClient httpClient, ModelsRepositoryServiceVersion serviceVersion, String repositoryUri) throws URISyntaxException {
final String dtmi = "dtmi:com:example:TemperatureController;1";
List<String> expectedDependencies = Arrays.asList("dtmi:com:example:Thermostat;1", "dtmi:azure:DeviceManagement:DeviceInformation;1");
Expand All @@ -66,7 +62,6 @@ public void getModelsSingleDtmiWithDependencies(HttpClient httpClient, ModelsRep

@ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS)
@MethodSource("com.azure.iot.modelsrepository.TestHelper#getTestParameters")
@DoNotRecord(skipInPlayback = true) // TODO: Remove this once playback recordings are added.
public void getModelsEnsureNoDuplicates(HttpClient httpClient, ModelsRepositoryServiceVersion serviceVersion, String repositoryUri) throws URISyntaxException {
List<String> inputDtmis = Arrays.asList(
"dtmi:azure:DeviceManagement:DeviceInformation;1",
Expand All @@ -83,7 +78,6 @@ public void getModelsEnsureNoDuplicates(HttpClient httpClient, ModelsRepositoryS

@ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS)
@MethodSource("com.azure.iot.modelsrepository.TestHelper#getTestParameters")
@DoNotRecord(skipInPlayback = true) // TODO: Remove this once playback recordings are added.
public void getModelsSingleDtmiWithDepsDisableDependencyResolution(HttpClient httpClient, ModelsRepositoryServiceVersion serviceVersion, String repositoryUri) throws URISyntaxException {
final String dtmi = "dtmi:com:example:Thermostat;1";
ModelsRepositoryAsyncClient client = getAsyncClient(httpClient, serviceVersion, repositoryUri);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@

class ModelsRepositoryTestBase extends TestBase {

private static final String PLAYBACK_ENDPOINT = "https://playback.net/";


protected ModelsRepositoryClientBuilder getModelsRepositoryClientbuilder(HttpClient httpClient, ModelsRepositoryServiceVersion serviceVersion, String repositoryEndpoint) throws URISyntaxException {
ModelsRepositoryClientBuilder builder = new ModelsRepositoryClientBuilder();
builder.serviceVersion(serviceVersion);
Expand All @@ -23,7 +20,7 @@ protected ModelsRepositoryClientBuilder getModelsRepositoryClientbuilder(HttpCli
builder.httpClient(interceptorManager.getPlaybackClient());
// Use fake credentials for playback mode.
// Connect to a special host when running tests in playback mode.
builder.repositoryEndpoint(PLAYBACK_ENDPOINT);
builder.repositoryEndpoint(repositoryEndpoint);
return builder;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
class TestHelper {
public static final String DISPLAY_NAME_WITH_ARGUMENTS = "{displayName} with [{arguments}]";
private static final String AZURE_IOT_MODELSREPOSITORY_TEST_SERVICE_VERSIONS = "AZURE_IOT_MODELSREPOSITORY_TEST_SERVICE_VERSIONS";
private static final String LOCAL_TEST_REPOSITORY_PATH = System.getProperty("user.dir") + "/src/test/resources/TestModelRepo/";
private static final String LOCAL_TEST_REPOSITORY_PATH = (System.getProperty("user.dir") + "/src/test/resources/TestModelRepo/").replace("\\", "/");

private static final String SERVICE_VERSION_FROM_ENV =
Configuration.getGlobalConfiguration().get(AZURE_IOT_MODELSREPOSITORY_TEST_SERVICE_VERSIONS);
Expand Down
Loading