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 indexDataModels(path, content);
105 }
106
107 if (tree.has("$schema") && !tree.get("$schema").isNull()) {
108 indexJsonSchema(tree, path, content);
109 }
110
111 indexAvro(path, content, tree);
112 } catch (Exception e) {
113
114 }
115
116 try {
117 indexProto(path, content);
118 return;
119 } catch (Exception e) {
120
121 }
122 }
123
124 private void indexAvro(Path path, ContentHandle content, JsonNode parsed) {
125
126 String ns = parsed.get("namespace").asText();
127 String name = parsed.get("name").asText();
128 String resourceName = ns != null ? ns + "." + name : name;
129 IndexedResource resource = new IndexedResource(path, ArtifactType.AVRO, resourceName, content);
130 this.index.add(resource);
131 }
132
133 private void indexProto(Path path, ContentHandle content) {
134 ProtobufFile.toProtoFileElement(content.content());
135
136 IndexedResource resource = new IndexedResource(path, ArtifactType.PROTOBUF, null, content);
137 this.index.add(resource);
138 }
139
140 private void indexJsonSchema(JsonNode schema, Path path, ContentHandle content) {
141 String resourceName = null;
142 if (schema.has("$id")) {
143 resourceName = schema.get("$id").asText(null);
144 }
145 IndexedResource resource = new IndexedResource(path, ArtifactType.JSON, resourceName, content);
146 this.index.add(resource);
147 }
148
149 private void indexDataModels(Path path, ContentHandle content) {
150 try {
151
152 String contentType = ContentTypes.APPLICATION_JSON;
153 String fileName = path.getFileName().toString().toLowerCase();
154 if (fileName.endsWith(".yaml") || fileName.endsWith(".yml")) {
155 contentType = ContentTypes.APPLICATION_YAML;
156 }
157
158
159 TypedContent typedContent = TypedContent.create(content, contentType);
160 JsonNode node = ContentTypeUtil.parseJsonOrYaml(typedContent);
161 Document doc = Library.readDocument((ObjectNode) node);
162
163 if (doc == null) {
164 throw new UnsupportedOperationException("Content is not OpenAPI or AsyncAPI.");
165 }
166
167 String type = ArtifactType.OPENAPI;
168 if (ModelTypeUtil.isAsyncApiModel(doc)) {
169 type = ArtifactType.ASYNCAPI;
170 }
171
172 IndexedResource resource = new IndexedResource(path, type, null, content);
173 this.index.add(resource);
174 } catch (IOException e) {
175 throw new RuntimeException("Failed to parse OpenAPI/AsyncAPI document", e);
176 }
177 }
178
179 }