Skip to content

Commit

Permalink
DMR: Add Support for models repo metadata functionality and eliminate…
Browse files Browse the repository at this point in the history
… TryFromExpanded from API surface (#24083)
  • Loading branch information
avagraw authored Oct 7, 2021
1 parent 5e3b44b commit e329a8a
Show file tree
Hide file tree
Showing 61 changed files with 2,373 additions and 188 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ public static boolean isValidDtmi(String dtmi) {
if (dtmi == null || dtmi.isEmpty()) {
return false;
}

return VALID_DTMI_PATTERN.matcher(dtmi).matches();
}

Expand Down Expand Up @@ -74,6 +73,44 @@ public static URI getModelUri(String dtmi, URI repositoryUri, boolean expanded)
}
}

/**
* Generates the model path.
*
* @param dtmi DigitalTwin Model Id.
* @param expanded Is model from precomputed values
* @return The model path.
*/
public static String getModelPath(String dtmi, boolean expanded) {
String dtmiPath = dtmiToPath(dtmi);

if (expanded) {
dtmiPath = dtmiPath.replace(ModelsRepositoryConstants.JSON_EXTENSION,
ModelsRepositoryConstants.JSON_EXPANDED_EXTENSION);
}

return dtmiPath;
}

/**
* Generates the model repository's metadata URI.
*
* @param repositoryUri The repository uri
* @return The repository metadata uri.
* @throws IllegalArgumentException if the provided repository URI is not valid
*/
public static URI getMetadataUri(URI repositoryUri) {
try {
String stringUri = repositoryUri.toString();
if (stringUri.endsWith("/")) {
return new URI(stringUri + ModelsRepositoryConstants.MODELS_REPOSITORY_METADATA_FILE);
} else {
return new URI(stringUri + "/" + ModelsRepositoryConstants.MODELS_REPOSITORY_METADATA_FILE);
}
} catch (URISyntaxException e) {
throw new IllegalArgumentException("Invalid uri syntax");
}
}

static String dtmiToPath(String dtmi) {
if (!isValidDtmi(dtmi)) {
throw new IllegalArgumentException(String.format(StatusStrings.INVALID_DTMI_FORMAT_S, dtmi));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,7 @@ public enum ModelDependencyResolution {
DISABLED,

/**
* Enable model dependency resolution. The client will parse models and calculate dependencies recursively.
* Enable model dependency resolution.
*/
ENABLED,

/**
* Try to get pre-computed model dependencies using .expanded.json.
* If the model expanded form does not exist, it will fall back to {@link ModelDependencyResolution#ENABLED}.
*/
TRY_FROM_EXPANDED,
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public final class ModelsRepositoryClientBuilder {
// Fields with default values.
private URI repositoryEndpoint;

private ModelDependencyResolution modelDependencyResolution = ModelDependencyResolution.TRY_FROM_EXPANDED;
private ModelDependencyResolution modelDependencyResolution = ModelDependencyResolution.ENABLED;

// optional/have default values
private ModelsRepositoryServiceVersion serviceVersion;
Expand Down Expand Up @@ -152,7 +152,7 @@ private static HttpPipeline constructPipeline(
/**
* Create a {@link ModelsRepositoryClient} based on the builder settings.
*
* @return the created synchronous ModelsRepotioryClient
* @return the created synchronous ModelsRepositoryClient
*/
public ModelsRepositoryClient buildClient() {
return new ModelsRepositoryClient(buildAsyncClient());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
import com.azure.core.util.Context;
import com.azure.core.util.logging.ClientLogger;
import com.azure.iot.modelsrepository.DtmiConventions;
import com.azure.iot.modelsrepository.ModelDependencyResolution;
import com.azure.iot.modelsrepository.implementation.models.FetchResult;
import com.azure.iot.modelsrepository.implementation.models.FetchMetadataResult;
import com.azure.iot.modelsrepository.implementation.models.FetchModelResult;
import reactor.core.publisher.Mono;

import java.io.File;
Expand Down Expand Up @@ -39,15 +39,15 @@ class FileModelFetcher implements ModelFetcher {
}

@Override
public Mono<FetchResult> fetchAsync(String dtmi, URI repositoryUri, ModelDependencyResolution resolutionOption, Context context) {
public Mono<FetchModelResult> fetchModelAsync(String dtmi, URI repositoryUri, boolean tryFromExpanded, Context context) {
return Mono.defer(() -> {
Queue<String> work = new LinkedList<>();

try {
if (resolutionOption == ModelDependencyResolution.TRY_FROM_EXPANDED) {
work.add(getPath(dtmi, repositoryUri, true));
if (tryFromExpanded) {
work.add(getModelPath(dtmi, repositoryUri, true));
}
work.add(getPath(dtmi, repositoryUri, false));
work.add(getModelPath(dtmi, repositoryUri, false));
} catch (MalformedURLException | URISyntaxException e) {
return Mono.error(new AzureException(e));
}
Expand All @@ -63,7 +63,7 @@ public Mono<FetchResult> fetchAsync(String dtmi, URI repositoryUri, ModelDepende
if (Files.exists(path)) {
try {
return Mono.just(
new FetchResult()
new FetchModelResult()
.setDefinition(new String(Files.readAllBytes(path), StandardCharsets.UTF_8))
.setPath(tryContentPath));
} catch (IOException e) {
Expand All @@ -80,8 +80,41 @@ public Mono<FetchResult> fetchAsync(String dtmi, URI repositoryUri, ModelDepende
});
}

private String getPath(String dtmi, URI repositoryUri, boolean expanded) throws URISyntaxException, MalformedURLException {
@Override
public Mono<FetchMetadataResult> fetchMetadataAsync(URI repositoryUri, Context context) {
try {
String tryContentPath = getMetadataPath(repositoryUri);
Path path = Paths.get(new File(tryContentPath).getPath());

logger.info(StatusStrings.FETCHING_METADATA_CONTENT, path);

if (Files.exists(path)) {
try {
return Mono.just(
new FetchMetadataResult()
.setDefinition(new String(Files.readAllBytes(path), StandardCharsets.UTF_8))
.setPath(tryContentPath));
} catch (IOException e) {
logger.error(String.format(StatusStrings.ERROR_FETCHING_METADATA_CONTENT + " Error: %s.",
path.toString(), e.getMessage()));
return Mono.error(new AzureException(e));
}
}

logger.error(String.format(StatusStrings.ERROR_FETCHING_METADATA_CONTENT, path.toString()));
return Mono.just(new FetchMetadataResult());
} catch (MalformedURLException | URISyntaxException e) {
return Mono.error(new AzureException(e));
}
}

private String getModelPath(String dtmi, URI repositoryUri, boolean expanded) throws URISyntaxException, MalformedURLException {
return DtmiConventions.getModelUri(dtmi, repositoryUri, expanded)
.getPath();
}

private String getMetadataPath(URI repositoryUri) throws URISyntaxException, MalformedURLException {
return DtmiConventions.getMetadataUri(repositoryUri)
.getPath();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
import com.azure.core.util.Context;
import com.azure.core.util.logging.ClientLogger;
import com.azure.iot.modelsrepository.DtmiConventions;
import com.azure.iot.modelsrepository.ModelDependencyResolution;
import com.azure.iot.modelsrepository.implementation.models.FetchResult;
import com.azure.iot.modelsrepository.implementation.models.FetchMetadataResult;
import com.azure.iot.modelsrepository.implementation.models.FetchModelResult;
import com.fasterxml.jackson.core.JsonProcessingException;
import reactor.core.publisher.Mono;

import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
Expand All @@ -31,14 +33,14 @@ class HttpModelFetcher implements ModelFetcher {
}

@Override
public Mono<FetchResult> fetchAsync(String dtmi, URI repositoryUri, ModelDependencyResolution resolutionOption, Context context) {
public Mono<FetchModelResult> fetchModelAsync(String dtmi, URI repositoryUri, boolean tryFromExpanded, Context context) {
return Mono.defer(() -> {
Queue<String> work = new LinkedList<>();
try {
if (resolutionOption == ModelDependencyResolution.TRY_FROM_EXPANDED) {
work.add(getPath(dtmi, repositoryUri, true));
if (tryFromExpanded) {
work.add(getModelPath(dtmi, repositoryUri, true));
}
work.add(getPath(dtmi, repositoryUri, false));
work.add(getModelPath(dtmi, repositoryUri, false));
} catch (Exception e) {
return Mono.error(new AzureException(e));
}
Expand All @@ -52,13 +54,40 @@ public Mono<FetchResult> fetchAsync(String dtmi, URI repositoryUri, ModelDepende
if (work.size() != 0) {
return evaluatePath(work.poll(), context);
} else {
logger.error(String.format(StatusStrings.ERROR_FETCHING_MODEL_CONTENT, tryContentPath));
return Mono.error(error);
}
})
.map(s -> new FetchResult().setPath(tryContentPath).setDefinition(s));
.map(s -> new FetchModelResult().setPath(tryContentPath).setDefinition(s));
});
}

@Override
public Mono<FetchMetadataResult> fetchMetadataAsync(URI repositoryUri, Context context) {
try {
String tryContentPath = getMetadataPath(repositoryUri);

logger.info(StatusStrings.FETCHING_METADATA_CONTENT, tryContentPath);

return evaluatePath(tryContentPath, context)
.onErrorResume(error -> {
logger.error(String.format(StatusStrings.ERROR_FETCHING_METADATA_CONTENT + " Error: %s",
tryContentPath, error.getMessage()));
return Mono.error(error);
})
.map(s -> {
try {
return new FetchMetadataResult().setPath(tryContentPath).setDefinition(s);
} catch (JsonProcessingException e) {
logger.error(String.format(StatusStrings.ERROR_FETCHING_METADATA_CONTENT, tryContentPath));
return null;
}
});
} catch (MalformedURLException | URISyntaxException e) {
return Mono.error(new AzureException(e));
}
}

private Mono<String> evaluatePath(String tryContentPath, Context context) {
return protocolLayer
.getModelsRepository()
Expand All @@ -69,7 +98,11 @@ private Mono<String> evaluatePath(String tryContentPath, Context context) {
});
}

private String getPath(String dtmi, URI repositoryUri, boolean expanded) throws URISyntaxException {
return DtmiConventions.getModelUri(dtmi, repositoryUri, expanded).getPath();
private String getModelPath(String dtmi, URI repositoryUri, boolean expanded) throws URISyntaxException {
return DtmiConventions.getModelPath(dtmi, expanded);
}

private String getMetadataPath(URI repositoryUri) throws URISyntaxException, MalformedURLException {
return DtmiConventions.getMetadataUri(repositoryUri).getPath();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,25 @@

package com.azure.iot.modelsrepository.implementation;

import com.azure.iot.modelsrepository.implementation.models.FetchResult;
import com.azure.iot.modelsrepository.implementation.models.FetchModelResult;

import java.util.Map;

/**
* This type is used to unify the expand operation return types in the recursive function and has no other use cases.
* Do not take any dependencies on this type.
*/
class IntermediateFetchResult {
private final FetchResult fetchResult;
class IntermediateFetchModelResult {
private final FetchModelResult fetchModelResult;
private final Map<String, String> map;

IntermediateFetchResult(FetchResult fetchResult, Map<String, String> map) {
this.fetchResult = fetchResult;
IntermediateFetchModelResult(FetchModelResult fetchModelResult, Map<String, String> map) {
this.fetchModelResult = fetchModelResult;
this.map = map;
}

public FetchResult getFetchResult() {
return fetchResult;
public FetchModelResult getFetchModelResult() {
return fetchModelResult;
}

public Map<String, String> getMap() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
package com.azure.iot.modelsrepository.implementation;

import com.azure.core.util.Context;
import com.azure.iot.modelsrepository.ModelDependencyResolution;
import com.azure.iot.modelsrepository.implementation.models.FetchResult;
import com.azure.iot.modelsrepository.implementation.models.FetchMetadataResult;
import com.azure.iot.modelsrepository.implementation.models.FetchModelResult;
import reactor.core.publisher.Mono;

import java.net.URI;

interface ModelFetcher {
Mono<FetchResult> fetchAsync(String dtmi, URI repositoryUri, ModelDependencyResolution resolutionOption, Context context);
Mono<FetchModelResult> fetchModelAsync(String dtmi, URI repositoryUri, boolean tryFromExpanded, Context context);
Mono<FetchMetadataResult> fetchMetadataAsync(URI repositoryUri, Context context);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public class ModelsRepositoryConstants {
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";
public static final String MODELS_REPOSITORY_METADATA_FILE = "metadata.json";

// DTDL conventions
public static final String DTDL_TYPE = "@type";
Expand Down
Loading

0 comments on commit e329a8a

Please sign in to comment.