Skip to content

Commit e329a8a

Browse files
authored
DMR: Add Support for models repo metadata functionality and eliminate TryFromExpanded from API surface (Azure#24083)
1 parent 5e3b44b commit e329a8a

File tree

61 files changed

+2373
-188
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+2373
-188
lines changed

sdk/modelsrepository/azure-iot-modelsrepository/src/main/java/com/azure/iot/modelsrepository/DtmiConventions.java

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ public static boolean isValidDtmi(String dtmi) {
4141
if (dtmi == null || dtmi.isEmpty()) {
4242
return false;
4343
}
44-
4544
return VALID_DTMI_PATTERN.matcher(dtmi).matches();
4645
}
4746

@@ -74,6 +73,44 @@ public static URI getModelUri(String dtmi, URI repositoryUri, boolean expanded)
7473
}
7574
}
7675

76+
/**
77+
* Generates the model path.
78+
*
79+
* @param dtmi DigitalTwin Model Id.
80+
* @param expanded Is model from precomputed values
81+
* @return The model path.
82+
*/
83+
public static String getModelPath(String dtmi, boolean expanded) {
84+
String dtmiPath = dtmiToPath(dtmi);
85+
86+
if (expanded) {
87+
dtmiPath = dtmiPath.replace(ModelsRepositoryConstants.JSON_EXTENSION,
88+
ModelsRepositoryConstants.JSON_EXPANDED_EXTENSION);
89+
}
90+
91+
return dtmiPath;
92+
}
93+
94+
/**
95+
* Generates the model repository's metadata URI.
96+
*
97+
* @param repositoryUri The repository uri
98+
* @return The repository metadata uri.
99+
* @throws IllegalArgumentException if the provided repository URI is not valid
100+
*/
101+
public static URI getMetadataUri(URI repositoryUri) {
102+
try {
103+
String stringUri = repositoryUri.toString();
104+
if (stringUri.endsWith("/")) {
105+
return new URI(stringUri + ModelsRepositoryConstants.MODELS_REPOSITORY_METADATA_FILE);
106+
} else {
107+
return new URI(stringUri + "/" + ModelsRepositoryConstants.MODELS_REPOSITORY_METADATA_FILE);
108+
}
109+
} catch (URISyntaxException e) {
110+
throw new IllegalArgumentException("Invalid uri syntax");
111+
}
112+
}
113+
77114
static String dtmiToPath(String dtmi) {
78115
if (!isValidDtmi(dtmi)) {
79116
throw new IllegalArgumentException(String.format(StatusStrings.INVALID_DTMI_FORMAT_S, dtmi));

sdk/modelsrepository/azure-iot-modelsrepository/src/main/java/com/azure/iot/modelsrepository/ModelDependencyResolution.java

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,7 @@ public enum ModelDependencyResolution {
1313
DISABLED,
1414

1515
/**
16-
* Enable model dependency resolution. The client will parse models and calculate dependencies recursively.
16+
* Enable model dependency resolution.
1717
*/
1818
ENABLED,
19-
20-
/**
21-
* Try to get pre-computed model dependencies using .expanded.json.
22-
* If the model expanded form does not exist, it will fall back to {@link ModelDependencyResolution#ENABLED}.
23-
*/
24-
TRY_FROM_EXPANDED,
2519
}

sdk/modelsrepository/azure-iot-modelsrepository/src/main/java/com/azure/iot/modelsrepository/ModelsRepositoryClientBuilder.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public final class ModelsRepositoryClientBuilder {
5858
// Fields with default values.
5959
private URI repositoryEndpoint;
6060

61-
private ModelDependencyResolution modelDependencyResolution = ModelDependencyResolution.TRY_FROM_EXPANDED;
61+
private ModelDependencyResolution modelDependencyResolution = ModelDependencyResolution.ENABLED;
6262

6363
// optional/have default values
6464
private ModelsRepositoryServiceVersion serviceVersion;
@@ -152,7 +152,7 @@ private static HttpPipeline constructPipeline(
152152
/**
153153
* Create a {@link ModelsRepositoryClient} based on the builder settings.
154154
*
155-
* @return the created synchronous ModelsRepotioryClient
155+
* @return the created synchronous ModelsRepositoryClient
156156
*/
157157
public ModelsRepositoryClient buildClient() {
158158
return new ModelsRepositoryClient(buildAsyncClient());

sdk/modelsrepository/azure-iot-modelsrepository/src/main/java/com/azure/iot/modelsrepository/implementation/FileModelFetcher.java

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
import com.azure.core.util.Context;
88
import com.azure.core.util.logging.ClientLogger;
99
import com.azure.iot.modelsrepository.DtmiConventions;
10-
import com.azure.iot.modelsrepository.ModelDependencyResolution;
11-
import com.azure.iot.modelsrepository.implementation.models.FetchResult;
10+
import com.azure.iot.modelsrepository.implementation.models.FetchMetadataResult;
11+
import com.azure.iot.modelsrepository.implementation.models.FetchModelResult;
1212
import reactor.core.publisher.Mono;
1313

1414
import java.io.File;
@@ -39,15 +39,15 @@ class FileModelFetcher implements ModelFetcher {
3939
}
4040

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

4646
try {
47-
if (resolutionOption == ModelDependencyResolution.TRY_FROM_EXPANDED) {
48-
work.add(getPath(dtmi, repositoryUri, true));
47+
if (tryFromExpanded) {
48+
work.add(getModelPath(dtmi, repositoryUri, true));
4949
}
50-
work.add(getPath(dtmi, repositoryUri, false));
50+
work.add(getModelPath(dtmi, repositoryUri, false));
5151
} catch (MalformedURLException | URISyntaxException e) {
5252
return Mono.error(new AzureException(e));
5353
}
@@ -63,7 +63,7 @@ public Mono<FetchResult> fetchAsync(String dtmi, URI repositoryUri, ModelDepende
6363
if (Files.exists(path)) {
6464
try {
6565
return Mono.just(
66-
new FetchResult()
66+
new FetchModelResult()
6767
.setDefinition(new String(Files.readAllBytes(path), StandardCharsets.UTF_8))
6868
.setPath(tryContentPath));
6969
} catch (IOException e) {
@@ -80,8 +80,41 @@ public Mono<FetchResult> fetchAsync(String dtmi, URI repositoryUri, ModelDepende
8080
});
8181
}
8282

83-
private String getPath(String dtmi, URI repositoryUri, boolean expanded) throws URISyntaxException, MalformedURLException {
83+
@Override
84+
public Mono<FetchMetadataResult> fetchMetadataAsync(URI repositoryUri, Context context) {
85+
try {
86+
String tryContentPath = getMetadataPath(repositoryUri);
87+
Path path = Paths.get(new File(tryContentPath).getPath());
88+
89+
logger.info(StatusStrings.FETCHING_METADATA_CONTENT, path);
90+
91+
if (Files.exists(path)) {
92+
try {
93+
return Mono.just(
94+
new FetchMetadataResult()
95+
.setDefinition(new String(Files.readAllBytes(path), StandardCharsets.UTF_8))
96+
.setPath(tryContentPath));
97+
} catch (IOException e) {
98+
logger.error(String.format(StatusStrings.ERROR_FETCHING_METADATA_CONTENT + " Error: %s.",
99+
path.toString(), e.getMessage()));
100+
return Mono.error(new AzureException(e));
101+
}
102+
}
103+
104+
logger.error(String.format(StatusStrings.ERROR_FETCHING_METADATA_CONTENT, path.toString()));
105+
return Mono.just(new FetchMetadataResult());
106+
} catch (MalformedURLException | URISyntaxException e) {
107+
return Mono.error(new AzureException(e));
108+
}
109+
}
110+
111+
private String getModelPath(String dtmi, URI repositoryUri, boolean expanded) throws URISyntaxException, MalformedURLException {
84112
return DtmiConventions.getModelUri(dtmi, repositoryUri, expanded)
85113
.getPath();
86114
}
115+
116+
private String getMetadataPath(URI repositoryUri) throws URISyntaxException, MalformedURLException {
117+
return DtmiConventions.getMetadataUri(repositoryUri)
118+
.getPath();
119+
}
87120
}

sdk/modelsrepository/azure-iot-modelsrepository/src/main/java/com/azure/iot/modelsrepository/implementation/HttpModelFetcher.java

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@
77
import com.azure.core.util.Context;
88
import com.azure.core.util.logging.ClientLogger;
99
import com.azure.iot.modelsrepository.DtmiConventions;
10-
import com.azure.iot.modelsrepository.ModelDependencyResolution;
11-
import com.azure.iot.modelsrepository.implementation.models.FetchResult;
10+
import com.azure.iot.modelsrepository.implementation.models.FetchMetadataResult;
11+
import com.azure.iot.modelsrepository.implementation.models.FetchModelResult;
12+
import com.fasterxml.jackson.core.JsonProcessingException;
1213
import reactor.core.publisher.Mono;
1314

15+
import java.net.MalformedURLException;
1416
import java.net.URI;
1517
import java.net.URISyntaxException;
1618
import java.nio.charset.StandardCharsets;
@@ -31,14 +33,14 @@ class HttpModelFetcher implements ModelFetcher {
3133
}
3234

3335
@Override
34-
public Mono<FetchResult> fetchAsync(String dtmi, URI repositoryUri, ModelDependencyResolution resolutionOption, Context context) {
36+
public Mono<FetchModelResult> fetchModelAsync(String dtmi, URI repositoryUri, boolean tryFromExpanded, Context context) {
3537
return Mono.defer(() -> {
3638
Queue<String> work = new LinkedList<>();
3739
try {
38-
if (resolutionOption == ModelDependencyResolution.TRY_FROM_EXPANDED) {
39-
work.add(getPath(dtmi, repositoryUri, true));
40+
if (tryFromExpanded) {
41+
work.add(getModelPath(dtmi, repositoryUri, true));
4042
}
41-
work.add(getPath(dtmi, repositoryUri, false));
43+
work.add(getModelPath(dtmi, repositoryUri, false));
4244
} catch (Exception e) {
4345
return Mono.error(new AzureException(e));
4446
}
@@ -52,13 +54,40 @@ public Mono<FetchResult> fetchAsync(String dtmi, URI repositoryUri, ModelDepende
5254
if (work.size() != 0) {
5355
return evaluatePath(work.poll(), context);
5456
} else {
57+
logger.error(String.format(StatusStrings.ERROR_FETCHING_MODEL_CONTENT, tryContentPath));
5558
return Mono.error(error);
5659
}
5760
})
58-
.map(s -> new FetchResult().setPath(tryContentPath).setDefinition(s));
61+
.map(s -> new FetchModelResult().setPath(tryContentPath).setDefinition(s));
5962
});
6063
}
6164

65+
@Override
66+
public Mono<FetchMetadataResult> fetchMetadataAsync(URI repositoryUri, Context context) {
67+
try {
68+
String tryContentPath = getMetadataPath(repositoryUri);
69+
70+
logger.info(StatusStrings.FETCHING_METADATA_CONTENT, tryContentPath);
71+
72+
return evaluatePath(tryContentPath, context)
73+
.onErrorResume(error -> {
74+
logger.error(String.format(StatusStrings.ERROR_FETCHING_METADATA_CONTENT + " Error: %s",
75+
tryContentPath, error.getMessage()));
76+
return Mono.error(error);
77+
})
78+
.map(s -> {
79+
try {
80+
return new FetchMetadataResult().setPath(tryContentPath).setDefinition(s);
81+
} catch (JsonProcessingException e) {
82+
logger.error(String.format(StatusStrings.ERROR_FETCHING_METADATA_CONTENT, tryContentPath));
83+
return null;
84+
}
85+
});
86+
} catch (MalformedURLException | URISyntaxException e) {
87+
return Mono.error(new AzureException(e));
88+
}
89+
}
90+
6291
private Mono<String> evaluatePath(String tryContentPath, Context context) {
6392
return protocolLayer
6493
.getModelsRepository()
@@ -69,7 +98,11 @@ private Mono<String> evaluatePath(String tryContentPath, Context context) {
6998
});
7099
}
71100

72-
private String getPath(String dtmi, URI repositoryUri, boolean expanded) throws URISyntaxException {
73-
return DtmiConventions.getModelUri(dtmi, repositoryUri, expanded).getPath();
101+
private String getModelPath(String dtmi, URI repositoryUri, boolean expanded) throws URISyntaxException {
102+
return DtmiConventions.getModelPath(dtmi, expanded);
103+
}
104+
105+
private String getMetadataPath(URI repositoryUri) throws URISyntaxException, MalformedURLException {
106+
return DtmiConventions.getMetadataUri(repositoryUri).getPath();
74107
}
75108
}

sdk/modelsrepository/azure-iot-modelsrepository/src/main/java/com/azure/iot/modelsrepository/implementation/IntermediateFetchResult.java renamed to sdk/modelsrepository/azure-iot-modelsrepository/src/main/java/com/azure/iot/modelsrepository/implementation/IntermediateFetchModelResult.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,25 @@
33

44
package com.azure.iot.modelsrepository.implementation;
55

6-
import com.azure.iot.modelsrepository.implementation.models.FetchResult;
6+
import com.azure.iot.modelsrepository.implementation.models.FetchModelResult;
77

88
import java.util.Map;
99

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

18-
IntermediateFetchResult(FetchResult fetchResult, Map<String, String> map) {
19-
this.fetchResult = fetchResult;
18+
IntermediateFetchModelResult(FetchModelResult fetchModelResult, Map<String, String> map) {
19+
this.fetchModelResult = fetchModelResult;
2020
this.map = map;
2121
}
2222

23-
public FetchResult getFetchResult() {
24-
return fetchResult;
23+
public FetchModelResult getFetchModelResult() {
24+
return fetchModelResult;
2525
}
2626

2727
public Map<String, String> getMap() {

sdk/modelsrepository/azure-iot-modelsrepository/src/main/java/com/azure/iot/modelsrepository/implementation/ModelFetcher.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
package com.azure.iot.modelsrepository.implementation;
55

66
import com.azure.core.util.Context;
7-
import com.azure.iot.modelsrepository.ModelDependencyResolution;
8-
import com.azure.iot.modelsrepository.implementation.models.FetchResult;
7+
import com.azure.iot.modelsrepository.implementation.models.FetchMetadataResult;
8+
import com.azure.iot.modelsrepository.implementation.models.FetchModelResult;
99
import reactor.core.publisher.Mono;
1010

1111
import java.net.URI;
1212

1313
interface ModelFetcher {
14-
Mono<FetchResult> fetchAsync(String dtmi, URI repositoryUri, ModelDependencyResolution resolutionOption, Context context);
14+
Mono<FetchModelResult> fetchModelAsync(String dtmi, URI repositoryUri, boolean tryFromExpanded, Context context);
15+
Mono<FetchMetadataResult> fetchMetadataAsync(URI repositoryUri, Context context);
1516
}

sdk/modelsrepository/azure-iot-modelsrepository/src/main/java/com/azure/iot/modelsrepository/implementation/ModelsRepositoryConstants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ public class ModelsRepositoryConstants {
99
public static final String JSON_EXTENSION = ".json";
1010
public static final String JSON_EXPANDED_EXTENSION = ".expanded.json";
1111
public static final String DEFAULT_MODELS_REPOSITORY_ENDPOINT = "https://devicemodels.azure.com";
12+
public static final String MODELS_REPOSITORY_METADATA_FILE = "metadata.json";
1213

1314
// DTDL conventions
1415
public static final String DTDL_TYPE = "@type";

0 commit comments

Comments
 (0)