1 package io.apicurio.registry.maven.refs;
2
3 import com.fasterxml.jackson.databind.JsonNode;
4 import com.fasterxml.jackson.databind.ObjectMapper;
5 import com.fasterxml.jackson.databind.node.ObjectNode;
6 import io.apicurio.datamodels.Library;
7 import io.apicurio.datamodels.models.Document;
8 import io.apicurio.datamodels.util.ModelTypeUtil;
9 import io.apicurio.registry.content.ContentHandle;
10 import io.apicurio.registry.content.TypedContent;
11 import io.apicurio.registry.content.util.ContentTypeUtil;
12 import io.apicurio.registry.rest.client.models.VersionMetaData;
13 import io.apicurio.registry.types.ArtifactType;
14 import io.apicurio.registry.types.ContentTypes;
15 import io.apicurio.registry.utils.protobuf.schema.ProtobufFile;
16
17 import java.io.IOException;
18 import java.nio.file.Path;
19 import java.util.HashSet;
20 import java.util.Set;
21
22
23
24
25
26
27
28
29
30
31 public class ReferenceIndex {
32
33 private static final ObjectMapper mapper = new ObjectMapper();
34
35 private Set<IndexedResource> index = new HashSet<>();
36 private Set<Path> schemaPaths = new HashSet<>();
37
38
39
40
41 public ReferenceIndex() {
42 }
43
44
45
46
47
48
49 public ReferenceIndex(Path schemaPath) {
50 this.schemaPaths.add(schemaPath);
51 }
52
53
54
55
56 public void addSchemaPath(Path path) {
57 this.schemaPaths.add(path);
58 }
59
60
61
62
63
64
65
66 public IndexedResource lookup(String resourceName, Path relativeToFile) {
67 return index.stream().filter(resource -> resource.matches(resourceName, relativeToFile, schemaPaths))
68 .findFirst().orElse(null);
69 }
70
71
72
73
74
75
76
77 public void index(String resourceName, VersionMetaData vmd) {
78 IndexedResource res = new IndexedResource(null, null, resourceName, null);
79 res.setRegistration(vmd);
80 this.index.add(res);
81 }
82
83
84
85
86
87
88
89 public void index(Path path, ContentHandle content) {
90 try {
91
92 String contentType = ContentTypes.APPLICATION_JSON;
93 String fileName = path.getFileName().toString().toLowerCase();
94 if (fileName.endsWith(".yaml") || fileName.endsWith(".yml")) {
95 contentType = ContentTypes.APPLICATION_YAML;
96 }
97
98
99 TypedContent typedContent = TypedContent.create(content, contentType);
100 JsonNode tree = ContentTypeUtil.parseJsonOrYaml(typedContent);
101
102
103 if (tree.has("openapi") || tree.has("swagger") || tree.has("asyncapi")
104 || tree.has("openrpc")) {
105 indexDataModels(path, content);
106 }
107
108 if (tree.has("$schema") && !tree.get("$schema").isNull()) {
109 indexJsonSchema(tree, path, content);
110 }
111
112 indexAvro(path, content, tree);
113 } catch (Exception e) {
114
115 }
116
117 try {
118 indexProto(path, content);
119 return;
120 } catch (Exception e) {
121
122 }
123 }
124
125 private void indexAvro(Path path, ContentHandle content, JsonNode parsed) {
126
127 String ns = parsed.get("namespace").asText();
128 String name = parsed.get("name").asText();
129 String resourceName = ns != null ? ns + "." + name : name;
130 IndexedResource resource = new IndexedResource(path, ArtifactType.AVRO, resourceName, content);
131 this.index.add(resource);
132 }
133
134 private void indexProto(Path path, ContentHandle content) {
135 ProtobufFile.toProtoFileElement(content.content());
136
137 IndexedResource resource = new IndexedResource(path, ArtifactType.PROTOBUF, null, content);
138 this.index.add(resource);
139 }
140
141 private void indexJsonSchema(JsonNode schema, Path path, ContentHandle content) {
142 String resourceName = null;
143 if (schema.has("$id")) {
144 resourceName = schema.get("$id").asText(null);
145 }
146 IndexedResource resource = new IndexedResource(path, ArtifactType.JSON, resourceName, content);
147 this.index.add(resource);
148 }
149
150 private void indexDataModels(Path path, ContentHandle content) {
151 try {
152
153 String contentType = ContentTypes.APPLICATION_JSON;
154 String fileName = path.getFileName().toString().toLowerCase();
155 if (fileName.endsWith(".yaml") || fileName.endsWith(".yml")) {
156 contentType = ContentTypes.APPLICATION_YAML;
157 }
158
159
160 TypedContent typedContent = TypedContent.create(content, contentType);
161 JsonNode node = ContentTypeUtil.parseJsonOrYaml(typedContent);
162 Document doc = Library.readDocument((ObjectNode) node);
163
164 if (doc == null) {
165 throw new UnsupportedOperationException(
166 "Content is not OpenAPI, AsyncAPI, or OpenRPC.");
167 }
168
169 String type = ArtifactType.OPENAPI;
170 if (ModelTypeUtil.isAsyncApiModel(doc)) {
171 type = ArtifactType.ASYNCAPI;
172 } else if (ModelTypeUtil.isOpenRpcModel(doc)) {
173 type = ArtifactType.OPENRPC;
174 }
175
176 IndexedResource resource = new IndexedResource(path, type, null, content);
177 this.index.add(resource);
178 } catch (IOException e) {
179 throw new RuntimeException("Failed to parse OpenAPI/AsyncAPI document", e);
180 }
181 }
182
183 }