Merge pull request #2992 from jimschubert/swagger_codegen_ignore_docs

[codegen ignore] Add tests and docs, fix two minor issues
This commit is contained in:
wing328 2016-05-30 21:34:12 +08:00
commit fb68f61a03
4 changed files with 184 additions and 12 deletions

View File

@ -408,6 +408,44 @@ java -Dapis -DmodelTests=false {opts}
When using selective generation, _only_ the templates needed for the specific generation will be used.
### Ignore file format
Swagger codegen supports a `.swagger-codegen-ignore` file, similar to `.gitignore` or `.dockerignore` you're probably already familiar with.
The ignore file allows for better control over overwriting existing files than the `--skip-overwrite` flag. With the ignore file, you can specify individual files or directories can be ignored. This can be useful, for example if you only want a subset of the generated code.
Examples:
```
# Swagger Codegen Ignore
# Lines beginning with a # are comments
# This should match build.sh located anywhere.
build.sh
# Matches build.sh in the root
/build.sh
# Exclude all recursively
docs/**
# Explicitly allow files excluded by other rules
!docs/UserApi.md
# Recursively exclude directories named Api
# You can't negate files below this directory.
src/**/Api/
# When this file is nested under /Api (excluded above),
# this rule is ignored because parent directory is excluded by previous rule.
!src/**/PetApiTests.cs
# Exclude a single, nested file explicitly
src/IO.Swagger.Test/Model/AnimalFarmTests.cs
```
The `.swagger-codegen-ignore` file must exist in the root of the output directory.
### Customizing the generator
There are different aspects of customizing the code generator beyond just creating or modifying templates. Each language has a supporting configuration file to handle different type mappings, etc:

View File

@ -1,20 +1,27 @@
package io.swagger.codegen.ignore.rules;
import java.nio.file.FileSystems;
import java.nio.file.PathMatcher;
import java.nio.file.*;
import java.util.List;
public class DirectoryRule extends FileRule {
private PathMatcher matcher = null;
private PathMatcher directoryMatcher = null;
private PathMatcher contentsMatcher = null;
DirectoryRule(List<Part> syntax, String definition) {
super(syntax, definition);
matcher = FileSystems.getDefault().getPathMatcher("glob:**/"+this.getPattern());
String pattern = this.getPattern();
StringBuilder sb = new StringBuilder();
sb.append("glob:");
sb.append(pattern);
if(!pattern.endsWith("/")) sb.append("/");
directoryMatcher = FileSystems.getDefault().getPathMatcher(sb.toString());
sb.append("**");
contentsMatcher = FileSystems.getDefault().getPathMatcher(sb.toString());
}
@Override
public Boolean matches(String relativePath) {
return matcher.matches(FileSystems.getDefault().getPath(relativePath));
return contentsMatcher.matches(FileSystems.getDefault().getPath(relativePath)) || directoryMatcher.matches(FileSystems.getDefault().getPath(relativePath));
}
}

View File

@ -78,6 +78,13 @@ public class IgnoreLineParser {
i++;
continue;
} else {
if (sb.length() > 0) {
// A MATCH_ANY may commonly follow a filename or some other character. Dump that to results before the MATCH_ANY.
parts.add(new Part(Token.TEXT, sb.toString()));
sb.delete(0, sb.length());
}
parts.add(new Part(Token.MATCH_ANY));
continue;
}

View File

@ -1,21 +1,141 @@
package io.swagger.codegen.ignore;
import org.testng.annotations.Test;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.annotations.*;
import static org.testng.Assert.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
public class CodegenIgnoreProcessorTest {
@Test
public void loadCodegenRules() throws Exception {
private static final Logger LOGGER = LoggerFactory.getLogger(CodegenIgnoreProcessorTest.class);
private Boolean allowed;
private final String filename;
private final String ignoreDefinition;
private final String description;
private String outputDir;
private File target;
private Path temp;
private CodegenIgnoreProcessorTest(String filename, String ignoreDefinition, String description) throws IOException {
this.filename = filename;
this.ignoreDefinition = ignoreDefinition;
this.description = description;
}
CodegenIgnoreProcessorTest allowed() {
this.allowed = true;
return this;
}
CodegenIgnoreProcessorTest ignored() {
this.allowed = false;
return this;
}
private void prepareTestFiles() throws IOException {
// NOTE: Each test needs its own directory because .swagger-codegen-ignore needs to exist at the root.
temp = Files.createTempDirectory(getClass().getSimpleName());
this.outputDir = temp.toFile().getAbsolutePath();
target = new File(this.outputDir, this.filename);
boolean mkdirs = target.getParentFile().mkdirs();
if(!mkdirs) {
LOGGER.warn("Failed to create directories for CodegenIgnoreProcessorTest test file. Directory may already exist.");
}
Path created = Files.createFile(target.toPath());
if(!created.toFile().exists()) {
throw new IOException("Failed to write CodegenIgnoreProcessorTest test file.");
}
// System.out.print(String.format("Created codegen ignore processor test file: %s\n", created.toAbsolutePath()));
File ignoreFile = new File(this.outputDir, ".swagger-codegen-ignore");
try (FileOutputStream stream = new FileOutputStream(ignoreFile)) {
stream.write(this.ignoreDefinition.getBytes());
}
}
@AfterTest
public void afterTest() throws IOException {
if(temp != null && temp.toFile().exists() && temp.toFile().isDirectory()) {
FileUtils.deleteDirectory(temp.toFile());
}
}
@Test
public void getInclusionRules() throws Exception {
public void evaluate() {
// Arrange
try {
// Lazily setup files to avoid conflicts and creation when these tests may not even run.
prepareTestFiles();
} catch (IOException e) {
e.printStackTrace();
fail("Failed to prepare test files. " + e.getMessage());
}
CodegenIgnoreProcessor processor = new CodegenIgnoreProcessor(outputDir);
Boolean actual = null;
// Act
actual = processor.allowsFile(target);
// Assert
assertEquals(actual, this.allowed, this.description);
}
@Test
public void getExclusionRules() throws Exception {
@Factory
public static Object[] factoryMethod() throws IOException {
return new Object[] {
// Matching filenames
new CodegenIgnoreProcessorTest("build.sh", "build.sh", "A file when matching should ignore.").ignored(),
new CodegenIgnoreProcessorTest("src/build.sh", "**/build.sh", "A file when matching nested files should ignore.").ignored(),
new CodegenIgnoreProcessorTest("Build.sh", "build.sh", "A file when non-matching should allow.").allowed(),
new CodegenIgnoreProcessorTest("build.sh", "/build.sh", "A rooted file when matching should ignore.").ignored(),
new CodegenIgnoreProcessorTest("nested/build.sh", "/build.sh", "A rooted file definition when non-matching should allow.").allowed(),
new CodegenIgnoreProcessorTest("src/IO.Swagger.Test/Model/AnimalFarmTests.cs", "src/IO.Swagger.Test/Model/AnimalFarmTests.cs", "A file when matching exactly should ignore.").ignored(),
// Matching spaces in filenames
new CodegenIgnoreProcessorTest("src/properly escaped.txt", "**/properly escaped.txt", "A file when matching nested files with spaces in the name should ignore.").ignored(),
new CodegenIgnoreProcessorTest("src/improperly escaped.txt", "**/improperly\\ escaped.txt", "A file when matching nested files with spaces in the name (improperly escaped rule) should allow.").allowed(),
// Match All
new CodegenIgnoreProcessorTest("docs/somefile.md", "docs/**", "A recursive file (0 level) when matching should ignore.").ignored(),
new CodegenIgnoreProcessorTest("docs/1/somefile.md", "docs/**", "A recursive file (1 level) when matching should ignore.").ignored(),
new CodegenIgnoreProcessorTest("docs/1/2/3/somefile.md", "docs/**", "A recursive file (n level) when matching should ignore.").ignored(),
// Match Any
new CodegenIgnoreProcessorTest("docs/1/2/3/somefile.md", "docs/**/somefile.*", "A recursive file with match-any extension when matching should ignore.").ignored(),
new CodegenIgnoreProcessorTest("docs/1/2/3/somefile.java", "docs/**/*.java", "A recursive file with match-any file name when matching should ignore.").ignored(),
new CodegenIgnoreProcessorTest("docs/1/2/3/4/somefile.md", "docs/**/*", "A recursive file with match-any file name when matching should ignore.").ignored(),
new CodegenIgnoreProcessorTest("docs/1/2/3/4/5/somefile.md", "docs/**/anyfile.*", "A recursive file with match-any extension when non-matching should allow.").allowed(),
// Directory matches
new CodegenIgnoreProcessorTest("docs/1/Users/a", "docs/**/Users/", "A directory rule when matching should be ignored.").ignored(),
new CodegenIgnoreProcessorTest("docs/1/Users1/a", "docs/**/Users/", "A directory rule when non-matching should be allowed.").allowed(),
// Negation of excluded recursive files
new CodegenIgnoreProcessorTest("docs/UserApi.md", "docs/**\n!docs/UserApi.md", "A pattern negating a previous ignore FILE rule should be allowed.").allowed(),
// Negation of excluded directories
new CodegenIgnoreProcessorTest("docs/1/Users/UserApi.md", "docs/**/Users/\n!docs/1/Users/UserApi.md", "A pattern negating a previous ignore DIRECTORY rule should be ignored.").ignored(),
// Other matches which may not be parsed for correctness, but are free because of PathMatcher
new CodegenIgnoreProcessorTest("docs/1/2/3/Some99File.md", "**/*[0-9]*", "A file when matching against simple regex patterns when matching should be ignored.").ignored(),
new CodegenIgnoreProcessorTest("docs/1/2/3/SomeFile.md", "**/*.{java,md}", "A file when matching against grouped subpatterns for extension when matching (md) should be ignored.").ignored(),
new CodegenIgnoreProcessorTest("docs/1/2/3/SomeFile.java", "**/*.{java,md}", "A file when matching against grouped subpatterns for extension when matching (java) should be ignored.").ignored(),
new CodegenIgnoreProcessorTest("docs/1/2/3/SomeFile.txt", "**/*.{java,md}", "A file when matching against grouped subpatterns for extension when non-matching should be allowed.").allowed(),
new CodegenIgnoreProcessorTest("docs/1/2/3/foo.c", "**/*.?", "A file when matching against required single-character extension when matching should be ignored.").ignored(),
new CodegenIgnoreProcessorTest("docs/1/2/3/foo.cc", "**/*.?", "A file when matching against required single-character extension when non-matching should be allowed.").allowed()
};
}
}