Skip to content

Commit

Permalink
sanitizes model names so they're valid class/interface names fixes #4097
Browse files Browse the repository at this point in the history
  • Loading branch information
snebjorn authored and wing328 committed Oct 15, 2019
1 parent fead3ee commit 5de0f01
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -426,17 +426,22 @@ private void fixStringModel(Schema m) {
* Generates a unique model name. Non-alphanumeric characters will be replaced
* with underscores
*
* e.g. io.schema.User_name => io_schema_User_name
*
* @param title String title field in the schema if present
* @param key String model name
*
* @return if provided the sanitized {@code title}, else the sanitized {@code key}
*/
private String resolveModelName(String title, String key) {
if (title == null) {
// for auto-generated schema name, replace non-alphanumeric characters with underscore
// to avoid bugs with schema look up with inline schema created on the fly
// e.g. io.schema.User_name => io_schema_User_name
return uniqueName(key).replaceAll("[^A-Za-z0-9]", "_");
if (key == null) {
LOGGER.warn("Found an inline schema without the `title` attribute. Default the model name to InlineObject instead. To have better control of the model naming, define the model separately so that it can be reused throughout the spec.");
return uniqueName("InlineObject");
}
return uniqueName(sanitizeName(key));
} else {
return uniqueName(title);
return uniqueName(sanitizeName(title));
}
}

Expand All @@ -452,29 +457,31 @@ private void addGenerated(String name, Schema model) {
generatedSignature.put(Json.pretty(model), name);
}

private String uniqueName(String key) {
if (key == null) {
key = "InlineObject";
LOGGER.warn("Found an inline schema without the `title` attribute. Default the model name to InlineObject instead. To have better control of the model naming, define the model separately so that it can be reused throughout the spec.");
/**
* Sanitizes the input so that it's valid name for a class or interface
*
* e.g. 12.schema.User name => _2_schema_User_name
*/
private String sanitizeName(final String name) {
return name
.replaceAll("^[0-9]", "_") // e.g. 12object => _2object
.replaceAll("[^A-Za-z0-9]", "_"); // e.g. io.schema.User name => io_schema_User_name
}

private String uniqueName(final String name) {
if (openapi.getComponents().getSchemas() == null) {
return name;
}

String uniqueName = name;
int count = 0;
boolean done = false;
key = key.replaceAll("/", "_"); // e.g. /me/videos => _me_videos
key = key.replaceAll("[^a-z_\\.A-Z0-9 ]", ""); // FIXME: a parameter
// should not be assigned. Also declare the methods parameters as 'final'.
while (!done) {
String name = key;
if (count > 0) {
name = key + "_" + count;
}
if (openapi.getComponents().getSchemas() == null) {
return name;
} else if (!openapi.getComponents().getSchemas().containsKey(name)) {
return name;
while (true) {
if (!openapi.getComponents().getSchemas().containsKey(uniqueName)) {
return uniqueName;
}
count += 1;
uniqueName = name + "_" + ++count;
}
return key;
// TODO it would probably be a good idea to check against a list of used uniqueNames to make sure there are no collisions
}

private void flattenProperties(Map<String, Schema> properties, String path) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,35 @@ public void resolveInlineModelTestWithTitle() {
assertNotNull(address.getProperties().get("street"));
}

@Test
public void resolveInlineModelTestWithTitleWithSpaces() {
OpenAPI openapi = new OpenAPI();
openapi.setComponents(new Components());
openapi.getComponents().addSchemas("User", new ObjectSchema()
.name("user")
.description("a common user")
.addProperties("name", new StringSchema())
.addProperties("address", new ObjectSchema()
.title("User Address Title")
.readOnly(false)
.description("description")
.name("name")
.addProperties("street", new StringSchema())
.addProperties("city", new StringSchema())));

new InlineModelResolver().flatten(openapi);

Schema user = openapi.getComponents().getSchemas().get("User");

assertNotNull(user);
assertTrue(user.getProperties().get("address") instanceof Schema);

Schema address = openapi.getComponents().getSchemas().get("User_Address_Title");
assertNotNull(address);
assertNotNull(address.getProperties().get("city"));
assertNotNull(address.getProperties().get("street"));
}

@Test
public void resolveInlineModel2EqualInnerModels() {
OpenAPI openapi = new OpenAPI();
Expand Down

0 comments on commit 5de0f01

Please sign in to comment.