1 package io.apicurio.registry.maven;
2
3 import com.fasterxml.jackson.core.JsonProcessingException;
4 import io.apicurio.registry.content.ContentHandle;
5 import io.apicurio.registry.content.TypedContent;
6 import io.apicurio.registry.rest.client.RegistryClient;
7 import io.apicurio.registry.rest.client.models.ArtifactReference;
8 import io.apicurio.registry.rules.compatibility.jsonschema.JsonUtil;
9 import io.apicurio.registry.types.ContentTypes;
10 import org.everit.json.schema.ArraySchema;
11 import org.everit.json.schema.ObjectSchema;
12 import org.everit.json.schema.ReferenceSchema;
13 import org.everit.json.schema.Schema;
14 import org.slf4j.Logger;
15 import org.slf4j.LoggerFactory;
16
17 import java.io.File;
18 import java.io.FileNotFoundException;
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.Collections;
22 import java.util.HashMap;
23 import java.util.HashSet;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Objects;
27 import java.util.Set;
28 import java.util.concurrent.ExecutionException;
29 import java.util.stream.Collectors;
30
31 public class JsonSchemaDirectoryParser extends AbstractDirectoryParser<Schema> {
32
33 private static final String JSON_SCHEMA_EXTENSION = ".json";
34 private static final Logger log = LoggerFactory.getLogger(JsonSchemaDirectoryParser.class);
35
36 public JsonSchemaDirectoryParser(RegistryClient client) {
37 super(client);
38 }
39
40 @Override
41 public ParsedDirectoryWrapper<Schema> parse(File rootSchemaFile) {
42 return parseDirectory(rootSchemaFile.getParentFile(), rootSchemaFile);
43 }
44
45 @Override
46 public List<ArtifactReference> handleSchemaReferences(RegisterArtifact rootArtifact,
47 org.everit.json.schema.Schema rootSchema, Map<String, TypedContent> fileContents)
48 throws FileNotFoundException, ExecutionException, InterruptedException {
49
50 if (rootSchema instanceof ObjectSchema) {
51
52 ObjectSchema objectSchema = (ObjectSchema) rootSchema;
53 Set<ArtifactReference> references = new HashSet<>();
54
55 Map<String, org.everit.json.schema.Schema> rootSchemaPropertySchemas = objectSchema
56 .getPropertySchemas();
57
58 for (String schemaKey : rootSchemaPropertySchemas.keySet()) {
59
60 List<ArtifactReference> nestedArtifactReferences = new ArrayList<>();
61
62 if (rootSchemaPropertySchemas.get(schemaKey) instanceof ReferenceSchema) {
63
64 ReferenceSchema nestedSchema = (ReferenceSchema) rootSchemaPropertySchemas.get(schemaKey);
65 RegisterArtifact nestedRegisterArtifact = buildFromRoot(rootArtifact,
66 nestedSchema.getSchemaLocation());
67
68 if (nestedSchema.getReferredSchema() instanceof ObjectSchema) {
69 ObjectSchema nestedObjectSchema = (ObjectSchema) nestedSchema.getReferredSchema();
70 nestedArtifactReferences = handleSchemaReferences(nestedRegisterArtifact,
71 nestedObjectSchema, fileContents);
72 }
73
74 references.add(registerNestedSchema(nestedSchema.getSchemaLocation(),
75 nestedArtifactReferences, nestedRegisterArtifact,
76 fileContents.get(nestedSchema.getSchemaLocation()).getContent().content()));
77
78 } else if (rootSchemaPropertySchemas.get(schemaKey) instanceof ArraySchema) {
79
80 final ArraySchema arraySchema = (ArraySchema) rootSchemaPropertySchemas.get(schemaKey);
81 if (arraySchema.getAllItemSchema() instanceof ReferenceSchema) {
82
83 ReferenceSchema arrayElementSchema = (ReferenceSchema) arraySchema.getAllItemSchema();
84 RegisterArtifact nestedRegisterArtifact = buildFromRoot(rootArtifact,
85 arrayElementSchema.getSchemaLocation());
86
87 if (arrayElementSchema.getReferredSchema() instanceof ObjectSchema) {
88
89 nestedArtifactReferences = handleSchemaReferences(nestedRegisterArtifact,
90 arrayElementSchema, fileContents);
91 }
92 references.add(registerNestedSchema(arrayElementSchema.getSchemaLocation(),
93 nestedArtifactReferences, nestedRegisterArtifact, fileContents
94 .get(arrayElementSchema.getSchemaLocation()).getContent().content()));
95 }
96 }
97 }
98 return new ArrayList<>(references);
99 } else {
100 return Collections.emptyList();
101 }
102 }
103
104 private JsonSchemaWrapper parseDirectory(File directory, File rootSchema) {
105 Set<File> typesToAdd = Arrays
106 .stream(Objects.requireNonNull(
107 directory.listFiles((dir, name) -> name.endsWith(JSON_SCHEMA_EXTENSION))))
108 .filter(file -> !file.getName().equals(rootSchema.getName())).collect(Collectors.toSet());
109
110 Map<String, Schema> processed = new HashMap<>();
111 Map<String, TypedContent> schemaContents = new HashMap<>();
112
113 while (processed.size() != typesToAdd.size()) {
114 boolean fileParsed = false;
115 for (File typeToAdd : typesToAdd) {
116 if (typeToAdd.getName().equals(rootSchema.getName())) {
117 continue;
118 }
119 try {
120 final ContentHandle schemaContent = readSchemaContent(typeToAdd);
121 final TypedContent typedSchemaContent = TypedContent.create(schemaContent,
122 ContentTypes.APPLICATION_JSON);
123 final Schema schema = JsonUtil.readSchema(schemaContent.content(), schemaContents, false);
124 processed.put(schema.getId(), schema);
125 schemaContents.put(schema.getId(), typedSchemaContent);
126 fileParsed = true;
127 } catch (JsonProcessingException ex) {
128 log.warn(
129 "Error processing json schema with name {}. This usually means that the references are not ready yet to parse it",
130 typeToAdd.getName());
131 }
132 }
133
134
135
136 if (!fileParsed) {
137 throw new IllegalStateException(
138 "Error found in the directory structure. Check that all required files are present.");
139 }
140 }
141
142 try {
143 return new JsonSchemaWrapper(
144 JsonUtil.readSchema(readSchemaContent(rootSchema).content(), schemaContents, false),
145 schemaContents);
146 } catch (JsonProcessingException e) {
147 throw new RuntimeException("Unable to parse main schema", e);
148 }
149 }
150
151 public static class JsonSchemaWrapper implements ParsedDirectoryWrapper<Schema> {
152 final Schema schema;
153 final Map<String, TypedContent> fileContents;
154
155 public JsonSchemaWrapper(Schema schema, Map<String, TypedContent> fileContents) {
156 this.schema = schema;
157 this.fileContents = fileContents;
158 }
159
160 @Override
161 public Schema getSchema() {
162 return schema;
163 }
164
165 @Override
166 public Map<String, TypedContent> getSchemaContents() {
167 return fileContents;
168 }
169 }
170
171 }