Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import org.jsonschema2pojo.DefaultGenerationConfig;

import java.nio.charset.Charset;

public class PolyGenerationConfig extends DefaultGenerationConfig {

@Override
Expand All @@ -28,4 +30,9 @@ public boolean isUseLongIntegers() {
public boolean isIncludeAdditionalProperties() {
return false;
}

@Override
public String getOutputEncoding() {
return Charset.defaultCharset().name();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import org.jsonschema2pojo.SchemaMapper;

import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package io.polyapi.plugin.service.schema;

import com.fasterxml.jackson.databind.JsonNode;
import org.jsonschema2pojo.FragmentResolver;
import org.jsonschema2pojo.JsonPointerUtils;

import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

import static java.lang.String.format;
import static java.util.Arrays.asList;
import static org.apache.commons.lang3.StringUtils.split;

/**
* Class that copies parent {@link FragmentResolver} but that adds a {@link URLDecoder#decode(String, Charset)} to the part to evaluate it.
*/
public class PolyFragmentResolver extends FragmentResolver {
public JsonNode resolve(JsonNode tree, String path, String refFragmentPathDelimiters) {
return resolve(tree, new ArrayList<>(asList(split(path, refFragmentPathDelimiters))));
}

private JsonNode resolve(JsonNode tree, List<String> path) {
if (path.isEmpty()) {
return tree;
} else {
String part = path.remove(0);
if (tree.isArray()) {
try {
int index = Integer.parseInt(part);
return resolve(tree.get(index), path);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Not a valid array index: " + part);
}
}
String decodedPart = JsonPointerUtils.decodeReferenceToken(URLDecoder.decode(part.replace("+","%2B"), Charset.defaultCharset()));
if (tree.has(decodedPart)) {
return resolve(tree.get(decodedPart), path);
} else {
throw new IllegalArgumentException("Path not present: " + decodedPart);
}
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class PolyRuleFactory extends RuleFactory {
private NameHelper overwrittingNameHelper;

public PolyRuleFactory(GenerationConfig config) {
super(config, new Jackson2Annotator(config), new SchemaStore());
super(config, new Jackson2Annotator(config), new PolySchemaStore());
this.overwrittingNameHelper = new JsonSchemaNameHelper(config);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package io.polyapi.plugin.service.schema;

import com.fasterxml.jackson.databind.JsonNode;
import org.jsonschema2pojo.FragmentResolver;
import org.jsonschema2pojo.Schema;
import org.jsonschema2pojo.SchemaStore;

import java.net.URI;
import java.net.URISyntaxException;

import static org.apache.commons.lang3.StringUtils.*;

/**
* This class is a copy of the parent {@link SchemaStore} with the difference that uses a {@link PolyFragmentResolver} instead of a {@link FragmentResolver}.
*/
public class PolySchemaStore extends SchemaStore {
protected final FragmentResolver fragmentResolver = new PolyFragmentResolver();

/**
* Create or look up a new schema which has the given ID and read the
* contents of the given ID as a URL. If a schema with the given ID is
* already known, then a reference to the original schema will be returned.
*
* @param id the id of the schema being created
* @param refFragmentPathDelimiters A string containing any characters
* that should act as path delimiters when resolving $ref fragments.
* @return a schema object containing the contents of the given path
*/
public synchronized Schema create(URI id, String refFragmentPathDelimiters) {

URI normalizedId = id.normalize();

if (!schemas.containsKey(normalizedId)) {

URI baseId = removeFragment(id).normalize();
if (!schemas.containsKey(baseId)) {
logger.debug("Reading schema: " + baseId);
final JsonNode baseContent = contentResolver.resolve(baseId);
schemas.put(baseId, new Schema(baseId, baseContent, null));
}

final Schema baseSchema = schemas.get(baseId);
if (normalizedId.toString().contains("#")) {
JsonNode childContent = fragmentResolver.resolve(baseSchema.getContent(), '#' + id.getFragment(), refFragmentPathDelimiters);
schemas.put(normalizedId, new Schema(normalizedId, childContent, baseSchema));
}
}

return schemas.get(normalizedId);
}

/**
* Create or look up a new schema using the given schema as a parent and the
* path as a relative reference. If a schema with the given parent and
* relative path is already known, then a reference to the original schema
* will be returned.
*
* @param parent the schema which is the parent of the schema to be created.
* @param path the relative path of this schema (will be used to create a
* complete URI by resolving this path against the parent
* schema's id)
* @param refFragmentPathDelimiters A string containing any characters
* that should act as path delimiters when resolving $ref fragments.
* @return a schema object containing the contents of the given path
*/
@SuppressWarnings("PMD.UselessParentheses")
public Schema create(Schema parent, String path, String refFragmentPathDelimiters) {
if (!path.equals("#")) {
// if path is an empty string then resolving it below results in jumping up a level. e.g. "/path/to/file.json" becomes "/path/to"
path = stripEnd(path, "#?&/");
}

// encode the fragment for any funny characters
if (path.contains("#")) {
String pathExcludingFragment = substringBefore(path, "#");
String fragment = substringAfter(path, "#");
URI fragmentURI;
try {
fragmentURI = new URI(null, null, fragment);
} catch (URISyntaxException e) {
throw new IllegalArgumentException("Invalid fragment: " + fragment + " in path: " + path);
}
path = pathExcludingFragment + "#" + fragmentURI.getRawFragment();
}
URI id = (parent == null || parent.getId() == null) ? URI.create(path) : parent.getId().resolve(path);
String stringId = id.toString();
if (stringId.endsWith("#")) {
try {
id = new URI(stripEnd(stringId, "#"));
} catch (URISyntaxException e) {
throw new IllegalArgumentException("Bad path: " + stringId);
}
}
if (selfReferenceWithoutParentFile(parent, path) || substringBefore(stringId, "#").isEmpty()) {
JsonNode parentContent = parent.getGrandParent().getContent();
if (schemas.containsKey(id)) {
return schemas.get(id);
} else {
Schema schema = new Schema(id, fragmentResolver.resolve(parentContent, path, refFragmentPathDelimiters), parent.getGrandParent());
schemas.put(id, schema);
return schema;
}
}
return create(id, refFragmentPathDelimiters);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ public static Stream<Arguments> generateSource() {
createArguments(12, "Schema with enum with '-' in one of the options.", "Identifier", "TestResponse"),
createArguments(13, "Schema with different types that have the same enum.", "Identifier", DEFAULT_RESPONSE_NAME, "Data"),
createArguments(14, "Schema that is an Integer."),
createArguments(15, "Schema with multiple enums with the same name and properties.", DEFAULT_RESPONSE_NAME, "DashMinusstyle", "DashMinusstyle_", "Other"));
createArguments(15, "Schema with multiple enums with the same name and properties.", DEFAULT_RESPONSE_NAME, "DashMinusstyle", "DashMinusstyle_", "Other"),
createArguments(16, "Schema with json property yhat has a space in it.", "Data", DEFAULT_RESPONSE_NAME));
}

public static Stream<Arguments> getTypeSource() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"$schema": "http://json-schema.org/draft-06/schema#",
"definitions": {
"Data": {
"type": "object",
"additionalProperties": false,
"properties": {
"hell o": {
"type": "string"
}
},
"required": [
"hell o"
],
"title": "Data"
}
},
"type": "object",
"additionalProperties": false,
"properties": {
"data": {
"$ref": "#/definitions/Data"
}
},
"required": [
"data"
],
"title": "ResponseType"
}